blob: f88cd96ef538bf8c7771d0948caaf674f0fa0f99 [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( \
glennrp847370c2011-07-05 17:37:15 +0000241 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
242 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000243 }
cristy4c08aed2011-07-01 19:47:50 +0000244#define LBR02Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000245 { \
cristy4c08aed2011-07-01 19:47:50 +0000246 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000247 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000248 SetPixelGreen(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000249 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
250 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000251 }
cristy4c08aed2011-07-01 19:47:50 +0000252#define LBR02Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000253 { \
254 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000255 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
256 SetPixelBlue(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000257 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
258 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000259 }
cristy4c08aed2011-07-01 19:47:50 +0000260#define LBR02Alpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000261 { \
262 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000263 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
264 SetPixelAlpha(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000265 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
266 (pixel) ); \
glennrp8e58efd2011-05-20 12:16:29 +0000267 }
268
cristy4c08aed2011-07-01 19:47:50 +0000269#define LBR02RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000270 { \
cristyef618312011-06-25 12:26:44 +0000271 LBR02PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000272 LBR02Green((pixel)); \
273 LBR02Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000274 }
glennrp8e58efd2011-05-20 12:16:29 +0000275
cristy4c08aed2011-07-01 19:47:50 +0000276#define LBR02RGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000277 { \
cristy4c08aed2011-07-01 19:47:50 +0000278 LBR02RGB((pixel)); \
279 LBR02Alpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000280 }
glennrp8e58efd2011-05-20 12:16:29 +0000281
282/* LBR03: Replicate top 3 bits (only used with opaque pixels during
283 PNG8 quantization) */
284
cristy4c08aed2011-07-01 19:47:50 +0000285#define LBR03PixelPacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000286 { \
287 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
288 (pixelpacket).red=ScaleCharToQuantum( \
289 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
290 }
glennrp91d99252011-06-25 14:30:13 +0000291#define LBR03PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000292 { \
293 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
294 (pixelpacket).green=ScaleCharToQuantum( \
295 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
296 }
glennrp91d99252011-06-25 14:30:13 +0000297#define LBR03PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000298 { \
299 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
300 (pixelpacket).blue=ScaleCharToQuantum( \
301 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
302 }
303
glennrp91d99252011-06-25 14:30:13 +0000304#define LBR03PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000305 { \
cristy4c08aed2011-07-01 19:47:50 +0000306 LBR03PixelPacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000307 LBR03PacketGreen((pixelpacket)); \
308 LBR03PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000309 }
glennrp8e58efd2011-05-20 12:16:29 +0000310
cristyef618312011-06-25 12:26:44 +0000311#define LBR03PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000312 { \
cristy4c08aed2011-07-01 19:47:50 +0000313 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000314 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000315 SetPixelRed(image, ScaleCharToQuantum( \
316 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000317 }
cristy4c08aed2011-07-01 19:47:50 +0000318#define LBR03Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000319 { \
cristy4c08aed2011-07-01 19:47:50 +0000320 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000321 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000322 SetPixelGreen(image, ScaleCharToQuantum( \
323 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000324 }
cristy4c08aed2011-07-01 19:47:50 +0000325#define LBR03Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000326 { \
cristy4c08aed2011-07-01 19:47:50 +0000327 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000328 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000329 SetPixelBlue(image, ScaleCharToQuantum( \
330 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000331 }
332
cristy4c08aed2011-07-01 19:47:50 +0000333#define LBR03RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000334 { \
cristyef618312011-06-25 12:26:44 +0000335 LBR03PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000336 LBR03Green((pixel)); \
337 LBR03Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000338 }
glennrp8e58efd2011-05-20 12:16:29 +0000339
340/* LBR04: Replicate top 4 bits */
341
cristy4c08aed2011-07-01 19:47:50 +0000342#define LBR04PixelPacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000343 { \
344 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
345 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
346 }
glennrp91d99252011-06-25 14:30:13 +0000347#define LBR04PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000348 { \
349 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
350 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
351 }
glennrp91d99252011-06-25 14:30:13 +0000352#define LBR04PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000353 { \
354 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
355 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
356 }
cristy4c08aed2011-07-01 19:47:50 +0000357#define LBR04PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000358 { \
cristy4c08aed2011-07-01 19:47:50 +0000359 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
360 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
glennrp8e58efd2011-05-20 12:16:29 +0000361 }
362
glennrp91d99252011-06-25 14:30:13 +0000363#define LBR04PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000364 { \
cristy4c08aed2011-07-01 19:47:50 +0000365 LBR04PixelPacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000366 LBR04PacketGreen((pixelpacket)); \
367 LBR04PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000368 }
glennrp8e58efd2011-05-20 12:16:29 +0000369
glennrp91d99252011-06-25 14:30:13 +0000370#define LBR04PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000371 { \
glennrp91d99252011-06-25 14:30:13 +0000372 LBR04PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000373 LBR04PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000374 }
glennrp8e58efd2011-05-20 12:16:29 +0000375
cristyef618312011-06-25 12:26:44 +0000376#define LBR04PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000377 { \
cristy4c08aed2011-07-01 19:47:50 +0000378 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000379 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000380 SetPixelRed(image,\
381 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000382 }
cristy4c08aed2011-07-01 19:47:50 +0000383#define LBR04Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000384 { \
cristy4c08aed2011-07-01 19:47:50 +0000385 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000386 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000387 SetPixelGreen(image,\
388 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000389 }
cristy4c08aed2011-07-01 19:47:50 +0000390#define LBR04Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000391 { \
392 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000393 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
394 SetPixelBlue(image,\
395 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000396 }
cristy4c08aed2011-07-01 19:47:50 +0000397#define LBR04Alpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000398 { \
399 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000400 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
401 SetPixelAlpha(image,\
402 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000403 }
404
cristy4c08aed2011-07-01 19:47:50 +0000405#define LBR04RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000406 { \
cristyef618312011-06-25 12:26:44 +0000407 LBR04PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000408 LBR04Green((pixel)); \
409 LBR04Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000410 }
glennrp8e58efd2011-05-20 12:16:29 +0000411
cristy4c08aed2011-07-01 19:47:50 +0000412#define LBR04RGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000413 { \
cristy4c08aed2011-07-01 19:47:50 +0000414 LBR04RGB((pixel)); \
415 LBR04Alpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000416 }
glennrp8e58efd2011-05-20 12:16:29 +0000417
418
419/* LBR08: Replicate top 8 bits */
420
cristy4c08aed2011-07-01 19:47:50 +0000421#define LBR08PixelPacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000422 { \
423 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
424 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
425 }
glennrp91d99252011-06-25 14:30:13 +0000426#define LBR08PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000427 { \
428 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
429 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
430 }
glennrp91d99252011-06-25 14:30:13 +0000431#define LBR08PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000432 { \
433 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
434 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
435 }
cristy4c08aed2011-07-01 19:47:50 +0000436#define LBR08PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000437 { \
cristy4c08aed2011-07-01 19:47:50 +0000438 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
439 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000440 }
441
glennrp91d99252011-06-25 14:30:13 +0000442#define LBR08PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000443 { \
cristy4c08aed2011-07-01 19:47:50 +0000444 LBR08PixelPacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000445 LBR08PacketGreen((pixelpacket)); \
446 LBR08PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000447 }
glennrp8e58efd2011-05-20 12:16:29 +0000448
glennrp91d99252011-06-25 14:30:13 +0000449#define LBR08PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000450 { \
glennrp91d99252011-06-25 14:30:13 +0000451 LBR08PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000452 LBR08PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000453 }
glennrp8e58efd2011-05-20 12:16:29 +0000454
cristyef618312011-06-25 12:26:44 +0000455#define LBR08PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000456 { \
457 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000458 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
459 SetPixelRed(image,\
460 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000461 }
cristy4c08aed2011-07-01 19:47:50 +0000462#define LBR08Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000463 { \
464 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000465 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
466 SetPixelGreen(image,\
467 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000468 }
cristy4c08aed2011-07-01 19:47:50 +0000469#define LBR08Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000470 { \
471 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000472 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
473 SetPixelBlue(image,\
474 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000475 }
cristy4c08aed2011-07-01 19:47:50 +0000476#define LBR08Alpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000477 { \
478 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000479 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
480 SetPixelAlpha(image,\
481 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000482 }
483
cristy4c08aed2011-07-01 19:47:50 +0000484#define LBR08RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000485 { \
cristyef618312011-06-25 12:26:44 +0000486 LBR08PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000487 LBR08Green((pixel)); \
488 LBR08Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000489 }
glennrp8e58efd2011-05-20 12:16:29 +0000490
cristy4c08aed2011-07-01 19:47:50 +0000491#define LBR08RGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000492 { \
cristy4c08aed2011-07-01 19:47:50 +0000493 LBR08RGB((pixel)); \
494 LBR08Alpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000495 }
glennrp8e58efd2011-05-20 12:16:29 +0000496
497
498/* LBR16: Replicate top 16 bits */
499
cristy4c08aed2011-07-01 19:47:50 +0000500#define LBR16PixelPacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000501 { \
502 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
503 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
504 }
glennrp91d99252011-06-25 14:30:13 +0000505#define LBR16PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000506 { \
507 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
508 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
509 }
glennrp91d99252011-06-25 14:30:13 +0000510#define LBR16PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000511 { \
512 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
513 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
514 }
cristy4c08aed2011-07-01 19:47:50 +0000515#define LBR16PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000516 { \
cristy4c08aed2011-07-01 19:47:50 +0000517 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
518 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000519 }
520
glennrp91d99252011-06-25 14:30:13 +0000521#define LBR16PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000522 { \
cristy4c08aed2011-07-01 19:47:50 +0000523 LBR16PixelPacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000524 LBR16PacketGreen((pixelpacket)); \
525 LBR16PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000526 }
glennrp8e58efd2011-05-20 12:16:29 +0000527
glennrp91d99252011-06-25 14:30:13 +0000528#define LBR16PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000529 { \
glennrp91d99252011-06-25 14:30:13 +0000530 LBR16PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000531 LBR16PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000532 }
glennrp8e58efd2011-05-20 12:16:29 +0000533
cristyef618312011-06-25 12:26:44 +0000534#define LBR16PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000535 { \
536 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000537 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
538 SetPixelRed(image,\
539 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000540 }
cristy4c08aed2011-07-01 19:47:50 +0000541#define LBR16Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000542 { \
543 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000544 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
545 SetPixelGreen(image,\
546 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000547 }
cristy4c08aed2011-07-01 19:47:50 +0000548#define LBR16Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000549 { \
550 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000551 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
552 SetPixelBlue(image,\
553 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000554 }
cristy4c08aed2011-07-01 19:47:50 +0000555#define LBR16Alpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000556 { \
557 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000558 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
559 SetPixelAlpha(image,\
560 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000561 }
562
cristy4c08aed2011-07-01 19:47:50 +0000563#define LBR16RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000564 { \
cristyef618312011-06-25 12:26:44 +0000565 LBR16PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000566 LBR16Green((pixel)); \
567 LBR16Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000568 }
glennrp8e58efd2011-05-20 12:16:29 +0000569
cristy4c08aed2011-07-01 19:47:50 +0000570#define LBR16RGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000571 { \
cristy4c08aed2011-07-01 19:47:50 +0000572 LBR16RGB((pixel)); \
573 LBR16Alpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000574 }
glennrp8e58efd2011-05-20 12:16:29 +0000575
cristy3ed852e2009-09-05 21:47:34 +0000576/*
577 Establish thread safety.
578 setjmp/longjmp is claimed to be safe on these platforms:
579 setjmp/longjmp is alleged to be unsafe on these platforms:
580*/
581#ifndef SETJMP_IS_THREAD_SAFE
582#define PNG_SETJMP_NOT_THREAD_SAFE
583#endif
584
585#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
586static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000587 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000588#endif
589
590/*
591 This temporary until I set up malloc'ed object attributes array.
592 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
593 waste more memory.
594*/
595#define MNG_MAX_OBJECTS 256
596
597/*
598 If this not defined, spec is interpreted strictly. If it is
599 defined, an attempt will be made to recover from some errors,
600 including
601 o global PLTE too short
602*/
603#undef MNG_LOOSE
604
605/*
606 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
607 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
608 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
609 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
610 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
611 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
612 will be enabled by default in libpng-1.2.0.
613*/
cristy3ed852e2009-09-05 21:47:34 +0000614#ifdef PNG_MNG_FEATURES_SUPPORTED
615# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
616# define PNG_READ_EMPTY_PLTE_SUPPORTED
617# endif
618# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
619# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
620# endif
621#endif
622
623/*
cristybb503372010-05-27 20:51:26 +0000624 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000625 This macro is only defined in libpng-1.0.3 and later.
626 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
627*/
628#ifndef PNG_UINT_31_MAX
629#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
630#endif
631
632/*
633 Constant strings for known chunk types. If you need to add a chunk,
634 add a string holding the name here. To make the code more
635 portable, we use ASCII numbers like this, not characters.
636*/
637
638static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
639static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
640static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
641static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
642static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
643static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
644static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
645static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
646static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
647static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
648static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
649static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
650static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
651static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
652static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
653static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
654static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
655static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
656static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
657static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
658static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
659static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
660static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
661static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
662static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
663static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
664static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
665static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
666static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
667static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
668static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
669static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
670static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
671static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
672
673#if defined(JNG_SUPPORTED)
674static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
675static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
676static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
677static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
678static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
679static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
680#endif
681
682/*
683Other known chunks that are not yet supported by ImageMagick:
684static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
685static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
686static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
687static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
688static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
689static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
690static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
691static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
692*/
693
694typedef struct _MngBox
695{
cristy8182b072010-05-30 20:10:53 +0000696 long
cristy3ed852e2009-09-05 21:47:34 +0000697 left,
698 right,
699 top,
700 bottom;
701} MngBox;
702
703typedef struct _MngPair
704{
cristy8182b072010-05-30 20:10:53 +0000705 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000706 a,
707 b;
708} MngPair;
709
710#ifdef MNG_OBJECT_BUFFERS
711typedef struct _MngBuffer
712{
713
cristybb503372010-05-27 20:51:26 +0000714 size_t
cristy3ed852e2009-09-05 21:47:34 +0000715 height,
716 width;
717
718 Image
719 *image;
720
721 png_color
722 plte[256];
723
724 int
725 reference_count;
726
727 unsigned char
728 alpha_sample_depth,
729 compression_method,
730 color_type,
731 concrete,
732 filter_method,
733 frozen,
734 image_type,
735 interlace_method,
736 pixel_sample_depth,
737 plte_length,
738 sample_depth,
739 viewable;
740} MngBuffer;
741#endif
742
743typedef struct _MngInfo
744{
745
746#ifdef MNG_OBJECT_BUFFERS
747 MngBuffer
748 *ob[MNG_MAX_OBJECTS];
749#endif
750
751 Image *
752 image;
753
754 RectangleInfo
755 page;
756
757 int
758 adjoin,
759#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
760 bytes_in_read_buffer,
761 found_empty_plte,
762#endif
763 equal_backgrounds,
764 equal_chrms,
765 equal_gammas,
766#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
767 defined(PNG_MNG_FEATURES_SUPPORTED)
768 equal_palettes,
769#endif
770 equal_physs,
771 equal_srgbs,
772 framing_mode,
773 have_global_bkgd,
774 have_global_chrm,
775 have_global_gama,
776 have_global_phys,
777 have_global_sbit,
778 have_global_srgb,
779 have_saved_bkgd_index,
780 have_write_global_chrm,
781 have_write_global_gama,
782 have_write_global_plte,
783 have_write_global_srgb,
784 need_fram,
785 object_id,
786 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000787 saved_bkgd_index;
788
789 int
790 new_number_colors;
791
cristybb503372010-05-27 20:51:26 +0000792 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000793 image_found,
794 loop_count[256],
795 loop_iteration[256],
796 scenes_found,
797 x_off[MNG_MAX_OBJECTS],
798 y_off[MNG_MAX_OBJECTS];
799
800 MngBox
801 clip,
802 frame,
803 image_box,
804 object_clip[MNG_MAX_OBJECTS];
805
806 unsigned char
807 /* These flags could be combined into one byte */
808 exists[MNG_MAX_OBJECTS],
809 frozen[MNG_MAX_OBJECTS],
810 loop_active[256],
811 invisible[MNG_MAX_OBJECTS],
812 viewable[MNG_MAX_OBJECTS];
813
814 MagickOffsetType
815 loop_jump[256];
816
817 png_colorp
818 global_plte;
819
820 png_color_8
821 global_sbit;
822
823 png_byte
824#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
825 read_buffer[8],
826#endif
827 global_trns[256];
828
829 float
830 global_gamma;
831
832 ChromaticityInfo
833 global_chrm;
834
835 RenderingIntent
836 global_srgb_intent;
837
cristy35ef8242010-06-03 16:24:13 +0000838 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000839 delay,
840 global_plte_length,
841 global_trns_length,
842 global_x_pixels_per_unit,
843 global_y_pixels_per_unit,
844 mng_width,
845 mng_height,
846 ticks_per_second;
847
glennrpb9cfe272010-12-21 15:08:06 +0000848 MagickBooleanType
849 need_blob;
850
cristy3ed852e2009-09-05 21:47:34 +0000851 unsigned int
852 IsPalette,
853 global_phys_unit_type,
854 basi_warning,
855 clon_warning,
856 dhdr_warning,
857 jhdr_warning,
858 magn_warning,
859 past_warning,
860 phyg_warning,
861 phys_warning,
862 sbit_warning,
863 show_warning,
864 mng_type,
865 write_mng,
866 write_png_colortype,
867 write_png_depth,
glennrp18682582011-06-30 18:11:47 +0000868 write_png_compression_level,
869 write_png_compression_strategy,
870 write_png_compression_filter,
cristy3ed852e2009-09-05 21:47:34 +0000871 write_png8,
872 write_png24,
873 write_png32;
874
875#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000876 size_t
cristy3ed852e2009-09-05 21:47:34 +0000877 basi_width,
878 basi_height;
879
880 unsigned int
881 basi_depth,
882 basi_color_type,
883 basi_compression_method,
884 basi_filter_type,
885 basi_interlace_method,
886 basi_red,
887 basi_green,
888 basi_blue,
889 basi_alpha,
890 basi_viewable;
891#endif
892
893 png_uint_16
894 magn_first,
895 magn_last,
896 magn_mb,
897 magn_ml,
898 magn_mr,
899 magn_mt,
900 magn_mx,
901 magn_my,
902 magn_methx,
903 magn_methy;
904
905 PixelPacket
906 mng_global_bkgd;
907
glennrp26f37912010-12-23 16:22:42 +0000908 /* Added at version 6.6.6-7 */
909 MagickBooleanType
910 ping_exclude_bKGD,
911 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000912 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000913 ping_exclude_EXIF,
914 ping_exclude_gAMA,
915 ping_exclude_iCCP,
916 /* ping_exclude_iTXt, */
917 ping_exclude_oFFs,
918 ping_exclude_pHYs,
919 ping_exclude_sRGB,
920 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000921 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000922 ping_exclude_vpAg,
923 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000924 ping_exclude_zTXt,
925 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000926
cristy3ed852e2009-09-05 21:47:34 +0000927} MngInfo;
928#endif /* VER */
929
930/*
931 Forward declarations.
932*/
933static MagickBooleanType
934 WritePNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000935
cristy3ed852e2009-09-05 21:47:34 +0000936static MagickBooleanType
937 WriteMNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000938
cristy3ed852e2009-09-05 21:47:34 +0000939#if defined(JNG_SUPPORTED)
940static MagickBooleanType
941 WriteJNGImage(const ImageInfo *,Image *);
942#endif
943
glennrp0c3e06b2010-11-19 13:45:02 +0000944#if PNG_LIBPNG_VER > 10011
945
glennrpfd05d622011-02-25 04:10:33 +0000946
glennrp0c3e06b2010-11-19 13:45:02 +0000947#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
948static MagickBooleanType
glennrpfd05d622011-02-25 04:10:33 +0000949LosslessReduceDepthOK(Image *image)
glennrp0c3e06b2010-11-19 13:45:02 +0000950{
glennrp67b9c1a2011-04-22 18:47:36 +0000951 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
952 *
953 * This is true if the high byte and the next highest byte of
954 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000955 * are equal to each other. We check this by seeing if the samples
956 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000957 *
958 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000959 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000960 */
961
glennrp3faa9a32011-04-23 14:00:25 +0000962#define QuantumToCharToQuantumEqQuantum(quantum) \
963 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
964
glennrp0c3e06b2010-11-19 13:45:02 +0000965 MagickBooleanType
966 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000967
glennrp03e11f62011-04-22 13:30:16 +0000968 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000969 {
970
cristy4c08aed2011-07-01 19:47:50 +0000971 const Quantum
glennrp0c3e06b2010-11-19 13:45:02 +0000972 *p;
973
974 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000975 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
976 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
977 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
978 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000979
980 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
981 {
982 int indx;
983
984 for (indx=0; indx < (ssize_t) image->colors; indx++)
985 {
glennrp3faa9a32011-04-23 14:00:25 +0000986 ok_to_reduce=(
987 QuantumToCharToQuantumEqQuantum(
988 image->colormap[indx].red) &&
989 QuantumToCharToQuantumEqQuantum(
990 image->colormap[indx].green) &&
991 QuantumToCharToQuantumEqQuantum(
992 image->colormap[indx].blue)) ?
993 MagickTrue : MagickFalse;
994
glennrp0c3e06b2010-11-19 13:45:02 +0000995 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000996 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000997 }
998 }
999
1000 if ((ok_to_reduce != MagickFalse) &&
1001 (image->storage_class != PseudoClass))
1002 {
1003 ssize_t
1004 y;
1005
1006 register ssize_t
1007 x;
1008
1009 for (y=0; y < (ssize_t) image->rows; y++)
1010 {
1011 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1012
cristy4c08aed2011-07-01 19:47:50 +00001013 if (p == (const Quantum *) NULL)
glennrp0c3e06b2010-11-19 13:45:02 +00001014 {
1015 ok_to_reduce = MagickFalse;
1016 break;
1017 }
1018
1019 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1020 {
glennrp3faa9a32011-04-23 14:00:25 +00001021 ok_to_reduce=
cristy4c08aed2011-07-01 19:47:50 +00001022 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1023 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1024 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
glennrp3faa9a32011-04-23 14:00:25 +00001025 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001026
1027 if (ok_to_reduce == MagickFalse)
1028 break;
1029
cristy4c08aed2011-07-01 19:47:50 +00001030 p+=GetPixelChannels(image);
glennrp0c3e06b2010-11-19 13:45:02 +00001031 }
glennrp8640fb52010-11-23 15:48:26 +00001032 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +00001033 break;
1034 }
1035 }
1036
1037 if (ok_to_reduce != MagickFalse)
1038 {
glennrp0c3e06b2010-11-19 13:45:02 +00001039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001040 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +00001041 }
glennrpa6a06632011-01-19 15:15:34 +00001042 else
1043 {
1044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001045 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +00001046 }
glennrp0c3e06b2010-11-19 13:45:02 +00001047 }
1048
1049 return ok_to_reduce;
1050}
1051#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1052
glennrpe610a072010-08-05 17:08:46 +00001053static int
glennrpcf002022011-01-30 02:38:15 +00001054Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +00001055{
glennrpe610a072010-08-05 17:08:46 +00001056 switch (intent)
1057 {
1058 case PerceptualIntent:
1059 return 0;
glennrp0fe50b42010-11-16 03:52:51 +00001060
glennrpe610a072010-08-05 17:08:46 +00001061 case RelativeIntent:
1062 return 1;
glennrp0fe50b42010-11-16 03:52:51 +00001063
glennrpe610a072010-08-05 17:08:46 +00001064 case SaturationIntent:
1065 return 2;
glennrp0fe50b42010-11-16 03:52:51 +00001066
glennrpe610a072010-08-05 17:08:46 +00001067 case AbsoluteIntent:
1068 return 3;
glennrp0fe50b42010-11-16 03:52:51 +00001069
glennrpe610a072010-08-05 17:08:46 +00001070 default:
1071 return -1;
1072 }
1073}
1074
1075static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +00001076Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +00001077{
glennrpcf002022011-01-30 02:38:15 +00001078 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001079 {
1080 case 0:
1081 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001082
glennrpe610a072010-08-05 17:08:46 +00001083 case 1:
1084 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001085
glennrpe610a072010-08-05 17:08:46 +00001086 case 2:
1087 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001088
glennrpe610a072010-08-05 17:08:46 +00001089 case 3:
1090 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001091
glennrpe610a072010-08-05 17:08:46 +00001092 default:
1093 return UndefinedIntent;
1094 }
1095}
1096
cristybb503372010-05-27 20:51:26 +00001097static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001098{
1099 if (x > y)
1100 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001101
cristy3ed852e2009-09-05 21:47:34 +00001102 return(y);
1103}
glennrp0c3e06b2010-11-19 13:45:02 +00001104
cristybb503372010-05-27 20:51:26 +00001105static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001106{
1107 if (x < y)
1108 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001109
cristy3ed852e2009-09-05 21:47:34 +00001110 return(y);
1111}
glennrp0c3e06b2010-11-19 13:45:02 +00001112
cristy3ed852e2009-09-05 21:47:34 +00001113
1114/*
1115%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1116% %
1117% %
1118% %
1119% I m a g e I s G r a y %
1120% %
1121% %
1122% %
1123%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1124% %
cristy4c08aed2011-07-01 19:47:50 +00001125% Like IsImageGray except does not change DirectClass to PseudoClass %
cristy3ed852e2009-09-05 21:47:34 +00001126% %
1127%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1128*/
1129static MagickBooleanType ImageIsGray(Image *image)
1130{
cristy4c08aed2011-07-01 19:47:50 +00001131 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001132 *p;
1133
cristybb503372010-05-27 20:51:26 +00001134 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001135 i,
1136 x,
1137 y;
1138
1139 assert(image != (Image *) NULL);
1140 assert(image->signature == MagickSignature);
1141 if (image->debug != MagickFalse)
1142 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1143
1144 if (image->storage_class == PseudoClass)
1145 {
cristybb503372010-05-27 20:51:26 +00001146 for (i=0; i < (ssize_t) image->colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00001147 if (IsPixelPacketGray(image->colormap+i) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001148 return(MagickFalse);
1149 return(MagickTrue);
1150 }
cristybb503372010-05-27 20:51:26 +00001151 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001152 {
1153 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00001154 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001155 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +00001156 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001157 {
cristy4c08aed2011-07-01 19:47:50 +00001158 if (IsPixelGray(image,p) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001159 return(MagickFalse);
cristy4c08aed2011-07-01 19:47:50 +00001160 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001161 }
1162 }
1163 return(MagickTrue);
1164}
glennrpd5045b42010-03-24 12:40:35 +00001165#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001166#endif /* MAGICKCORE_PNG_DELEGATE */
1167
1168/*
1169%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1170% %
1171% %
1172% %
1173% I s M N G %
1174% %
1175% %
1176% %
1177%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1178%
1179% IsMNG() returns MagickTrue if the image format type, identified by the
1180% magick string, is MNG.
1181%
1182% The format of the IsMNG method is:
1183%
1184% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1185%
1186% A description of each parameter follows:
1187%
1188% o magick: compare image format pattern against these bytes.
1189%
1190% o length: Specifies the length of the magick string.
1191%
1192%
1193*/
1194static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1195{
1196 if (length < 8)
1197 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001198
cristy3ed852e2009-09-05 21:47:34 +00001199 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1200 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001201
cristy3ed852e2009-09-05 21:47:34 +00001202 return(MagickFalse);
1203}
1204
1205/*
1206%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1207% %
1208% %
1209% %
1210% I s J N G %
1211% %
1212% %
1213% %
1214%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1215%
1216% IsJNG() returns MagickTrue if the image format type, identified by the
1217% magick string, is JNG.
1218%
1219% The format of the IsJNG method is:
1220%
1221% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1222%
1223% A description of each parameter follows:
1224%
1225% o magick: compare image format pattern against these bytes.
1226%
1227% o length: Specifies the length of the magick string.
1228%
1229%
1230*/
1231static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1232{
1233 if (length < 8)
1234 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001235
cristy3ed852e2009-09-05 21:47:34 +00001236 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1237 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001238
cristy3ed852e2009-09-05 21:47:34 +00001239 return(MagickFalse);
1240}
1241
1242/*
1243%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1244% %
1245% %
1246% %
1247% I s P N G %
1248% %
1249% %
1250% %
1251%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1252%
1253% IsPNG() returns MagickTrue if the image format type, identified by the
1254% magick string, is PNG.
1255%
1256% The format of the IsPNG method is:
1257%
1258% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1259%
1260% A description of each parameter follows:
1261%
1262% o magick: compare image format pattern against these bytes.
1263%
1264% o length: Specifies the length of the magick string.
1265%
1266*/
1267static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1268{
1269 if (length < 8)
1270 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001271
cristy3ed852e2009-09-05 21:47:34 +00001272 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1273 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001274
cristy3ed852e2009-09-05 21:47:34 +00001275 return(MagickFalse);
1276}
1277
1278#if defined(MAGICKCORE_PNG_DELEGATE)
1279#if defined(__cplusplus) || defined(c_plusplus)
1280extern "C" {
1281#endif
1282
glennrpd5045b42010-03-24 12:40:35 +00001283#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001284static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001285{
1286 unsigned char
1287 buffer[4];
1288
1289 assert(image != (Image *) NULL);
1290 assert(image->signature == MagickSignature);
1291 buffer[0]=(unsigned char) (value >> 24);
1292 buffer[1]=(unsigned char) (value >> 16);
1293 buffer[2]=(unsigned char) (value >> 8);
1294 buffer[3]=(unsigned char) value;
1295 return((size_t) WriteBlob(image,4,buffer));
1296}
1297
1298static void PNGLong(png_bytep p,png_uint_32 value)
1299{
1300 *p++=(png_byte) ((value >> 24) & 0xff);
1301 *p++=(png_byte) ((value >> 16) & 0xff);
1302 *p++=(png_byte) ((value >> 8) & 0xff);
1303 *p++=(png_byte) (value & 0xff);
1304}
1305
glennrpa521b2f2010-10-29 04:11:03 +00001306#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001307static void PNGsLong(png_bytep p,png_int_32 value)
1308{
1309 *p++=(png_byte) ((value >> 24) & 0xff);
1310 *p++=(png_byte) ((value >> 16) & 0xff);
1311 *p++=(png_byte) ((value >> 8) & 0xff);
1312 *p++=(png_byte) (value & 0xff);
1313}
glennrpa521b2f2010-10-29 04:11:03 +00001314#endif
cristy3ed852e2009-09-05 21:47:34 +00001315
1316static void PNGShort(png_bytep p,png_uint_16 value)
1317{
1318 *p++=(png_byte) ((value >> 8) & 0xff);
1319 *p++=(png_byte) (value & 0xff);
1320}
1321
1322static void PNGType(png_bytep p,png_bytep type)
1323{
1324 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1325}
1326
glennrp03812ae2010-12-24 01:31:34 +00001327static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1328 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001329{
1330 if (logging != MagickFalse)
1331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001332 " Writing %c%c%c%c chunk, length: %.20g",
1333 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001334}
glennrpd5045b42010-03-24 12:40:35 +00001335#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001336
1337#if defined(__cplusplus) || defined(c_plusplus)
1338}
1339#endif
1340
glennrpd5045b42010-03-24 12:40:35 +00001341#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001342/*
1343%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1344% %
1345% %
1346% %
1347% R e a d P N G I m a g e %
1348% %
1349% %
1350% %
1351%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1352%
1353% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1354% Multiple-image Network Graphics (MNG) image file and returns it. It
1355% allocates the memory necessary for the new Image structure and returns a
1356% pointer to the new image or set of images.
1357%
1358% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1359%
1360% The format of the ReadPNGImage method is:
1361%
1362% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1363%
1364% A description of each parameter follows:
1365%
1366% o image_info: the image info.
1367%
1368% o exception: return any errors or warnings in this structure.
1369%
1370% To do, more or less in chronological order (as of version 5.5.2,
1371% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1372%
1373% Get 16-bit cheap transparency working.
1374%
1375% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1376%
1377% Preserve all unknown and not-yet-handled known chunks found in input
1378% PNG file and copy them into output PNG files according to the PNG
1379% copying rules.
1380%
1381% (At this point, PNG encoding should be in full MNG compliance)
1382%
1383% Provide options for choice of background to use when the MNG BACK
1384% chunk is not present or is not mandatory (i.e., leave transparent,
1385% user specified, MNG BACK, PNG bKGD)
1386%
1387% Implement LOOP/ENDL [done, but could do discretionary loops more
1388% efficiently by linking in the duplicate frames.].
1389%
1390% Decode and act on the MHDR simplicity profile (offer option to reject
1391% files or attempt to process them anyway when the profile isn't LC or VLC).
1392%
1393% Upgrade to full MNG without Delta-PNG.
1394%
1395% o BACK [done a while ago except for background image ID]
1396% o MOVE [done 15 May 1999]
1397% o CLIP [done 15 May 1999]
1398% o DISC [done 19 May 1999]
1399% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1400% o SEEK [partially done 19 May 1999 (discard function only)]
1401% o SHOW
1402% o PAST
1403% o BASI
1404% o MNG-level tEXt/iTXt/zTXt
1405% o pHYg
1406% o pHYs
1407% o sBIT
1408% o bKGD
1409% o iTXt (wait for libpng implementation).
1410%
1411% Use the scene signature to discover when an identical scene is
1412% being reused, and just point to the original image->exception instead
1413% of storing another set of pixels. This not specific to MNG
1414% but could be applied generally.
1415%
1416% Upgrade to full MNG with Delta-PNG.
1417%
1418% JNG tEXt/iTXt/zTXt
1419%
1420% We will not attempt to read files containing the CgBI chunk.
1421% They are really Xcode files meant for display on the iPhone.
1422% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001423% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001424% since irretrievable loss of color data has occurred due to the
1425% use of premultiplied alpha.
1426*/
1427
1428#if defined(__cplusplus) || defined(c_plusplus)
1429extern "C" {
1430#endif
1431
1432/*
1433 This the function that does the actual reading of data. It is
1434 the same as the one supplied in libpng, except that it receives the
1435 datastream from the ReadBlob() function instead of standard input.
1436*/
1437static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1438{
1439 Image
1440 *image;
1441
1442 image=(Image *) png_get_io_ptr(png_ptr);
1443 if (length)
1444 {
1445 png_size_t
1446 check;
1447
1448 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1449 if (check != length)
1450 {
1451 char
1452 msg[MaxTextExtent];
1453
cristy3b6fd2e2011-05-20 12:53:50 +00001454 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001455 "Expected %.20g bytes; found %.20g bytes",(double) length,
1456 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001457 png_warning(png_ptr,msg);
1458 png_error(png_ptr,"Read Exception");
1459 }
1460 }
1461}
1462
1463#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1464 !defined(PNG_MNG_FEATURES_SUPPORTED)
1465/* We use mng_get_data() instead of png_get_data() if we have a libpng
1466 * older than libpng-1.0.3a, which was the first to allow the empty
1467 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1468 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1469 * encountered after an empty PLTE, so we have to look ahead for bKGD
1470 * chunks and remove them from the datastream that is passed to libpng,
1471 * and store their contents for later use.
1472 */
1473static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1474{
1475 MngInfo
1476 *mng_info;
1477
1478 Image
1479 *image;
1480
1481 png_size_t
1482 check;
1483
cristybb503372010-05-27 20:51:26 +00001484 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001485 i;
1486
1487 i=0;
1488 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1489 image=(Image *) mng_info->image;
1490 while (mng_info->bytes_in_read_buffer && length)
1491 {
1492 data[i]=mng_info->read_buffer[i];
1493 mng_info->bytes_in_read_buffer--;
1494 length--;
1495 i++;
1496 }
1497 if (length)
1498 {
1499 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001500
cristy3ed852e2009-09-05 21:47:34 +00001501 if (check != length)
1502 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001503
cristy3ed852e2009-09-05 21:47:34 +00001504 if (length == 4)
1505 {
1506 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1507 (data[3] == 0))
1508 {
1509 check=(png_size_t) ReadBlob(image,(size_t) length,
1510 (char *) mng_info->read_buffer);
1511 mng_info->read_buffer[4]=0;
1512 mng_info->bytes_in_read_buffer=4;
1513 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1514 mng_info->found_empty_plte=MagickTrue;
1515 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1516 {
1517 mng_info->found_empty_plte=MagickFalse;
1518 mng_info->have_saved_bkgd_index=MagickFalse;
1519 }
1520 }
glennrp0fe50b42010-11-16 03:52:51 +00001521
cristy3ed852e2009-09-05 21:47:34 +00001522 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1523 (data[3] == 1))
1524 {
1525 check=(png_size_t) ReadBlob(image,(size_t) length,
1526 (char *) mng_info->read_buffer);
1527 mng_info->read_buffer[4]=0;
1528 mng_info->bytes_in_read_buffer=4;
1529 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1530 if (mng_info->found_empty_plte)
1531 {
1532 /*
1533 Skip the bKGD data byte and CRC.
1534 */
1535 check=(png_size_t)
1536 ReadBlob(image,5,(char *) mng_info->read_buffer);
1537 check=(png_size_t) ReadBlob(image,(size_t) length,
1538 (char *) mng_info->read_buffer);
1539 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1540 mng_info->have_saved_bkgd_index=MagickTrue;
1541 mng_info->bytes_in_read_buffer=0;
1542 }
1543 }
1544 }
1545 }
1546}
1547#endif
1548
1549static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1550{
1551 Image
1552 *image;
1553
1554 image=(Image *) png_get_io_ptr(png_ptr);
1555 if (length)
1556 {
1557 png_size_t
1558 check;
1559
cristybb503372010-05-27 20:51:26 +00001560 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001561
cristy3ed852e2009-09-05 21:47:34 +00001562 if (check != length)
1563 png_error(png_ptr,"WriteBlob Failed");
1564 }
1565}
1566
1567static void png_flush_data(png_structp png_ptr)
1568{
1569 (void) png_ptr;
1570}
1571
1572#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1573static int PalettesAreEqual(Image *a,Image *b)
1574{
cristybb503372010-05-27 20:51:26 +00001575 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001576 i;
1577
1578 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1579 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001580
cristy3ed852e2009-09-05 21:47:34 +00001581 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1582 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001583
cristy3ed852e2009-09-05 21:47:34 +00001584 if (a->colors != b->colors)
1585 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001586
cristybb503372010-05-27 20:51:26 +00001587 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001588 {
1589 if ((a->colormap[i].red != b->colormap[i].red) ||
1590 (a->colormap[i].green != b->colormap[i].green) ||
1591 (a->colormap[i].blue != b->colormap[i].blue))
1592 return((int) MagickFalse);
1593 }
glennrp0fe50b42010-11-16 03:52:51 +00001594
cristy3ed852e2009-09-05 21:47:34 +00001595 return((int) MagickTrue);
1596}
1597#endif
1598
1599static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1600{
1601 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1602 mng_info->exists[i] && !mng_info->frozen[i])
1603 {
1604#ifdef MNG_OBJECT_BUFFERS
1605 if (mng_info->ob[i] != (MngBuffer *) NULL)
1606 {
1607 if (mng_info->ob[i]->reference_count > 0)
1608 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001609
cristy3ed852e2009-09-05 21:47:34 +00001610 if (mng_info->ob[i]->reference_count == 0)
1611 {
1612 if (mng_info->ob[i]->image != (Image *) NULL)
1613 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001614
cristy3ed852e2009-09-05 21:47:34 +00001615 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1616 }
1617 }
1618 mng_info->ob[i]=(MngBuffer *) NULL;
1619#endif
1620 mng_info->exists[i]=MagickFalse;
1621 mng_info->invisible[i]=MagickFalse;
1622 mng_info->viewable[i]=MagickFalse;
1623 mng_info->frozen[i]=MagickFalse;
1624 mng_info->x_off[i]=0;
1625 mng_info->y_off[i]=0;
1626 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001627 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001628 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001629 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001630 }
1631}
1632
glennrp21f0e622011-01-07 16:20:57 +00001633static void MngInfoFreeStruct(MngInfo *mng_info,
1634 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001635{
glennrp21f0e622011-01-07 16:20:57 +00001636 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001637 {
cristybb503372010-05-27 20:51:26 +00001638 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001639 i;
1640
1641 for (i=1; i < MNG_MAX_OBJECTS; i++)
1642 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001643
cristy3ed852e2009-09-05 21:47:34 +00001644 if (mng_info->global_plte != (png_colorp) NULL)
1645 mng_info->global_plte=(png_colorp)
1646 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001647
cristy3ed852e2009-09-05 21:47:34 +00001648 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1649 *have_mng_structure=MagickFalse;
1650 }
1651}
1652
1653static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1654{
1655 MngBox
1656 box;
1657
1658 box=box1;
1659 if (box.left < box2.left)
1660 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001661
cristy3ed852e2009-09-05 21:47:34 +00001662 if (box.top < box2.top)
1663 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001664
cristy3ed852e2009-09-05 21:47:34 +00001665 if (box.right > box2.right)
1666 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001667
cristy3ed852e2009-09-05 21:47:34 +00001668 if (box.bottom > box2.bottom)
1669 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001670
cristy3ed852e2009-09-05 21:47:34 +00001671 return box;
1672}
1673
1674static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1675{
1676 MngBox
1677 box;
1678
1679 /*
1680 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1681 */
cristybb503372010-05-27 20:51:26 +00001682 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1683 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1684 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1685 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001686 if (delta_type != 0)
1687 {
1688 box.left+=previous_box.left;
1689 box.right+=previous_box.right;
1690 box.top+=previous_box.top;
1691 box.bottom+=previous_box.bottom;
1692 }
glennrp0fe50b42010-11-16 03:52:51 +00001693
cristy3ed852e2009-09-05 21:47:34 +00001694 return(box);
1695}
1696
1697static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1698 unsigned char *p)
1699{
1700 MngPair
1701 pair;
1702 /*
cristybb503372010-05-27 20:51:26 +00001703 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001704 */
cristy8182b072010-05-30 20:10:53 +00001705 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1706 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001707
cristy3ed852e2009-09-05 21:47:34 +00001708 if (delta_type != 0)
1709 {
1710 pair.a+=previous_pair.a;
1711 pair.b+=previous_pair.b;
1712 }
glennrp0fe50b42010-11-16 03:52:51 +00001713
cristy3ed852e2009-09-05 21:47:34 +00001714 return(pair);
1715}
1716
cristy8182b072010-05-30 20:10:53 +00001717static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001718{
cristy8182b072010-05-30 20:10:53 +00001719 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001720}
1721
glennrpcf002022011-01-30 02:38:15 +00001722static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001723{
1724 Image
1725 *image;
1726
1727 image=(Image *) png_get_error_ptr(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001728
cristy3ed852e2009-09-05 21:47:34 +00001729 if (image->debug != MagickFalse)
1730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1731 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001732
cristy3ed852e2009-09-05 21:47:34 +00001733 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1734 message,"`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001735
glennrpe4017e32011-01-08 17:16:09 +00001736#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001737 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1738 * are building with libpng-1.4.x and can be ignored.
1739 */
cristy3ed852e2009-09-05 21:47:34 +00001740 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001741#else
1742 png_longjmp(ping,1);
1743#endif
cristy3ed852e2009-09-05 21:47:34 +00001744}
1745
glennrpcf002022011-01-30 02:38:15 +00001746static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001747{
1748 Image
1749 *image;
1750
1751 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1752 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001753
cristy3ed852e2009-09-05 21:47:34 +00001754 image=(Image *) png_get_error_ptr(ping);
1755 if (image->debug != MagickFalse)
1756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristycc23b9a2010-05-09 22:37:43 +00001757 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001758
cristy3ed852e2009-09-05 21:47:34 +00001759 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1760 message,"`%s'",image->filename);
1761}
1762
1763#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001764static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001765{
1766#if (PNG_LIBPNG_VER < 10011)
1767 png_voidp
1768 ret;
1769
1770 png_ptr=png_ptr;
1771 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001772
cristy3ed852e2009-09-05 21:47:34 +00001773 if (ret == NULL)
1774 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001775
cristy3ed852e2009-09-05 21:47:34 +00001776 return(ret);
1777#else
1778 png_ptr=png_ptr;
1779 return((png_voidp) AcquireMagickMemory((size_t) size));
1780#endif
1781}
1782
1783/*
1784 Free a pointer. It is removed from the list at the same time.
1785*/
glennrpcf002022011-01-30 02:38:15 +00001786static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001787{
1788 png_ptr=png_ptr;
1789 ptr=RelinquishMagickMemory(ptr);
1790 return((png_free_ptr) NULL);
1791}
1792#endif
1793
1794#if defined(__cplusplus) || defined(c_plusplus)
1795}
1796#endif
1797
1798static int
glennrpcf002022011-01-30 02:38:15 +00001799Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristy3ed852e2009-09-05 21:47:34 +00001800 png_textp text,int ii)
1801{
cristybb503372010-05-27 20:51:26 +00001802 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001803 i;
1804
1805 register unsigned char
1806 *dp;
1807
1808 register png_charp
1809 sp;
1810
1811 png_uint_32
1812 length,
1813 nibbles;
1814
1815 StringInfo
1816 *profile;
1817
glennrp0c3e06b2010-11-19 13:45:02 +00001818 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001819 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1820 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1821 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1822 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1823 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1824 13,14,15};
1825
1826 sp=text[ii].text+1;
1827 /* look for newline */
1828 while (*sp != '\n')
1829 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001830
cristy3ed852e2009-09-05 21:47:34 +00001831 /* look for length */
1832 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1833 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001834
cristyf2f27272009-12-17 14:48:46 +00001835 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001836
glennrp97f90e22011-02-22 05:47:58 +00001837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1838 " length: %lu",(unsigned long) length);
1839
cristy3ed852e2009-09-05 21:47:34 +00001840 while (*sp != ' ' && *sp != '\n')
1841 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001842
cristy3ed852e2009-09-05 21:47:34 +00001843 /* allocate space */
1844 if (length == 0)
1845 {
1846 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1847 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1848 return(MagickFalse);
1849 }
glennrp0fe50b42010-11-16 03:52:51 +00001850
cristy3ed852e2009-09-05 21:47:34 +00001851 profile=AcquireStringInfo(length);
glennrp0fe50b42010-11-16 03:52:51 +00001852
cristy3ed852e2009-09-05 21:47:34 +00001853 if (profile == (StringInfo *) NULL)
1854 {
1855 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1856 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1857 "unable to copy profile");
1858 return(MagickFalse);
1859 }
glennrp0fe50b42010-11-16 03:52:51 +00001860
cristy3ed852e2009-09-05 21:47:34 +00001861 /* copy profile, skipping white space and column 1 "=" signs */
1862 dp=GetStringInfoDatum(profile);
1863 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001864
cristybb503372010-05-27 20:51:26 +00001865 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001866 {
1867 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1868 {
1869 if (*sp == '\0')
1870 {
1871 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1872 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1873 profile=DestroyStringInfo(profile);
1874 return(MagickFalse);
1875 }
1876 sp++;
1877 }
glennrp0fe50b42010-11-16 03:52:51 +00001878
cristy3ed852e2009-09-05 21:47:34 +00001879 if (i%2 == 0)
1880 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001881
cristy3ed852e2009-09-05 21:47:34 +00001882 else
1883 (*dp++)+=unhex[(int) *sp++];
1884 }
1885 /*
1886 We have already read "Raw profile type.
1887 */
1888 (void) SetImageProfile(image,&text[ii].key[17],profile);
1889 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001890
cristy3ed852e2009-09-05 21:47:34 +00001891 if (image_info->verbose)
1892 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001893
cristy3ed852e2009-09-05 21:47:34 +00001894 return MagickTrue;
1895}
1896
1897#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1898static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1899{
1900 Image
1901 *image;
1902
1903
1904 /* The unknown chunk structure contains the chunk data:
1905 png_byte name[5];
1906 png_byte *data;
1907 png_size_t size;
1908
1909 Note that libpng has already taken care of the CRC handling.
1910 */
1911
1912
1913 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1914 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1915 return(0); /* Did not recognize */
1916
1917 /* recognized vpAg */
1918
1919 if (chunk->size != 9)
1920 return(-1); /* Error return */
1921
1922 if (chunk->data[8] != 0)
1923 return(0); /* ImageMagick requires pixel units */
1924
1925 image=(Image *) png_get_user_chunk_ptr(ping);
1926
cristybb503372010-05-27 20:51:26 +00001927 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001928 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001929
cristybb503372010-05-27 20:51:26 +00001930 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001931 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1932
1933 /* Return one of the following: */
1934 /* return(-n); chunk had an error */
1935 /* return(0); did not recognize */
1936 /* return(n); success */
1937
1938 return(1);
1939
1940}
1941#endif
1942
1943/*
1944%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1945% %
1946% %
1947% %
1948% R e a d O n e P N G I m a g e %
1949% %
1950% %
1951% %
1952%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1953%
1954% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1955% (minus the 8-byte signature) and returns it. It allocates the memory
1956% necessary for the new Image structure and returns a pointer to the new
1957% image.
1958%
1959% The format of the ReadOnePNGImage method is:
1960%
1961% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1962% ExceptionInfo *exception)
1963%
1964% A description of each parameter follows:
1965%
1966% o mng_info: Specifies a pointer to a MngInfo structure.
1967%
1968% o image_info: the image info.
1969%
1970% o exception: return any errors or warnings in this structure.
1971%
1972*/
1973static Image *ReadOnePNGImage(MngInfo *mng_info,
1974 const ImageInfo *image_info, ExceptionInfo *exception)
1975{
1976 /* Read one PNG image */
1977
glennrpcc95c3f2011-04-18 16:46:48 +00001978 /* To do: Read the tIME chunk into the date:modify property */
1979 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1980
cristy3ed852e2009-09-05 21:47:34 +00001981 Image
1982 *image;
1983
1984 int
glennrp4eb39312011-03-30 21:34:55 +00001985 intent,
glennrpcb395ac2011-03-30 19:50:23 +00001986 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00001987 num_text,
glennrp4eb39312011-03-30 21:34:55 +00001988 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00001989 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00001990 pass,
1991 ping_bit_depth,
1992 ping_color_type,
1993 ping_interlace_method,
1994 ping_compression_method,
1995 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00001996 ping_num_trans,
1997 unit_type;
1998
1999 double
2000 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002001
glennrpa6a06632011-01-19 15:15:34 +00002002 LongPixelPacket
2003 transparent_color;
2004
cristy3ed852e2009-09-05 21:47:34 +00002005 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002006 logging,
cristy3ed852e2009-09-05 21:47:34 +00002007 status;
2008
glennrpfaa852b2010-03-30 12:17:00 +00002009 png_bytep
2010 ping_trans_alpha;
2011
2012 png_color_16p
2013 ping_background,
2014 ping_trans_color;
2015
cristy3ed852e2009-09-05 21:47:34 +00002016 png_info
2017 *end_info,
2018 *ping_info;
2019
2020 png_struct
2021 *ping;
2022
2023 png_textp
2024 text;
2025
glennrpfaa852b2010-03-30 12:17:00 +00002026 png_uint_32
2027 ping_height,
2028 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002029 ping_rowbytes,
2030 x_resolution,
2031 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002032
cristy3ed852e2009-09-05 21:47:34 +00002033 QuantumInfo
2034 *quantum_info;
2035
2036 unsigned char
glennrpcf002022011-01-30 02:38:15 +00002037 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002038
cristybb503372010-05-27 20:51:26 +00002039 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002040 y;
2041
2042 register unsigned char
2043 *p;
2044
cristybb503372010-05-27 20:51:26 +00002045 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002046 i,
2047 x;
2048
cristy4c08aed2011-07-01 19:47:50 +00002049 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002050 *q;
2051
2052 size_t
glennrp39992b42010-11-14 00:03:43 +00002053 length,
cristy3ed852e2009-09-05 21:47:34 +00002054 row_offset;
2055
cristyeb3b22a2011-03-31 20:16:11 +00002056 ssize_t
2057 j;
2058
cristy3ed852e2009-09-05 21:47:34 +00002059#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2060 png_byte unused_chunks[]=
2061 {
2062 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2063 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2064 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2065 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2066 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2067 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2068 };
2069#endif
2070
2071 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002072 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002073
2074#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002075 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002076#endif
2077
glennrp25c1e2b2010-03-25 01:39:56 +00002078#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002079 if (image_info->verbose)
2080 printf("Your PNG library (libpng-%s) is rather old.\n",
2081 PNG_LIBPNG_VER_STRING);
2082#endif
2083
glennrp61b4c952009-11-10 20:40:41 +00002084#if (PNG_LIBPNG_VER >= 10400)
2085# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2086 if (image_info->verbose)
2087 {
2088 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2089 PNG_LIBPNG_VER_STRING);
2090 printf("Please update it.\n");
2091 }
2092# endif
2093#endif
2094
2095
cristyed552522009-10-16 14:04:35 +00002096 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002097 image=mng_info->image;
2098
glennrpa6a06632011-01-19 15:15:34 +00002099 if (logging != MagickFalse)
2100 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2101 " image->matte=%d",(int) image->matte);
2102
glennrp0e319732011-01-25 21:53:13 +00002103 /* Set to an out-of-range color unless tRNS chunk is present */
2104 transparent_color.red=65537;
2105 transparent_color.green=65537;
2106 transparent_color.blue=65537;
cristy4c08aed2011-07-01 19:47:50 +00002107 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002108
glennrpcb395ac2011-03-30 19:50:23 +00002109 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002110 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002111 num_raw_profiles = 0;
2112
cristy3ed852e2009-09-05 21:47:34 +00002113 /*
2114 Allocate the PNG structures
2115 */
2116#ifdef PNG_USER_MEM_SUPPORTED
2117 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
glennrpcf002022011-01-30 02:38:15 +00002118 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2119 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002120#else
2121 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00002122 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002123#endif
2124 if (ping == (png_struct *) NULL)
2125 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002126
cristy3ed852e2009-09-05 21:47:34 +00002127 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002128
cristy3ed852e2009-09-05 21:47:34 +00002129 if (ping_info == (png_info *) NULL)
2130 {
2131 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2132 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2133 }
glennrp0fe50b42010-11-16 03:52:51 +00002134
cristy3ed852e2009-09-05 21:47:34 +00002135 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002136
cristy3ed852e2009-09-05 21:47:34 +00002137 if (end_info == (png_info *) NULL)
2138 {
2139 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2140 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2141 }
glennrp0fe50b42010-11-16 03:52:51 +00002142
glennrpcf002022011-01-30 02:38:15 +00002143 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002144
glennrpfaa852b2010-03-30 12:17:00 +00002145 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002146 {
2147 /*
2148 PNG image is corrupt.
2149 */
2150 png_destroy_read_struct(&ping,&ping_info,&end_info);
2151#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002152 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002153#endif
2154 if (logging != MagickFalse)
2155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2156 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002157
cristy3ed852e2009-09-05 21:47:34 +00002158 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002159 {
2160 InheritException(exception,&image->exception);
2161 image->columns=0;
2162 }
glennrp0fe50b42010-11-16 03:52:51 +00002163
cristy3ed852e2009-09-05 21:47:34 +00002164 return(GetFirstImageInList(image));
2165 }
2166 /*
2167 Prepare PNG for reading.
2168 */
glennrpfaa852b2010-03-30 12:17:00 +00002169
cristy3ed852e2009-09-05 21:47:34 +00002170 mng_info->image_found++;
2171 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002172
cristy3ed852e2009-09-05 21:47:34 +00002173 if (LocaleCompare(image_info->magick,"MNG") == 0)
2174 {
2175#if defined(PNG_MNG_FEATURES_SUPPORTED)
2176 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2177 png_set_read_fn(ping,image,png_get_data);
2178#else
2179#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2180 png_permit_empty_plte(ping,MagickTrue);
2181 png_set_read_fn(ping,image,png_get_data);
2182#else
2183 mng_info->image=image;
2184 mng_info->bytes_in_read_buffer=0;
2185 mng_info->found_empty_plte=MagickFalse;
2186 mng_info->have_saved_bkgd_index=MagickFalse;
2187 png_set_read_fn(ping,mng_info,mng_get_data);
2188#endif
2189#endif
2190 }
glennrp0fe50b42010-11-16 03:52:51 +00002191
cristy3ed852e2009-09-05 21:47:34 +00002192 else
2193 png_set_read_fn(ping,image,png_get_data);
2194
2195#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2196 /* Ignore unused chunks and all unknown chunks except for vpAg */
2197 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2198 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2199 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2200 (int)sizeof(unused_chunks)/5);
2201 /* Callback for other unknown chunks */
2202 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2203#endif
2204
glennrp991e92a2010-01-28 03:09:00 +00002205#if (PNG_LIBPNG_VER < 10400)
2206# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2207 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002208 /* Disable thread-unsafe features of pnggccrd */
2209 if (png_access_version_number() >= 10200)
2210 {
2211 png_uint_32 mmx_disable_mask=0;
2212 png_uint_32 asm_flags;
2213
2214 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2215 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2216 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2217 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2218 asm_flags=png_get_asm_flags(ping);
2219 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2220 }
glennrp991e92a2010-01-28 03:09:00 +00002221# endif
cristy3ed852e2009-09-05 21:47:34 +00002222#endif
2223
2224 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002225
2226 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2227 &ping_bit_depth,&ping_color_type,
2228 &ping_interlace_method,&ping_compression_method,
2229 &ping_filter_method);
2230
2231 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2232 &ping_trans_color);
2233
2234 (void) png_get_bKGD(ping, ping_info, &ping_background);
2235
2236 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002237 {
glennrpfaa852b2010-03-30 12:17:00 +00002238 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2239 {
2240 png_set_packing(ping);
2241 ping_bit_depth = 8;
2242 }
cristy3ed852e2009-09-05 21:47:34 +00002243 }
glennrpfaa852b2010-03-30 12:17:00 +00002244
2245 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002246 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002247 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00002248 if (logging != MagickFalse)
2249 {
2250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002251 " PNG width: %.20g, height: %.20g",
2252 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002253
cristy3ed852e2009-09-05 21:47:34 +00002254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2255 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002256 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002257
cristy3ed852e2009-09-05 21:47:34 +00002258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2259 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002260 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002261
cristy3ed852e2009-09-05 21:47:34 +00002262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2263 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002264 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002265 }
2266
glennrpfaa852b2010-03-30 12:17:00 +00002267#ifdef PNG_READ_iCCP_SUPPORTED
2268 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002269 {
2270 int
2271 compression;
2272
glennrpe4017e32011-01-08 17:16:09 +00002273#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002274 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002275 info;
2276#else
2277 png_bytep
2278 info;
2279#endif
2280
2281 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002282 name;
2283
2284 png_uint_32
2285 profile_length;
2286
2287 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2288 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002289
cristy3ed852e2009-09-05 21:47:34 +00002290 if (profile_length != 0)
2291 {
2292 StringInfo
2293 *profile;
2294
2295 if (logging != MagickFalse)
2296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2297 " Reading PNG iCCP chunk.");
2298 profile=AcquireStringInfo(profile_length);
2299 SetStringInfoDatum(profile,(const unsigned char *) info);
2300 (void) SetImageProfile(image,"icc",profile);
2301 profile=DestroyStringInfo(profile);
2302 }
2303 }
2304#endif
2305#if defined(PNG_READ_sRGB_SUPPORTED)
2306 {
cristy3ed852e2009-09-05 21:47:34 +00002307 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00002308 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2309 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00002310
cristy3ed852e2009-09-05 21:47:34 +00002311 if (png_get_sRGB(ping,ping_info,&intent))
2312 {
glennrpcf002022011-01-30 02:38:15 +00002313 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2314 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002315
cristy3ed852e2009-09-05 21:47:34 +00002316 if (logging != MagickFalse)
2317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002318 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002319 }
2320 }
2321#endif
2322 {
glennrpfaa852b2010-03-30 12:17:00 +00002323 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2324 if (mng_info->have_global_gama)
2325 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002326
cristy3ed852e2009-09-05 21:47:34 +00002327 if (png_get_gAMA(ping,ping_info,&file_gamma))
2328 {
2329 image->gamma=(float) file_gamma;
2330 if (logging != MagickFalse)
2331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2332 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2333 }
2334 }
glennrpfaa852b2010-03-30 12:17:00 +00002335 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2336 {
2337 if (mng_info->have_global_chrm != MagickFalse)
2338 {
2339 (void) png_set_cHRM(ping,ping_info,
2340 mng_info->global_chrm.white_point.x,
2341 mng_info->global_chrm.white_point.y,
2342 mng_info->global_chrm.red_primary.x,
2343 mng_info->global_chrm.red_primary.y,
2344 mng_info->global_chrm.green_primary.x,
2345 mng_info->global_chrm.green_primary.y,
2346 mng_info->global_chrm.blue_primary.x,
2347 mng_info->global_chrm.blue_primary.y);
2348 }
2349 }
glennrp0fe50b42010-11-16 03:52:51 +00002350
glennrpfaa852b2010-03-30 12:17:00 +00002351 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002352 {
2353 (void) png_get_cHRM(ping,ping_info,
2354 &image->chromaticity.white_point.x,
2355 &image->chromaticity.white_point.y,
2356 &image->chromaticity.red_primary.x,
2357 &image->chromaticity.red_primary.y,
2358 &image->chromaticity.green_primary.x,
2359 &image->chromaticity.green_primary.y,
2360 &image->chromaticity.blue_primary.x,
2361 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002362
cristy3ed852e2009-09-05 21:47:34 +00002363 if (logging != MagickFalse)
2364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2365 " Reading PNG cHRM chunk.");
2366 }
glennrp0fe50b42010-11-16 03:52:51 +00002367
glennrpe610a072010-08-05 17:08:46 +00002368 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002369 {
glennrpe610a072010-08-05 17:08:46 +00002370 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002371 Magick_RenderingIntent_to_PNG_RenderingIntent
2372 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00002373 png_set_gAMA(ping,ping_info,0.45455f);
2374 png_set_cHRM(ping,ping_info,
2375 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2376 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002377 }
cristy3ed852e2009-09-05 21:47:34 +00002378#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002379 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002380 {
cristy905ef802011-02-23 00:29:18 +00002381 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2382 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002383
cristy3ed852e2009-09-05 21:47:34 +00002384 if (logging != MagickFalse)
2385 if (image->page.x || image->page.y)
2386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002387 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2388 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002389 }
2390#endif
2391#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002392 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2393 {
2394 if (mng_info->have_global_phys)
2395 {
2396 png_set_pHYs(ping,ping_info,
2397 mng_info->global_x_pixels_per_unit,
2398 mng_info->global_y_pixels_per_unit,
2399 mng_info->global_phys_unit_type);
2400 }
2401 }
2402
2403 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002404 {
cristy3ed852e2009-09-05 21:47:34 +00002405 /*
2406 Set image resolution.
2407 */
2408 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002409 &unit_type);
2410 image->x_resolution=(double) x_resolution;
2411 image->y_resolution=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002412
cristy3ed852e2009-09-05 21:47:34 +00002413 if (unit_type == PNG_RESOLUTION_METER)
2414 {
2415 image->units=PixelsPerCentimeterResolution;
2416 image->x_resolution=(double) x_resolution/100.0;
2417 image->y_resolution=(double) y_resolution/100.0;
2418 }
glennrp0fe50b42010-11-16 03:52:51 +00002419
cristy3ed852e2009-09-05 21:47:34 +00002420 if (logging != MagickFalse)
2421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002422 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2423 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002424 }
cristy3ed852e2009-09-05 21:47:34 +00002425#endif
glennrp823b55c2011-03-14 18:46:46 +00002426
glennrpfaa852b2010-03-30 12:17:00 +00002427 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002428 {
2429 int
2430 number_colors;
2431
2432 png_colorp
2433 palette;
2434
2435 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002436
cristy3ed852e2009-09-05 21:47:34 +00002437 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002438 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002439 {
2440 if (mng_info->global_plte_length)
2441 {
2442 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2443 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002444
glennrpfaa852b2010-03-30 12:17:00 +00002445 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002446 if (mng_info->global_trns_length)
2447 {
2448 if (mng_info->global_trns_length >
2449 mng_info->global_plte_length)
2450 (void) ThrowMagickException(&image->exception,
2451 GetMagickModule(),CoderError,
2452 "global tRNS has more entries than global PLTE",
2453 "`%s'",image_info->filename);
2454 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2455 (int) mng_info->global_trns_length,NULL);
2456 }
glennrpbfd9e612011-04-22 14:02:20 +00002457#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002458 if (
2459#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2460 mng_info->have_saved_bkgd_index ||
2461#endif
glennrpfaa852b2010-03-30 12:17:00 +00002462 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002463 {
2464 png_color_16
2465 background;
2466
2467#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2468 if (mng_info->have_saved_bkgd_index)
2469 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002470#endif
glennrpfaa852b2010-03-30 12:17:00 +00002471 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2472 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002473
cristy3ed852e2009-09-05 21:47:34 +00002474 background.red=(png_uint_16)
2475 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002476
cristy3ed852e2009-09-05 21:47:34 +00002477 background.green=(png_uint_16)
2478 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002479
cristy3ed852e2009-09-05 21:47:34 +00002480 background.blue=(png_uint_16)
2481 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002482
glennrpc6c391a2011-04-27 02:23:56 +00002483 background.gray=(png_uint_16)
2484 mng_info->global_plte[background.index].green;
2485
cristy3ed852e2009-09-05 21:47:34 +00002486 png_set_bKGD(ping,ping_info,&background);
2487 }
2488#endif
2489 }
2490 else
2491 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2492 CoderError,"No global PLTE in file","`%s'",
2493 image_info->filename);
2494 }
2495 }
2496
glennrpbfd9e612011-04-22 14:02:20 +00002497#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002498 if (mng_info->have_global_bkgd &&
2499 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002500 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002501
glennrpfaa852b2010-03-30 12:17:00 +00002502 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002503 {
glennrpbfd9e612011-04-22 14:02:20 +00002504 unsigned int
2505 bkgd_scale;
2506
cristy3ed852e2009-09-05 21:47:34 +00002507 /*
2508 Set image background color.
2509 */
2510 if (logging != MagickFalse)
2511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2512 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002513
glennrpbfd9e612011-04-22 14:02:20 +00002514 /* Scale background components to 16-bit, then scale
2515 * to quantum depth
2516 */
2517 if (logging != MagickFalse)
2518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2519 " raw ping_background=(%d,%d,%d).",ping_background->red,
2520 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002521
glennrpbfd9e612011-04-22 14:02:20 +00002522 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002523
glennrpbfd9e612011-04-22 14:02:20 +00002524 if (ping_bit_depth == 1)
2525 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002526
glennrpbfd9e612011-04-22 14:02:20 +00002527 else if (ping_bit_depth == 2)
2528 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002529
glennrpbfd9e612011-04-22 14:02:20 +00002530 else if (ping_bit_depth == 4)
2531 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002532
glennrpbfd9e612011-04-22 14:02:20 +00002533 if (ping_bit_depth <= 8)
2534 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002535
glennrpbfd9e612011-04-22 14:02:20 +00002536 ping_background->red *= bkgd_scale;
2537 ping_background->green *= bkgd_scale;
2538 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002539
glennrpbfd9e612011-04-22 14:02:20 +00002540 if (logging != MagickFalse)
2541 {
glennrp2cbb4482010-06-02 04:37:24 +00002542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2543 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002544
glennrp2cbb4482010-06-02 04:37:24 +00002545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2546 " ping_background=(%d,%d,%d).",ping_background->red,
2547 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002548 }
glennrp2cbb4482010-06-02 04:37:24 +00002549
glennrpbfd9e612011-04-22 14:02:20 +00002550 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002551 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002552
glennrpbfd9e612011-04-22 14:02:20 +00002553 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002554 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002555
glennrpbfd9e612011-04-22 14:02:20 +00002556 image->background_color.blue=
2557 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002558
cristy4c08aed2011-07-01 19:47:50 +00002559 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002560
glennrpbfd9e612011-04-22 14:02:20 +00002561 if (logging != MagickFalse)
2562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2563 " image->background_color=(%.20g,%.20g,%.20g).",
2564 (double) image->background_color.red,
2565 (double) image->background_color.green,
2566 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002567 }
glennrpbfd9e612011-04-22 14:02:20 +00002568#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002569
glennrpfaa852b2010-03-30 12:17:00 +00002570 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002571 {
2572 /*
glennrpa6a06632011-01-19 15:15:34 +00002573 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002574 */
2575 int
2576 max_sample;
2577
cristy35ef8242010-06-03 16:24:13 +00002578 size_t
2579 one=1;
2580
cristy3ed852e2009-09-05 21:47:34 +00002581 if (logging != MagickFalse)
2582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2583 " Reading PNG tRNS chunk.");
2584
cristyf9cca6a2010-06-04 23:49:28 +00002585 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002586
glennrpfaa852b2010-03-30 12:17:00 +00002587 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2588 (int)ping_trans_color->gray > max_sample) ||
2589 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2590 ((int)ping_trans_color->red > max_sample ||
2591 (int)ping_trans_color->green > max_sample ||
2592 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002593 {
2594 if (logging != MagickFalse)
2595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2596 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002597 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002598 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002599 image->matte=MagickFalse;
2600 }
2601 else
2602 {
glennrpa6a06632011-01-19 15:15:34 +00002603 int
2604 scale_to_short;
2605
2606 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2607
2608 /* Scale transparent_color to short */
2609 transparent_color.red= scale_to_short*ping_trans_color->red;
2610 transparent_color.green= scale_to_short*ping_trans_color->green;
2611 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy4c08aed2011-07-01 19:47:50 +00002612 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002613
glennrpfaa852b2010-03-30 12:17:00 +00002614 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002615 {
glennrp0f111982010-07-07 20:18:33 +00002616 if (logging != MagickFalse)
2617 {
2618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2619 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002620
glennrp0f111982010-07-07 20:18:33 +00002621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00002622 " scaled graylevel is %d.",transparent_color.alpha);
glennrp0f111982010-07-07 20:18:33 +00002623 }
cristy4c08aed2011-07-01 19:47:50 +00002624 transparent_color.red=transparent_color.alpha;
2625 transparent_color.green=transparent_color.alpha;
2626 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002627 }
2628 }
2629 }
2630#if defined(PNG_READ_sBIT_SUPPORTED)
2631 if (mng_info->have_global_sbit)
2632 {
glennrpfaa852b2010-03-30 12:17:00 +00002633 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002634 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2635 }
2636#endif
2637 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002638
cristy3ed852e2009-09-05 21:47:34 +00002639 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002640
2641 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2642
cristy3ed852e2009-09-05 21:47:34 +00002643 /*
2644 Initialize image structure.
2645 */
2646 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002647 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002648 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002649 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002650 if (mng_info->mng_type == 0)
2651 {
glennrpfaa852b2010-03-30 12:17:00 +00002652 mng_info->mng_width=ping_width;
2653 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002654 mng_info->frame=mng_info->image_box;
2655 mng_info->clip=mng_info->image_box;
2656 }
glennrp0fe50b42010-11-16 03:52:51 +00002657
cristy3ed852e2009-09-05 21:47:34 +00002658 else
2659 {
2660 image->page.y=mng_info->y_off[mng_info->object_id];
2661 }
glennrp0fe50b42010-11-16 03:52:51 +00002662
cristy3ed852e2009-09-05 21:47:34 +00002663 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002664 image->columns=ping_width;
2665 image->rows=ping_height;
2666 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002667 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002668 {
cristybefe4d22010-06-07 01:18:58 +00002669 size_t
2670 one;
2671
cristy3ed852e2009-09-05 21:47:34 +00002672 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002673 one=1;
2674 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002675#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2676 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002677 image->colors=256;
2678#else
2679 if (image->colors > 65536L)
2680 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002681#endif
glennrpfaa852b2010-03-30 12:17:00 +00002682 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002683 {
2684 int
2685 number_colors;
2686
2687 png_colorp
2688 palette;
2689
2690 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002691 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002692
cristy3ed852e2009-09-05 21:47:34 +00002693 if (logging != MagickFalse)
2694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2695 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2696 }
2697 }
2698
2699 if (image->storage_class == PseudoClass)
2700 {
2701 /*
2702 Initialize image colormap.
2703 */
2704 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2705 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002706
glennrpfaa852b2010-03-30 12:17:00 +00002707 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002708 {
2709 int
2710 number_colors;
2711
2712 png_colorp
2713 palette;
2714
2715 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002716
glennrp6af6cf12011-04-22 13:05:16 +00002717 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002718 {
2719 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2720 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2721 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2722 }
glennrp6af6cf12011-04-22 13:05:16 +00002723
glennrp67b9c1a2011-04-22 18:47:36 +00002724 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002725 {
2726 image->colormap[i].red=0;
2727 image->colormap[i].green=0;
2728 image->colormap[i].blue=0;
2729 }
cristy3ed852e2009-09-05 21:47:34 +00002730 }
glennrp0fe50b42010-11-16 03:52:51 +00002731
cristy3ed852e2009-09-05 21:47:34 +00002732 else
2733 {
cristybb503372010-05-27 20:51:26 +00002734 size_t
cristy3ed852e2009-09-05 21:47:34 +00002735 scale;
2736
glennrpfaa852b2010-03-30 12:17:00 +00002737 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002738
cristy3ed852e2009-09-05 21:47:34 +00002739 if (scale < 1)
2740 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002741
cristybb503372010-05-27 20:51:26 +00002742 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002743 {
2744 image->colormap[i].red=(Quantum) (i*scale);
2745 image->colormap[i].green=(Quantum) (i*scale);
2746 image->colormap[i].blue=(Quantum) (i*scale);
2747 }
2748 }
2749 }
glennrp147bc912011-03-30 18:47:21 +00002750
glennrpcb395ac2011-03-30 19:50:23 +00002751 /* Set some properties for reporting by "identify" */
2752 {
glennrp147bc912011-03-30 18:47:21 +00002753 char
2754 msg[MaxTextExtent];
2755
2756 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2757 ping_interlace_method in value */
2758
cristy3b6fd2e2011-05-20 12:53:50 +00002759 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002760 "%d, %d",(int) ping_width, (int) ping_height);
2761 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
glennrp147bc912011-03-30 18:47:21 +00002762
cristy3b6fd2e2011-05-20 12:53:50 +00002763 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
glennrp147bc912011-03-30 18:47:21 +00002764 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2765
cristy3b6fd2e2011-05-20 12:53:50 +00002766 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
glennrp147bc912011-03-30 18:47:21 +00002767 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2768
cristy3b6fd2e2011-05-20 12:53:50 +00002769 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002770 (int) ping_interlace_method);
2771 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
glennrpcb395ac2011-03-30 19:50:23 +00002772 }
glennrp147bc912011-03-30 18:47:21 +00002773
cristy3ed852e2009-09-05 21:47:34 +00002774 /*
2775 Read image scanlines.
2776 */
2777 if (image->delay != 0)
2778 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002779
glennrp0ca69b12010-07-26 01:57:52 +00002780 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002781 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2782 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002783 {
2784 if (logging != MagickFalse)
2785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002786 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002787 mng_info->scenes_found-1);
2788 png_destroy_read_struct(&ping,&ping_info,&end_info);
2789#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002790 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002791#endif
2792 if (logging != MagickFalse)
2793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2794 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002795
cristy3ed852e2009-09-05 21:47:34 +00002796 return(image);
2797 }
glennrp0fe50b42010-11-16 03:52:51 +00002798
cristy3ed852e2009-09-05 21:47:34 +00002799 if (logging != MagickFalse)
2800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2801 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002802
cristy3ed852e2009-09-05 21:47:34 +00002803 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002804 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2805 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002806
cristy3ed852e2009-09-05 21:47:34 +00002807 else
glennrpcf002022011-01-30 02:38:15 +00002808 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2809 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002810
glennrpcf002022011-01-30 02:38:15 +00002811 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002812 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002813
cristy3ed852e2009-09-05 21:47:34 +00002814 if (logging != MagickFalse)
2815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2816 " Converting PNG pixels to pixel packets");
2817 /*
2818 Convert PNG pixels to pixel packets.
2819 */
glennrpfaa852b2010-03-30 12:17:00 +00002820 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002821 {
2822 /*
2823 PNG image is corrupt.
2824 */
2825 png_destroy_read_struct(&ping,&ping_info,&end_info);
2826#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002827 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002828#endif
2829 if (quantum_info != (QuantumInfo *) NULL)
2830 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002831
glennrpcf002022011-01-30 02:38:15 +00002832 if (ping_pixels != (unsigned char *) NULL)
2833 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002834
cristy3ed852e2009-09-05 21:47:34 +00002835 if (logging != MagickFalse)
2836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2837 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002838
cristy3ed852e2009-09-05 21:47:34 +00002839 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002840 {
2841 InheritException(exception,&image->exception);
2842 image->columns=0;
2843 }
glennrp0fe50b42010-11-16 03:52:51 +00002844
cristy3ed852e2009-09-05 21:47:34 +00002845 return(GetFirstImageInList(image));
2846 }
glennrp0fe50b42010-11-16 03:52:51 +00002847
cristyed552522009-10-16 14:04:35 +00002848 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002849
cristyed552522009-10-16 14:04:35 +00002850 if (quantum_info == (QuantumInfo *) NULL)
2851 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002852
glennrpc8cbc5d2011-01-01 00:12:34 +00002853 {
2854
2855 MagickBooleanType
2856 found_transparent_pixel;
2857
2858 found_transparent_pixel=MagickFalse;
2859
cristy3ed852e2009-09-05 21:47:34 +00002860 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002861 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002862 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002863 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002864 /*
2865 Convert image to DirectClass pixel packets.
2866 */
glennrp67b9c1a2011-04-22 18:47:36 +00002867#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2868 int
2869 depth;
2870
2871 depth=(ssize_t) ping_bit_depth;
2872#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002873 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2874 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2875 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2876 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002877
glennrpc8cbc5d2011-01-01 00:12:34 +00002878 for (y=0; y < (ssize_t) image->rows; y++)
2879 {
2880 if (num_passes > 1)
2881 row_offset=ping_rowbytes*y;
2882
2883 else
2884 row_offset=0;
2885
glennrpcf002022011-01-30 02:38:15 +00002886 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002887 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2888
cristy4c08aed2011-07-01 19:47:50 +00002889 if (q == (const Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00002890 break;
2891
glennrpc8cbc5d2011-01-01 00:12:34 +00002892 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2893 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002894 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002895
2896 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2897 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002898 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002899
2900 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2901 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002902 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002903
2904 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2905 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002906 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002907
2908 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2909 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002910 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002911
glennrpc8cbc5d2011-01-01 00:12:34 +00002912 if (found_transparent_pixel == MagickFalse)
2913 {
2914 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002915 if (y== 0 && logging != MagickFalse)
2916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2917 " Looking for cheap transparent pixel");
2918
glennrpc8cbc5d2011-01-01 00:12:34 +00002919 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2920 {
glennrp5aa37f62011-01-02 03:07:57 +00002921 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2922 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy4c08aed2011-07-01 19:47:50 +00002923 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00002924 {
glennrpa6a06632011-01-19 15:15:34 +00002925 if (logging != MagickFalse)
2926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2927 " ...got one.");
2928
glennrpc8cbc5d2011-01-01 00:12:34 +00002929 found_transparent_pixel = MagickTrue;
2930 break;
2931 }
glennrp4f25bd02011-01-01 18:51:28 +00002932 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2933 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp847370c2011-07-05 17:37:15 +00002934 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2935 transparent_color.red &&
2936 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2937 transparent_color.green &&
2938 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2939 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002940 {
glennrpa6a06632011-01-19 15:15:34 +00002941 if (logging != MagickFalse)
2942 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2943 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002944 found_transparent_pixel = MagickTrue;
2945 break;
2946 }
cristy4c08aed2011-07-01 19:47:50 +00002947 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00002948 }
2949 }
2950
2951 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2952 {
2953 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2954 image->rows);
2955
2956 if (status == MagickFalse)
2957 break;
2958 }
2959 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2960 break;
2961 }
2962
2963 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2964 {
2965 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00002966 if (status == MagickFalse)
2967 break;
2968 }
cristy3ed852e2009-09-05 21:47:34 +00002969 }
cristy3ed852e2009-09-05 21:47:34 +00002970 }
glennrp0fe50b42010-11-16 03:52:51 +00002971
cristy3ed852e2009-09-05 21:47:34 +00002972 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00002973
cristy3ed852e2009-09-05 21:47:34 +00002974 for (pass=0; pass < num_passes; pass++)
2975 {
2976 Quantum
2977 *quantum_scanline;
2978
2979 register Quantum
2980 *r;
2981
2982 /*
2983 Convert grayscale image to PseudoClass pixel packets.
2984 */
glennrpc17d96f2011-06-27 01:20:11 +00002985 if (logging != MagickFalse)
2986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2987 " Converting grayscale pixels to pixel packets");
cristy4c08aed2011-07-01 19:47:50 +00002988
glennrpfaa852b2010-03-30 12:17:00 +00002989 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00002990 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002991
cristy3ed852e2009-09-05 21:47:34 +00002992 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2993 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00002994
cristy3ed852e2009-09-05 21:47:34 +00002995 if (quantum_scanline == (Quantum *) NULL)
2996 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002997
cristybb503372010-05-27 20:51:26 +00002998 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002999 {
3000 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003001 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003002
cristy3ed852e2009-09-05 21:47:34 +00003003 else
3004 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003005
glennrpcf002022011-01-30 02:38:15 +00003006 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00003007 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003008
cristy4c08aed2011-07-01 19:47:50 +00003009 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003010 break;
glennrp0fe50b42010-11-16 03:52:51 +00003011
glennrpcf002022011-01-30 02:38:15 +00003012 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003013 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003014
glennrpfaa852b2010-03-30 12:17:00 +00003015 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003016 {
3017 case 1:
3018 {
cristybb503372010-05-27 20:51:26 +00003019 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003020 bit;
3021
cristybb503372010-05-27 20:51:26 +00003022 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003023 {
3024 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003025 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003026 p++;
3027 }
glennrp0fe50b42010-11-16 03:52:51 +00003028
cristy3ed852e2009-09-05 21:47:34 +00003029 if ((image->columns % 8) != 0)
3030 {
cristybb503372010-05-27 20:51:26 +00003031 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003032 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003033 }
glennrp0fe50b42010-11-16 03:52:51 +00003034
cristy3ed852e2009-09-05 21:47:34 +00003035 break;
3036 }
glennrp47b9dd52010-11-24 18:12:06 +00003037
cristy3ed852e2009-09-05 21:47:34 +00003038 case 2:
3039 {
cristybb503372010-05-27 20:51:26 +00003040 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003041 {
glennrpa18d5bc2011-04-23 14:51:34 +00003042 *r++=(*p >> 6) & 0x03;
3043 *r++=(*p >> 4) & 0x03;
3044 *r++=(*p >> 2) & 0x03;
3045 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003046 }
glennrp0fe50b42010-11-16 03:52:51 +00003047
cristy3ed852e2009-09-05 21:47:34 +00003048 if ((image->columns % 4) != 0)
3049 {
cristybb503372010-05-27 20:51:26 +00003050 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003051 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003052 }
glennrp0fe50b42010-11-16 03:52:51 +00003053
cristy3ed852e2009-09-05 21:47:34 +00003054 break;
3055 }
glennrp47b9dd52010-11-24 18:12:06 +00003056
cristy3ed852e2009-09-05 21:47:34 +00003057 case 4:
3058 {
cristybb503372010-05-27 20:51:26 +00003059 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003060 {
glennrpa18d5bc2011-04-23 14:51:34 +00003061 *r++=(*p >> 4) & 0x0f;
3062 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003063 }
glennrp0fe50b42010-11-16 03:52:51 +00003064
cristy3ed852e2009-09-05 21:47:34 +00003065 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003066 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003067
cristy3ed852e2009-09-05 21:47:34 +00003068 break;
3069 }
glennrp47b9dd52010-11-24 18:12:06 +00003070
cristy3ed852e2009-09-05 21:47:34 +00003071 case 8:
3072 {
glennrpfaa852b2010-03-30 12:17:00 +00003073 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003074 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003075 {
glennrpa18d5bc2011-04-23 14:51:34 +00003076 *r++=*p++;
cristy4c08aed2011-07-01 19:47:50 +00003077 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3078 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003079 found_transparent_pixel = MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00003080 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003081 }
glennrp0fe50b42010-11-16 03:52:51 +00003082
cristy3ed852e2009-09-05 21:47:34 +00003083 else
cristybb503372010-05-27 20:51:26 +00003084 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003085 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003086
cristy3ed852e2009-09-05 21:47:34 +00003087 break;
3088 }
glennrp47b9dd52010-11-24 18:12:06 +00003089
cristy3ed852e2009-09-05 21:47:34 +00003090 case 16:
3091 {
cristybb503372010-05-27 20:51:26 +00003092 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003093 {
glennrpc17d96f2011-06-27 01:20:11 +00003094#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003095 size_t
3096 quantum;
3097
3098 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003099 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003100
3101 else
glennrpc17d96f2011-06-27 01:20:11 +00003102 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003103
glennrp58f77c72011-04-23 14:09:09 +00003104 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003105 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003106 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003107
3108 if (ping_color_type == 4)
3109 {
glennrpc17d96f2011-06-27 01:20:11 +00003110 if (image->colors > 256)
3111 quantum=((*p++) << 8);
3112 else
3113 quantum=0;
3114
glennrp58f77c72011-04-23 14:09:09 +00003115 quantum|=(*p++);
cristy4c08aed2011-07-01 19:47:50 +00003116 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3117 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003118 found_transparent_pixel = MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00003119 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003120 }
glennrp58f77c72011-04-23 14:09:09 +00003121
3122#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3123 *r++=(*p++);
3124 p++; /* strip low byte */
3125
3126 if (ping_color_type == 4)
3127 {
cristy4c08aed2011-07-01 19:47:50 +00003128 SetPixelAlpha(image,*p++,q);
3129 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003130 found_transparent_pixel = MagickTrue;
3131 p++;
cristy4c08aed2011-07-01 19:47:50 +00003132 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003133 }
cristy3ed852e2009-09-05 21:47:34 +00003134#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003135 }
glennrp47b9dd52010-11-24 18:12:06 +00003136
cristy3ed852e2009-09-05 21:47:34 +00003137 break;
3138 }
glennrp47b9dd52010-11-24 18:12:06 +00003139
cristy3ed852e2009-09-05 21:47:34 +00003140 default:
3141 break;
3142 }
glennrp3faa9a32011-04-23 14:00:25 +00003143
cristy3ed852e2009-09-05 21:47:34 +00003144 /*
3145 Transfer image scanline.
3146 */
3147 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003148
cristy4c08aed2011-07-01 19:47:50 +00003149 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3150
3151 if (q == (const Quantum *) NULL)
3152 break;
cristybb503372010-05-27 20:51:26 +00003153 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00003154 {
3155 SetPixelIndex(image,*r++,q);
3156 q+=GetPixelChannels(image);
3157 }
glennrp0fe50b42010-11-16 03:52:51 +00003158
cristy3ed852e2009-09-05 21:47:34 +00003159 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3160 break;
glennrp0fe50b42010-11-16 03:52:51 +00003161
cristy7a287bf2010-02-14 02:18:09 +00003162 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3163 {
cristycee97112010-05-28 00:44:52 +00003164 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003165 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003166
cristy7a287bf2010-02-14 02:18:09 +00003167 if (status == MagickFalse)
3168 break;
3169 }
cristy3ed852e2009-09-05 21:47:34 +00003170 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003171
cristy7a287bf2010-02-14 02:18:09 +00003172 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003173 {
3174 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003175
cristy3ed852e2009-09-05 21:47:34 +00003176 if (status == MagickFalse)
3177 break;
3178 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003179
cristy3ed852e2009-09-05 21:47:34 +00003180 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3181 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003182
3183 image->matte=found_transparent_pixel;
3184
3185 if (logging != MagickFalse)
3186 {
3187 if (found_transparent_pixel != MagickFalse)
3188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3189 " Found transparent pixel");
3190 else
glennrp5aa37f62011-01-02 03:07:57 +00003191 {
3192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3193 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003194
glennrp5aa37f62011-01-02 03:07:57 +00003195 ping_color_type&=0x03;
3196 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003197 }
3198 }
3199
cristyb32b90a2009-09-07 21:45:48 +00003200 if (quantum_info != (QuantumInfo *) NULL)
3201 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00003202
cristy5c6f7892010-05-05 22:53:29 +00003203 if (image->storage_class == PseudoClass)
3204 {
cristyaeb2cbc2010-05-07 13:28:58 +00003205 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003206 matte;
3207
3208 matte=image->matte;
3209 image->matte=MagickFalse;
3210 (void) SyncImage(image);
cristyaeb2cbc2010-05-07 13:28:58 +00003211 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003212 }
glennrp47b9dd52010-11-24 18:12:06 +00003213
glennrp4eb39312011-03-30 21:34:55 +00003214 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003215
3216 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003217 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003218 {
3219 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003220 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003221 image->colors=2;
3222 (void) SetImageBackgroundColor(image);
3223#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003224 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003225#endif
3226 if (logging != MagickFalse)
3227 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3228 " exit ReadOnePNGImage() early.");
3229 return(image);
3230 }
glennrp47b9dd52010-11-24 18:12:06 +00003231
glennrpfaa852b2010-03-30 12:17:00 +00003232 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003233 {
3234 ClassType
3235 storage_class;
3236
3237 /*
3238 Image has a transparent background.
3239 */
3240 storage_class=image->storage_class;
3241 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003242
glennrp3c218112010-11-27 15:31:26 +00003243/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003244
glennrp0fe50b42010-11-16 03:52:51 +00003245 if (storage_class == PseudoClass)
3246 {
3247 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003248 {
glennrp0fe50b42010-11-16 03:52:51 +00003249 for (x=0; x < ping_num_trans; x++)
3250 {
cristy4c08aed2011-07-01 19:47:50 +00003251 image->colormap[x].alpha =
3252 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003253 }
glennrpc11cf6a2010-03-20 16:46:19 +00003254 }
glennrp47b9dd52010-11-24 18:12:06 +00003255
glennrp0fe50b42010-11-16 03:52:51 +00003256 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3257 {
3258 for (x=0; x < (int) image->colors; x++)
3259 {
3260 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy4c08aed2011-07-01 19:47:50 +00003261 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003262 {
cristy4c08aed2011-07-01 19:47:50 +00003263 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003264 }
3265 }
3266 }
3267 (void) SyncImage(image);
3268 }
glennrp47b9dd52010-11-24 18:12:06 +00003269
glennrpa6a06632011-01-19 15:15:34 +00003270#if 1 /* Should have already been done above, but glennrp problem P10
3271 * needs this.
3272 */
glennrp0fe50b42010-11-16 03:52:51 +00003273 else
3274 {
3275 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003276 {
glennrp0fe50b42010-11-16 03:52:51 +00003277 image->storage_class=storage_class;
3278 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3279
cristy4c08aed2011-07-01 19:47:50 +00003280 if (q == (const Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003281 break;
3282
glennrp0fe50b42010-11-16 03:52:51 +00003283
glennrpa6a06632011-01-19 15:15:34 +00003284 /* Caution: on a Q8 build, this does not distinguish between
3285 * 16-bit colors that differ only in the low byte
3286 */
glennrp0fe50b42010-11-16 03:52:51 +00003287 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3288 {
glennrp847370c2011-07-05 17:37:15 +00003289 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3290 transparent_color.red &&
3291 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3292 transparent_color.green &&
3293 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3294 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003295 {
cristy4c08aed2011-07-01 19:47:50 +00003296 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003297 }
glennrp0fe50b42010-11-16 03:52:51 +00003298
glennrp67b9c1a2011-04-22 18:47:36 +00003299#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003300 else
glennrp4f25bd02011-01-01 18:51:28 +00003301 {
cristy4c08aed2011-07-01 19:47:50 +00003302 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003303 }
glennrpa6a06632011-01-19 15:15:34 +00003304#endif
glennrp0fe50b42010-11-16 03:52:51 +00003305
cristy4c08aed2011-07-01 19:47:50 +00003306 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003307 }
3308
3309 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3310 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003311 }
glennrp0fe50b42010-11-16 03:52:51 +00003312 }
glennrpa6a06632011-01-19 15:15:34 +00003313#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003314
cristy3ed852e2009-09-05 21:47:34 +00003315 image->storage_class=DirectClass;
3316 }
glennrp3c218112010-11-27 15:31:26 +00003317
cristyb40fc462010-08-08 00:49:49 +00003318 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3319 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3320 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00003321
cristyeb3b22a2011-03-31 20:16:11 +00003322 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003323 {
3324 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003325 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3326 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003327 else
glennrpa0ed0092011-04-18 16:36:29 +00003328 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3329 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003330
glennrp4eb39312011-03-30 21:34:55 +00003331 if (status != MagickFalse)
3332 for (i=0; i < (ssize_t) num_text; i++)
3333 {
3334 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003335
glennrp4eb39312011-03-30 21:34:55 +00003336 if (logging != MagickFalse)
3337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3338 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003339
glennrp4eb39312011-03-30 21:34:55 +00003340 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003341 {
glennrp4eb39312011-03-30 21:34:55 +00003342 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
3343 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003344 }
glennrp0fe50b42010-11-16 03:52:51 +00003345
glennrp4eb39312011-03-30 21:34:55 +00003346 else
3347 {
3348 char
3349 *value;
3350
3351 length=text[i].text_length;
3352 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3353 sizeof(*value));
3354 if (value == (char *) NULL)
3355 {
3356 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3357 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3358 image->filename);
3359 break;
3360 }
3361 *value='\0';
3362 (void) ConcatenateMagickString(value,text[i].text,length+2);
3363
3364 /* Don't save "density" or "units" property if we have a pHYs
3365 * chunk
3366 */
3367 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3368 (LocaleCompare(text[i].key,"density") != 0 &&
3369 LocaleCompare(text[i].key,"units") != 0))
3370 (void) SetImageProperty(image,text[i].key,value);
3371
3372 if (logging != MagickFalse)
3373 {
3374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3375 " length: %lu",(unsigned long) length);
3376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3377 " Keyword: %s",text[i].key);
3378 }
3379
3380 value=DestroyString(value);
3381 }
3382 }
3383 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003384 }
glennrp3c218112010-11-27 15:31:26 +00003385
cristy3ed852e2009-09-05 21:47:34 +00003386#ifdef MNG_OBJECT_BUFFERS
3387 /*
3388 Store the object if necessary.
3389 */
3390 if (object_id && !mng_info->frozen[object_id])
3391 {
3392 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3393 {
3394 /*
3395 create a new object buffer.
3396 */
3397 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003398 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003399
cristy3ed852e2009-09-05 21:47:34 +00003400 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3401 {
3402 mng_info->ob[object_id]->image=(Image *) NULL;
3403 mng_info->ob[object_id]->reference_count=1;
3404 }
3405 }
glennrp47b9dd52010-11-24 18:12:06 +00003406
cristy3ed852e2009-09-05 21:47:34 +00003407 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3408 mng_info->ob[object_id]->frozen)
3409 {
3410 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3411 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3412 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3413 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003414
cristy3ed852e2009-09-05 21:47:34 +00003415 if (mng_info->ob[object_id]->frozen)
3416 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3417 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3418 "`%s'",image->filename);
3419 }
glennrp0fe50b42010-11-16 03:52:51 +00003420
cristy3ed852e2009-09-05 21:47:34 +00003421 else
3422 {
cristy3ed852e2009-09-05 21:47:34 +00003423
3424 if (mng_info->ob[object_id]->image != (Image *) NULL)
3425 mng_info->ob[object_id]->image=DestroyImage
3426 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003427
cristy3ed852e2009-09-05 21:47:34 +00003428 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3429 &image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00003430
cristy3ed852e2009-09-05 21:47:34 +00003431 if (mng_info->ob[object_id]->image != (Image *) NULL)
3432 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003433
cristy3ed852e2009-09-05 21:47:34 +00003434 else
3435 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3436 ResourceLimitError,"Cloning image for object buffer failed",
3437 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003438
glennrpfaa852b2010-03-30 12:17:00 +00003439 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003440 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003441
glennrpfaa852b2010-03-30 12:17:00 +00003442 mng_info->ob[object_id]->width=ping_width;
3443 mng_info->ob[object_id]->height=ping_height;
3444 mng_info->ob[object_id]->color_type=ping_color_type;
3445 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3446 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3447 mng_info->ob[object_id]->compression_method=
3448 ping_compression_method;
3449 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003450
glennrpfaa852b2010-03-30 12:17:00 +00003451 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003452 {
3453 int
3454 number_colors;
3455
3456 png_colorp
3457 plte;
3458
3459 /*
3460 Copy the PLTE to the object buffer.
3461 */
3462 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3463 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003464
cristy3ed852e2009-09-05 21:47:34 +00003465 for (i=0; i < number_colors; i++)
3466 {
3467 mng_info->ob[object_id]->plte[i]=plte[i];
3468 }
3469 }
glennrp47b9dd52010-11-24 18:12:06 +00003470
cristy3ed852e2009-09-05 21:47:34 +00003471 else
3472 mng_info->ob[object_id]->plte_length=0;
3473 }
3474 }
3475#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003476
3477 /* Set image->matte to MagickTrue if the input colortype supports
3478 * alpha or if a valid tRNS chunk is present, no matter whether there
3479 * is actual transparency present.
3480 */
3481 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3482 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3483 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3484 MagickTrue : MagickFalse;
3485
glennrpcb395ac2011-03-30 19:50:23 +00003486 /* Set more properties for identify to retrieve */
3487 {
3488 char
3489 msg[MaxTextExtent];
3490
glennrp4eb39312011-03-30 21:34:55 +00003491 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003492 {
3493 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003494 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003495 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrpcb395ac2011-03-30 19:50:23 +00003496 (void) SetImageProperty(image,"PNG:text ",msg);
3497 }
3498
3499 if (num_raw_profiles != 0)
3500 {
cristy3b6fd2e2011-05-20 12:53:50 +00003501 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003502 "%d were found", num_raw_profiles);
3503 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3504 }
3505
glennrpcb395ac2011-03-30 19:50:23 +00003506 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003507 {
cristy3b6fd2e2011-05-20 12:53:50 +00003508 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003509 "chunk was found (see Chromaticity, above)");
3510 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3511 }
glennrpcb395ac2011-03-30 19:50:23 +00003512
3513 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003514 {
cristy3b6fd2e2011-05-20 12:53:50 +00003515 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003516 "chunk was found (see Background color, above)");
3517 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3518 }
3519
cristy3b6fd2e2011-05-20 12:53:50 +00003520 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003521 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003522
3523 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3524 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3525
glennrpcb395ac2011-03-30 19:50:23 +00003526 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3527 (void) SetImageProperty(image,"PNG:tRNS ",msg);
glennrp4eb39312011-03-30 21:34:55 +00003528
3529#if defined(PNG_sRGB_SUPPORTED)
3530 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3531 {
cristy3b6fd2e2011-05-20 12:53:50 +00003532 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003533 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003534 (int) intent);
3535 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3536 }
3537#endif
3538
3539 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3540 {
cristy3b6fd2e2011-05-20 12:53:50 +00003541 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003542 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003543 file_gamma);
3544 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3545 }
3546
3547#if defined(PNG_pHYs_SUPPORTED)
3548 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3549 {
cristy3b6fd2e2011-05-20 12:53:50 +00003550 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003551 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003552 (double) x_resolution,(double) y_resolution, unit_type);
3553 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3554 }
3555#endif
3556
3557#if defined(PNG_oFFs_SUPPORTED)
3558 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3559 {
cristy3b6fd2e2011-05-20 12:53:50 +00003560 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003561 (double) image->page.x,(double) image->page.y);
3562 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3563 }
3564#endif
3565
glennrp07523c72011-03-31 18:12:10 +00003566 if ((image->page.width != 0 && image->page.width != image->columns) ||
3567 (image->page.height != 0 && image->page.height != image->rows))
3568 {
cristy3b6fd2e2011-05-20 12:53:50 +00003569 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003570 "width=%.20g, height=%.20g",
3571 (double) image->page.width,(double) image->page.height);
3572 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3573 }
glennrpcb395ac2011-03-30 19:50:23 +00003574 }
3575
cristy3ed852e2009-09-05 21:47:34 +00003576 /*
3577 Relinquish resources.
3578 */
3579 png_destroy_read_struct(&ping,&ping_info,&end_info);
3580
glennrpcf002022011-01-30 02:38:15 +00003581 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003582#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003583 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003584#endif
3585
3586 if (logging != MagickFalse)
3587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3588 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003589
cristy3ed852e2009-09-05 21:47:34 +00003590 return(image);
3591
3592/* end of reading one PNG image */
3593}
3594
3595static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3596{
3597 Image
3598 *image,
3599 *previous;
3600
3601 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003602 have_mng_structure,
3603 logging,
cristy3ed852e2009-09-05 21:47:34 +00003604 status;
3605
3606 MngInfo
3607 *mng_info;
3608
3609 char
3610 magic_number[MaxTextExtent];
3611
cristy3ed852e2009-09-05 21:47:34 +00003612 ssize_t
3613 count;
3614
3615 /*
3616 Open image file.
3617 */
3618 assert(image_info != (const ImageInfo *) NULL);
3619 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003620
cristy3ed852e2009-09-05 21:47:34 +00003621 if (image_info->debug != MagickFalse)
3622 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3623 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003624
cristy3ed852e2009-09-05 21:47:34 +00003625 assert(exception != (ExceptionInfo *) NULL);
3626 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003627 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003628 image=AcquireImage(image_info);
3629 mng_info=(MngInfo *) NULL;
3630 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003631
cristy3ed852e2009-09-05 21:47:34 +00003632 if (status == MagickFalse)
3633 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003634
cristy3ed852e2009-09-05 21:47:34 +00003635 /*
3636 Verify PNG signature.
3637 */
3638 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003639
glennrpdde35db2011-02-21 12:06:32 +00003640 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003641 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003642
cristy3ed852e2009-09-05 21:47:34 +00003643 /*
3644 Allocate a MngInfo structure.
3645 */
3646 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003647 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003648
cristy3ed852e2009-09-05 21:47:34 +00003649 if (mng_info == (MngInfo *) NULL)
3650 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003651
cristy3ed852e2009-09-05 21:47:34 +00003652 /*
3653 Initialize members of the MngInfo structure.
3654 */
3655 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3656 mng_info->image=image;
3657 have_mng_structure=MagickTrue;
3658
3659 previous=image;
3660 image=ReadOnePNGImage(mng_info,image_info,exception);
3661 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003662
cristy3ed852e2009-09-05 21:47:34 +00003663 if (image == (Image *) NULL)
3664 {
3665 if (previous != (Image *) NULL)
3666 {
3667 if (previous->signature != MagickSignature)
3668 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003669
cristy3ed852e2009-09-05 21:47:34 +00003670 (void) CloseBlob(previous);
3671 (void) DestroyImageList(previous);
3672 }
glennrp0fe50b42010-11-16 03:52:51 +00003673
cristy3ed852e2009-09-05 21:47:34 +00003674 if (logging != MagickFalse)
3675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3676 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003677
cristy3ed852e2009-09-05 21:47:34 +00003678 return((Image *) NULL);
3679 }
glennrp47b9dd52010-11-24 18:12:06 +00003680
cristy3ed852e2009-09-05 21:47:34 +00003681 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003682
cristy3ed852e2009-09-05 21:47:34 +00003683 if ((image->columns == 0) || (image->rows == 0))
3684 {
3685 if (logging != MagickFalse)
3686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3687 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003688
cristy3ed852e2009-09-05 21:47:34 +00003689 ThrowReaderException(CorruptImageError,"CorruptImage");
3690 }
glennrp47b9dd52010-11-24 18:12:06 +00003691
cristy3ed852e2009-09-05 21:47:34 +00003692 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3693 {
3694 (void) SetImageType(image,TrueColorType);
3695 image->matte=MagickFalse;
3696 }
glennrp0fe50b42010-11-16 03:52:51 +00003697
cristy3ed852e2009-09-05 21:47:34 +00003698 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3699 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +00003700
cristy3ed852e2009-09-05 21:47:34 +00003701 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3703 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3704 (double) image->page.width,(double) image->page.height,
3705 (double) image->page.x,(double) image->page.y);
3706
3707 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003709
cristy3ed852e2009-09-05 21:47:34 +00003710 return(image);
3711}
3712
3713
3714
3715#if defined(JNG_SUPPORTED)
3716/*
3717%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3718% %
3719% %
3720% %
3721% R e a d O n e J N G I m a g e %
3722% %
3723% %
3724% %
3725%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3726%
3727% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3728% (minus the 8-byte signature) and returns it. It allocates the memory
3729% necessary for the new Image structure and returns a pointer to the new
3730% image.
3731%
3732% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3733%
3734% The format of the ReadOneJNGImage method is:
3735%
3736% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3737% ExceptionInfo *exception)
3738%
3739% A description of each parameter follows:
3740%
3741% o mng_info: Specifies a pointer to a MngInfo structure.
3742%
3743% o image_info: the image info.
3744%
3745% o exception: return any errors or warnings in this structure.
3746%
3747*/
3748static Image *ReadOneJNGImage(MngInfo *mng_info,
3749 const ImageInfo *image_info, ExceptionInfo *exception)
3750{
3751 Image
3752 *alpha_image,
3753 *color_image,
3754 *image,
3755 *jng_image;
3756
3757 ImageInfo
3758 *alpha_image_info,
3759 *color_image_info;
3760
cristy4383ec82011-01-05 15:42:32 +00003761 MagickBooleanType
3762 logging;
3763
cristybb503372010-05-27 20:51:26 +00003764 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003765 y;
3766
3767 MagickBooleanType
3768 status;
3769
3770 png_uint_32
3771 jng_height,
3772 jng_width;
3773
3774 png_byte
3775 jng_color_type,
3776 jng_image_sample_depth,
3777 jng_image_compression_method,
3778 jng_image_interlace_method,
3779 jng_alpha_sample_depth,
3780 jng_alpha_compression_method,
3781 jng_alpha_filter_method,
3782 jng_alpha_interlace_method;
3783
cristy4c08aed2011-07-01 19:47:50 +00003784 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003785 *s;
3786
cristybb503372010-05-27 20:51:26 +00003787 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003788 i,
3789 x;
3790
cristy4c08aed2011-07-01 19:47:50 +00003791 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003792 *q;
3793
3794 register unsigned char
3795 *p;
3796
3797 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003798 read_JSEP,
3799 reading_idat,
3800 skip_to_iend;
3801
cristybb503372010-05-27 20:51:26 +00003802 size_t
cristy3ed852e2009-09-05 21:47:34 +00003803 length;
3804
3805 jng_alpha_compression_method=0;
3806 jng_alpha_sample_depth=8;
3807 jng_color_type=0;
3808 jng_height=0;
3809 jng_width=0;
3810 alpha_image=(Image *) NULL;
3811 color_image=(Image *) NULL;
3812 alpha_image_info=(ImageInfo *) NULL;
3813 color_image_info=(ImageInfo *) NULL;
3814
3815 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003816 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003817
3818 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003819
cristy4c08aed2011-07-01 19:47:50 +00003820 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003821 {
3822 /*
3823 Allocate next image structure.
3824 */
3825 if (logging != MagickFalse)
3826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3827 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003828
cristy3ed852e2009-09-05 21:47:34 +00003829 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00003830
cristy3ed852e2009-09-05 21:47:34 +00003831 if (GetNextImageInList(image) == (Image *) NULL)
3832 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003833
cristy3ed852e2009-09-05 21:47:34 +00003834 image=SyncNextImageInList(image);
3835 }
3836 mng_info->image=image;
3837
3838 /*
3839 Signature bytes have already been read.
3840 */
3841
3842 read_JSEP=MagickFalse;
3843 reading_idat=MagickFalse;
3844 skip_to_iend=MagickFalse;
3845 for (;;)
3846 {
3847 char
3848 type[MaxTextExtent];
3849
3850 unsigned char
3851 *chunk;
3852
3853 unsigned int
3854 count;
3855
3856 /*
3857 Read a new JNG chunk.
3858 */
3859 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3860 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003861
cristy3ed852e2009-09-05 21:47:34 +00003862 if (status == MagickFalse)
3863 break;
glennrp0fe50b42010-11-16 03:52:51 +00003864
cristy3ed852e2009-09-05 21:47:34 +00003865 type[0]='\0';
3866 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3867 length=ReadBlobMSBLong(image);
3868 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3869
3870 if (logging != MagickFalse)
3871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003872 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3873 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003874
3875 if (length > PNG_UINT_31_MAX || count == 0)
3876 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003877
cristy3ed852e2009-09-05 21:47:34 +00003878 p=NULL;
3879 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003880
cristy3ed852e2009-09-05 21:47:34 +00003881 if (length)
3882 {
3883 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003884
cristy3ed852e2009-09-05 21:47:34 +00003885 if (chunk == (unsigned char *) NULL)
3886 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003887
cristybb503372010-05-27 20:51:26 +00003888 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003889 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003890
cristy3ed852e2009-09-05 21:47:34 +00003891 p=chunk;
3892 }
glennrp47b9dd52010-11-24 18:12:06 +00003893
cristy3ed852e2009-09-05 21:47:34 +00003894 (void) ReadBlobMSBLong(image); /* read crc word */
3895
3896 if (skip_to_iend)
3897 {
3898 if (length)
3899 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003900
cristy3ed852e2009-09-05 21:47:34 +00003901 continue;
3902 }
3903
3904 if (memcmp(type,mng_JHDR,4) == 0)
3905 {
3906 if (length == 16)
3907 {
cristybb503372010-05-27 20:51:26 +00003908 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003909 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003910 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003911 (p[6] << 8) | p[7]);
3912 jng_color_type=p[8];
3913 jng_image_sample_depth=p[9];
3914 jng_image_compression_method=p[10];
3915 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003916
cristy3ed852e2009-09-05 21:47:34 +00003917 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3918 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003919
cristy3ed852e2009-09-05 21:47:34 +00003920 jng_alpha_sample_depth=p[12];
3921 jng_alpha_compression_method=p[13];
3922 jng_alpha_filter_method=p[14];
3923 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003924
cristy3ed852e2009-09-05 21:47:34 +00003925 if (logging != MagickFalse)
3926 {
3927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003928 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003929
cristy3ed852e2009-09-05 21:47:34 +00003930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003931 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003932
cristy3ed852e2009-09-05 21:47:34 +00003933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3934 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003935
cristy3ed852e2009-09-05 21:47:34 +00003936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3937 " jng_image_sample_depth: %3d",
3938 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003939
cristy3ed852e2009-09-05 21:47:34 +00003940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3941 " jng_image_compression_method:%3d",
3942 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003943
cristy3ed852e2009-09-05 21:47:34 +00003944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3945 " jng_image_interlace_method: %3d",
3946 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003947
cristy3ed852e2009-09-05 21:47:34 +00003948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3949 " jng_alpha_sample_depth: %3d",
3950 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003951
cristy3ed852e2009-09-05 21:47:34 +00003952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3953 " jng_alpha_compression_method:%3d",
3954 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003955
cristy3ed852e2009-09-05 21:47:34 +00003956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3957 " jng_alpha_filter_method: %3d",
3958 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00003959
cristy3ed852e2009-09-05 21:47:34 +00003960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3961 " jng_alpha_interlace_method: %3d",
3962 jng_alpha_interlace_method);
3963 }
3964 }
glennrp47b9dd52010-11-24 18:12:06 +00003965
cristy3ed852e2009-09-05 21:47:34 +00003966 if (length)
3967 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003968
cristy3ed852e2009-09-05 21:47:34 +00003969 continue;
3970 }
3971
3972
3973 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3974 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3975 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3976 {
3977 /*
3978 o create color_image
3979 o open color_blob, attached to color_image
3980 o if (color type has alpha)
3981 open alpha_blob, attached to alpha_image
3982 */
3983
cristy73bd4a52010-10-05 11:24:23 +00003984 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003985
cristy3ed852e2009-09-05 21:47:34 +00003986 if (color_image_info == (ImageInfo *) NULL)
3987 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003988
cristy3ed852e2009-09-05 21:47:34 +00003989 GetImageInfo(color_image_info);
3990 color_image=AcquireImage(color_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003991
cristy3ed852e2009-09-05 21:47:34 +00003992 if (color_image == (Image *) NULL)
3993 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3994
3995 if (logging != MagickFalse)
3996 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3997 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003998
cristy3ed852e2009-09-05 21:47:34 +00003999 (void) AcquireUniqueFilename(color_image->filename);
4000 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4001 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004002
cristy3ed852e2009-09-05 21:47:34 +00004003 if (status == MagickFalse)
4004 return((Image *) NULL);
4005
4006 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4007 {
4008 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004009 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004010
cristy3ed852e2009-09-05 21:47:34 +00004011 if (alpha_image_info == (ImageInfo *) NULL)
4012 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004013
cristy3ed852e2009-09-05 21:47:34 +00004014 GetImageInfo(alpha_image_info);
4015 alpha_image=AcquireImage(alpha_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00004016
cristy3ed852e2009-09-05 21:47:34 +00004017 if (alpha_image == (Image *) NULL)
4018 {
4019 alpha_image=DestroyImage(alpha_image);
4020 ThrowReaderException(ResourceLimitError,
4021 "MemoryAllocationFailed");
4022 }
glennrp0fe50b42010-11-16 03:52:51 +00004023
cristy3ed852e2009-09-05 21:47:34 +00004024 if (logging != MagickFalse)
4025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4026 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004027
cristy3ed852e2009-09-05 21:47:34 +00004028 (void) AcquireUniqueFilename(alpha_image->filename);
4029 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4030 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004031
cristy3ed852e2009-09-05 21:47:34 +00004032 if (status == MagickFalse)
4033 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004034
cristy3ed852e2009-09-05 21:47:34 +00004035 if (jng_alpha_compression_method == 0)
4036 {
4037 unsigned char
4038 data[18];
4039
4040 if (logging != MagickFalse)
4041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4042 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004043
cristy3ed852e2009-09-05 21:47:34 +00004044 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4045 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004046
cristy3ed852e2009-09-05 21:47:34 +00004047 (void) WriteBlobMSBULong(alpha_image,13L);
4048 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004049 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004050 PNGLong(data+4,jng_width);
4051 PNGLong(data+8,jng_height);
4052 data[12]=jng_alpha_sample_depth;
4053 data[13]=0; /* color_type gray */
4054 data[14]=0; /* compression method 0 */
4055 data[15]=0; /* filter_method 0 */
4056 data[16]=0; /* interlace_method 0 */
4057 (void) WriteBlob(alpha_image,17,data);
4058 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4059 }
4060 }
4061 reading_idat=MagickTrue;
4062 }
4063
4064 if (memcmp(type,mng_JDAT,4) == 0)
4065 {
glennrp47b9dd52010-11-24 18:12:06 +00004066 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004067
4068 if (logging != MagickFalse)
4069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4070 " Copying JDAT chunk data to color_blob.");
4071
4072 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004073
cristy3ed852e2009-09-05 21:47:34 +00004074 if (length)
4075 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004076
cristy3ed852e2009-09-05 21:47:34 +00004077 continue;
4078 }
4079
4080 if (memcmp(type,mng_IDAT,4) == 0)
4081 {
4082 png_byte
4083 data[5];
4084
glennrp47b9dd52010-11-24 18:12:06 +00004085 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004086
4087 if (image_info->ping == MagickFalse)
4088 {
4089 if (logging != MagickFalse)
4090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4091 " Copying IDAT chunk data to alpha_blob.");
4092
cristybb503372010-05-27 20:51:26 +00004093 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004094 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004095 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004096 (void) WriteBlob(alpha_image,4,data);
4097 (void) WriteBlob(alpha_image,length,chunk);
4098 (void) WriteBlobMSBULong(alpha_image,
4099 crc32(crc32(0,data,4),chunk,(uInt) length));
4100 }
glennrp0fe50b42010-11-16 03:52:51 +00004101
cristy3ed852e2009-09-05 21:47:34 +00004102 if (length)
4103 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004104
cristy3ed852e2009-09-05 21:47:34 +00004105 continue;
4106 }
4107
4108 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4109 {
glennrp47b9dd52010-11-24 18:12:06 +00004110 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004111
4112 if (image_info->ping == MagickFalse)
4113 {
4114 if (logging != MagickFalse)
4115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4116 " Copying JDAA chunk data to alpha_blob.");
4117
4118 (void) WriteBlob(alpha_image,length,chunk);
4119 }
glennrp0fe50b42010-11-16 03:52:51 +00004120
cristy3ed852e2009-09-05 21:47:34 +00004121 if (length)
4122 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004123
cristy3ed852e2009-09-05 21:47:34 +00004124 continue;
4125 }
4126
4127 if (memcmp(type,mng_JSEP,4) == 0)
4128 {
4129 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004130
cristy3ed852e2009-09-05 21:47:34 +00004131 if (length)
4132 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004133
cristy3ed852e2009-09-05 21:47:34 +00004134 continue;
4135 }
4136
4137 if (memcmp(type,mng_bKGD,4) == 0)
4138 {
4139 if (length == 2)
4140 {
4141 image->background_color.red=ScaleCharToQuantum(p[1]);
4142 image->background_color.green=image->background_color.red;
4143 image->background_color.blue=image->background_color.red;
4144 }
glennrp0fe50b42010-11-16 03:52:51 +00004145
cristy3ed852e2009-09-05 21:47:34 +00004146 if (length == 6)
4147 {
4148 image->background_color.red=ScaleCharToQuantum(p[1]);
4149 image->background_color.green=ScaleCharToQuantum(p[3]);
4150 image->background_color.blue=ScaleCharToQuantum(p[5]);
4151 }
glennrp0fe50b42010-11-16 03:52:51 +00004152
cristy3ed852e2009-09-05 21:47:34 +00004153 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4154 continue;
4155 }
4156
4157 if (memcmp(type,mng_gAMA,4) == 0)
4158 {
4159 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004160 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004161
cristy3ed852e2009-09-05 21:47:34 +00004162 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4163 continue;
4164 }
4165
4166 if (memcmp(type,mng_cHRM,4) == 0)
4167 {
4168 if (length == 32)
4169 {
cristy8182b072010-05-30 20:10:53 +00004170 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4171 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4172 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4173 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4174 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4175 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4176 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4177 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004178 }
glennrp47b9dd52010-11-24 18:12:06 +00004179
cristy3ed852e2009-09-05 21:47:34 +00004180 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4181 continue;
4182 }
4183
4184 if (memcmp(type,mng_sRGB,4) == 0)
4185 {
4186 if (length == 1)
4187 {
glennrpe610a072010-08-05 17:08:46 +00004188 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004189 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004190 image->gamma=0.45455f;
4191 image->chromaticity.red_primary.x=0.6400f;
4192 image->chromaticity.red_primary.y=0.3300f;
4193 image->chromaticity.green_primary.x=0.3000f;
4194 image->chromaticity.green_primary.y=0.6000f;
4195 image->chromaticity.blue_primary.x=0.1500f;
4196 image->chromaticity.blue_primary.y=0.0600f;
4197 image->chromaticity.white_point.x=0.3127f;
4198 image->chromaticity.white_point.y=0.3290f;
4199 }
glennrp47b9dd52010-11-24 18:12:06 +00004200
cristy3ed852e2009-09-05 21:47:34 +00004201 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4202 continue;
4203 }
4204
4205 if (memcmp(type,mng_oFFs,4) == 0)
4206 {
4207 if (length > 8)
4208 {
glennrp5eae7602011-02-22 15:21:32 +00004209 image->page.x=(ssize_t) mng_get_long(p);
4210 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004211
cristy3ed852e2009-09-05 21:47:34 +00004212 if ((int) p[8] != 0)
4213 {
4214 image->page.x/=10000;
4215 image->page.y/=10000;
4216 }
4217 }
glennrp47b9dd52010-11-24 18:12:06 +00004218
cristy3ed852e2009-09-05 21:47:34 +00004219 if (length)
4220 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004221
cristy3ed852e2009-09-05 21:47:34 +00004222 continue;
4223 }
4224
4225 if (memcmp(type,mng_pHYs,4) == 0)
4226 {
4227 if (length > 8)
4228 {
cristy8182b072010-05-30 20:10:53 +00004229 image->x_resolution=(double) mng_get_long(p);
4230 image->y_resolution=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004231 if ((int) p[8] == PNG_RESOLUTION_METER)
4232 {
4233 image->units=PixelsPerCentimeterResolution;
4234 image->x_resolution=image->x_resolution/100.0f;
4235 image->y_resolution=image->y_resolution/100.0f;
4236 }
4237 }
glennrp0fe50b42010-11-16 03:52:51 +00004238
cristy3ed852e2009-09-05 21:47:34 +00004239 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4240 continue;
4241 }
4242
4243#if 0
4244 if (memcmp(type,mng_iCCP,4) == 0)
4245 {
glennrpfd05d622011-02-25 04:10:33 +00004246 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004247 if (length)
4248 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004249
cristy3ed852e2009-09-05 21:47:34 +00004250 continue;
4251 }
4252#endif
4253
4254 if (length)
4255 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4256
4257 if (memcmp(type,mng_IEND,4))
4258 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004259
cristy3ed852e2009-09-05 21:47:34 +00004260 break;
4261 }
4262
4263
4264 /* IEND found */
4265
4266 /*
4267 Finish up reading image data:
4268
4269 o read main image from color_blob.
4270
4271 o close color_blob.
4272
4273 o if (color_type has alpha)
4274 if alpha_encoding is PNG
4275 read secondary image from alpha_blob via ReadPNG
4276 if alpha_encoding is JPEG
4277 read secondary image from alpha_blob via ReadJPEG
4278
4279 o close alpha_blob.
4280
4281 o copy intensity of secondary image into
cristy4c08aed2011-07-01 19:47:50 +00004282 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004283
4284 o destroy the secondary image.
4285 */
4286
4287 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004288
cristy3ed852e2009-09-05 21:47:34 +00004289 if (logging != MagickFalse)
4290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4291 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004292
cristy3b6fd2e2011-05-20 12:53:50 +00004293 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004294 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004295
cristy3ed852e2009-09-05 21:47:34 +00004296 color_image_info->ping=MagickFalse; /* To do: avoid this */
4297 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004298
cristy3ed852e2009-09-05 21:47:34 +00004299 if (jng_image == (Image *) NULL)
4300 return((Image *) NULL);
4301
4302 (void) RelinquishUniqueFileResource(color_image->filename);
4303 color_image=DestroyImage(color_image);
4304 color_image_info=DestroyImageInfo(color_image_info);
4305
4306 if (jng_image == (Image *) NULL)
4307 return((Image *) NULL);
4308
4309 if (logging != MagickFalse)
4310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4311 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004312
cristy3ed852e2009-09-05 21:47:34 +00004313 image->rows=jng_height;
4314 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004315
cristybb503372010-05-27 20:51:26 +00004316 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004317 {
4318 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
4319 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004320 for (x=(ssize_t) image->columns; x != 0; x--)
4321 {
4322 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4323 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4324 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4325 q+=GetPixelChannels(image);
4326 s+=GetPixelChannels(jng_image);
4327 }
glennrp47b9dd52010-11-24 18:12:06 +00004328
cristy3ed852e2009-09-05 21:47:34 +00004329 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4330 break;
4331 }
glennrp0fe50b42010-11-16 03:52:51 +00004332
cristy3ed852e2009-09-05 21:47:34 +00004333 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004334
cristy3ed852e2009-09-05 21:47:34 +00004335 if (image_info->ping == MagickFalse)
4336 {
4337 if (jng_color_type >= 12)
4338 {
4339 if (jng_alpha_compression_method == 0)
4340 {
4341 png_byte
4342 data[5];
4343 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4344 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004345 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004346 (void) WriteBlob(alpha_image,4,data);
4347 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4348 }
glennrp0fe50b42010-11-16 03:52:51 +00004349
cristy3ed852e2009-09-05 21:47:34 +00004350 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004351
cristy3ed852e2009-09-05 21:47:34 +00004352 if (logging != MagickFalse)
4353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00004354 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004355
cristy3b6fd2e2011-05-20 12:53:50 +00004356 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004357 "%s",alpha_image->filename);
4358
4359 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004360
cristy3ed852e2009-09-05 21:47:34 +00004361 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004362 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004363 {
4364 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristy4c08aed2011-07-01 19:47:50 +00004365 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +00004366 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004367
cristy3ed852e2009-09-05 21:47:34 +00004368 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00004369 for (x=(ssize_t) image->columns; x != 0; x--)
4370 {
4371 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4372 q+=GetPixelChannels(image);
4373 s+=GetPixelChannels(jng_image);
4374 }
glennrp0fe50b42010-11-16 03:52:51 +00004375
cristy3ed852e2009-09-05 21:47:34 +00004376 else
cristy4c08aed2011-07-01 19:47:50 +00004377 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004378 {
cristy4c08aed2011-07-01 19:47:50 +00004379 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4380 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004381 image->matte=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00004382 q+=GetPixelChannels(image);
4383 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004384 }
glennrp0fe50b42010-11-16 03:52:51 +00004385
cristy3ed852e2009-09-05 21:47:34 +00004386 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4387 break;
4388 }
4389 (void) RelinquishUniqueFileResource(alpha_image->filename);
4390 alpha_image=DestroyImage(alpha_image);
4391 alpha_image_info=DestroyImageInfo(alpha_image_info);
4392 if (jng_image != (Image *) NULL)
4393 jng_image=DestroyImage(jng_image);
4394 }
4395 }
4396
glennrp47b9dd52010-11-24 18:12:06 +00004397 /* Read the JNG image. */
4398
cristy3ed852e2009-09-05 21:47:34 +00004399 if (mng_info->mng_type == 0)
4400 {
4401 mng_info->mng_width=jng_width;
4402 mng_info->mng_height=jng_height;
4403 }
glennrp0fe50b42010-11-16 03:52:51 +00004404
cristy3ed852e2009-09-05 21:47:34 +00004405 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004406 {
4407 image->page.width=jng_width;
4408 image->page.height=jng_height;
4409 }
4410
cristy3ed852e2009-09-05 21:47:34 +00004411 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004412 {
4413 image->page.x=mng_info->x_off[mng_info->object_id];
4414 image->page.y=mng_info->y_off[mng_info->object_id];
4415 }
4416
cristy3ed852e2009-09-05 21:47:34 +00004417 else
glennrp0fe50b42010-11-16 03:52:51 +00004418 {
4419 image->page.y=mng_info->y_off[mng_info->object_id];
4420 }
4421
cristy3ed852e2009-09-05 21:47:34 +00004422 mng_info->image_found++;
4423 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4424 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004425
cristy3ed852e2009-09-05 21:47:34 +00004426 if (logging != MagickFalse)
4427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4428 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004429
cristy3ed852e2009-09-05 21:47:34 +00004430 return(image);
4431}
4432
4433/*
4434%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4435% %
4436% %
4437% %
4438% R e a d J N G I m a g e %
4439% %
4440% %
4441% %
4442%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4443%
4444% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4445% (including the 8-byte signature) and returns it. It allocates the memory
4446% necessary for the new Image structure and returns a pointer to the new
4447% image.
4448%
4449% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4450%
4451% The format of the ReadJNGImage method is:
4452%
4453% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4454% *exception)
4455%
4456% A description of each parameter follows:
4457%
4458% o image_info: the image info.
4459%
4460% o exception: return any errors or warnings in this structure.
4461%
4462*/
4463
4464static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4465{
4466 Image
4467 *image,
4468 *previous;
4469
4470 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004471 have_mng_structure,
4472 logging,
cristy3ed852e2009-09-05 21:47:34 +00004473 status;
4474
4475 MngInfo
4476 *mng_info;
4477
4478 char
4479 magic_number[MaxTextExtent];
4480
cristy3ed852e2009-09-05 21:47:34 +00004481 size_t
4482 count;
4483
4484 /*
4485 Open image file.
4486 */
4487 assert(image_info != (const ImageInfo *) NULL);
4488 assert(image_info->signature == MagickSignature);
4489 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4490 assert(exception != (ExceptionInfo *) NULL);
4491 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004492 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004493 image=AcquireImage(image_info);
4494 mng_info=(MngInfo *) NULL;
4495 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004496
cristy3ed852e2009-09-05 21:47:34 +00004497 if (status == MagickFalse)
4498 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004499
cristy3ed852e2009-09-05 21:47:34 +00004500 if (LocaleCompare(image_info->magick,"JNG") != 0)
4501 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004502
glennrp47b9dd52010-11-24 18:12:06 +00004503 /* Verify JNG signature. */
4504
cristy3ed852e2009-09-05 21:47:34 +00004505 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004506
glennrp3b8763e2011-02-21 12:08:18 +00004507 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004508 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004509
glennrp47b9dd52010-11-24 18:12:06 +00004510 /* Allocate a MngInfo structure. */
4511
cristy3ed852e2009-09-05 21:47:34 +00004512 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004513 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004514
cristy3ed852e2009-09-05 21:47:34 +00004515 if (mng_info == (MngInfo *) NULL)
4516 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004517
glennrp47b9dd52010-11-24 18:12:06 +00004518 /* Initialize members of the MngInfo structure. */
4519
cristy3ed852e2009-09-05 21:47:34 +00004520 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4521 have_mng_structure=MagickTrue;
4522
4523 mng_info->image=image;
4524 previous=image;
4525 image=ReadOneJNGImage(mng_info,image_info,exception);
4526 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004527
cristy3ed852e2009-09-05 21:47:34 +00004528 if (image == (Image *) NULL)
4529 {
4530 if (IsImageObject(previous) != MagickFalse)
4531 {
4532 (void) CloseBlob(previous);
4533 (void) DestroyImageList(previous);
4534 }
glennrp0fe50b42010-11-16 03:52:51 +00004535
cristy3ed852e2009-09-05 21:47:34 +00004536 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 return((Image *) NULL);
4541 }
4542 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004543
cristy3ed852e2009-09-05 21:47:34 +00004544 if (image->columns == 0 || image->rows == 0)
4545 {
4546 if (logging != MagickFalse)
4547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4548 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004549
cristy3ed852e2009-09-05 21:47:34 +00004550 ThrowReaderException(CorruptImageError,"CorruptImage");
4551 }
glennrp0fe50b42010-11-16 03:52:51 +00004552
cristy3ed852e2009-09-05 21:47:34 +00004553 if (logging != MagickFalse)
4554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004555
cristy3ed852e2009-09-05 21:47:34 +00004556 return(image);
4557}
4558#endif
4559
4560static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4561{
4562 char
4563 page_geometry[MaxTextExtent];
4564
4565 Image
4566 *image,
4567 *previous;
4568
cristy4383ec82011-01-05 15:42:32 +00004569 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004570 logging,
4571 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004572
cristy3ed852e2009-09-05 21:47:34 +00004573 volatile int
4574 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004575 object_id,
4576 term_chunk_found,
4577 skip_to_iend;
4578
cristybb503372010-05-27 20:51:26 +00004579 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004580 image_count=0;
4581
4582 MagickBooleanType
4583 status;
4584
4585 MagickOffsetType
4586 offset;
4587
4588 MngInfo
4589 *mng_info;
4590
4591 MngBox
4592 default_fb,
4593 fb,
4594 previous_fb;
4595
4596#if defined(MNG_INSERT_LAYERS)
4597 PixelPacket
4598 mng_background_color;
4599#endif
4600
4601 register unsigned char
4602 *p;
4603
cristybb503372010-05-27 20:51:26 +00004604 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004605 i;
4606
4607 size_t
4608 count;
4609
cristybb503372010-05-27 20:51:26 +00004610 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004611 loop_level;
4612
4613 volatile short
4614 skipping_loop;
4615
4616#if defined(MNG_INSERT_LAYERS)
4617 unsigned int
4618 mandatory_back=0;
4619#endif
4620
4621 volatile unsigned int
4622#ifdef MNG_OBJECT_BUFFERS
4623 mng_background_object=0,
4624#endif
4625 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4626
cristybb503372010-05-27 20:51:26 +00004627 size_t
cristy3ed852e2009-09-05 21:47:34 +00004628 default_frame_timeout,
4629 frame_timeout,
4630#if defined(MNG_INSERT_LAYERS)
4631 image_height,
4632 image_width,
4633#endif
4634 length;
4635
glennrp38ea0832010-06-02 18:50:28 +00004636 /* These delays are all measured in image ticks_per_second,
4637 * not in MNG ticks_per_second
4638 */
cristybb503372010-05-27 20:51:26 +00004639 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004640 default_frame_delay,
4641 final_delay,
4642 final_image_delay,
4643 frame_delay,
4644#if defined(MNG_INSERT_LAYERS)
4645 insert_layers,
4646#endif
4647 mng_iterations=1,
4648 simplicity=0,
4649 subframe_height=0,
4650 subframe_width=0;
4651
4652 previous_fb.top=0;
4653 previous_fb.bottom=0;
4654 previous_fb.left=0;
4655 previous_fb.right=0;
4656 default_fb.top=0;
4657 default_fb.bottom=0;
4658 default_fb.left=0;
4659 default_fb.right=0;
4660
glennrp47b9dd52010-11-24 18:12:06 +00004661 /* Open image file. */
4662
cristy3ed852e2009-09-05 21:47:34 +00004663 assert(image_info != (const ImageInfo *) NULL);
4664 assert(image_info->signature == MagickSignature);
4665 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4666 assert(exception != (ExceptionInfo *) NULL);
4667 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004668 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004669 image=AcquireImage(image_info);
4670 mng_info=(MngInfo *) NULL;
4671 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004672
cristy3ed852e2009-09-05 21:47:34 +00004673 if (status == MagickFalse)
4674 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004675
cristy3ed852e2009-09-05 21:47:34 +00004676 first_mng_object=MagickFalse;
4677 skipping_loop=(-1);
4678 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004679
4680 /* Allocate a MngInfo structure. */
4681
cristy73bd4a52010-10-05 11:24:23 +00004682 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004683
cristy3ed852e2009-09-05 21:47:34 +00004684 if (mng_info == (MngInfo *) NULL)
4685 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004686
glennrp47b9dd52010-11-24 18:12:06 +00004687 /* Initialize members of the MngInfo structure. */
4688
cristy3ed852e2009-09-05 21:47:34 +00004689 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4690 mng_info->image=image;
4691 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004692
4693 if (LocaleCompare(image_info->magick,"MNG") == 0)
4694 {
4695 char
4696 magic_number[MaxTextExtent];
4697
glennrp47b9dd52010-11-24 18:12:06 +00004698 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004699 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4700 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4701 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004702
4703 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004704 for (i=0; i < MNG_MAX_OBJECTS; i++)
4705 {
cristybb503372010-05-27 20:51:26 +00004706 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4707 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004708 }
4709 mng_info->exists[0]=MagickTrue;
4710 }
glennrp47b9dd52010-11-24 18:12:06 +00004711
cristy3ed852e2009-09-05 21:47:34 +00004712 first_mng_object=MagickTrue;
4713 mng_type=0;
4714#if defined(MNG_INSERT_LAYERS)
4715 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4716#endif
4717 default_frame_delay=0;
4718 default_frame_timeout=0;
4719 frame_delay=0;
4720 final_delay=1;
4721 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4722 object_id=0;
4723 skip_to_iend=MagickFalse;
4724 term_chunk_found=MagickFalse;
4725 mng_info->framing_mode=1;
4726#if defined(MNG_INSERT_LAYERS)
4727 mandatory_back=MagickFalse;
4728#endif
4729#if defined(MNG_INSERT_LAYERS)
4730 mng_background_color=image->background_color;
4731#endif
4732 default_fb=mng_info->frame;
4733 previous_fb=mng_info->frame;
4734 do
4735 {
4736 char
4737 type[MaxTextExtent];
4738
4739 if (LocaleCompare(image_info->magick,"MNG") == 0)
4740 {
4741 unsigned char
4742 *chunk;
4743
4744 /*
4745 Read a new chunk.
4746 */
4747 type[0]='\0';
4748 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4749 length=ReadBlobMSBLong(image);
4750 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4751
4752 if (logging != MagickFalse)
4753 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004754 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4755 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004756
4757 if (length > PNG_UINT_31_MAX)
4758 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004759
cristy3ed852e2009-09-05 21:47:34 +00004760 if (count == 0)
4761 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004762
cristy3ed852e2009-09-05 21:47:34 +00004763 p=NULL;
4764 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004765
cristy3ed852e2009-09-05 21:47:34 +00004766 if (length)
4767 {
4768 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004769
cristy3ed852e2009-09-05 21:47:34 +00004770 if (chunk == (unsigned char *) NULL)
4771 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004772
cristybb503372010-05-27 20:51:26 +00004773 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004774 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004775
cristy3ed852e2009-09-05 21:47:34 +00004776 p=chunk;
4777 }
glennrp0fe50b42010-11-16 03:52:51 +00004778
cristy3ed852e2009-09-05 21:47:34 +00004779 (void) ReadBlobMSBLong(image); /* read crc word */
4780
4781#if !defined(JNG_SUPPORTED)
4782 if (memcmp(type,mng_JHDR,4) == 0)
4783 {
4784 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004785
cristy3ed852e2009-09-05 21:47:34 +00004786 if (mng_info->jhdr_warning == 0)
4787 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4788 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004789
cristy3ed852e2009-09-05 21:47:34 +00004790 mng_info->jhdr_warning++;
4791 }
4792#endif
4793 if (memcmp(type,mng_DHDR,4) == 0)
4794 {
4795 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004796
cristy3ed852e2009-09-05 21:47:34 +00004797 if (mng_info->dhdr_warning == 0)
4798 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4799 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004800
cristy3ed852e2009-09-05 21:47:34 +00004801 mng_info->dhdr_warning++;
4802 }
4803 if (memcmp(type,mng_MEND,4) == 0)
4804 break;
glennrp47b9dd52010-11-24 18:12:06 +00004805
cristy3ed852e2009-09-05 21:47:34 +00004806 if (skip_to_iend)
4807 {
4808 if (memcmp(type,mng_IEND,4) == 0)
4809 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004810
cristy3ed852e2009-09-05 21:47:34 +00004811 if (length)
4812 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004813
cristy3ed852e2009-09-05 21:47:34 +00004814 if (logging != MagickFalse)
4815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4816 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004817
cristy3ed852e2009-09-05 21:47:34 +00004818 continue;
4819 }
glennrp0fe50b42010-11-16 03:52:51 +00004820
cristy3ed852e2009-09-05 21:47:34 +00004821 if (memcmp(type,mng_MHDR,4) == 0)
4822 {
cristybb503372010-05-27 20:51:26 +00004823 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004824 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004825
cristybb503372010-05-27 20:51:26 +00004826 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004827 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004828
cristy3ed852e2009-09-05 21:47:34 +00004829 if (logging != MagickFalse)
4830 {
4831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004832 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004834 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004835 }
glennrp0fe50b42010-11-16 03:52:51 +00004836
cristy3ed852e2009-09-05 21:47:34 +00004837 p+=8;
cristy8182b072010-05-30 20:10:53 +00004838 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004839
cristy3ed852e2009-09-05 21:47:34 +00004840 if (mng_info->ticks_per_second == 0)
4841 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004842
cristy3ed852e2009-09-05 21:47:34 +00004843 else
4844 default_frame_delay=1UL*image->ticks_per_second/
4845 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004846
cristy3ed852e2009-09-05 21:47:34 +00004847 frame_delay=default_frame_delay;
4848 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004849
cristy3ed852e2009-09-05 21:47:34 +00004850 if (length > 16)
4851 {
4852 p+=16;
cristy8182b072010-05-30 20:10:53 +00004853 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004854 }
glennrp0fe50b42010-11-16 03:52:51 +00004855
cristy3ed852e2009-09-05 21:47:34 +00004856 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004857
cristy3ed852e2009-09-05 21:47:34 +00004858 if ((simplicity != 0) && ((simplicity | 11) == 11))
4859 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004860
cristy3ed852e2009-09-05 21:47:34 +00004861 if ((simplicity != 0) && ((simplicity | 9) == 9))
4862 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004863
cristy3ed852e2009-09-05 21:47:34 +00004864#if defined(MNG_INSERT_LAYERS)
4865 if (mng_type != 3)
4866 insert_layers=MagickTrue;
4867#endif
cristy4c08aed2011-07-01 19:47:50 +00004868 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004869 {
glennrp47b9dd52010-11-24 18:12:06 +00004870 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004871 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00004872
cristy3ed852e2009-09-05 21:47:34 +00004873 if (GetNextImageInList(image) == (Image *) NULL)
4874 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004875
cristy3ed852e2009-09-05 21:47:34 +00004876 image=SyncNextImageInList(image);
4877 mng_info->image=image;
4878 }
4879
4880 if ((mng_info->mng_width > 65535L) ||
4881 (mng_info->mng_height > 65535L))
4882 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004883
cristy3b6fd2e2011-05-20 12:53:50 +00004884 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004885 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004886 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004887
cristy3ed852e2009-09-05 21:47:34 +00004888 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004889 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004890 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004891 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004892 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004893
cristy3ed852e2009-09-05 21:47:34 +00004894 for (i=0; i < MNG_MAX_OBJECTS; i++)
4895 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004896
cristy3ed852e2009-09-05 21:47:34 +00004897 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4898 continue;
4899 }
4900
4901 if (memcmp(type,mng_TERM,4) == 0)
4902 {
4903 int
4904 repeat=0;
4905
4906
4907 if (length)
4908 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004909
cristy3ed852e2009-09-05 21:47:34 +00004910 if (repeat == 3)
4911 {
cristy8182b072010-05-30 20:10:53 +00004912 final_delay=(png_uint_32) mng_get_long(&p[2]);
4913 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004914
cristy3ed852e2009-09-05 21:47:34 +00004915 if (mng_iterations == PNG_UINT_31_MAX)
4916 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004917
cristy3ed852e2009-09-05 21:47:34 +00004918 image->iterations=mng_iterations;
4919 term_chunk_found=MagickTrue;
4920 }
glennrp0fe50b42010-11-16 03:52:51 +00004921
cristy3ed852e2009-09-05 21:47:34 +00004922 if (logging != MagickFalse)
4923 {
4924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4925 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004926
cristy3ed852e2009-09-05 21:47:34 +00004927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004928 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004929
cristy3ed852e2009-09-05 21:47:34 +00004930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004931 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004932 }
glennrp0fe50b42010-11-16 03:52:51 +00004933
cristy3ed852e2009-09-05 21:47:34 +00004934 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4935 continue;
4936 }
4937 if (memcmp(type,mng_DEFI,4) == 0)
4938 {
4939 if (mng_type == 3)
4940 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4941 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4942 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004943
cristy3ed852e2009-09-05 21:47:34 +00004944 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004945
cristy3ed852e2009-09-05 21:47:34 +00004946 if (mng_type == 2 && object_id != 0)
4947 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4948 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4949 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004950
cristy3ed852e2009-09-05 21:47:34 +00004951 if (object_id > MNG_MAX_OBJECTS)
4952 {
4953 /*
4954 Instead ofsuing a warning we should allocate a larger
4955 MngInfo structure and continue.
4956 */
4957 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4958 CoderError,"object id too large","`%s'",image->filename);
4959 object_id=MNG_MAX_OBJECTS;
4960 }
glennrp0fe50b42010-11-16 03:52:51 +00004961
cristy3ed852e2009-09-05 21:47:34 +00004962 if (mng_info->exists[object_id])
4963 if (mng_info->frozen[object_id])
4964 {
4965 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4966 (void) ThrowMagickException(&image->exception,
4967 GetMagickModule(),CoderError,
4968 "DEFI cannot redefine a frozen MNG object","`%s'",
4969 image->filename);
4970 continue;
4971 }
glennrp0fe50b42010-11-16 03:52:51 +00004972
cristy3ed852e2009-09-05 21:47:34 +00004973 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004974
cristy3ed852e2009-09-05 21:47:34 +00004975 if (length > 2)
4976 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00004977
cristy3ed852e2009-09-05 21:47:34 +00004978 /*
4979 Extract object offset info.
4980 */
4981 if (length > 11)
4982 {
glennrp0fe50b42010-11-16 03:52:51 +00004983 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4984 (p[5] << 16) | (p[6] << 8) | p[7]);
4985
4986 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4987 (p[9] << 16) | (p[10] << 8) | p[11]);
4988
cristy3ed852e2009-09-05 21:47:34 +00004989 if (logging != MagickFalse)
4990 {
4991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004992 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004993 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00004994
cristy3ed852e2009-09-05 21:47:34 +00004995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004996 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004997 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00004998 }
4999 }
glennrp0fe50b42010-11-16 03:52:51 +00005000
cristy3ed852e2009-09-05 21:47:34 +00005001 /*
5002 Extract object clipping info.
5003 */
5004 if (length > 27)
5005 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5006 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005007
cristy3ed852e2009-09-05 21:47:34 +00005008 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5009 continue;
5010 }
5011 if (memcmp(type,mng_bKGD,4) == 0)
5012 {
5013 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005014
cristy3ed852e2009-09-05 21:47:34 +00005015 if (length > 5)
5016 {
5017 mng_info->mng_global_bkgd.red=
5018 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005019
cristy3ed852e2009-09-05 21:47:34 +00005020 mng_info->mng_global_bkgd.green=
5021 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005022
cristy3ed852e2009-09-05 21:47:34 +00005023 mng_info->mng_global_bkgd.blue=
5024 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005025
cristy3ed852e2009-09-05 21:47:34 +00005026 mng_info->have_global_bkgd=MagickTrue;
5027 }
glennrp0fe50b42010-11-16 03:52:51 +00005028
cristy3ed852e2009-09-05 21:47:34 +00005029 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5030 continue;
5031 }
5032 if (memcmp(type,mng_BACK,4) == 0)
5033 {
5034#if defined(MNG_INSERT_LAYERS)
5035 if (length > 6)
5036 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005037
cristy3ed852e2009-09-05 21:47:34 +00005038 else
5039 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005040
cristy3ed852e2009-09-05 21:47:34 +00005041 if (mandatory_back && length > 5)
5042 {
5043 mng_background_color.red=
5044 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005045
cristy3ed852e2009-09-05 21:47:34 +00005046 mng_background_color.green=
5047 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005048
cristy3ed852e2009-09-05 21:47:34 +00005049 mng_background_color.blue=
5050 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005051
cristy4c08aed2011-07-01 19:47:50 +00005052 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005053 }
glennrp0fe50b42010-11-16 03:52:51 +00005054
cristy3ed852e2009-09-05 21:47:34 +00005055#ifdef MNG_OBJECT_BUFFERS
5056 if (length > 8)
5057 mng_background_object=(p[7] << 8) | p[8];
5058#endif
5059#endif
5060 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5061 continue;
5062 }
glennrp47b9dd52010-11-24 18:12:06 +00005063
cristy3ed852e2009-09-05 21:47:34 +00005064 if (memcmp(type,mng_PLTE,4) == 0)
5065 {
glennrp47b9dd52010-11-24 18:12:06 +00005066 /* Read global PLTE. */
5067
cristy3ed852e2009-09-05 21:47:34 +00005068 if (length && (length < 769))
5069 {
5070 if (mng_info->global_plte == (png_colorp) NULL)
5071 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5072 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005073
cristybb503372010-05-27 20:51:26 +00005074 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005075 {
5076 mng_info->global_plte[i].red=p[3*i];
5077 mng_info->global_plte[i].green=p[3*i+1];
5078 mng_info->global_plte[i].blue=p[3*i+2];
5079 }
glennrp0fe50b42010-11-16 03:52:51 +00005080
cristy35ef8242010-06-03 16:24:13 +00005081 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005082 }
5083#ifdef MNG_LOOSE
5084 for ( ; i < 256; i++)
5085 {
5086 mng_info->global_plte[i].red=i;
5087 mng_info->global_plte[i].green=i;
5088 mng_info->global_plte[i].blue=i;
5089 }
glennrp0fe50b42010-11-16 03:52:51 +00005090
cristy3ed852e2009-09-05 21:47:34 +00005091 if (length)
5092 mng_info->global_plte_length=256;
5093#endif
5094 else
5095 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005096
cristy3ed852e2009-09-05 21:47:34 +00005097 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5098 continue;
5099 }
glennrp47b9dd52010-11-24 18:12:06 +00005100
cristy3ed852e2009-09-05 21:47:34 +00005101 if (memcmp(type,mng_tRNS,4) == 0)
5102 {
5103 /* read global tRNS */
5104
5105 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005106 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005107 mng_info->global_trns[i]=p[i];
5108
5109#ifdef MNG_LOOSE
5110 for ( ; i < 256; i++)
5111 mng_info->global_trns[i]=255;
5112#endif
cristy12560f32010-06-03 16:51:08 +00005113 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005114 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5115 continue;
5116 }
5117 if (memcmp(type,mng_gAMA,4) == 0)
5118 {
5119 if (length == 4)
5120 {
cristybb503372010-05-27 20:51:26 +00005121 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005122 igamma;
5123
cristy8182b072010-05-30 20:10:53 +00005124 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005125 mng_info->global_gamma=((float) igamma)*0.00001;
5126 mng_info->have_global_gama=MagickTrue;
5127 }
glennrp0fe50b42010-11-16 03:52:51 +00005128
cristy3ed852e2009-09-05 21:47:34 +00005129 else
5130 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005131
cristy3ed852e2009-09-05 21:47:34 +00005132 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5133 continue;
5134 }
5135
5136 if (memcmp(type,mng_cHRM,4) == 0)
5137 {
glennrp47b9dd52010-11-24 18:12:06 +00005138 /* Read global cHRM */
5139
cristy3ed852e2009-09-05 21:47:34 +00005140 if (length == 32)
5141 {
cristy8182b072010-05-30 20:10:53 +00005142 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5143 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5144 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005145 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005146 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005147 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005148 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005149 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005150 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005151 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005152 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005153 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005154 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005155 mng_info->have_global_chrm=MagickTrue;
5156 }
5157 else
5158 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005159
cristy3ed852e2009-09-05 21:47:34 +00005160 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5161 continue;
5162 }
glennrp47b9dd52010-11-24 18:12:06 +00005163
cristy3ed852e2009-09-05 21:47:34 +00005164 if (memcmp(type,mng_sRGB,4) == 0)
5165 {
5166 /*
5167 Read global sRGB.
5168 */
5169 if (length)
5170 {
glennrpe610a072010-08-05 17:08:46 +00005171 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005172 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005173 mng_info->have_global_srgb=MagickTrue;
5174 }
5175 else
5176 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005177
cristy3ed852e2009-09-05 21:47:34 +00005178 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5179 continue;
5180 }
glennrp47b9dd52010-11-24 18:12:06 +00005181
cristy3ed852e2009-09-05 21:47:34 +00005182 if (memcmp(type,mng_iCCP,4) == 0)
5183 {
glennrpfd05d622011-02-25 04:10:33 +00005184 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005185
5186 /*
5187 Read global iCCP.
5188 */
5189 if (length)
5190 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005191
cristy3ed852e2009-09-05 21:47:34 +00005192 continue;
5193 }
glennrp47b9dd52010-11-24 18:12:06 +00005194
cristy3ed852e2009-09-05 21:47:34 +00005195 if (memcmp(type,mng_FRAM,4) == 0)
5196 {
5197 if (mng_type == 3)
5198 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5199 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5200 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005201
cristy3ed852e2009-09-05 21:47:34 +00005202 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5203 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005204
cristy3ed852e2009-09-05 21:47:34 +00005205 frame_delay=default_frame_delay;
5206 frame_timeout=default_frame_timeout;
5207 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005208
cristy3ed852e2009-09-05 21:47:34 +00005209 if (length)
5210 if (p[0])
5211 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005212
cristy3ed852e2009-09-05 21:47:34 +00005213 if (logging != MagickFalse)
5214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5215 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005216
cristy3ed852e2009-09-05 21:47:34 +00005217 if (length > 6)
5218 {
glennrp47b9dd52010-11-24 18:12:06 +00005219 /* Note the delay and frame clipping boundaries. */
5220
cristy3ed852e2009-09-05 21:47:34 +00005221 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005222
cristybb503372010-05-27 20:51:26 +00005223 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005224 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005225
cristy3ed852e2009-09-05 21:47:34 +00005226 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005227
cristybb503372010-05-27 20:51:26 +00005228 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005229 {
5230 int
5231 change_delay,
5232 change_timeout,
5233 change_clipping;
5234
5235 change_delay=(*p++);
5236 change_timeout=(*p++);
5237 change_clipping=(*p++);
5238 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005239
cristy3ed852e2009-09-05 21:47:34 +00005240 if (change_delay)
5241 {
cristy8182b072010-05-30 20:10:53 +00005242 frame_delay=1UL*image->ticks_per_second*
5243 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005244
cristy8182b072010-05-30 20:10:53 +00005245 if (mng_info->ticks_per_second != 0)
5246 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005247
glennrpbb010dd2010-06-01 13:07:15 +00005248 else
5249 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005250
cristy3ed852e2009-09-05 21:47:34 +00005251 if (change_delay == 2)
5252 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005253
cristy3ed852e2009-09-05 21:47:34 +00005254 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005255
cristy3ed852e2009-09-05 21:47:34 +00005256 if (logging != MagickFalse)
5257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005258 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005259 }
glennrp47b9dd52010-11-24 18:12:06 +00005260
cristy3ed852e2009-09-05 21:47:34 +00005261 if (change_timeout)
5262 {
glennrpbb010dd2010-06-01 13:07:15 +00005263 frame_timeout=1UL*image->ticks_per_second*
5264 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005265
glennrpbb010dd2010-06-01 13:07:15 +00005266 if (mng_info->ticks_per_second != 0)
5267 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005268
glennrpbb010dd2010-06-01 13:07:15 +00005269 else
5270 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005271
cristy3ed852e2009-09-05 21:47:34 +00005272 if (change_delay == 2)
5273 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005274
cristy3ed852e2009-09-05 21:47:34 +00005275 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005276
cristy3ed852e2009-09-05 21:47:34 +00005277 if (logging != MagickFalse)
5278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005279 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005280 }
glennrp47b9dd52010-11-24 18:12:06 +00005281
cristy3ed852e2009-09-05 21:47:34 +00005282 if (change_clipping)
5283 {
5284 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5285 p+=17;
5286 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005287
cristy3ed852e2009-09-05 21:47:34 +00005288 if (logging != MagickFalse)
5289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005290 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005291 (double) fb.left,(double) fb.right,(double) fb.top,
5292 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005293
cristy3ed852e2009-09-05 21:47:34 +00005294 if (change_clipping == 2)
5295 default_fb=fb;
5296 }
5297 }
5298 }
5299 mng_info->clip=fb;
5300 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005301
cristybb503372010-05-27 20:51:26 +00005302 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005303 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005304
cristybb503372010-05-27 20:51:26 +00005305 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005306 -mng_info->clip.top);
5307 /*
5308 Insert a background layer behind the frame if framing_mode is 4.
5309 */
5310#if defined(MNG_INSERT_LAYERS)
5311 if (logging != MagickFalse)
5312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005313 " subframe_width=%.20g, subframe_height=%.20g",(double)
5314 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005315
cristy3ed852e2009-09-05 21:47:34 +00005316 if (insert_layers && (mng_info->framing_mode == 4) &&
5317 (subframe_width) && (subframe_height))
5318 {
glennrp47b9dd52010-11-24 18:12:06 +00005319 /* Allocate next image structure. */
cristy4c08aed2011-07-01 19:47:50 +00005320 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005321 {
5322 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005323
cristy3ed852e2009-09-05 21:47:34 +00005324 if (GetNextImageInList(image) == (Image *) NULL)
5325 {
5326 image=DestroyImageList(image);
5327 MngInfoFreeStruct(mng_info,&have_mng_structure);
5328 return((Image *) NULL);
5329 }
glennrp47b9dd52010-11-24 18:12:06 +00005330
cristy3ed852e2009-09-05 21:47:34 +00005331 image=SyncNextImageInList(image);
5332 }
glennrp0fe50b42010-11-16 03:52:51 +00005333
cristy3ed852e2009-09-05 21:47:34 +00005334 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005335
cristy3ed852e2009-09-05 21:47:34 +00005336 if (term_chunk_found)
5337 {
5338 image->start_loop=MagickTrue;
5339 image->iterations=mng_iterations;
5340 term_chunk_found=MagickFalse;
5341 }
glennrp0fe50b42010-11-16 03:52:51 +00005342
cristy3ed852e2009-09-05 21:47:34 +00005343 else
5344 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005345
cristy3ed852e2009-09-05 21:47:34 +00005346 image->columns=subframe_width;
5347 image->rows=subframe_height;
5348 image->page.width=subframe_width;
5349 image->page.height=subframe_height;
5350 image->page.x=mng_info->clip.left;
5351 image->page.y=mng_info->clip.top;
5352 image->background_color=mng_background_color;
5353 image->matte=MagickFalse;
5354 image->delay=0;
5355 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005356
cristy3ed852e2009-09-05 21:47:34 +00005357 if (logging != MagickFalse)
5358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005359 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005360 (double) mng_info->clip.left,(double) mng_info->clip.right,
5361 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005362 }
5363#endif
5364 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5365 continue;
5366 }
5367 if (memcmp(type,mng_CLIP,4) == 0)
5368 {
5369 unsigned int
5370 first_object,
5371 last_object;
5372
5373 /*
5374 Read CLIP.
5375 */
5376 first_object=(p[0] << 8) | p[1];
5377 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005378
cristy3ed852e2009-09-05 21:47:34 +00005379 for (i=(int) first_object; i <= (int) last_object; i++)
5380 {
5381 if (mng_info->exists[i] && !mng_info->frozen[i])
5382 {
5383 MngBox
5384 box;
5385
5386 box=mng_info->object_clip[i];
5387 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5388 }
5389 }
glennrp47b9dd52010-11-24 18:12:06 +00005390
cristy3ed852e2009-09-05 21:47:34 +00005391 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5392 continue;
5393 }
5394 if (memcmp(type,mng_SAVE,4) == 0)
5395 {
5396 for (i=1; i < MNG_MAX_OBJECTS; i++)
5397 if (mng_info->exists[i])
5398 {
5399 mng_info->frozen[i]=MagickTrue;
5400#ifdef MNG_OBJECT_BUFFERS
5401 if (mng_info->ob[i] != (MngBuffer *) NULL)
5402 mng_info->ob[i]->frozen=MagickTrue;
5403#endif
5404 }
glennrp0fe50b42010-11-16 03:52:51 +00005405
cristy3ed852e2009-09-05 21:47:34 +00005406 if (length)
5407 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005408
cristy3ed852e2009-09-05 21:47:34 +00005409 continue;
5410 }
5411
5412 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5413 {
glennrp47b9dd52010-11-24 18:12:06 +00005414 /* Read DISC or SEEK. */
5415
cristy3ed852e2009-09-05 21:47:34 +00005416 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5417 {
5418 for (i=1; i < MNG_MAX_OBJECTS; i++)
5419 MngInfoDiscardObject(mng_info,i);
5420 }
glennrp0fe50b42010-11-16 03:52:51 +00005421
cristy3ed852e2009-09-05 21:47:34 +00005422 else
5423 {
cristybb503372010-05-27 20:51:26 +00005424 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005425 j;
5426
cristybb503372010-05-27 20:51:26 +00005427 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005428 {
5429 i=p[j] << 8 | p[j+1];
5430 MngInfoDiscardObject(mng_info,i);
5431 }
5432 }
glennrp0fe50b42010-11-16 03:52:51 +00005433
cristy3ed852e2009-09-05 21:47:34 +00005434 if (length)
5435 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005436
cristy3ed852e2009-09-05 21:47:34 +00005437 continue;
5438 }
glennrp47b9dd52010-11-24 18:12:06 +00005439
cristy3ed852e2009-09-05 21:47:34 +00005440 if (memcmp(type,mng_MOVE,4) == 0)
5441 {
cristybb503372010-05-27 20:51:26 +00005442 size_t
cristy3ed852e2009-09-05 21:47:34 +00005443 first_object,
5444 last_object;
5445
glennrp47b9dd52010-11-24 18:12:06 +00005446 /* read MOVE */
5447
cristy3ed852e2009-09-05 21:47:34 +00005448 first_object=(p[0] << 8) | p[1];
5449 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005450 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005451 {
5452 if (mng_info->exists[i] && !mng_info->frozen[i])
5453 {
5454 MngPair
5455 new_pair;
5456
5457 MngPair
5458 old_pair;
5459
5460 old_pair.a=mng_info->x_off[i];
5461 old_pair.b=mng_info->y_off[i];
5462 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5463 mng_info->x_off[i]=new_pair.a;
5464 mng_info->y_off[i]=new_pair.b;
5465 }
5466 }
glennrp47b9dd52010-11-24 18:12:06 +00005467
cristy3ed852e2009-09-05 21:47:34 +00005468 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5469 continue;
5470 }
5471
5472 if (memcmp(type,mng_LOOP,4) == 0)
5473 {
cristybb503372010-05-27 20:51:26 +00005474 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005475 loop_level=chunk[0];
5476 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005477
5478 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005479 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005480
cristy3ed852e2009-09-05 21:47:34 +00005481 if (logging != MagickFalse)
5482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005483 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5484 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005485
cristy3ed852e2009-09-05 21:47:34 +00005486 if (loop_iters == 0)
5487 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005488
cristy3ed852e2009-09-05 21:47:34 +00005489 else
5490 {
5491 mng_info->loop_jump[loop_level]=TellBlob(image);
5492 mng_info->loop_count[loop_level]=loop_iters;
5493 }
glennrp0fe50b42010-11-16 03:52:51 +00005494
cristy3ed852e2009-09-05 21:47:34 +00005495 mng_info->loop_iteration[loop_level]=0;
5496 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5497 continue;
5498 }
glennrp47b9dd52010-11-24 18:12:06 +00005499
cristy3ed852e2009-09-05 21:47:34 +00005500 if (memcmp(type,mng_ENDL,4) == 0)
5501 {
5502 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005503
cristy3ed852e2009-09-05 21:47:34 +00005504 if (skipping_loop > 0)
5505 {
5506 if (skipping_loop == loop_level)
5507 {
5508 /*
5509 Found end of zero-iteration loop.
5510 */
5511 skipping_loop=(-1);
5512 mng_info->loop_active[loop_level]=0;
5513 }
5514 }
glennrp47b9dd52010-11-24 18:12:06 +00005515
cristy3ed852e2009-09-05 21:47:34 +00005516 else
5517 {
5518 if (mng_info->loop_active[loop_level] == 1)
5519 {
5520 mng_info->loop_count[loop_level]--;
5521 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005522
cristy3ed852e2009-09-05 21:47:34 +00005523 if (logging != MagickFalse)
5524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005525 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005526 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005527 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005528
cristy3ed852e2009-09-05 21:47:34 +00005529 if (mng_info->loop_count[loop_level] != 0)
5530 {
5531 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5532 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005533
cristy3ed852e2009-09-05 21:47:34 +00005534 if (offset < 0)
5535 ThrowReaderException(CorruptImageError,
5536 "ImproperImageHeader");
5537 }
glennrp47b9dd52010-11-24 18:12:06 +00005538
cristy3ed852e2009-09-05 21:47:34 +00005539 else
5540 {
5541 short
5542 last_level;
5543
5544 /*
5545 Finished loop.
5546 */
5547 mng_info->loop_active[loop_level]=0;
5548 last_level=(-1);
5549 for (i=0; i < loop_level; i++)
5550 if (mng_info->loop_active[i] == 1)
5551 last_level=(short) i;
5552 loop_level=last_level;
5553 }
5554 }
5555 }
glennrp47b9dd52010-11-24 18:12:06 +00005556
cristy3ed852e2009-09-05 21:47:34 +00005557 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5558 continue;
5559 }
glennrp47b9dd52010-11-24 18:12:06 +00005560
cristy3ed852e2009-09-05 21:47:34 +00005561 if (memcmp(type,mng_CLON,4) == 0)
5562 {
5563 if (mng_info->clon_warning == 0)
5564 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5565 CoderError,"CLON is not implemented yet","`%s'",
5566 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005567
cristy3ed852e2009-09-05 21:47:34 +00005568 mng_info->clon_warning++;
5569 }
glennrp47b9dd52010-11-24 18:12:06 +00005570
cristy3ed852e2009-09-05 21:47:34 +00005571 if (memcmp(type,mng_MAGN,4) == 0)
5572 {
5573 png_uint_16
5574 magn_first,
5575 magn_last,
5576 magn_mb,
5577 magn_ml,
5578 magn_mr,
5579 magn_mt,
5580 magn_mx,
5581 magn_my,
5582 magn_methx,
5583 magn_methy;
5584
5585 if (length > 1)
5586 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005587
cristy3ed852e2009-09-05 21:47:34 +00005588 else
5589 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005590
cristy3ed852e2009-09-05 21:47:34 +00005591 if (length > 3)
5592 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005593
cristy3ed852e2009-09-05 21:47:34 +00005594 else
5595 magn_last=magn_first;
5596#ifndef MNG_OBJECT_BUFFERS
5597 if (magn_first || magn_last)
5598 if (mng_info->magn_warning == 0)
5599 {
5600 (void) ThrowMagickException(&image->exception,
5601 GetMagickModule(),CoderError,
5602 "MAGN is not implemented yet for nonzero objects",
5603 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005604
cristy3ed852e2009-09-05 21:47:34 +00005605 mng_info->magn_warning++;
5606 }
5607#endif
5608 if (length > 4)
5609 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005610
cristy3ed852e2009-09-05 21:47:34 +00005611 else
5612 magn_methx=0;
5613
5614 if (length > 6)
5615 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005616
cristy3ed852e2009-09-05 21:47:34 +00005617 else
5618 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005619
cristy3ed852e2009-09-05 21:47:34 +00005620 if (magn_mx == 0)
5621 magn_mx=1;
5622
5623 if (length > 8)
5624 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005625
cristy3ed852e2009-09-05 21:47:34 +00005626 else
5627 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005628
cristy3ed852e2009-09-05 21:47:34 +00005629 if (magn_my == 0)
5630 magn_my=1;
5631
5632 if (length > 10)
5633 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005634
cristy3ed852e2009-09-05 21:47:34 +00005635 else
5636 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005637
cristy3ed852e2009-09-05 21:47:34 +00005638 if (magn_ml == 0)
5639 magn_ml=1;
5640
5641 if (length > 12)
5642 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005643
cristy3ed852e2009-09-05 21:47:34 +00005644 else
5645 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005646
cristy3ed852e2009-09-05 21:47:34 +00005647 if (magn_mr == 0)
5648 magn_mr=1;
5649
5650 if (length > 14)
5651 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005652
cristy3ed852e2009-09-05 21:47:34 +00005653 else
5654 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005655
cristy3ed852e2009-09-05 21:47:34 +00005656 if (magn_mt == 0)
5657 magn_mt=1;
5658
5659 if (length > 16)
5660 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005661
cristy3ed852e2009-09-05 21:47:34 +00005662 else
5663 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005664
cristy3ed852e2009-09-05 21:47:34 +00005665 if (magn_mb == 0)
5666 magn_mb=1;
5667
5668 if (length > 17)
5669 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005670
cristy3ed852e2009-09-05 21:47:34 +00005671 else
5672 magn_methy=magn_methx;
5673
glennrp47b9dd52010-11-24 18:12:06 +00005674
cristy3ed852e2009-09-05 21:47:34 +00005675 if (magn_methx > 5 || magn_methy > 5)
5676 if (mng_info->magn_warning == 0)
5677 {
5678 (void) ThrowMagickException(&image->exception,
5679 GetMagickModule(),CoderError,
5680 "Unknown MAGN method in MNG datastream","`%s'",
5681 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005682
cristy3ed852e2009-09-05 21:47:34 +00005683 mng_info->magn_warning++;
5684 }
5685#ifdef MNG_OBJECT_BUFFERS
5686 /* Magnify existing objects in the range magn_first to magn_last */
5687#endif
5688 if (magn_first == 0 || magn_last == 0)
5689 {
5690 /* Save the magnification factors for object 0 */
5691 mng_info->magn_mb=magn_mb;
5692 mng_info->magn_ml=magn_ml;
5693 mng_info->magn_mr=magn_mr;
5694 mng_info->magn_mt=magn_mt;
5695 mng_info->magn_mx=magn_mx;
5696 mng_info->magn_my=magn_my;
5697 mng_info->magn_methx=magn_methx;
5698 mng_info->magn_methy=magn_methy;
5699 }
5700 }
glennrp47b9dd52010-11-24 18:12:06 +00005701
cristy3ed852e2009-09-05 21:47:34 +00005702 if (memcmp(type,mng_PAST,4) == 0)
5703 {
5704 if (mng_info->past_warning == 0)
5705 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5706 CoderError,"PAST is not implemented yet","`%s'",
5707 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005708
cristy3ed852e2009-09-05 21:47:34 +00005709 mng_info->past_warning++;
5710 }
glennrp47b9dd52010-11-24 18:12:06 +00005711
cristy3ed852e2009-09-05 21:47:34 +00005712 if (memcmp(type,mng_SHOW,4) == 0)
5713 {
5714 if (mng_info->show_warning == 0)
5715 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5716 CoderError,"SHOW is not implemented yet","`%s'",
5717 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005718
cristy3ed852e2009-09-05 21:47:34 +00005719 mng_info->show_warning++;
5720 }
glennrp47b9dd52010-11-24 18:12:06 +00005721
cristy3ed852e2009-09-05 21:47:34 +00005722 if (memcmp(type,mng_sBIT,4) == 0)
5723 {
5724 if (length < 4)
5725 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005726
cristy3ed852e2009-09-05 21:47:34 +00005727 else
5728 {
5729 mng_info->global_sbit.gray=p[0];
5730 mng_info->global_sbit.red=p[0];
5731 mng_info->global_sbit.green=p[1];
5732 mng_info->global_sbit.blue=p[2];
5733 mng_info->global_sbit.alpha=p[3];
5734 mng_info->have_global_sbit=MagickTrue;
5735 }
5736 }
5737 if (memcmp(type,mng_pHYs,4) == 0)
5738 {
5739 if (length > 8)
5740 {
5741 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005742 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005743 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005744 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005745 mng_info->global_phys_unit_type=p[8];
5746 mng_info->have_global_phys=MagickTrue;
5747 }
glennrp47b9dd52010-11-24 18:12:06 +00005748
cristy3ed852e2009-09-05 21:47:34 +00005749 else
5750 mng_info->have_global_phys=MagickFalse;
5751 }
5752 if (memcmp(type,mng_pHYg,4) == 0)
5753 {
5754 if (mng_info->phyg_warning == 0)
5755 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5756 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005757
cristy3ed852e2009-09-05 21:47:34 +00005758 mng_info->phyg_warning++;
5759 }
5760 if (memcmp(type,mng_BASI,4) == 0)
5761 {
5762 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005763
cristy3ed852e2009-09-05 21:47:34 +00005764 if (mng_info->basi_warning == 0)
5765 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5766 CoderError,"BASI is not implemented yet","`%s'",
5767 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005768
cristy3ed852e2009-09-05 21:47:34 +00005769 mng_info->basi_warning++;
5770#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005771 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005772 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005773 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005774 (p[6] << 8) | p[7]);
5775 basi_color_type=p[8];
5776 basi_compression_method=p[9];
5777 basi_filter_type=p[10];
5778 basi_interlace_method=p[11];
5779 if (length > 11)
5780 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005781
cristy3ed852e2009-09-05 21:47:34 +00005782 else
5783 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005784
cristy3ed852e2009-09-05 21:47:34 +00005785 if (length > 13)
5786 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005787
cristy3ed852e2009-09-05 21:47:34 +00005788 else
5789 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005790
cristy3ed852e2009-09-05 21:47:34 +00005791 if (length > 15)
5792 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005793
cristy3ed852e2009-09-05 21:47:34 +00005794 else
5795 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005796
cristy3ed852e2009-09-05 21:47:34 +00005797 if (length > 17)
5798 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005799
cristy3ed852e2009-09-05 21:47:34 +00005800 else
5801 {
5802 if (basi_sample_depth == 16)
5803 basi_alpha=65535L;
5804 else
5805 basi_alpha=255;
5806 }
glennrp47b9dd52010-11-24 18:12:06 +00005807
cristy3ed852e2009-09-05 21:47:34 +00005808 if (length > 19)
5809 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005810
cristy3ed852e2009-09-05 21:47:34 +00005811 else
5812 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005813
cristy3ed852e2009-09-05 21:47:34 +00005814#endif
5815 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5816 continue;
5817 }
glennrp47b9dd52010-11-24 18:12:06 +00005818
cristy3ed852e2009-09-05 21:47:34 +00005819 if (memcmp(type,mng_IHDR,4)
5820#if defined(JNG_SUPPORTED)
5821 && memcmp(type,mng_JHDR,4)
5822#endif
5823 )
5824 {
5825 /* Not an IHDR or JHDR chunk */
5826 if (length)
5827 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005828
cristy3ed852e2009-09-05 21:47:34 +00005829 continue;
5830 }
5831/* Process IHDR */
5832 if (logging != MagickFalse)
5833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5834 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005835
cristy3ed852e2009-09-05 21:47:34 +00005836 mng_info->exists[object_id]=MagickTrue;
5837 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005838
cristy3ed852e2009-09-05 21:47:34 +00005839 if (mng_info->invisible[object_id])
5840 {
5841 if (logging != MagickFalse)
5842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5843 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005844
cristy3ed852e2009-09-05 21:47:34 +00005845 skip_to_iend=MagickTrue;
5846 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5847 continue;
5848 }
5849#if defined(MNG_INSERT_LAYERS)
5850 if (length < 8)
5851 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005852
cristy8182b072010-05-30 20:10:53 +00005853 image_width=(size_t) mng_get_long(p);
5854 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005855#endif
5856 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5857
5858 /*
5859 Insert a transparent background layer behind the entire animation
5860 if it is not full screen.
5861 */
5862#if defined(MNG_INSERT_LAYERS)
5863 if (insert_layers && mng_type && first_mng_object)
5864 {
5865 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5866 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005867 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005868 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005869 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005870 {
cristy4c08aed2011-07-01 19:47:50 +00005871 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005872 {
5873 /*
5874 Allocate next image structure.
5875 */
5876 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005877
cristy3ed852e2009-09-05 21:47:34 +00005878 if (GetNextImageInList(image) == (Image *) NULL)
5879 {
5880 image=DestroyImageList(image);
5881 MngInfoFreeStruct(mng_info,&have_mng_structure);
5882 return((Image *) NULL);
5883 }
glennrp47b9dd52010-11-24 18:12:06 +00005884
cristy3ed852e2009-09-05 21:47:34 +00005885 image=SyncNextImageInList(image);
5886 }
5887 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005888
cristy3ed852e2009-09-05 21:47:34 +00005889 if (term_chunk_found)
5890 {
5891 image->start_loop=MagickTrue;
5892 image->iterations=mng_iterations;
5893 term_chunk_found=MagickFalse;
5894 }
glennrp47b9dd52010-11-24 18:12:06 +00005895
cristy3ed852e2009-09-05 21:47:34 +00005896 else
5897 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005898
5899 /* Make a background rectangle. */
5900
cristy3ed852e2009-09-05 21:47:34 +00005901 image->delay=0;
5902 image->columns=mng_info->mng_width;
5903 image->rows=mng_info->mng_height;
5904 image->page.width=mng_info->mng_width;
5905 image->page.height=mng_info->mng_height;
5906 image->page.x=0;
5907 image->page.y=0;
5908 image->background_color=mng_background_color;
5909 (void) SetImageBackgroundColor(image);
5910 if (logging != MagickFalse)
5911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005912 " Inserted transparent background layer, W=%.20g, H=%.20g",
5913 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005914 }
5915 }
5916 /*
5917 Insert a background layer behind the upcoming image if
5918 framing_mode is 3, and we haven't already inserted one.
5919 */
5920 if (insert_layers && (mng_info->framing_mode == 3) &&
5921 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5922 (simplicity & 0x08)))
5923 {
cristy4c08aed2011-07-01 19:47:50 +00005924 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005925 {
5926 /*
5927 Allocate next image structure.
5928 */
5929 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005930
cristy3ed852e2009-09-05 21:47:34 +00005931 if (GetNextImageInList(image) == (Image *) NULL)
5932 {
5933 image=DestroyImageList(image);
5934 MngInfoFreeStruct(mng_info,&have_mng_structure);
5935 return((Image *) NULL);
5936 }
glennrp47b9dd52010-11-24 18:12:06 +00005937
cristy3ed852e2009-09-05 21:47:34 +00005938 image=SyncNextImageInList(image);
5939 }
glennrp0fe50b42010-11-16 03:52:51 +00005940
cristy3ed852e2009-09-05 21:47:34 +00005941 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005942
cristy3ed852e2009-09-05 21:47:34 +00005943 if (term_chunk_found)
5944 {
5945 image->start_loop=MagickTrue;
5946 image->iterations=mng_iterations;
5947 term_chunk_found=MagickFalse;
5948 }
glennrp0fe50b42010-11-16 03:52:51 +00005949
cristy3ed852e2009-09-05 21:47:34 +00005950 else
5951 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005952
cristy3ed852e2009-09-05 21:47:34 +00005953 image->delay=0;
5954 image->columns=subframe_width;
5955 image->rows=subframe_height;
5956 image->page.width=subframe_width;
5957 image->page.height=subframe_height;
5958 image->page.x=mng_info->clip.left;
5959 image->page.y=mng_info->clip.top;
5960 image->background_color=mng_background_color;
5961 image->matte=MagickFalse;
5962 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005963
cristy3ed852e2009-09-05 21:47:34 +00005964 if (logging != MagickFalse)
5965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005966 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005967 (double) mng_info->clip.left,(double) mng_info->clip.right,
5968 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005969 }
5970#endif /* MNG_INSERT_LAYERS */
5971 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005972
cristy4c08aed2011-07-01 19:47:50 +00005973 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005974 {
5975 /*
5976 Allocate next image structure.
5977 */
5978 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005979
cristy3ed852e2009-09-05 21:47:34 +00005980 if (GetNextImageInList(image) == (Image *) NULL)
5981 {
5982 image=DestroyImageList(image);
5983 MngInfoFreeStruct(mng_info,&have_mng_structure);
5984 return((Image *) NULL);
5985 }
glennrp47b9dd52010-11-24 18:12:06 +00005986
cristy3ed852e2009-09-05 21:47:34 +00005987 image=SyncNextImageInList(image);
5988 }
5989 mng_info->image=image;
5990 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5991 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00005992
cristy3ed852e2009-09-05 21:47:34 +00005993 if (status == MagickFalse)
5994 break;
glennrp0fe50b42010-11-16 03:52:51 +00005995
cristy3ed852e2009-09-05 21:47:34 +00005996 if (term_chunk_found)
5997 {
5998 image->start_loop=MagickTrue;
5999 term_chunk_found=MagickFalse;
6000 }
glennrp0fe50b42010-11-16 03:52:51 +00006001
cristy3ed852e2009-09-05 21:47:34 +00006002 else
6003 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006004
cristy3ed852e2009-09-05 21:47:34 +00006005 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6006 {
6007 image->delay=frame_delay;
6008 frame_delay=default_frame_delay;
6009 }
glennrp0fe50b42010-11-16 03:52:51 +00006010
cristy3ed852e2009-09-05 21:47:34 +00006011 else
6012 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006013
cristy3ed852e2009-09-05 21:47:34 +00006014 image->page.width=mng_info->mng_width;
6015 image->page.height=mng_info->mng_height;
6016 image->page.x=mng_info->x_off[object_id];
6017 image->page.y=mng_info->y_off[object_id];
6018 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006019
cristy3ed852e2009-09-05 21:47:34 +00006020 /*
6021 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6022 */
glennrp47b9dd52010-11-24 18:12:06 +00006023
cristy3ed852e2009-09-05 21:47:34 +00006024 if (logging != MagickFalse)
6025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6026 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6027 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006028
cristybb503372010-05-27 20:51:26 +00006029 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006030
cristy3ed852e2009-09-05 21:47:34 +00006031 if (offset < 0)
6032 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6033 }
6034
6035 previous=image;
6036 mng_info->image=image;
6037 mng_info->mng_type=mng_type;
6038 mng_info->object_id=object_id;
6039
6040 if (memcmp(type,mng_IHDR,4) == 0)
6041 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006042
cristy3ed852e2009-09-05 21:47:34 +00006043#if defined(JNG_SUPPORTED)
6044 else
6045 image=ReadOneJNGImage(mng_info,image_info,exception);
6046#endif
6047
6048 if (image == (Image *) NULL)
6049 {
6050 if (IsImageObject(previous) != MagickFalse)
6051 {
6052 (void) DestroyImageList(previous);
6053 (void) CloseBlob(previous);
6054 }
glennrp47b9dd52010-11-24 18:12:06 +00006055
cristy3ed852e2009-09-05 21:47:34 +00006056 MngInfoFreeStruct(mng_info,&have_mng_structure);
6057 return((Image *) NULL);
6058 }
glennrp0fe50b42010-11-16 03:52:51 +00006059
cristy3ed852e2009-09-05 21:47:34 +00006060 if (image->columns == 0 || image->rows == 0)
6061 {
6062 (void) CloseBlob(image);
6063 image=DestroyImageList(image);
6064 MngInfoFreeStruct(mng_info,&have_mng_structure);
6065 return((Image *) NULL);
6066 }
glennrp0fe50b42010-11-16 03:52:51 +00006067
cristy3ed852e2009-09-05 21:47:34 +00006068 mng_info->image=image;
6069
6070 if (mng_type)
6071 {
6072 MngBox
6073 crop_box;
6074
6075 if (mng_info->magn_methx || mng_info->magn_methy)
6076 {
6077 png_uint_32
6078 magnified_height,
6079 magnified_width;
6080
6081 if (logging != MagickFalse)
6082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6083 " Processing MNG MAGN chunk");
6084
6085 if (mng_info->magn_methx == 1)
6086 {
6087 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006088
cristy3ed852e2009-09-05 21:47:34 +00006089 if (image->columns > 1)
6090 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006091
cristy3ed852e2009-09-05 21:47:34 +00006092 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006093 magnified_width += (png_uint_32)
6094 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006095 }
glennrp47b9dd52010-11-24 18:12:06 +00006096
cristy3ed852e2009-09-05 21:47:34 +00006097 else
6098 {
cristy4e5bc842010-06-09 13:56:01 +00006099 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006100
cristy3ed852e2009-09-05 21:47:34 +00006101 if (image->columns > 1)
6102 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006103
cristy3ed852e2009-09-05 21:47:34 +00006104 if (image->columns > 2)
6105 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006106
cristy3ed852e2009-09-05 21:47:34 +00006107 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006108 magnified_width += (png_uint_32)
6109 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006110 }
glennrp47b9dd52010-11-24 18:12:06 +00006111
cristy3ed852e2009-09-05 21:47:34 +00006112 if (mng_info->magn_methy == 1)
6113 {
6114 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006115
cristy3ed852e2009-09-05 21:47:34 +00006116 if (image->rows > 1)
6117 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006118
cristy3ed852e2009-09-05 21:47:34 +00006119 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006120 magnified_height += (png_uint_32)
6121 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006122 }
glennrp47b9dd52010-11-24 18:12:06 +00006123
cristy3ed852e2009-09-05 21:47:34 +00006124 else
6125 {
cristy4e5bc842010-06-09 13:56:01 +00006126 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006127
cristy3ed852e2009-09-05 21:47:34 +00006128 if (image->rows > 1)
6129 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006130
cristy3ed852e2009-09-05 21:47:34 +00006131 if (image->rows > 2)
6132 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006133
cristy3ed852e2009-09-05 21:47:34 +00006134 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006135 magnified_height += (png_uint_32)
6136 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006137 }
glennrp47b9dd52010-11-24 18:12:06 +00006138
cristy3ed852e2009-09-05 21:47:34 +00006139 if (magnified_height > image->rows ||
6140 magnified_width > image->columns)
6141 {
6142 Image
6143 *large_image;
6144
6145 int
6146 yy;
6147
cristy4c08aed2011-07-01 19:47:50 +00006148 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006149 *next,
6150 *prev;
6151
6152 png_uint_16
6153 magn_methx,
6154 magn_methy;
6155
cristy4c08aed2011-07-01 19:47:50 +00006156 ssize_t
6157 m,
6158 y;
6159
6160 register Quantum
6161 *n,
6162 *q;
6163
6164 register ssize_t
6165 x;
6166
glennrp47b9dd52010-11-24 18:12:06 +00006167 /* Allocate next image structure. */
6168
cristy3ed852e2009-09-05 21:47:34 +00006169 if (logging != MagickFalse)
6170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6171 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006172
cristy3ed852e2009-09-05 21:47:34 +00006173 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00006174
cristy3ed852e2009-09-05 21:47:34 +00006175 if (GetNextImageInList(image) == (Image *) NULL)
6176 {
6177 image=DestroyImageList(image);
6178 MngInfoFreeStruct(mng_info,&have_mng_structure);
6179 return((Image *) NULL);
6180 }
6181
6182 large_image=SyncNextImageInList(image);
6183
6184 large_image->columns=magnified_width;
6185 large_image->rows=magnified_height;
6186
6187 magn_methx=mng_info->magn_methx;
6188 magn_methy=mng_info->magn_methy;
6189
glennrp3faa9a32011-04-23 14:00:25 +00006190#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006191#define QM unsigned short
6192 if (magn_methx != 1 || magn_methy != 1)
6193 {
6194 /*
6195 Scale pixels to unsigned shorts to prevent
6196 overflow of intermediate values of interpolations
6197 */
cristybb503372010-05-27 20:51:26 +00006198 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006199 {
6200 q=GetAuthenticPixels(image,0,y,image->columns,1,
6201 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006202
cristybb503372010-05-27 20:51:26 +00006203 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006204 {
cristy4c08aed2011-07-01 19:47:50 +00006205 SetPixelRed(image,ScaleQuantumToShort(
6206 GetPixelRed(image,q)),q);
6207 SetPixelGreen(image,ScaleQuantumToShort(
6208 GetPixelGreen(image,q)),q);
6209 SetPixelBlue(image,ScaleQuantumToShort(
6210 GetPixelBlue(image,q)),q);
6211 SetPixelAlpha(image,ScaleQuantumToShort(
6212 GetPixelAlpha(image,q)),q);
6213 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006214 }
glennrp47b9dd52010-11-24 18:12:06 +00006215
cristy3ed852e2009-09-05 21:47:34 +00006216 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6217 break;
6218 }
6219 }
6220#else
6221#define QM Quantum
6222#endif
6223
6224 if (image->matte != MagickFalse)
6225 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006226
cristy3ed852e2009-09-05 21:47:34 +00006227 else
6228 {
cristy4c08aed2011-07-01 19:47:50 +00006229 large_image->background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00006230 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006231
cristy3ed852e2009-09-05 21:47:34 +00006232 if (magn_methx == 4)
6233 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006234
cristy3ed852e2009-09-05 21:47:34 +00006235 if (magn_methx == 5)
6236 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006237
cristy3ed852e2009-09-05 21:47:34 +00006238 if (magn_methy == 4)
6239 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006240
cristy3ed852e2009-09-05 21:47:34 +00006241 if (magn_methy == 5)
6242 magn_methy=3;
6243 }
6244
6245 /* magnify the rows into the right side of the large image */
6246
6247 if (logging != MagickFalse)
6248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006249 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006250 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006251 yy=0;
6252 length=(size_t) image->columns;
cristy4c08aed2011-07-01 19:47:50 +00006253 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6254 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006255
cristy4c08aed2011-07-01 19:47:50 +00006256 if ((prev == (Quantum *) NULL) ||
6257 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006258 {
6259 image=DestroyImageList(image);
6260 MngInfoFreeStruct(mng_info,&have_mng_structure);
6261 ThrowReaderException(ResourceLimitError,
6262 "MemoryAllocationFailed");
6263 }
glennrp47b9dd52010-11-24 18:12:06 +00006264
cristy3ed852e2009-09-05 21:47:34 +00006265 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6266 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006267
cristybb503372010-05-27 20:51:26 +00006268 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006269 {
6270 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006271 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006272
cristybb503372010-05-27 20:51:26 +00006273 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6274 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006275
cristybb503372010-05-27 20:51:26 +00006276 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6277 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006278
cristybb503372010-05-27 20:51:26 +00006279 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006280 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006281
cristy3ed852e2009-09-05 21:47:34 +00006282 else
cristybb503372010-05-27 20:51:26 +00006283 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006284
cristy3ed852e2009-09-05 21:47:34 +00006285 n=prev;
6286 prev=next;
6287 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006288
cristybb503372010-05-27 20:51:26 +00006289 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006290 {
6291 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6292 exception);
6293 (void) CopyMagickMemory(next,n,length);
6294 }
glennrp47b9dd52010-11-24 18:12:06 +00006295
cristy3ed852e2009-09-05 21:47:34 +00006296 for (i=0; i < m; i++, yy++)
6297 {
cristy4c08aed2011-07-01 19:47:50 +00006298 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006299 *pixels;
6300
cristybb503372010-05-27 20:51:26 +00006301 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006302 pixels=prev;
6303 n=next;
6304 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006305 1,exception);
cristy3ed852e2009-09-05 21:47:34 +00006306 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00006307
cristybb503372010-05-27 20:51:26 +00006308 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006309 {
glennrpfd05d622011-02-25 04:10:33 +00006310 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006311 /*
6312 if (image->storage_class == PseudoClass)
6313 {
6314 }
6315 */
6316
6317 if (magn_methy <= 1)
6318 {
glennrpbb4f99d2011-05-22 11:13:17 +00006319 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006320 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
glennrp847370c2011-07-05 17:37:15 +00006321 SetPixelGreen(large_image,GetPixelGreen(image,
6322 pixels),q);
6323 SetPixelBlue(large_image,GetPixelBlue(image,
6324 pixels),q);
6325 SetPixelAlpha(large_image,GetPixelAlpha(image,
6326 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006327 }
glennrp47b9dd52010-11-24 18:12:06 +00006328
cristy3ed852e2009-09-05 21:47:34 +00006329 else if (magn_methy == 2 || magn_methy == 4)
6330 {
6331 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006332 {
glennrp847370c2011-07-05 17:37:15 +00006333 SetPixelRed(large_image,GetPixelRed(image,
6334 pixels),q);
6335 SetPixelGreen(large_image,GetPixelGreen(image,
6336 pixels),q);
6337 SetPixelBlue(large_image,GetPixelBlue(image,
6338 pixels),q);
6339 SetPixelAlpha(large_image,GetPixelAlpha(image,
6340 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006341 }
glennrp47b9dd52010-11-24 18:12:06 +00006342
cristy3ed852e2009-09-05 21:47:34 +00006343 else
6344 {
6345 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006346 SetPixelRed(large_image,((QM) (((ssize_t)
6347 (2*i*(GetPixelRed(image,n)
6348 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006349 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006350 +GetPixelRed(image,pixels)))),q);
6351 SetPixelGreen(large_image,((QM) (((ssize_t)
6352 (2*i*(GetPixelGreen(image,n)
6353 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006354 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006355 +GetPixelGreen(image,pixels)))),q);
6356 SetPixelBlue(large_image,((QM) (((ssize_t)
6357 (2*i*(GetPixelBlue(image,n)
6358 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006359 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006360 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006361
cristy3ed852e2009-09-05 21:47:34 +00006362 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006363 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6364 (2*i*(GetPixelAlpha(image,n)
6365 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006366 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006367 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006368 }
glennrp47b9dd52010-11-24 18:12:06 +00006369
cristy3ed852e2009-09-05 21:47:34 +00006370 if (magn_methy == 4)
6371 {
6372 /* Replicate nearest */
6373 if (i <= ((m+1) << 1))
glennrp847370c2011-07-05 17:37:15 +00006374 SetPixelAlpha(large_image,GetPixelAlpha(image,
6375 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006376 else
glennrp847370c2011-07-05 17:37:15 +00006377 SetPixelAlpha(large_image,GetPixelAlpha(image,
6378 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006379 }
6380 }
glennrp47b9dd52010-11-24 18:12:06 +00006381
cristy3ed852e2009-09-05 21:47:34 +00006382 else /* if (magn_methy == 3 || magn_methy == 5) */
6383 {
6384 /* Replicate nearest */
6385 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006386 {
glennrp847370c2011-07-05 17:37:15 +00006387 SetPixelRed(large_image,GetPixelRed(image,
6388 pixels),q);
6389 SetPixelGreen(large_image,GetPixelGreen(image,
6390 pixels),q);
6391 SetPixelBlue(large_image,GetPixelBlue(image,
6392 pixels),q);
6393 SetPixelAlpha(large_image,GetPixelAlpha(image,
6394 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006395 }
glennrp47b9dd52010-11-24 18:12:06 +00006396
cristy3ed852e2009-09-05 21:47:34 +00006397 else
glennrpbb4f99d2011-05-22 11:13:17 +00006398 {
cristy4c08aed2011-07-01 19:47:50 +00006399 SetPixelRed(large_image,GetPixelRed(image,n),q);
glennrp847370c2011-07-05 17:37:15 +00006400 SetPixelGreen(large_image,GetPixelGreen(image,n),
6401 q);
6402 SetPixelBlue(large_image,GetPixelBlue(image,n),
6403 q);
6404 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6405 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006406 }
glennrp47b9dd52010-11-24 18:12:06 +00006407
cristy3ed852e2009-09-05 21:47:34 +00006408 if (magn_methy == 5)
6409 {
cristy4c08aed2011-07-01 19:47:50 +00006410 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6411 (GetPixelAlpha(image,n)
6412 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006413 +m))/((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006414 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006415 }
6416 }
cristy4c08aed2011-07-01 19:47:50 +00006417 n+=GetPixelChannels(image);
6418 q+=GetPixelChannels(large_image);
6419 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006420 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006421
cristy3ed852e2009-09-05 21:47:34 +00006422 if (SyncAuthenticPixels(large_image,exception) == 0)
6423 break;
glennrp47b9dd52010-11-24 18:12:06 +00006424
cristy3ed852e2009-09-05 21:47:34 +00006425 } /* i */
6426 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006427
cristy4c08aed2011-07-01 19:47:50 +00006428 prev=(Quantum *) RelinquishMagickMemory(prev);
6429 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006430
6431 length=image->columns;
6432
6433 if (logging != MagickFalse)
6434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6435 " Delete original image");
6436
6437 DeleteImageFromList(&image);
6438
6439 image=large_image;
6440
6441 mng_info->image=image;
6442
6443 /* magnify the columns */
6444 if (logging != MagickFalse)
6445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006446 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006447
cristybb503372010-05-27 20:51:26 +00006448 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006449 {
cristy4c08aed2011-07-01 19:47:50 +00006450 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006451 *pixels;
6452
6453 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00006454 pixels=q+(image->columns-length)*GetPixelChannels(image);
6455 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006456
cristybb503372010-05-27 20:51:26 +00006457 for (x=(ssize_t) (image->columns-length);
6458 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006459 {
glennrp7c7b3152011-04-26 04:01:27 +00006460 /* To do: Rewrite using Get/Set***PixelComponent() */
6461
cristybb503372010-05-27 20:51:26 +00006462 if (x == (ssize_t) (image->columns-length))
6463 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006464
cristybb503372010-05-27 20:51:26 +00006465 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6466 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006467
cristybb503372010-05-27 20:51:26 +00006468 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6469 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006470
cristybb503372010-05-27 20:51:26 +00006471 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006472 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006473
cristy3ed852e2009-09-05 21:47:34 +00006474 else
cristybb503372010-05-27 20:51:26 +00006475 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006476
cristy3ed852e2009-09-05 21:47:34 +00006477 for (i=0; i < m; i++)
6478 {
6479 if (magn_methx <= 1)
6480 {
6481 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006482 SetPixelRed(image,GetPixelRed(image,pixels),q);
6483 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6484 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6485 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006486 }
glennrp47b9dd52010-11-24 18:12:06 +00006487
cristy3ed852e2009-09-05 21:47:34 +00006488 else if (magn_methx == 2 || magn_methx == 4)
6489 {
6490 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006491 {
cristy4c08aed2011-07-01 19:47:50 +00006492 SetPixelRed(image,GetPixelRed(image,pixels),q);
6493 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6494 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6495 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006496 }
glennrp47b9dd52010-11-24 18:12:06 +00006497
glennrpbb4f99d2011-05-22 11:13:17 +00006498 /* To do: Rewrite using Get/Set***PixelComponent() */
cristy3ed852e2009-09-05 21:47:34 +00006499 else
6500 {
6501 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006502 SetPixelRed(image,(QM) ((2*i*(
6503 GetPixelRed(image,n)
6504 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006505 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006506 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006507
cristy4c08aed2011-07-01 19:47:50 +00006508 SetPixelGreen(image,(QM) ((2*i*(
6509 GetPixelGreen(image,n)
6510 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006511 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006512 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006513
cristy4c08aed2011-07-01 19:47:50 +00006514 SetPixelBlue(image,(QM) ((2*i*(
6515 GetPixelBlue(image,n)
6516 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006517 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006518 GetPixelBlue(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006519 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006520 SetPixelAlpha(image,(QM) ((2*i*(
6521 GetPixelAlpha(image,n)
6522 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006523 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006524 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006525 }
glennrp47b9dd52010-11-24 18:12:06 +00006526
cristy3ed852e2009-09-05 21:47:34 +00006527 if (magn_methx == 4)
6528 {
6529 /* Replicate nearest */
6530 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006531 {
cristy4c08aed2011-07-01 19:47:50 +00006532 SetPixelAlpha(image,
6533 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006534 }
cristy3ed852e2009-09-05 21:47:34 +00006535 else
glennrpbb4f99d2011-05-22 11:13:17 +00006536 {
cristy4c08aed2011-07-01 19:47:50 +00006537 SetPixelAlpha(image,
6538 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006539 }
cristy3ed852e2009-09-05 21:47:34 +00006540 }
6541 }
glennrp47b9dd52010-11-24 18:12:06 +00006542
cristy3ed852e2009-09-05 21:47:34 +00006543 else /* if (magn_methx == 3 || magn_methx == 5) */
6544 {
6545 /* Replicate nearest */
6546 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006547 {
cristy4c08aed2011-07-01 19:47:50 +00006548 SetPixelRed(image,GetPixelRed(image,pixels),q);
6549 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6550 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6551 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006552 }
glennrp47b9dd52010-11-24 18:12:06 +00006553
cristy3ed852e2009-09-05 21:47:34 +00006554 else
glennrpbb4f99d2011-05-22 11:13:17 +00006555 {
cristy4c08aed2011-07-01 19:47:50 +00006556 SetPixelRed(image,GetPixelRed(image,n),q);
6557 SetPixelGreen(image,GetPixelGreen(image,n),q);
6558 SetPixelBlue(image,GetPixelBlue(image,n),q);
6559 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006560 }
glennrp47b9dd52010-11-24 18:12:06 +00006561
cristy3ed852e2009-09-05 21:47:34 +00006562 if (magn_methx == 5)
6563 {
6564 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006565 SetPixelAlpha(image,
6566 (QM) ((2*i*( GetPixelAlpha(image,n)
6567 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006568 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006569 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006570 }
6571 }
cristy4c08aed2011-07-01 19:47:50 +00006572 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006573 }
cristy4c08aed2011-07-01 19:47:50 +00006574 n+=GetPixelChannels(image);
6575 p+=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 }
glennrp3faa9a32011-04-23 14:00:25 +00006581#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006582 if (magn_methx != 1 || magn_methy != 1)
6583 {
6584 /*
6585 Rescale pixels to Quantum
6586 */
cristybb503372010-05-27 20:51:26 +00006587 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006588 {
6589 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006590
cristybb503372010-05-27 20:51:26 +00006591 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006592 {
cristy4c08aed2011-07-01 19:47:50 +00006593 SetPixelRed(image,ScaleShortToQuantum(
6594 GetPixelRed(image,q)),q);
6595 SetPixelGreen(image,ScaleShortToQuantum(
6596 GetPixelGreen(image,q)),q);
6597 SetPixelBlue(image,ScaleShortToQuantum(
6598 GetPixelBlue(image,q)),q);
6599 SetPixelAlpha(image,ScaleShortToQuantum(
6600 GetPixelAlpha(image,q)),q);
6601 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006602 }
glennrp47b9dd52010-11-24 18:12:06 +00006603
cristy3ed852e2009-09-05 21:47:34 +00006604 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6605 break;
6606 }
6607 }
6608#endif
6609 if (logging != MagickFalse)
6610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6611 " Finished MAGN processing");
6612 }
6613 }
6614
6615 /*
6616 Crop_box is with respect to the upper left corner of the MNG.
6617 */
6618 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6619 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6620 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6621 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6622 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6623 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6624 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6625 if ((crop_box.left != (mng_info->image_box.left
6626 +mng_info->x_off[object_id])) ||
6627 (crop_box.right != (mng_info->image_box.right
6628 +mng_info->x_off[object_id])) ||
6629 (crop_box.top != (mng_info->image_box.top
6630 +mng_info->y_off[object_id])) ||
6631 (crop_box.bottom != (mng_info->image_box.bottom
6632 +mng_info->y_off[object_id])))
6633 {
6634 if (logging != MagickFalse)
6635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6636 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006637
cristy3ed852e2009-09-05 21:47:34 +00006638 if ((crop_box.left < crop_box.right) &&
6639 (crop_box.top < crop_box.bottom))
6640 {
6641 Image
6642 *im;
6643
6644 RectangleInfo
6645 crop_info;
6646
6647 /*
6648 Crop_info is with respect to the upper left corner of
6649 the image.
6650 */
6651 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6652 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006653 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6654 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006655 image->page.width=image->columns;
6656 image->page.height=image->rows;
6657 image->page.x=0;
6658 image->page.y=0;
6659 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006660
cristy3ed852e2009-09-05 21:47:34 +00006661 if (im != (Image *) NULL)
6662 {
6663 image->columns=im->columns;
6664 image->rows=im->rows;
6665 im=DestroyImage(im);
6666 image->page.width=image->columns;
6667 image->page.height=image->rows;
6668 image->page.x=crop_box.left;
6669 image->page.y=crop_box.top;
6670 }
6671 }
glennrp47b9dd52010-11-24 18:12:06 +00006672
cristy3ed852e2009-09-05 21:47:34 +00006673 else
6674 {
6675 /*
6676 No pixels in crop area. The MNG spec still requires
6677 a layer, though, so make a single transparent pixel in
6678 the top left corner.
6679 */
6680 image->columns=1;
6681 image->rows=1;
6682 image->colors=2;
6683 (void) SetImageBackgroundColor(image);
6684 image->page.width=1;
6685 image->page.height=1;
6686 image->page.x=0;
6687 image->page.y=0;
6688 }
6689 }
6690#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6691 image=mng_info->image;
6692#endif
6693 }
6694
glennrp2b013e42010-11-24 16:55:50 +00006695#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6696 /* PNG does not handle depths greater than 16 so reduce it even
6697 * if lossy
6698 */
6699 if (image->depth > 16)
6700 image->depth=16;
6701#endif
6702
glennrp3faa9a32011-04-23 14:00:25 +00006703#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006704 if (LosslessReduceDepthOK(image) != MagickFalse)
6705 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006706#endif
glennrpd6afd542010-11-19 01:53:05 +00006707
cristy3ed852e2009-09-05 21:47:34 +00006708 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006709
cristy3ed852e2009-09-05 21:47:34 +00006710 if (image_info->number_scenes != 0)
6711 {
6712 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006713 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006714 break;
6715 }
glennrpd6afd542010-11-19 01:53:05 +00006716
cristy3ed852e2009-09-05 21:47:34 +00006717 if (logging != MagickFalse)
6718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6719 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006720
cristy3ed852e2009-09-05 21:47:34 +00006721 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006722
cristy3ed852e2009-09-05 21:47:34 +00006723 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006724
cristy3ed852e2009-09-05 21:47:34 +00006725 if (logging != MagickFalse)
6726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6727 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006728
cristy3ed852e2009-09-05 21:47:34 +00006729#if defined(MNG_INSERT_LAYERS)
6730 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6731 (mng_info->mng_height))
6732 {
6733 /*
6734 Insert a background layer if nothing else was found.
6735 */
6736 if (logging != MagickFalse)
6737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6738 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006739
cristy4c08aed2011-07-01 19:47:50 +00006740 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006741 {
6742 /*
6743 Allocate next image structure.
6744 */
6745 AcquireNextImage(image_info,image);
6746 if (GetNextImageInList(image) == (Image *) NULL)
6747 {
6748 image=DestroyImageList(image);
6749 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006750
cristy3ed852e2009-09-05 21:47:34 +00006751 if (logging != MagickFalse)
6752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6753 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006754
cristy3ed852e2009-09-05 21:47:34 +00006755 return((Image *) NULL);
6756 }
6757 image=SyncNextImageInList(image);
6758 }
6759 image->columns=mng_info->mng_width;
6760 image->rows=mng_info->mng_height;
6761 image->page.width=mng_info->mng_width;
6762 image->page.height=mng_info->mng_height;
6763 image->page.x=0;
6764 image->page.y=0;
6765 image->background_color=mng_background_color;
6766 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006767
cristy3ed852e2009-09-05 21:47:34 +00006768 if (image_info->ping == MagickFalse)
6769 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006770
cristy3ed852e2009-09-05 21:47:34 +00006771 mng_info->image_found++;
6772 }
6773#endif
6774 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006775
cristy3ed852e2009-09-05 21:47:34 +00006776 if (mng_iterations == 1)
6777 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006778
cristy3ed852e2009-09-05 21:47:34 +00006779 while (GetPreviousImageInList(image) != (Image *) NULL)
6780 {
6781 image_count++;
6782 if (image_count > 10*mng_info->image_found)
6783 {
6784 if (logging != MagickFalse)
6785 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006786
cristy3ed852e2009-09-05 21:47:34 +00006787 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6788 CoderError,"Linked list is corrupted, beginning of list not found",
6789 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006790
cristy3ed852e2009-09-05 21:47:34 +00006791 return((Image *) NULL);
6792 }
glennrp0fe50b42010-11-16 03:52:51 +00006793
cristy3ed852e2009-09-05 21:47:34 +00006794 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006795
cristy3ed852e2009-09-05 21:47:34 +00006796 if (GetNextImageInList(image) == (Image *) NULL)
6797 {
6798 if (logging != MagickFalse)
6799 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006800
cristy3ed852e2009-09-05 21:47:34 +00006801 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6802 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6803 image_info->filename);
6804 }
6805 }
glennrp47b9dd52010-11-24 18:12:06 +00006806
cristy3ed852e2009-09-05 21:47:34 +00006807 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6808 GetNextImageInList(image) ==
6809 (Image *) NULL)
6810 {
6811 if (logging != MagickFalse)
6812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6813 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006814
cristy3ed852e2009-09-05 21:47:34 +00006815 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6816 CoderError,"image->next for first image is NULL but shouldn't be.",
6817 "`%s'",image_info->filename);
6818 }
glennrp47b9dd52010-11-24 18:12:06 +00006819
cristy3ed852e2009-09-05 21:47:34 +00006820 if (mng_info->image_found == 0)
6821 {
6822 if (logging != MagickFalse)
6823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6824 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006825
cristy3ed852e2009-09-05 21:47:34 +00006826 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6827 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006828
cristy3ed852e2009-09-05 21:47:34 +00006829 if (image != (Image *) NULL)
6830 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006831
cristy3ed852e2009-09-05 21:47:34 +00006832 MngInfoFreeStruct(mng_info,&have_mng_structure);
6833 return((Image *) NULL);
6834 }
6835
6836 if (mng_info->ticks_per_second)
6837 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6838 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006839
cristy3ed852e2009-09-05 21:47:34 +00006840 else
6841 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006842
cristy3ed852e2009-09-05 21:47:34 +00006843 /* Find final nonzero image delay */
6844 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006845
cristy3ed852e2009-09-05 21:47:34 +00006846 while (GetNextImageInList(image) != (Image *) NULL)
6847 {
6848 if (image->delay)
6849 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006850
cristy3ed852e2009-09-05 21:47:34 +00006851 image=GetNextImageInList(image);
6852 }
glennrp0fe50b42010-11-16 03:52:51 +00006853
cristy3ed852e2009-09-05 21:47:34 +00006854 if (final_delay < final_image_delay)
6855 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006856
cristy3ed852e2009-09-05 21:47:34 +00006857 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006858
cristy3ed852e2009-09-05 21:47:34 +00006859 if (logging != MagickFalse)
6860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006861 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6862 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006863
cristy3ed852e2009-09-05 21:47:34 +00006864 if (logging != MagickFalse)
6865 {
6866 int
6867 scene;
6868
6869 scene=0;
6870 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006871
cristy3ed852e2009-09-05 21:47:34 +00006872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6873 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006874
cristy3ed852e2009-09-05 21:47:34 +00006875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006876 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006877
cristy3ed852e2009-09-05 21:47:34 +00006878 while (GetNextImageInList(image) != (Image *) NULL)
6879 {
6880 image=GetNextImageInList(image);
6881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006882 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006883 }
6884 }
6885
6886 image=GetFirstImageInList(image);
6887#ifdef MNG_COALESCE_LAYERS
6888 if (insert_layers)
6889 {
6890 Image
6891 *next_image,
6892 *next;
6893
cristybb503372010-05-27 20:51:26 +00006894 size_t
cristy3ed852e2009-09-05 21:47:34 +00006895 scene;
6896
6897 if (logging != MagickFalse)
6898 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006899
cristy3ed852e2009-09-05 21:47:34 +00006900 scene=image->scene;
6901 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006902
cristy3ed852e2009-09-05 21:47:34 +00006903 if (next_image == (Image *) NULL)
6904 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006905
cristy3ed852e2009-09-05 21:47:34 +00006906 image=DestroyImageList(image);
6907 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006908
cristy3ed852e2009-09-05 21:47:34 +00006909 for (next=image; next != (Image *) NULL; next=next_image)
6910 {
6911 next->page.width=mng_info->mng_width;
6912 next->page.height=mng_info->mng_height;
6913 next->page.x=0;
6914 next->page.y=0;
6915 next->scene=scene++;
6916 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006917
cristy3ed852e2009-09-05 21:47:34 +00006918 if (next_image == (Image *) NULL)
6919 break;
glennrp47b9dd52010-11-24 18:12:06 +00006920
cristy3ed852e2009-09-05 21:47:34 +00006921 if (next->delay == 0)
6922 {
6923 scene--;
6924 next_image->previous=GetPreviousImageInList(next);
6925 if (GetPreviousImageInList(next) == (Image *) NULL)
6926 image=next_image;
6927 else
6928 next->previous->next=next_image;
6929 next=DestroyImage(next);
6930 }
6931 }
6932 }
6933#endif
6934
6935 while (GetNextImageInList(image) != (Image *) NULL)
6936 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006937
cristy3ed852e2009-09-05 21:47:34 +00006938 image->dispose=BackgroundDispose;
6939
6940 if (logging != MagickFalse)
6941 {
6942 int
6943 scene;
6944
6945 scene=0;
6946 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006947
cristy3ed852e2009-09-05 21:47:34 +00006948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6949 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006950
cristy3ed852e2009-09-05 21:47:34 +00006951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006952 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6953 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006954
cristy3ed852e2009-09-05 21:47:34 +00006955 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006956 {
6957 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006958
cristyf2faecf2010-05-28 19:19:36 +00006959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006960 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6961 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006962 }
6963 }
glennrp47b9dd52010-11-24 18:12:06 +00006964
cristy3ed852e2009-09-05 21:47:34 +00006965 image=GetFirstImageInList(image);
6966 MngInfoFreeStruct(mng_info,&have_mng_structure);
6967 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006968
cristy3ed852e2009-09-05 21:47:34 +00006969 if (logging != MagickFalse)
6970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006971
cristy3ed852e2009-09-05 21:47:34 +00006972 return(GetFirstImageInList(image));
6973}
glennrp25c1e2b2010-03-25 01:39:56 +00006974#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006975static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6976{
6977 printf("Your PNG library is too old: You have libpng-%s\n",
6978 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006979
cristy3ed852e2009-09-05 21:47:34 +00006980 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6981 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006982
cristy3ed852e2009-09-05 21:47:34 +00006983 return(Image *) NULL;
6984}
glennrp47b9dd52010-11-24 18:12:06 +00006985
cristy3ed852e2009-09-05 21:47:34 +00006986static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6987{
6988 return(ReadPNGImage(image_info,exception));
6989}
glennrp25c1e2b2010-03-25 01:39:56 +00006990#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006991#endif
6992
6993/*
6994%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6995% %
6996% %
6997% %
6998% R e g i s t e r P N G I m a g e %
6999% %
7000% %
7001% %
7002%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7003%
7004% RegisterPNGImage() adds properties for the PNG image format to
7005% the list of supported formats. The properties include the image format
7006% tag, a method to read and/or write the format, whether the format
7007% supports the saving of more than one frame to the same file or blob,
7008% whether the format supports native in-memory I/O, and a brief
7009% description of the format.
7010%
7011% The format of the RegisterPNGImage method is:
7012%
cristybb503372010-05-27 20:51:26 +00007013% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007014%
7015*/
cristybb503372010-05-27 20:51:26 +00007016ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007017{
7018 char
7019 version[MaxTextExtent];
7020
7021 MagickInfo
7022 *entry;
7023
7024 static const char
7025 *PNGNote=
7026 {
7027 "See http://www.libpng.org/ for details about the PNG format."
7028 },
glennrp47b9dd52010-11-24 18:12:06 +00007029
cristy3ed852e2009-09-05 21:47:34 +00007030 *JNGNote=
7031 {
7032 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7033 "format."
7034 },
glennrp47b9dd52010-11-24 18:12:06 +00007035
cristy3ed852e2009-09-05 21:47:34 +00007036 *MNGNote=
7037 {
7038 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7039 "format."
7040 };
7041
7042 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007043
cristy3ed852e2009-09-05 21:47:34 +00007044#if defined(PNG_LIBPNG_VER_STRING)
7045 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7046 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007047
cristy3ed852e2009-09-05 21:47:34 +00007048 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7049 {
7050 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7051 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7052 MaxTextExtent);
7053 }
7054#endif
glennrp47b9dd52010-11-24 18:12:06 +00007055
cristy3ed852e2009-09-05 21:47:34 +00007056 entry=SetMagickInfo("MNG");
7057 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007058
cristy3ed852e2009-09-05 21:47:34 +00007059#if defined(MAGICKCORE_PNG_DELEGATE)
7060 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7061 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7062#endif
glennrp47b9dd52010-11-24 18:12:06 +00007063
cristy3ed852e2009-09-05 21:47:34 +00007064 entry->magick=(IsImageFormatHandler *) IsMNG;
7065 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007066
cristy3ed852e2009-09-05 21:47:34 +00007067 if (*version != '\0')
7068 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007069
cristy3ed852e2009-09-05 21:47:34 +00007070 entry->module=ConstantString("PNG");
7071 entry->note=ConstantString(MNGNote);
7072 (void) RegisterMagickInfo(entry);
7073
7074 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007075
cristy3ed852e2009-09-05 21:47:34 +00007076#if defined(MAGICKCORE_PNG_DELEGATE)
7077 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7078 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7079#endif
glennrp47b9dd52010-11-24 18:12:06 +00007080
cristy3ed852e2009-09-05 21:47:34 +00007081 entry->magick=(IsImageFormatHandler *) IsPNG;
7082 entry->adjoin=MagickFalse;
7083 entry->description=ConstantString("Portable Network Graphics");
7084 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007085
cristy3ed852e2009-09-05 21:47:34 +00007086 if (*version != '\0')
7087 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007088
cristy3ed852e2009-09-05 21:47:34 +00007089 entry->note=ConstantString(PNGNote);
7090 (void) RegisterMagickInfo(entry);
7091
7092 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007093
cristy3ed852e2009-09-05 21:47:34 +00007094#if defined(MAGICKCORE_PNG_DELEGATE)
7095 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7096 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7097#endif
glennrp47b9dd52010-11-24 18:12:06 +00007098
cristy3ed852e2009-09-05 21:47:34 +00007099 entry->magick=(IsImageFormatHandler *) IsPNG;
7100 entry->adjoin=MagickFalse;
7101 entry->description=ConstantString(
7102 "8-bit indexed with optional binary transparency");
7103 entry->module=ConstantString("PNG");
7104 (void) RegisterMagickInfo(entry);
7105
7106 entry=SetMagickInfo("PNG24");
7107 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007108
cristy3ed852e2009-09-05 21:47:34 +00007109#if defined(ZLIB_VERSION)
7110 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7111 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007112
cristy3ed852e2009-09-05 21:47:34 +00007113 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7114 {
7115 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7116 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7117 }
7118#endif
glennrp47b9dd52010-11-24 18:12:06 +00007119
cristy3ed852e2009-09-05 21:47:34 +00007120 if (*version != '\0')
7121 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007122
cristy3ed852e2009-09-05 21:47:34 +00007123#if defined(MAGICKCORE_PNG_DELEGATE)
7124 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7125 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7126#endif
glennrp47b9dd52010-11-24 18:12:06 +00007127
cristy3ed852e2009-09-05 21:47:34 +00007128 entry->magick=(IsImageFormatHandler *) IsPNG;
7129 entry->adjoin=MagickFalse;
7130 entry->description=ConstantString("opaque 24-bit RGB");
7131 entry->module=ConstantString("PNG");
7132 (void) RegisterMagickInfo(entry);
7133
7134 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007135
cristy3ed852e2009-09-05 21:47:34 +00007136#if defined(MAGICKCORE_PNG_DELEGATE)
7137 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7138 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7139#endif
glennrp47b9dd52010-11-24 18:12:06 +00007140
cristy3ed852e2009-09-05 21:47:34 +00007141 entry->magick=(IsImageFormatHandler *) IsPNG;
7142 entry->adjoin=MagickFalse;
7143 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7144 entry->module=ConstantString("PNG");
7145 (void) RegisterMagickInfo(entry);
7146
7147 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007148
cristy3ed852e2009-09-05 21:47:34 +00007149#if defined(JNG_SUPPORTED)
7150#if defined(MAGICKCORE_PNG_DELEGATE)
7151 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7152 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7153#endif
7154#endif
glennrp47b9dd52010-11-24 18:12:06 +00007155
cristy3ed852e2009-09-05 21:47:34 +00007156 entry->magick=(IsImageFormatHandler *) IsJNG;
7157 entry->adjoin=MagickFalse;
7158 entry->description=ConstantString("JPEG Network Graphics");
7159 entry->module=ConstantString("PNG");
7160 entry->note=ConstantString(JNGNote);
7161 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007162
cristy18b17442009-10-25 18:36:48 +00007163#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007164 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007165#endif
glennrp47b9dd52010-11-24 18:12:06 +00007166
cristy3ed852e2009-09-05 21:47:34 +00007167 return(MagickImageCoderSignature);
7168}
7169
7170/*
7171%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7172% %
7173% %
7174% %
7175% U n r e g i s t e r P N G I m a g e %
7176% %
7177% %
7178% %
7179%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7180%
7181% UnregisterPNGImage() removes format registrations made by the
7182% PNG module from the list of supported formats.
7183%
7184% The format of the UnregisterPNGImage method is:
7185%
7186% UnregisterPNGImage(void)
7187%
7188*/
7189ModuleExport void UnregisterPNGImage(void)
7190{
7191 (void) UnregisterMagickInfo("MNG");
7192 (void) UnregisterMagickInfo("PNG");
7193 (void) UnregisterMagickInfo("PNG8");
7194 (void) UnregisterMagickInfo("PNG24");
7195 (void) UnregisterMagickInfo("PNG32");
7196 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007197
cristy3ed852e2009-09-05 21:47:34 +00007198#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007199 if (ping_semaphore != (SemaphoreInfo *) NULL)
7200 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007201#endif
7202}
7203
7204#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007205#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007206/*
7207%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7208% %
7209% %
7210% %
7211% W r i t e M N G I m a g e %
7212% %
7213% %
7214% %
7215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7216%
7217% WriteMNGImage() writes an image in the Portable Network Graphics
7218% Group's "Multiple-image Network Graphics" encoded image format.
7219%
7220% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7221%
7222% The format of the WriteMNGImage method is:
7223%
7224% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
7225%
7226% A description of each parameter follows.
7227%
7228% o image_info: the image info.
7229%
7230% o image: The image.
7231%
7232%
7233% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7234% "To do" under ReadPNGImage):
7235%
cristy3ed852e2009-09-05 21:47:34 +00007236% Preserve all unknown and not-yet-handled known chunks found in input
7237% PNG file and copy them into output PNG files according to the PNG
7238% copying rules.
7239%
7240% Write the iCCP chunk at MNG level when (icc profile length > 0)
7241%
7242% Improve selection of color type (use indexed-colour or indexed-colour
7243% with tRNS when 256 or fewer unique RGBA values are present).
7244%
7245% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7246% This will be complicated if we limit ourselves to generating MNG-LC
7247% files. For now we ignore disposal method 3 and simply overlay the next
7248% image on it.
7249%
7250% Check for identical PLTE's or PLTE/tRNS combinations and use a
7251% global MNG PLTE or PLTE/tRNS combination when appropriate.
7252% [mostly done 15 June 1999 but still need to take care of tRNS]
7253%
7254% Check for identical sRGB and replace with a global sRGB (and remove
7255% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7256% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7257% local gAMA/cHRM with local sRGB if appropriate).
7258%
7259% Check for identical sBIT chunks and write global ones.
7260%
7261% Provide option to skip writing the signature tEXt chunks.
7262%
7263% Use signatures to detect identical objects and reuse the first
7264% instance of such objects instead of writing duplicate objects.
7265%
7266% Use a smaller-than-32k value of compression window size when
7267% appropriate.
7268%
7269% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7270% ancillary text chunks and save profiles.
7271%
7272% Provide an option to force LC files (to ensure exact framing rate)
7273% instead of VLC.
7274%
7275% Provide an option to force VLC files instead of LC, even when offsets
7276% are present. This will involve expanding the embedded images with a
7277% transparent region at the top and/or left.
7278*/
7279
cristy3ed852e2009-09-05 21:47:34 +00007280static void
glennrpcf002022011-01-30 02:38:15 +00007281Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007282 png_info *ping_info, unsigned char *profile_type, unsigned char
7283 *profile_description, unsigned char *profile_data, png_uint_32 length)
7284{
cristy3ed852e2009-09-05 21:47:34 +00007285 png_textp
7286 text;
7287
cristybb503372010-05-27 20:51:26 +00007288 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007289 i;
7290
7291 unsigned char
7292 *sp;
7293
7294 png_charp
7295 dp;
7296
7297 png_uint_32
7298 allocated_length,
7299 description_length;
7300
7301 unsigned char
7302 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007303
cristy3ed852e2009-09-05 21:47:34 +00007304 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7305 return;
7306
7307 if (image_info->verbose)
7308 {
glennrp0fe50b42010-11-16 03:52:51 +00007309 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7310 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007311 }
glennrp0fe50b42010-11-16 03:52:51 +00007312
cristy3ed852e2009-09-05 21:47:34 +00007313 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7314 description_length=(png_uint_32) strlen((const char *) profile_description);
7315 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7316 + description_length);
7317 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7318 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7319 text[0].key[0]='\0';
7320 (void) ConcatenateMagickString(text[0].key,
7321 "Raw profile type ",MaxTextExtent);
7322 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7323 sp=profile_data;
7324 dp=text[0].text;
7325 *dp++='\n';
7326 (void) CopyMagickString(dp,(const char *) profile_description,
7327 allocated_length);
7328 dp+=description_length;
7329 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007330 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007331 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007332 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007333
cristybb503372010-05-27 20:51:26 +00007334 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007335 {
7336 if (i%36 == 0)
7337 *dp++='\n';
7338 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7339 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7340 }
glennrp47b9dd52010-11-24 18:12:06 +00007341
cristy3ed852e2009-09-05 21:47:34 +00007342 *dp++='\n';
7343 *dp='\0';
7344 text[0].text_length=(png_size_t) (dp-text[0].text);
7345 text[0].compression=image_info->compression == NoCompression ||
7346 (image_info->compression == UndefinedCompression &&
7347 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007348
cristy3ed852e2009-09-05 21:47:34 +00007349 if (text[0].text_length <= allocated_length)
7350 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007351
cristy3ed852e2009-09-05 21:47:34 +00007352 png_free(ping,text[0].text);
7353 png_free(ping,text[0].key);
7354 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007355}
7356
glennrpcf002022011-01-30 02:38:15 +00007357static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007358 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007359{
7360 char
7361 *name;
7362
7363 const StringInfo
7364 *profile;
7365
7366 unsigned char
7367 *data;
7368
7369 png_uint_32 length;
7370
7371 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007372
7373 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7374 {
cristy3ed852e2009-09-05 21:47:34 +00007375 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007376
cristy3ed852e2009-09-05 21:47:34 +00007377 if (profile != (const StringInfo *) NULL)
7378 {
7379 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007380 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007381
glennrp47b9dd52010-11-24 18:12:06 +00007382 if (LocaleNCompare(name,string,11) == 0)
7383 {
7384 if (logging != MagickFalse)
7385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7386 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007387
glennrpcf002022011-01-30 02:38:15 +00007388 ping_profile=CloneStringInfo(profile);
7389 data=GetStringInfoDatum(ping_profile),
7390 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007391 data[4]=data[3];
7392 data[3]=data[2];
7393 data[2]=data[1];
7394 data[1]=data[0];
7395 (void) WriteBlobMSBULong(image,length-5); /* data length */
7396 (void) WriteBlob(image,length-1,data+1);
7397 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007398 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007399 }
cristy3ed852e2009-09-05 21:47:34 +00007400 }
glennrp47b9dd52010-11-24 18:12:06 +00007401
cristy3ed852e2009-09-05 21:47:34 +00007402 name=GetNextImageProfile(image);
7403 }
glennrp47b9dd52010-11-24 18:12:06 +00007404
cristy3ed852e2009-09-05 21:47:34 +00007405 return(MagickTrue);
7406}
7407
glennrpb9cfe272010-12-21 15:08:06 +00007408
cristy3ed852e2009-09-05 21:47:34 +00007409/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007410static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7411 const ImageInfo *IMimage_info,Image *IMimage)
7412{
7413 Image
7414 *image;
7415
7416 ImageInfo
7417 *image_info;
7418
cristy3ed852e2009-09-05 21:47:34 +00007419 char
7420 s[2];
7421
7422 const char
7423 *name,
7424 *property,
7425 *value;
7426
7427 const StringInfo
7428 *profile;
7429
cristy3ed852e2009-09-05 21:47:34 +00007430 int
cristy3ed852e2009-09-05 21:47:34 +00007431 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007432 pass;
7433
glennrpe9c26dc2010-05-30 01:56:35 +00007434 png_byte
7435 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007436
glennrp39992b42010-11-14 00:03:43 +00007437 png_color
7438 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007439
glennrp5af765f2010-03-30 11:12:18 +00007440 png_color_16
7441 ping_background,
7442 ping_trans_color;
7443
cristy3ed852e2009-09-05 21:47:34 +00007444 png_info
7445 *ping_info;
7446
7447 png_struct
7448 *ping;
7449
glennrp5af765f2010-03-30 11:12:18 +00007450 png_uint_32
7451 ping_height,
7452 ping_width;
7453
cristybb503372010-05-27 20:51:26 +00007454 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007455 y;
7456
7457 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007458 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007459 logging,
glennrp58e01762011-01-07 15:28:54 +00007460 matte,
7461
glennrpda8f3a72011-02-27 23:54:12 +00007462 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007463 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007464 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007465 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007466 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007467 ping_have_bKGD,
7468 ping_have_pHYs,
7469 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007470
7471 ping_exclude_bKGD,
7472 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007473 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007474 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007475 ping_exclude_gAMA,
7476 ping_exclude_iCCP,
7477 /* ping_exclude_iTXt, */
7478 ping_exclude_oFFs,
7479 ping_exclude_pHYs,
7480 ping_exclude_sRGB,
7481 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007482 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007483 ping_exclude_vpAg,
7484 ping_exclude_zCCP, /* hex-encoded iCCP */
7485 ping_exclude_zTXt,
7486
glennrp8d3d6e52011-04-19 04:39:51 +00007487 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007488 ping_need_colortype_warning,
7489
glennrp82b3c532011-03-22 19:20:54 +00007490 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007491 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007492 tried_333,
7493 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007494
7495 QuantumInfo
7496 *quantum_info;
7497
cristybb503372010-05-27 20:51:26 +00007498 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007499 i,
7500 x;
7501
7502 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007503 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007504
glennrp5af765f2010-03-30 11:12:18 +00007505 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007506 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007507 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007508 ping_color_type,
7509 ping_interlace_method,
7510 ping_compression_method,
7511 ping_filter_method,
7512 ping_num_trans;
7513
cristybb503372010-05-27 20:51:26 +00007514 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007515 image_depth,
7516 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007517
cristybb503372010-05-27 20:51:26 +00007518 size_t
cristy3ed852e2009-09-05 21:47:34 +00007519 quality,
7520 rowbytes,
7521 save_image_depth;
7522
glennrpdfd70802010-11-14 01:23:35 +00007523 int
glennrpfd05d622011-02-25 04:10:33 +00007524 j,
glennrpf09bded2011-01-08 01:15:59 +00007525 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007526 number_opaque,
7527 number_semitransparent,
7528 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007529 ping_pHYs_unit_type;
7530
7531 png_uint_32
7532 ping_pHYs_x_resolution,
7533 ping_pHYs_y_resolution;
7534
cristy3ed852e2009-09-05 21:47:34 +00007535 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007536 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007537
glennrpb9cfe272010-12-21 15:08:06 +00007538 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7539 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007540 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007541 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007542
cristy3ed852e2009-09-05 21:47:34 +00007543#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007544 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007545#endif
7546
glennrp5af765f2010-03-30 11:12:18 +00007547 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007548 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007549 ping_color_type=0,
7550 ping_interlace_method=0,
7551 ping_compression_method=0,
7552 ping_filter_method=0,
7553 ping_num_trans = 0;
7554
7555 ping_background.red = 0;
7556 ping_background.green = 0;
7557 ping_background.blue = 0;
7558 ping_background.gray = 0;
7559 ping_background.index = 0;
7560
7561 ping_trans_color.red=0;
7562 ping_trans_color.green=0;
7563 ping_trans_color.blue=0;
7564 ping_trans_color.gray=0;
7565
glennrpdfd70802010-11-14 01:23:35 +00007566 ping_pHYs_unit_type = 0;
7567 ping_pHYs_x_resolution = 0;
7568 ping_pHYs_y_resolution = 0;
7569
glennrpda8f3a72011-02-27 23:54:12 +00007570 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007571 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007572 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007573 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007574 ping_have_bKGD=MagickFalse;
7575 ping_have_pHYs=MagickFalse;
7576 ping_have_tRNS=MagickFalse;
7577
glennrp0e8ea192010-12-24 18:00:33 +00007578 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7579 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007580 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007581 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007582 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007583 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7584 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7585 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7586 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7587 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7588 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007589 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007590 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7591 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7592 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7593
glennrp8d3d6e52011-04-19 04:39:51 +00007594 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007595 ping_need_colortype_warning = MagickFalse;
7596
glennrp8bb3a022010-12-13 20:40:04 +00007597 number_opaque = 0;
7598 number_semitransparent = 0;
7599 number_transparent = 0;
7600
glennrpfd05d622011-02-25 04:10:33 +00007601 if (logging != MagickFalse)
7602 {
7603 if (image->storage_class == UndefinedClass)
7604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7605 " storage_class=UndefinedClass");
7606 if (image->storage_class == DirectClass)
7607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7608 " storage_class=DirectClass");
7609 if (image->storage_class == PseudoClass)
7610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7611 " storage_class=PseudoClass");
7612 }
glennrp28af3712011-04-06 18:07:30 +00007613
glennrpc6c391a2011-04-27 02:23:56 +00007614 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007615 {
glennrpc6c391a2011-04-27 02:23:56 +00007616 if (image->storage_class != PseudoClass && image->colormap != NULL)
7617 {
7618 /* Free the bogus colormap; it can cause trouble later */
7619 if (logging != MagickFalse)
7620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7621 " Freeing bogus colormap");
7622 (void *) RelinquishMagickMemory(image->colormap);
7623 image->colormap=NULL;
7624 }
glennrp28af3712011-04-06 18:07:30 +00007625 }
glennrpbb4f99d2011-05-22 11:13:17 +00007626
cristy3ed852e2009-09-05 21:47:34 +00007627 if (image->colorspace != RGBColorspace)
7628 (void) TransformImageColorspace(image,RGBColorspace);
glennrp0fe50b42010-11-16 03:52:51 +00007629
glennrp3241bd02010-12-12 04:36:28 +00007630 /*
7631 Sometimes we get PseudoClass images whose RGB values don't match
7632 the colors in the colormap. This code syncs the RGB values.
7633 */
7634 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7635 (void) SyncImage(image);
7636
glennrpa6a06632011-01-19 15:15:34 +00007637#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7638 if (image->depth > 8)
7639 {
7640 if (logging != MagickFalse)
7641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7642 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7643
7644 image->depth=8;
7645 }
7646#endif
7647
glennrp8e58efd2011-05-20 12:16:29 +00007648 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007649 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7650 {
cristy4c08aed2011-07-01 19:47:50 +00007651 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007652 *r;
7653
7654 ExceptionInfo
7655 *exception;
7656
7657 exception=(&image->exception);
7658
7659 if (image->depth > 8)
7660 {
7661#if MAGICKCORE_QUANTUM_DEPTH > 16
7662 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007663 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007664
7665 for (y=0; y < (ssize_t) image->rows; y++)
7666 {
7667 r=GetAuthenticPixels(image,0,y,image->columns,1,
7668 exception);
7669
cristy4c08aed2011-07-01 19:47:50 +00007670 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007671 break;
7672
7673 for (x=0; x < (ssize_t) image->columns; x++)
7674 {
cristy4c08aed2011-07-01 19:47:50 +00007675 LBR16RGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007676 r++;
7677 }
glennrpbb4f99d2011-05-22 11:13:17 +00007678
glennrp8e58efd2011-05-20 12:16:29 +00007679 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7680 break;
7681 }
7682
7683 if (image->storage_class == PseudoClass && image->colormap != NULL)
7684 {
cristy3e08f112011-05-24 13:19:30 +00007685 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007686 {
glennrp91d99252011-06-25 14:30:13 +00007687 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007688 }
7689 }
7690#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7691 }
7692
7693 else if (image->depth > 4)
7694 {
7695#if MAGICKCORE_QUANTUM_DEPTH > 8
7696 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007697 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007698
7699 for (y=0; y < (ssize_t) image->rows; y++)
7700 {
7701 r=GetAuthenticPixels(image,0,y,image->columns,1,
7702 exception);
7703
cristy4c08aed2011-07-01 19:47:50 +00007704 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007705 break;
7706
7707 for (x=0; x < (ssize_t) image->columns; x++)
7708 {
cristy4c08aed2011-07-01 19:47:50 +00007709 LBR08RGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007710 r++;
7711 }
glennrpbb4f99d2011-05-22 11:13:17 +00007712
glennrp8e58efd2011-05-20 12:16:29 +00007713 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7714 break;
7715 }
7716
7717 if (image->storage_class == PseudoClass && image->colormap != NULL)
7718 {
cristy3e08f112011-05-24 13:19:30 +00007719 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007720 {
glennrp91d99252011-06-25 14:30:13 +00007721 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007722 }
7723 }
7724#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7725 }
7726 else
7727 if (image->depth > 2)
7728 {
7729 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007730 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007731
7732 for (y=0; y < (ssize_t) image->rows; y++)
7733 {
7734 r=GetAuthenticPixels(image,0,y,image->columns,1,
7735 exception);
7736
cristy4c08aed2011-07-01 19:47:50 +00007737 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007738 break;
7739
7740 for (x=0; x < (ssize_t) image->columns; x++)
7741 {
cristy4c08aed2011-07-01 19:47:50 +00007742 LBR04RGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007743 r++;
7744 }
glennrpbb4f99d2011-05-22 11:13:17 +00007745
glennrp8e58efd2011-05-20 12:16:29 +00007746 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7747 break;
7748 }
7749
7750 if (image->storage_class == PseudoClass && image->colormap != NULL)
7751 {
cristy3e08f112011-05-24 13:19:30 +00007752 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007753 {
glennrp91d99252011-06-25 14:30:13 +00007754 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007755 }
7756 }
7757 }
7758
7759 else if (image->depth > 1)
7760 {
7761 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007762 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007763
7764 for (y=0; y < (ssize_t) image->rows; y++)
7765 {
7766 r=GetAuthenticPixels(image,0,y,image->columns,1,
7767 exception);
7768
cristy4c08aed2011-07-01 19:47:50 +00007769 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007770 break;
7771
7772 for (x=0; x < (ssize_t) image->columns; x++)
7773 {
cristy4c08aed2011-07-01 19:47:50 +00007774 LBR02RGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007775 r++;
7776 }
glennrpbb4f99d2011-05-22 11:13:17 +00007777
glennrp8e58efd2011-05-20 12:16:29 +00007778 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7779 break;
7780 }
7781
7782 if (image->storage_class == PseudoClass && image->colormap != NULL)
7783 {
cristy3e08f112011-05-24 13:19:30 +00007784 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007785 {
glennrp91d99252011-06-25 14:30:13 +00007786 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007787 }
7788 }
7789 }
7790 else
7791 {
7792 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007793 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007794
7795 for (y=0; y < (ssize_t) image->rows; y++)
7796 {
7797 r=GetAuthenticPixels(image,0,y,image->columns,1,
7798 exception);
7799
cristy4c08aed2011-07-01 19:47:50 +00007800 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007801 break;
7802
7803 for (x=0; x < (ssize_t) image->columns; x++)
7804 {
cristy4c08aed2011-07-01 19:47:50 +00007805 LBR01RGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007806 r++;
7807 }
glennrpbb4f99d2011-05-22 11:13:17 +00007808
glennrp8e58efd2011-05-20 12:16:29 +00007809 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7810 break;
7811 }
7812
7813 if (image->storage_class == PseudoClass && image->colormap != NULL)
7814 {
cristy3e08f112011-05-24 13:19:30 +00007815 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007816 {
glennrp91d99252011-06-25 14:30:13 +00007817 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007818 }
7819 }
7820 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007821 }
7822
glennrp67b9c1a2011-04-22 18:47:36 +00007823 /* To do: set to next higher multiple of 8 */
7824 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007825 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007826
glennrp2b013e42010-11-24 16:55:50 +00007827#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7828 /* PNG does not handle depths greater than 16 so reduce it even
7829 * if lossy
7830 */
glennrp8e58efd2011-05-20 12:16:29 +00007831 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007832 image->depth=16;
7833#endif
7834
glennrp3faa9a32011-04-23 14:00:25 +00007835#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007836 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007837 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007838 image->depth = 8;
7839#endif
7840
glennrpc8c2f062011-02-25 19:00:33 +00007841 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007842 * we reduce the transparency to binary and run again, then if there
7843 * 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 +00007844 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7845 * palette. Then (To do) we take care of a final reduction that is only
7846 * needed if there are still 256 colors present and one of them has both
7847 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007848 */
glennrp82b3c532011-03-22 19:20:54 +00007849
glennrp8ca51ad2011-05-12 21:22:32 +00007850 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007851 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007852 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007853
glennrp8ca51ad2011-05-12 21:22:32 +00007854 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007855 {
7856 /* BUILD_PALETTE
7857 *
7858 * Sometimes we get DirectClass images that have 256 colors or fewer.
7859 * This code will build a colormap.
7860 *
7861 * Also, sometimes we get PseudoClass images with an out-of-date
7862 * colormap. This code will replace the colormap with a new one.
7863 * Sometimes we get PseudoClass images that have more than 256 colors.
7864 * This code will delete the colormap and change the image to
7865 * DirectClass.
7866 *
cristy4c08aed2011-07-01 19:47:50 +00007867 * If image->matte is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00007868 * even though it sometimes contains left-over non-opaque values.
7869 *
7870 * Also we gather some information (number of opaque, transparent,
7871 * and semitransparent pixels, and whether the image has any non-gray
7872 * pixels or only black-and-white pixels) that we might need later.
7873 *
7874 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7875 * we need to check for bogus non-opaque values, at least.
7876 */
glennrp3c218112010-11-27 15:31:26 +00007877
glennrpd71e86a2011-02-24 01:28:37 +00007878 ExceptionInfo
7879 *exception;
glennrp3c218112010-11-27 15:31:26 +00007880
glennrpd71e86a2011-02-24 01:28:37 +00007881 int
7882 n;
glennrp3c218112010-11-27 15:31:26 +00007883
glennrpd71e86a2011-02-24 01:28:37 +00007884 PixelPacket
7885 opaque[260],
7886 semitransparent[260],
7887 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007888
cristy4c08aed2011-07-01 19:47:50 +00007889 register const Quantum
7890 *s;
glennrp8bb3a022010-12-13 20:40:04 +00007891
cristy4c08aed2011-07-01 19:47:50 +00007892 register Quantum
7893 *q,
glennrpfd05d622011-02-25 04:10:33 +00007894 *r;
7895
glennrpd71e86a2011-02-24 01:28:37 +00007896 if (logging != MagickFalse)
7897 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7898 " Enter BUILD_PALETTE:");
7899
7900 if (logging != MagickFalse)
7901 {
glennrp03812ae2010-12-24 01:31:34 +00007902 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007903 " image->columns=%.20g",(double) image->columns);
7904 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7905 " image->rows=%.20g",(double) image->rows);
7906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7907 " image->matte=%.20g",(double) image->matte);
7908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7909 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00007910
glennrpfd05d622011-02-25 04:10:33 +00007911 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00007912 {
7913 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007914 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00007915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00007916 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00007917
glennrpd71e86a2011-02-24 01:28:37 +00007918 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00007919 {
glennrpd71e86a2011-02-24 01:28:37 +00007920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7921 " %d (%d,%d,%d,%d)",
7922 (int) i,
7923 (int) image->colormap[i].red,
7924 (int) image->colormap[i].green,
7925 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00007926 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00007927 }
glennrp2cc891a2010-12-24 13:44:32 +00007928
glennrpd71e86a2011-02-24 01:28:37 +00007929 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7930 {
7931 if (i > 255)
7932 {
7933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7934 " %d (%d,%d,%d,%d)",
7935 (int) i,
7936 (int) image->colormap[i].red,
7937 (int) image->colormap[i].green,
7938 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00007939 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00007940 }
7941 }
glennrp03812ae2010-12-24 01:31:34 +00007942 }
glennrp7ddcc222010-12-11 05:01:05 +00007943
glennrpd71e86a2011-02-24 01:28:37 +00007944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7945 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00007946
glennrpd71e86a2011-02-24 01:28:37 +00007947 if (image->colors == 0)
7948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7949 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00007950
glennrp8d3d6e52011-04-19 04:39:51 +00007951 if (ping_preserve_colormap == MagickFalse)
7952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7953 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00007954 }
7955
7956 exception=(&image->exception);
7957
7958 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00007959 number_opaque = 0;
7960 number_semitransparent = 0;
7961 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00007962
7963 for (y=0; y < (ssize_t) image->rows; y++)
7964 {
7965 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7966
cristy4c08aed2011-07-01 19:47:50 +00007967 if (q == (const Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00007968 break;
7969
7970 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00007971 {
glennrp4737d522011-04-29 03:33:42 +00007972 if (image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00007973 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00007974 {
7975 if (number_opaque < 259)
7976 {
7977 if (number_opaque == 0)
7978 {
cristy4c08aed2011-07-01 19:47:50 +00007979 GetPixelPacket(image, q, opaque);
7980 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00007981 number_opaque=1;
7982 }
glennrp2cc891a2010-12-24 13:44:32 +00007983
glennrpd71e86a2011-02-24 01:28:37 +00007984 for (i=0; i< (ssize_t) number_opaque; i++)
7985 {
cristy4c08aed2011-07-01 19:47:50 +00007986 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00007987 break;
7988 }
glennrp7ddcc222010-12-11 05:01:05 +00007989
glennrpd71e86a2011-02-24 01:28:37 +00007990 if (i == (ssize_t) number_opaque &&
7991 number_opaque < 259)
7992 {
7993 number_opaque++;
cristy4c08aed2011-07-01 19:47:50 +00007994 GetPixelPacket(image, q, opaque+i);
7995 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00007996 }
7997 }
7998 }
cristy4c08aed2011-07-01 19:47:50 +00007999 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008000 {
8001 if (number_transparent < 259)
8002 {
8003 if (number_transparent == 0)
8004 {
cristy4c08aed2011-07-01 19:47:50 +00008005 GetPixelPacket(image, q, transparent);
8006 ping_trans_color.red=(unsigned short)
8007 GetPixelRed(image,q);
8008 ping_trans_color.green=(unsigned short)
8009 GetPixelGreen(image,q);
8010 ping_trans_color.blue=(unsigned short)
8011 GetPixelBlue(image,q);
8012 ping_trans_color.gray=(unsigned short)
8013 GetPixelRed(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008014 number_transparent = 1;
8015 }
8016
8017 for (i=0; i< (ssize_t) number_transparent; i++)
8018 {
cristy4c08aed2011-07-01 19:47:50 +00008019 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008020 break;
8021 }
8022
8023 if (i == (ssize_t) number_transparent &&
8024 number_transparent < 259)
8025 {
8026 number_transparent++;
cristy4c08aed2011-07-01 19:47:50 +00008027 GetPixelPacket(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008028 }
8029 }
8030 }
8031 else
8032 {
8033 if (number_semitransparent < 259)
8034 {
8035 if (number_semitransparent == 0)
8036 {
cristy4c08aed2011-07-01 19:47:50 +00008037 GetPixelPacket(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008038 number_semitransparent = 1;
8039 }
8040
8041 for (i=0; i< (ssize_t) number_semitransparent; i++)
8042 {
cristy4c08aed2011-07-01 19:47:50 +00008043 if (IsPixelEquivalent(image,q, semitransparent+i)
8044 && GetPixelAlpha(image,q) ==
8045 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008046 break;
8047 }
8048
8049 if (i == (ssize_t) number_semitransparent &&
8050 number_semitransparent < 259)
8051 {
8052 number_semitransparent++;
cristy4c08aed2011-07-01 19:47:50 +00008053 GetPixelPacket(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008054 }
8055 }
8056 }
cristy4c08aed2011-07-01 19:47:50 +00008057 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008058 }
8059 }
8060
8061 if (ping_exclude_bKGD == MagickFalse)
8062 {
8063 /* Add the background color to the palette, if it
8064 * isn't already there.
8065 */
glennrpc6c391a2011-04-27 02:23:56 +00008066 if (logging != MagickFalse)
8067 {
8068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8069 " Check colormap for background (%d,%d,%d)",
8070 (int) image->background_color.red,
8071 (int) image->background_color.green,
8072 (int) image->background_color.blue);
8073 }
glennrpd71e86a2011-02-24 01:28:37 +00008074 for (i=0; i<number_opaque; i++)
8075 {
glennrpca7ad3a2011-04-26 04:44:54 +00008076 if (opaque[i].red == image->background_color.red &&
8077 opaque[i].green == image->background_color.green &&
8078 opaque[i].blue == image->background_color.blue)
8079 break;
glennrpd71e86a2011-02-24 01:28:37 +00008080 }
glennrpd71e86a2011-02-24 01:28:37 +00008081 if (number_opaque < 259 && i == number_opaque)
8082 {
glennrp8e045c82011-04-27 16:40:27 +00008083 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008084 ping_background.index = i;
8085 if (logging != MagickFalse)
8086 {
8087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8088 " background_color index is %d",(int) i);
8089 }
8090
glennrpd71e86a2011-02-24 01:28:37 +00008091 }
glennrpa080bc32011-03-11 18:03:44 +00008092 else if (logging != MagickFalse)
8093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8094 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008095 }
8096
8097 image_colors=number_opaque+number_transparent+number_semitransparent;
8098
glennrpa080bc32011-03-11 18:03:44 +00008099 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8100 {
8101 /* No room for the background color; remove it. */
8102 number_opaque--;
8103 image_colors--;
8104 }
8105
glennrpd71e86a2011-02-24 01:28:37 +00008106 if (logging != MagickFalse)
8107 {
8108 if (image_colors > 256)
8109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8110 " image has more than 256 colors");
8111
8112 else
8113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8114 " image has %d colors",image_colors);
8115 }
8116
glennrp8d3d6e52011-04-19 04:39:51 +00008117 if (ping_preserve_colormap != MagickFalse)
8118 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008119
glennrpfd05d622011-02-25 04:10:33 +00008120 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008121 {
8122 ping_have_color=MagickFalse;
8123 ping_have_non_bw=MagickFalse;
8124
8125 if(image_colors > 256)
8126 {
8127 for (y=0; y < (ssize_t) image->rows; y++)
8128 {
8129 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8130
cristy4c08aed2011-07-01 19:47:50 +00008131 if (q == (const Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008132 break;
8133
8134 /* Worst case is black-and-white; we are looking at every
8135 * pixel twice.
8136 */
8137
8138 if (ping_have_color == MagickFalse)
8139 {
8140 s=q;
8141 for (x=0; x < (ssize_t) image->columns; x++)
8142 {
cristy4c08aed2011-07-01 19:47:50 +00008143 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8144 GetPixelRed(image,s) != GetPixelBlue(image,s))
glennrpd71e86a2011-02-24 01:28:37 +00008145 {
8146 ping_have_color=MagickTrue;
8147 ping_have_non_bw=MagickTrue;
8148 break;
8149 }
cristy4c08aed2011-07-01 19:47:50 +00008150 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008151 }
8152 }
8153
8154 if (ping_have_non_bw == MagickFalse)
8155 {
8156 s=q;
8157 for (x=0; x < (ssize_t) image->columns; x++)
8158 {
cristy4c08aed2011-07-01 19:47:50 +00008159 if (GetPixelRed(image,s) != 0 &&
8160 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008161 {
8162 ping_have_non_bw=MagickTrue;
8163 }
cristy4c08aed2011-07-01 19:47:50 +00008164 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008165 }
8166 }
8167 }
glennrpbb4f99d2011-05-22 11:13:17 +00008168 }
8169 }
glennrpd71e86a2011-02-24 01:28:37 +00008170
8171 if (image_colors < 257)
8172 {
8173 PixelPacket
8174 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008175
glennrpd71e86a2011-02-24 01:28:37 +00008176 /*
8177 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008178 */
8179
glennrpd71e86a2011-02-24 01:28:37 +00008180 if (logging != MagickFalse)
8181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8182 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008183
glennrpd71e86a2011-02-24 01:28:37 +00008184 /* Sort palette, transparent first */;
8185
8186 n = 0;
8187
8188 for (i=0; i<number_transparent; i++)
8189 colormap[n++] = transparent[i];
8190
8191 for (i=0; i<number_semitransparent; i++)
8192 colormap[n++] = semitransparent[i];
8193
8194 for (i=0; i<number_opaque; i++)
8195 colormap[n++] = opaque[i];
8196
glennrpc6c391a2011-04-27 02:23:56 +00008197 ping_background.index +=
8198 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008199
glennrpd71e86a2011-02-24 01:28:37 +00008200 /* image_colors < 257; search the colormap instead of the pixels
8201 * to get ping_have_color and ping_have_non_bw
8202 */
8203 for (i=0; i<n; i++)
8204 {
8205 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008206 {
glennrpd71e86a2011-02-24 01:28:37 +00008207 if (colormap[i].red != colormap[i].green ||
8208 colormap[i].red != colormap[i].blue)
8209 {
8210 ping_have_color=MagickTrue;
8211 ping_have_non_bw=MagickTrue;
8212 break;
8213 }
8214 }
8215
8216 if (ping_have_non_bw == MagickFalse)
8217 {
8218 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008219 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008220 }
glennrp8bb3a022010-12-13 20:40:04 +00008221 }
8222
glennrpd71e86a2011-02-24 01:28:37 +00008223 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8224 (number_transparent == 0 && number_semitransparent == 0)) &&
8225 (((mng_info->write_png_colortype-1) ==
8226 PNG_COLOR_TYPE_PALETTE) ||
8227 (mng_info->write_png_colortype == 0)))
8228 {
glennrp6185c532011-01-14 17:58:40 +00008229 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008230 {
glennrpd71e86a2011-02-24 01:28:37 +00008231 if (n != (ssize_t) image_colors)
8232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8233 " image_colors (%d) and n (%d) don't match",
8234 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008235
glennrpd71e86a2011-02-24 01:28:37 +00008236 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8237 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008238 }
glennrp03812ae2010-12-24 01:31:34 +00008239
glennrpd71e86a2011-02-24 01:28:37 +00008240 image->colors = image_colors;
8241
8242 if (AcquireImageColormap(image,image_colors) ==
8243 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008244 ThrowWriterException(ResourceLimitError,
8245 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008246
8247 for (i=0; i< (ssize_t) image_colors; i++)
8248 image->colormap[i] = colormap[i];
8249
8250 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008251 {
glennrpd71e86a2011-02-24 01:28:37 +00008252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8253 " image->colors=%d (%d)",
8254 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008255
glennrpd71e86a2011-02-24 01:28:37 +00008256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8257 " Update the pixel indexes");
8258 }
glennrp6185c532011-01-14 17:58:40 +00008259
glennrpfd05d622011-02-25 04:10:33 +00008260 /* Sync the pixel indices with the new colormap */
8261
glennrpd71e86a2011-02-24 01:28:37 +00008262 for (y=0; y < (ssize_t) image->rows; y++)
8263 {
8264 q=GetAuthenticPixels(image,0,y,image->columns,1,
8265 exception);
glennrp6185c532011-01-14 17:58:40 +00008266
cristy4c08aed2011-07-01 19:47:50 +00008267 if (q == (const Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008268 break;
glennrp6185c532011-01-14 17:58:40 +00008269
glennrpbb4f99d2011-05-22 11:13:17 +00008270
glennrpd71e86a2011-02-24 01:28:37 +00008271 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008272 {
glennrpd71e86a2011-02-24 01:28:37 +00008273 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008274 {
glennrpd71e86a2011-02-24 01:28:37 +00008275 if ((image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008276 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8277 image->colormap[i].red == GetPixelRed(image,q) &&
8278 image->colormap[i].green == GetPixelGreen(image,q) &&
8279 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008280 {
cristy4c08aed2011-07-01 19:47:50 +00008281 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008282 break;
glennrp6185c532011-01-14 17:58:40 +00008283 }
glennrp6185c532011-01-14 17:58:40 +00008284 }
cristy4c08aed2011-07-01 19:47:50 +00008285 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008286 }
glennrp6185c532011-01-14 17:58:40 +00008287
glennrpd71e86a2011-02-24 01:28:37 +00008288 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8289 break;
8290 }
8291 }
8292 }
8293
8294 if (logging != MagickFalse)
8295 {
8296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8297 " image->colors=%d", (int) image->colors);
8298
8299 if (image->colormap != NULL)
8300 {
8301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008302 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008303
8304 for (i=0; i < (ssize_t) image->colors; i++)
8305 {
cristy72988482011-03-29 16:34:38 +00008306 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008307 {
8308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8309 " %d (%d,%d,%d,%d)",
8310 (int) i,
8311 (int) image->colormap[i].red,
8312 (int) image->colormap[i].green,
8313 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008314 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008315 }
glennrp6185c532011-01-14 17:58:40 +00008316 }
8317 }
glennrp03812ae2010-12-24 01:31:34 +00008318
glennrpd71e86a2011-02-24 01:28:37 +00008319 if (number_transparent < 257)
8320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8321 " number_transparent = %d",
8322 number_transparent);
8323 else
glennrp03812ae2010-12-24 01:31:34 +00008324
glennrpd71e86a2011-02-24 01:28:37 +00008325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8326 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008327
glennrpd71e86a2011-02-24 01:28:37 +00008328 if (number_opaque < 257)
8329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8330 " number_opaque = %d",
8331 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008332
glennrpd71e86a2011-02-24 01:28:37 +00008333 else
8334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8335 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008336
glennrpd71e86a2011-02-24 01:28:37 +00008337 if (number_semitransparent < 257)
8338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8339 " number_semitransparent = %d",
8340 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008341
glennrpd71e86a2011-02-24 01:28:37 +00008342 else
8343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8344 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008345
glennrpd71e86a2011-02-24 01:28:37 +00008346 if (ping_have_non_bw == MagickFalse)
8347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8348 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008349
glennrpd71e86a2011-02-24 01:28:37 +00008350 else if (ping_have_color == MagickFalse)
8351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8352 " All pixels and the background are gray");
8353
8354 else
8355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8356 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008357
glennrp03812ae2010-12-24 01:31:34 +00008358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8359 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008360 }
glennrpfd05d622011-02-25 04:10:33 +00008361
glennrpc8c2f062011-02-25 19:00:33 +00008362 if (mng_info->write_png8 == MagickFalse)
8363 break;
glennrpfd05d622011-02-25 04:10:33 +00008364
glennrpc8c2f062011-02-25 19:00:33 +00008365 /* Make any reductions necessary for the PNG8 format */
8366 if (image_colors <= 256 &&
8367 image_colors != 0 && image->colormap != NULL &&
8368 number_semitransparent == 0 &&
8369 number_transparent <= 1)
8370 break;
8371
8372 /* PNG8 can't have semitransparent colors so we threshold the
cristy4c08aed2011-07-01 19:47:50 +00008373 * alpha to 0 or OpaqueAlpha
glennrpc8c2f062011-02-25 19:00:33 +00008374 */
8375 if (number_semitransparent != 0)
8376 {
8377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8378 " Thresholding the alpha channel to binary");
8379
8380 for (y=0; y < (ssize_t) image->rows; y++)
8381 {
8382 r=GetAuthenticPixels(image,0,y,image->columns,1,
8383 exception);
8384
cristy4c08aed2011-07-01 19:47:50 +00008385 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008386 break;
8387
8388 for (x=0; x < (ssize_t) image->columns; x++)
8389 {
cristy4c08aed2011-07-01 19:47:50 +00008390 if (GetPixelAlpha(image,r) > TransparentAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008391 {
cristy4c08aed2011-07-01 19:47:50 +00008392 SetPixelPacket(image,&image->background_color,r);
8393 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008394 }
8395 else
cristy4c08aed2011-07-01 19:47:50 +00008396 SetPixelAlpha(image,OpaqueAlpha,r);
8397 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008398 }
glennrpbb4f99d2011-05-22 11:13:17 +00008399
glennrpc8c2f062011-02-25 19:00:33 +00008400 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8401 break;
8402
8403 if (image_colors != 0 && image_colors <= 256 &&
8404 image->colormap != NULL)
8405 for (i=0; i<image_colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00008406 image->colormap[i].alpha =
8407 (image->colormap[i].alpha > TransparentAlpha/2 ?
8408 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008409 }
8410 continue;
8411 }
8412
8413 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008414 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8415 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8416 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008417 */
glennrpd3371642011-03-22 19:42:23 +00008418 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8419 {
8420 if (logging != MagickFalse)
8421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8422 " Quantizing the background color to 4-4-4");
8423
8424 tried_444 = MagickTrue;
8425
glennrp91d99252011-06-25 14:30:13 +00008426 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008427
8428 if (logging != MagickFalse)
8429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8430 " Quantizing the pixel colors to 4-4-4");
8431
8432 if (image->colormap == NULL)
8433 {
8434 for (y=0; y < (ssize_t) image->rows; y++)
8435 {
8436 r=GetAuthenticPixels(image,0,y,image->columns,1,
8437 exception);
8438
cristy4c08aed2011-07-01 19:47:50 +00008439 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008440 break;
8441
8442 for (x=0; x < (ssize_t) image->columns; x++)
8443 {
cristy4c08aed2011-07-01 19:47:50 +00008444 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8445 LBR04RGB(r);
glennrpd3371642011-03-22 19:42:23 +00008446 r++;
8447 }
glennrpbb4f99d2011-05-22 11:13:17 +00008448
glennrpd3371642011-03-22 19:42:23 +00008449 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8450 break;
8451 }
8452 }
8453
8454 else /* Should not reach this; colormap already exists and
8455 must be <= 256 */
8456 {
8457 if (logging != MagickFalse)
8458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8459 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008460
glennrpd3371642011-03-22 19:42:23 +00008461 for (i=0; i<image_colors; i++)
8462 {
glennrp91d99252011-06-25 14:30:13 +00008463 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008464 }
8465 }
8466 continue;
8467 }
8468
glennrp82b3c532011-03-22 19:20:54 +00008469 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8470 {
8471 if (logging != MagickFalse)
8472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8473 " Quantizing the background color to 3-3-3");
8474
8475 tried_333 = MagickTrue;
8476
glennrp91d99252011-06-25 14:30:13 +00008477 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008478
8479 if (logging != MagickFalse)
8480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008481 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008482
8483 if (image->colormap == NULL)
8484 {
8485 for (y=0; y < (ssize_t) image->rows; y++)
8486 {
8487 r=GetAuthenticPixels(image,0,y,image->columns,1,
8488 exception);
8489
cristy4c08aed2011-07-01 19:47:50 +00008490 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008491 break;
8492
8493 for (x=0; x < (ssize_t) image->columns; x++)
8494 {
cristy4c08aed2011-07-01 19:47:50 +00008495 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8496 LBR03RGB(r);
glennrp82b3c532011-03-22 19:20:54 +00008497 r++;
8498 }
glennrpbb4f99d2011-05-22 11:13:17 +00008499
glennrp82b3c532011-03-22 19:20:54 +00008500 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8501 break;
8502 }
8503 }
8504
8505 else /* Should not reach this; colormap already exists and
8506 must be <= 256 */
8507 {
8508 if (logging != MagickFalse)
8509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008510 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008511 for (i=0; i<image_colors; i++)
8512 {
glennrp91d99252011-06-25 14:30:13 +00008513 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008514 }
glennrpd3371642011-03-22 19:42:23 +00008515 }
8516 continue;
glennrp82b3c532011-03-22 19:20:54 +00008517 }
glennrpc8c2f062011-02-25 19:00:33 +00008518
glennrp8ca51ad2011-05-12 21:22:32 +00008519 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008520 {
8521 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008522 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008523 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008524
glennrp8ca51ad2011-05-12 21:22:32 +00008525 tried_332 = MagickTrue;
8526
glennrp3faa9a32011-04-23 14:00:25 +00008527 /* Red and green were already done so we only quantize the blue
8528 * channel
8529 */
8530
glennrp91d99252011-06-25 14:30:13 +00008531 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008532
glennrpc8c2f062011-02-25 19:00:33 +00008533 if (logging != MagickFalse)
8534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008535 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008536
glennrpc8c2f062011-02-25 19:00:33 +00008537 if (image->colormap == NULL)
8538 {
8539 for (y=0; y < (ssize_t) image->rows; y++)
8540 {
8541 r=GetAuthenticPixels(image,0,y,image->columns,1,
8542 exception);
8543
cristy4c08aed2011-07-01 19:47:50 +00008544 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008545 break;
8546
8547 for (x=0; x < (ssize_t) image->columns; x++)
8548 {
cristy4c08aed2011-07-01 19:47:50 +00008549 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8550 LBR02Blue(r);
glennrp52a479c2011-02-26 21:14:38 +00008551 r++;
glennrpc8c2f062011-02-25 19:00:33 +00008552 }
glennrpbb4f99d2011-05-22 11:13:17 +00008553
glennrpc8c2f062011-02-25 19:00:33 +00008554 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8555 break;
8556 }
8557 }
glennrpfd05d622011-02-25 04:10:33 +00008558
glennrpc8c2f062011-02-25 19:00:33 +00008559 else /* Should not reach this; colormap already exists and
8560 must be <= 256 */
8561 {
8562 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008564 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008565 for (i=0; i<image_colors; i++)
8566 {
glennrp91d99252011-06-25 14:30:13 +00008567 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008568 }
8569 }
8570 continue;
8571 }
8572 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008573
8574 if (image_colors == 0 || image_colors > 256)
8575 {
8576 /* Take care of special case with 256 colors + 1 transparent
8577 * color. We don't need to quantize to 2-3-2-1; we only need to
8578 * eliminate one color, so we'll merge the two darkest red
8579 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8580 */
8581 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8582 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8583 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8584 {
8585 image->background_color.red=ScaleCharToQuantum(0x24);
8586 }
glennrpbb4f99d2011-05-22 11:13:17 +00008587
glennrp8ca51ad2011-05-12 21:22:32 +00008588 if (image->colormap == NULL)
8589 {
8590 for (y=0; y < (ssize_t) image->rows; y++)
8591 {
8592 r=GetAuthenticPixels(image,0,y,image->columns,1,
8593 exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008594
cristy4c08aed2011-07-01 19:47:50 +00008595 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008596 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008597
glennrp8ca51ad2011-05-12 21:22:32 +00008598 for (x=0; x < (ssize_t) image->columns; x++)
8599 {
cristy4c08aed2011-07-01 19:47:50 +00008600 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8601 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8602 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8603 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008604 {
cristy4c08aed2011-07-01 19:47:50 +00008605 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008606 }
cristy4c08aed2011-07-01 19:47:50 +00008607 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008608 }
glennrpbb4f99d2011-05-22 11:13:17 +00008609
glennrp8ca51ad2011-05-12 21:22:32 +00008610 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8611 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008612
glennrp8ca51ad2011-05-12 21:22:32 +00008613 }
8614 }
8615
8616 else
8617 {
8618 for (i=0; i<image_colors; i++)
8619 {
8620 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8621 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8622 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8623 {
8624 image->colormap[i].red=ScaleCharToQuantum(0x24);
8625 }
8626 }
8627 }
8628 }
glennrpd71e86a2011-02-24 01:28:37 +00008629 }
glennrpfd05d622011-02-25 04:10:33 +00008630 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008631
glennrpfd05d622011-02-25 04:10:33 +00008632 /* If we are excluding the tRNS chunk and there is transparency,
8633 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8634 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008635 */
glennrp0e8ea192010-12-24 18:00:33 +00008636 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8637 (number_transparent != 0 || number_semitransparent != 0))
8638 {
glennrpd17915c2011-04-29 14:24:22 +00008639 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008640
8641 if (ping_have_color == MagickFalse)
8642 mng_info->write_png_colortype = 5;
8643
8644 else
8645 mng_info->write_png_colortype = 7;
8646
glennrp8d579662011-02-23 02:05:02 +00008647 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008648 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008649 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008650
glennrp0e8ea192010-12-24 18:00:33 +00008651 }
8652
glennrpfd05d622011-02-25 04:10:33 +00008653 /* See if cheap transparency is possible. It is only possible
8654 * when there is a single transparent color, no semitransparent
8655 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008656 * as the transparent color. We only need this information if
8657 * we are writing a PNG with colortype 0 or 2, and we have not
8658 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008659 */
glennrp5a39f372011-02-25 04:52:16 +00008660 if (number_transparent == 1 &&
8661 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008662 {
8663 ping_have_cheap_transparency = MagickTrue;
8664
8665 if (number_semitransparent != 0)
8666 ping_have_cheap_transparency = MagickFalse;
8667
8668 else if (image_colors == 0 || image_colors > 256 ||
8669 image->colormap == NULL)
8670 {
8671 ExceptionInfo
8672 *exception;
8673
cristy4c08aed2011-07-01 19:47:50 +00008674 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008675 *q;
8676
8677 exception=(&image->exception);
8678
8679 for (y=0; y < (ssize_t) image->rows; y++)
8680 {
8681 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8682
cristy4c08aed2011-07-01 19:47:50 +00008683 if (q == (const Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008684 break;
8685
8686 for (x=0; x < (ssize_t) image->columns; x++)
8687 {
cristy4c08aed2011-07-01 19:47:50 +00008688 if (GetPixelAlpha(image,q) != TransparentAlpha &&
glennrp847370c2011-07-05 17:37:15 +00008689 (unsigned short) GetPixelRed(image,q) ==
8690 ping_trans_color.red &&
8691 (unsigned short) GetPixelGreen(image,q) ==
8692 ping_trans_color.green &&
8693 (unsigned short) GetPixelBlue(image,q) ==
8694 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008695 {
8696 ping_have_cheap_transparency = MagickFalse;
8697 break;
8698 }
8699
cristy4c08aed2011-07-01 19:47:50 +00008700 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008701 }
glennrpbb4f99d2011-05-22 11:13:17 +00008702
glennrpfd05d622011-02-25 04:10:33 +00008703 if (ping_have_cheap_transparency == MagickFalse)
8704 break;
8705 }
8706 }
8707 else
8708 {
glennrp67b9c1a2011-04-22 18:47:36 +00008709 /* Assuming that image->colormap[0] is the one transparent color
8710 * and that all others are opaque.
8711 */
glennrpfd05d622011-02-25 04:10:33 +00008712 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008713 for (i=1; i<image_colors; i++)
8714 if (image->colormap[i].red == image->colormap[0].red &&
8715 image->colormap[i].green == image->colormap[0].green &&
8716 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008717 {
glennrp67b9c1a2011-04-22 18:47:36 +00008718 ping_have_cheap_transparency = MagickFalse;
8719 break;
glennrpfd05d622011-02-25 04:10:33 +00008720 }
8721 }
glennrpbb4f99d2011-05-22 11:13:17 +00008722
glennrpfd05d622011-02-25 04:10:33 +00008723 if (logging != MagickFalse)
8724 {
8725 if (ping_have_cheap_transparency == MagickFalse)
8726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8727 " Cheap transparency is not possible.");
8728
8729 else
8730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8731 " Cheap transparency is possible.");
8732 }
8733 }
8734 else
8735 ping_have_cheap_transparency = MagickFalse;
8736
glennrp8640fb52010-11-23 15:48:26 +00008737 image_depth=image->depth;
8738
glennrp26c990a2010-11-23 02:23:20 +00008739 quantum_info = (QuantumInfo *) NULL;
8740 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008741 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008742 image_matte=image->matte;
8743
glennrp0fe50b42010-11-16 03:52:51 +00008744 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008745 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008746
glennrp52a479c2011-02-26 21:14:38 +00008747 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8748 (image->colors == 0 || image->colormap == NULL))
8749 {
glennrp52a479c2011-02-26 21:14:38 +00008750 image_info=DestroyImageInfo(image_info);
8751 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008752 (void) ThrowMagickException(&IMimage->exception,
8753 GetMagickModule(),CoderError,
8754 "Cannot write PNG8 or color-type 3; colormap is NULL",
8755 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008756#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8757 UnlockSemaphoreInfo(ping_semaphore);
8758#endif
8759 return(MagickFalse);
8760 }
8761
cristy3ed852e2009-09-05 21:47:34 +00008762 /*
8763 Allocate the PNG structures
8764 */
8765#ifdef PNG_USER_MEM_SUPPORTED
8766 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008767 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8768 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008769
cristy3ed852e2009-09-05 21:47:34 +00008770#else
8771 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008772 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008773
cristy3ed852e2009-09-05 21:47:34 +00008774#endif
8775 if (ping == (png_struct *) NULL)
8776 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008777
cristy3ed852e2009-09-05 21:47:34 +00008778 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008779
cristy3ed852e2009-09-05 21:47:34 +00008780 if (ping_info == (png_info *) NULL)
8781 {
8782 png_destroy_write_struct(&ping,(png_info **) NULL);
8783 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8784 }
glennrp0fe50b42010-11-16 03:52:51 +00008785
cristy3ed852e2009-09-05 21:47:34 +00008786 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008787 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008788
glennrp5af765f2010-03-30 11:12:18 +00008789 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008790 {
8791 /*
8792 PNG write failed.
8793 */
8794#ifdef PNG_DEBUG
8795 if (image_info->verbose)
8796 (void) printf("PNG write has failed.\n");
8797#endif
8798 png_destroy_write_struct(&ping,&ping_info);
8799#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008800 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008801#endif
glennrpda8f3a72011-02-27 23:54:12 +00008802 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008803 (void) CloseBlob(image);
8804 image_info=DestroyImageInfo(image_info);
8805 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008806 return(MagickFalse);
8807 }
8808 /*
8809 Prepare PNG for writing.
8810 */
8811#if defined(PNG_MNG_FEATURES_SUPPORTED)
8812 if (mng_info->write_mng)
8813 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008814
cristy3ed852e2009-09-05 21:47:34 +00008815#else
8816# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8817 if (mng_info->write_mng)
8818 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008819
cristy3ed852e2009-09-05 21:47:34 +00008820# endif
8821#endif
glennrp2b013e42010-11-24 16:55:50 +00008822
cristy3ed852e2009-09-05 21:47:34 +00008823 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008824
cristy4e5bc842010-06-09 13:56:01 +00008825 ping_width=(png_uint_32) image->columns;
8826 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008827
cristy3ed852e2009-09-05 21:47:34 +00008828 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8829 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008830
cristy3ed852e2009-09-05 21:47:34 +00008831 if (mng_info->write_png_depth != 0)
8832 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008833
cristy3ed852e2009-09-05 21:47:34 +00008834 /* Adjust requested depth to next higher valid depth if necessary */
8835 if (image_depth > 8)
8836 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008837
cristy3ed852e2009-09-05 21:47:34 +00008838 if ((image_depth > 4) && (image_depth < 8))
8839 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008840
cristy3ed852e2009-09-05 21:47:34 +00008841 if (image_depth == 3)
8842 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008843
cristy3ed852e2009-09-05 21:47:34 +00008844 if (logging != MagickFalse)
8845 {
8846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008847 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008849 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008851 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008853 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008855 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008856 }
glennrp8640fb52010-11-23 15:48:26 +00008857
cristy3ed852e2009-09-05 21:47:34 +00008858 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008859 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008860
glennrp26f37912010-12-23 16:22:42 +00008861
cristy3ed852e2009-09-05 21:47:34 +00008862#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008863 if (ping_exclude_pHYs == MagickFalse)
8864 {
cristy3ed852e2009-09-05 21:47:34 +00008865 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8866 (!mng_info->write_mng || !mng_info->equal_physs))
8867 {
glennrp0fe50b42010-11-16 03:52:51 +00008868 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8870 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008871
8872 if (image->units == PixelsPerInchResolution)
8873 {
glennrpdfd70802010-11-14 01:23:35 +00008874 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008875 ping_pHYs_x_resolution=
8876 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8877 ping_pHYs_y_resolution=
8878 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008879 }
glennrpdfd70802010-11-14 01:23:35 +00008880
cristy3ed852e2009-09-05 21:47:34 +00008881 else if (image->units == PixelsPerCentimeterResolution)
8882 {
glennrpdfd70802010-11-14 01:23:35 +00008883 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008884 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8885 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008886 }
glennrp991d11d2010-11-12 21:55:28 +00008887
cristy3ed852e2009-09-05 21:47:34 +00008888 else
8889 {
glennrpdfd70802010-11-14 01:23:35 +00008890 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8891 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8892 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00008893 }
glennrp991d11d2010-11-12 21:55:28 +00008894
glennrp823b55c2011-03-14 18:46:46 +00008895 if (logging != MagickFalse)
8896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8897 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8898 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8899 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00008900 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008901 }
glennrp26f37912010-12-23 16:22:42 +00008902 }
cristy3ed852e2009-09-05 21:47:34 +00008903#endif
glennrpa521b2f2010-10-29 04:11:03 +00008904
glennrp26f37912010-12-23 16:22:42 +00008905 if (ping_exclude_bKGD == MagickFalse)
8906 {
glennrpa521b2f2010-10-29 04:11:03 +00008907 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00008908 {
glennrpa521b2f2010-10-29 04:11:03 +00008909 unsigned int
8910 mask;
cristy3ed852e2009-09-05 21:47:34 +00008911
glennrpa521b2f2010-10-29 04:11:03 +00008912 mask=0xffff;
8913 if (ping_bit_depth == 8)
8914 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00008915
glennrpa521b2f2010-10-29 04:11:03 +00008916 if (ping_bit_depth == 4)
8917 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00008918
glennrpa521b2f2010-10-29 04:11:03 +00008919 if (ping_bit_depth == 2)
8920 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00008921
glennrpa521b2f2010-10-29 04:11:03 +00008922 if (ping_bit_depth == 1)
8923 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00008924
glennrpa521b2f2010-10-29 04:11:03 +00008925 ping_background.red=(png_uint_16)
8926 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008927
glennrpa521b2f2010-10-29 04:11:03 +00008928 ping_background.green=(png_uint_16)
8929 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008930
glennrpa521b2f2010-10-29 04:11:03 +00008931 ping_background.blue=(png_uint_16)
8932 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00008933
8934 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00008935 }
cristy3ed852e2009-09-05 21:47:34 +00008936
glennrp0fe50b42010-11-16 03:52:51 +00008937 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00008938 {
8939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8940 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00008941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8942 " background_color index is %d",
8943 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00008944
8945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8946 " ping_bit_depth=%d",ping_bit_depth);
8947 }
glennrp0fe50b42010-11-16 03:52:51 +00008948
8949 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008950 }
glennrp0fe50b42010-11-16 03:52:51 +00008951
cristy3ed852e2009-09-05 21:47:34 +00008952 /*
8953 Select the color type.
8954 */
8955 matte=image_matte;
8956 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00008957
glennrp1273f7b2011-02-24 03:20:30 +00008958 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00008959 {
glennrp0fe50b42010-11-16 03:52:51 +00008960
glennrpfd05d622011-02-25 04:10:33 +00008961 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00008962 for reducing the sample depth from 8. */
8963
glennrp0fe50b42010-11-16 03:52:51 +00008964 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00008965
glennrp8bb3a022010-12-13 20:40:04 +00008966 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008967
8968 /*
8969 Set image palette.
8970 */
8971 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8972
glennrp0fe50b42010-11-16 03:52:51 +00008973 if (logging != MagickFalse)
8974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8975 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00008976 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008977
8978 for (i=0; i < (ssize_t) number_colors; i++)
8979 {
8980 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8981 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8982 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8983 if (logging != MagickFalse)
8984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00008985#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00008986 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00008987#else
8988 " %5ld (%5d,%5d,%5d)",
8989#endif
glennrp0fe50b42010-11-16 03:52:51 +00008990 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8991
8992 }
glennrp2b013e42010-11-24 16:55:50 +00008993
glennrp8bb3a022010-12-13 20:40:04 +00008994 ping_have_PLTE=MagickTrue;
8995 image_depth=ping_bit_depth;
8996 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00008997
glennrp58e01762011-01-07 15:28:54 +00008998 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008999 {
glennrp0fe50b42010-11-16 03:52:51 +00009000 /*
9001 Identify which colormap entry is transparent.
9002 */
9003 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009004 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009005
glennrp8bb3a022010-12-13 20:40:04 +00009006 for (i=0; i < (ssize_t) number_transparent; i++)
9007 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009008
glennrp0fe50b42010-11-16 03:52:51 +00009009
glennrp2cc891a2010-12-24 13:44:32 +00009010 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009011 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009012
9013 if (ping_num_trans == 0)
9014 ping_have_tRNS=MagickFalse;
9015
glennrp8bb3a022010-12-13 20:40:04 +00009016 else
9017 ping_have_tRNS=MagickTrue;
9018 }
glennrp0fe50b42010-11-16 03:52:51 +00009019
glennrp1273f7b2011-02-24 03:20:30 +00009020 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009021 {
glennrp1273f7b2011-02-24 03:20:30 +00009022 /*
9023 * Identify which colormap entry is the background color.
9024 */
9025
glennrp4f25bd02011-01-01 18:51:28 +00009026 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9027 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9028 break;
glennrp0fe50b42010-11-16 03:52:51 +00009029
glennrp4f25bd02011-01-01 18:51:28 +00009030 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009031
9032 if (logging != MagickFalse)
9033 {
9034 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9035 " background_color index is %d",
9036 (int) ping_background.index);
9037 }
glennrp4f25bd02011-01-01 18:51:28 +00009038 }
cristy3ed852e2009-09-05 21:47:34 +00009039 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009040
cristy3ed852e2009-09-05 21:47:34 +00009041 else if (mng_info->write_png24)
9042 {
9043 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009044 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009045 }
glennrp0fe50b42010-11-16 03:52:51 +00009046
cristy3ed852e2009-09-05 21:47:34 +00009047 else if (mng_info->write_png32)
9048 {
9049 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009050 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009051 }
glennrp0fe50b42010-11-16 03:52:51 +00009052
glennrp8bb3a022010-12-13 20:40:04 +00009053 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009054 {
glennrp5af765f2010-03-30 11:12:18 +00009055 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009056
glennrp8bb3a022010-12-13 20:40:04 +00009057 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009058 {
glennrp5af765f2010-03-30 11:12:18 +00009059 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009060
glennrp5af765f2010-03-30 11:12:18 +00009061 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9062 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009063 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009064
glennrp8bb3a022010-12-13 20:40:04 +00009065 else
9066 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009067
9068 if (logging != MagickFalse)
9069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9070 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009071 }
glennrp0fe50b42010-11-16 03:52:51 +00009072
glennrp7c4c9e62011-03-21 20:23:32 +00009073 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009074 {
9075 if (logging != MagickFalse)
9076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009077 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009078
glennrpd6bf1612010-12-17 17:28:54 +00009079 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009080 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009081
glennrpd6bf1612010-12-17 17:28:54 +00009082 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009083 {
glennrp5af765f2010-03-30 11:12:18 +00009084 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009085 image_matte=MagickFalse;
9086 }
glennrp0fe50b42010-11-16 03:52:51 +00009087
glennrpd6bf1612010-12-17 17:28:54 +00009088 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009089 {
glennrp5af765f2010-03-30 11:12:18 +00009090 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009091 image_matte=MagickTrue;
9092 }
glennrp0fe50b42010-11-16 03:52:51 +00009093
glennrp5aa37f62011-01-02 03:07:57 +00009094 if (image_info->type == PaletteType ||
9095 image_info->type == PaletteMatteType)
9096 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9097
glennrp7c4c9e62011-03-21 20:23:32 +00009098 if (mng_info->write_png_colortype == 0 &&
9099 (image_info->type == UndefinedType ||
9100 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009101 {
glennrp5aa37f62011-01-02 03:07:57 +00009102 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009103 {
glennrp5aa37f62011-01-02 03:07:57 +00009104 if (image_matte == MagickFalse)
9105 {
9106 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9107 image_matte=MagickFalse;
9108 }
glennrp0fe50b42010-11-16 03:52:51 +00009109
glennrp0b206f52011-01-07 04:55:32 +00009110 else
glennrp5aa37f62011-01-02 03:07:57 +00009111 {
9112 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9113 image_matte=MagickTrue;
9114 }
9115 }
9116 else
glennrp8bb3a022010-12-13 20:40:04 +00009117 {
glennrp5aa37f62011-01-02 03:07:57 +00009118 if (image_matte == MagickFalse)
9119 {
9120 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9121 image_matte=MagickFalse;
9122 }
glennrp8bb3a022010-12-13 20:40:04 +00009123
glennrp0b206f52011-01-07 04:55:32 +00009124 else
glennrp5aa37f62011-01-02 03:07:57 +00009125 {
9126 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9127 image_matte=MagickTrue;
9128 }
9129 }
glennrp0fe50b42010-11-16 03:52:51 +00009130 }
glennrp5aa37f62011-01-02 03:07:57 +00009131
cristy3ed852e2009-09-05 21:47:34 +00009132 }
glennrp0fe50b42010-11-16 03:52:51 +00009133
cristy3ed852e2009-09-05 21:47:34 +00009134 if (logging != MagickFalse)
9135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009136 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009137
glennrp5af765f2010-03-30 11:12:18 +00009138 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009139 {
9140 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9141 ping_color_type == PNG_COLOR_TYPE_RGB ||
9142 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9143 ping_bit_depth=8;
9144 }
cristy3ed852e2009-09-05 21:47:34 +00009145
glennrpd6bf1612010-12-17 17:28:54 +00009146 old_bit_depth=ping_bit_depth;
9147
glennrp5af765f2010-03-30 11:12:18 +00009148 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009149 {
glennrp8d579662011-02-23 02:05:02 +00009150 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9151 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009152 }
glennrp8640fb52010-11-23 15:48:26 +00009153
glennrp5af765f2010-03-30 11:12:18 +00009154 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009155 {
cristy35ef8242010-06-03 16:24:13 +00009156 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009157 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009158
9159 if (image->colors == 0)
9160 {
glennrp0fe50b42010-11-16 03:52:51 +00009161 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00009162 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00009163 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009164 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009165 }
9166
cristy35ef8242010-06-03 16:24:13 +00009167 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009168 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009169 }
glennrp2b013e42010-11-24 16:55:50 +00009170
glennrpd6bf1612010-12-17 17:28:54 +00009171 if (logging != MagickFalse)
9172 {
9173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9174 " Number of colors: %.20g",(double) image_colors);
9175
9176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9177 " Tentative PNG bit depth: %d",ping_bit_depth);
9178 }
9179
9180 if (ping_bit_depth < (int) mng_info->write_png_depth)
9181 ping_bit_depth = mng_info->write_png_depth;
9182 }
glennrp2cc891a2010-12-24 13:44:32 +00009183
glennrp5af765f2010-03-30 11:12:18 +00009184 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009185
cristy3ed852e2009-09-05 21:47:34 +00009186 if (logging != MagickFalse)
9187 {
9188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009189 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009190
cristy3ed852e2009-09-05 21:47:34 +00009191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009192 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009193
cristy3ed852e2009-09-05 21:47:34 +00009194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009195 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009196
cristy3ed852e2009-09-05 21:47:34 +00009197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009198
glennrp8640fb52010-11-23 15:48:26 +00009199 " image->depth: %.20g",(double) image->depth);
9200
9201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009202 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009203 }
9204
glennrp58e01762011-01-07 15:28:54 +00009205 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009206 {
glennrp4f25bd02011-01-01 18:51:28 +00009207 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009208 {
glennrp7c4c9e62011-03-21 20:23:32 +00009209 if (mng_info->write_png_colortype == 0)
9210 {
9211 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009212
glennrp7c4c9e62011-03-21 20:23:32 +00009213 if (ping_have_color != MagickFalse)
9214 ping_color_type=PNG_COLOR_TYPE_RGBA;
9215 }
glennrp4f25bd02011-01-01 18:51:28 +00009216
9217 /*
9218 * Determine if there is any transparent color.
9219 */
9220 if (number_transparent + number_semitransparent == 0)
9221 {
9222 /*
9223 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9224 */
glennrpa6a06632011-01-19 15:15:34 +00009225
glennrp4f25bd02011-01-01 18:51:28 +00009226 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009227
9228 if (mng_info->write_png_colortype == 0)
9229 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009230 }
9231
9232 else
9233 {
9234 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009235 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009236
9237 mask=0xffff;
9238
9239 if (ping_bit_depth == 8)
9240 mask=0x00ff;
9241
9242 if (ping_bit_depth == 4)
9243 mask=0x000f;
9244
9245 if (ping_bit_depth == 2)
9246 mask=0x0003;
9247
9248 if (ping_bit_depth == 1)
9249 mask=0x0001;
9250
9251 ping_trans_color.red=(png_uint_16)
9252 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9253
9254 ping_trans_color.green=(png_uint_16)
9255 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9256
9257 ping_trans_color.blue=(png_uint_16)
9258 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9259
9260 ping_trans_color.gray=(png_uint_16)
cristy4c08aed2011-07-01 19:47:50 +00009261 (ScaleQuantumToShort(GetPixelPacketIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009262 image->colormap)) & mask);
9263
9264 ping_trans_color.index=(png_byte) 0;
9265
9266 ping_have_tRNS=MagickTrue;
9267 }
9268
9269 if (ping_have_tRNS != MagickFalse)
9270 {
9271 /*
glennrpfd05d622011-02-25 04:10:33 +00009272 * Determine if there is one and only one transparent color
9273 * and if so if it is fully transparent.
9274 */
9275 if (ping_have_cheap_transparency == MagickFalse)
9276 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009277 }
9278
9279 if (ping_have_tRNS != MagickFalse)
9280 {
glennrp7c4c9e62011-03-21 20:23:32 +00009281 if (mng_info->write_png_colortype == 0)
9282 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009283
9284 if (image_depth == 8)
9285 {
9286 ping_trans_color.red&=0xff;
9287 ping_trans_color.green&=0xff;
9288 ping_trans_color.blue&=0xff;
9289 ping_trans_color.gray&=0xff;
9290 }
9291 }
9292 }
cristy3ed852e2009-09-05 21:47:34 +00009293 else
9294 {
cristy3ed852e2009-09-05 21:47:34 +00009295 if (image_depth == 8)
9296 {
glennrp5af765f2010-03-30 11:12:18 +00009297 ping_trans_color.red&=0xff;
9298 ping_trans_color.green&=0xff;
9299 ping_trans_color.blue&=0xff;
9300 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009301 }
9302 }
9303 }
glennrp8640fb52010-11-23 15:48:26 +00009304
cristy3ed852e2009-09-05 21:47:34 +00009305 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009306
glennrp2e09f552010-11-14 00:38:48 +00009307 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009308 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009309
glennrp39992b42010-11-14 00:03:43 +00009310 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009311 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009312 ping_have_color == MagickFalse &&
9313 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009314 {
cristy35ef8242010-06-03 16:24:13 +00009315 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009316
cristy3ed852e2009-09-05 21:47:34 +00009317 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009318 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009319
glennrp7c4c9e62011-03-21 20:23:32 +00009320 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009321 {
glennrp5af765f2010-03-30 11:12:18 +00009322 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009323
cristy3ed852e2009-09-05 21:47:34 +00009324 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009325 {
9326 if (logging != MagickFalse)
9327 {
9328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9329 " Scaling ping_trans_color (0)");
9330 }
9331 ping_trans_color.gray*=0x0101;
9332 }
cristy3ed852e2009-09-05 21:47:34 +00009333 }
glennrp0fe50b42010-11-16 03:52:51 +00009334
cristy3ed852e2009-09-05 21:47:34 +00009335 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9336 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009337
glennrp136ee3a2011-04-27 15:47:45 +00009338 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009339 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009340 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009341
cristy3ed852e2009-09-05 21:47:34 +00009342 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009343 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009344
cristy3ed852e2009-09-05 21:47:34 +00009345 else
9346 {
glennrp5af765f2010-03-30 11:12:18 +00009347 ping_bit_depth=8;
9348 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009349 {
9350 if(!mng_info->write_png_depth)
9351 {
glennrp5af765f2010-03-30 11:12:18 +00009352 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009353
cristy35ef8242010-06-03 16:24:13 +00009354 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009355 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009356 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009357 }
9358 }
glennrp2b013e42010-11-24 16:55:50 +00009359
glennrp0fe50b42010-11-16 03:52:51 +00009360 else if (ping_color_type ==
9361 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009362 mng_info->IsPalette)
9363 {
cristy3ed852e2009-09-05 21:47:34 +00009364 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009365
cristy3ed852e2009-09-05 21:47:34 +00009366 int
9367 depth_4_ok=MagickTrue,
9368 depth_2_ok=MagickTrue,
9369 depth_1_ok=MagickTrue;
9370
cristybb503372010-05-27 20:51:26 +00009371 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009372 {
9373 unsigned char
9374 intensity;
9375
9376 intensity=ScaleQuantumToChar(image->colormap[i].red);
9377
9378 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9379 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9380 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9381 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009382 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009383 depth_1_ok=MagickFalse;
9384 }
glennrp2b013e42010-11-24 16:55:50 +00009385
cristy3ed852e2009-09-05 21:47:34 +00009386 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009387 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009388
cristy3ed852e2009-09-05 21:47:34 +00009389 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009390 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009391
cristy3ed852e2009-09-05 21:47:34 +00009392 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009393 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009394 }
9395 }
glennrp2b013e42010-11-24 16:55:50 +00009396
glennrp5af765f2010-03-30 11:12:18 +00009397 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009398 }
glennrp0fe50b42010-11-16 03:52:51 +00009399
cristy3ed852e2009-09-05 21:47:34 +00009400 else
glennrp0fe50b42010-11-16 03:52:51 +00009401
cristy3ed852e2009-09-05 21:47:34 +00009402 if (mng_info->IsPalette)
9403 {
glennrp17a14852010-05-10 03:01:59 +00009404 number_colors=image_colors;
9405
cristy3ed852e2009-09-05 21:47:34 +00009406 if (image_depth <= 8)
9407 {
cristy3ed852e2009-09-05 21:47:34 +00009408 /*
9409 Set image palette.
9410 */
glennrp5af765f2010-03-30 11:12:18 +00009411 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009412
glennrp58e01762011-01-07 15:28:54 +00009413 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009414 {
glennrp9c1eb072010-06-06 22:19:15 +00009415 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009416
glennrp3b51f0e2010-11-27 18:14:08 +00009417 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9419 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009420 }
glennrp0fe50b42010-11-16 03:52:51 +00009421
cristy3ed852e2009-09-05 21:47:34 +00009422 else
9423 {
cristybb503372010-05-27 20:51:26 +00009424 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009425 {
9426 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9427 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9428 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9429 }
glennrp0fe50b42010-11-16 03:52:51 +00009430
glennrp3b51f0e2010-11-27 18:14:08 +00009431 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009433 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009434 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009435
glennrp39992b42010-11-14 00:03:43 +00009436 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009437 }
glennrp0fe50b42010-11-16 03:52:51 +00009438
cristy3ed852e2009-09-05 21:47:34 +00009439 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009440 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009441 {
cristybefe4d22010-06-07 01:18:58 +00009442 size_t
9443 one;
9444
glennrp5af765f2010-03-30 11:12:18 +00009445 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009446 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009447
glennrpd17915c2011-04-29 14:24:22 +00009448 while ((one << ping_bit_depth) < (ssize_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009449 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009450 }
glennrp0fe50b42010-11-16 03:52:51 +00009451
glennrp5af765f2010-03-30 11:12:18 +00009452 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009453
glennrp58e01762011-01-07 15:28:54 +00009454 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009455 {
glennrp0fe50b42010-11-16 03:52:51 +00009456 /*
glennrpd6bf1612010-12-17 17:28:54 +00009457 * Set up trans_colors array.
9458 */
glennrp0fe50b42010-11-16 03:52:51 +00009459 assert(number_colors <= 256);
9460
glennrpd6bf1612010-12-17 17:28:54 +00009461 ping_num_trans=(unsigned short) (number_transparent +
9462 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009463
9464 if (ping_num_trans == 0)
9465 ping_have_tRNS=MagickFalse;
9466
glennrpd6bf1612010-12-17 17:28:54 +00009467 else
glennrp0fe50b42010-11-16 03:52:51 +00009468 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009469 if (logging != MagickFalse)
9470 {
9471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9472 " Scaling ping_trans_color (1)");
9473 }
glennrpd6bf1612010-12-17 17:28:54 +00009474 ping_have_tRNS=MagickTrue;
9475
9476 for (i=0; i < ping_num_trans; i++)
9477 {
cristy4c08aed2011-07-01 19:47:50 +00009478 ping_trans_alpha[i]= (png_byte)
9479 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009480 }
glennrp0fe50b42010-11-16 03:52:51 +00009481 }
9482 }
cristy3ed852e2009-09-05 21:47:34 +00009483 }
9484 }
glennrp0fe50b42010-11-16 03:52:51 +00009485
cristy3ed852e2009-09-05 21:47:34 +00009486 else
9487 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009488
cristy3ed852e2009-09-05 21:47:34 +00009489 if (image_depth < 8)
9490 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009491
cristy3ed852e2009-09-05 21:47:34 +00009492 if ((save_image_depth == 16) && (image_depth == 8))
9493 {
glennrp4f25bd02011-01-01 18:51:28 +00009494 if (logging != MagickFalse)
9495 {
9496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9497 " Scaling ping_trans_color from (%d,%d,%d)",
9498 (int) ping_trans_color.red,
9499 (int) ping_trans_color.green,
9500 (int) ping_trans_color.blue);
9501 }
9502
glennrp5af765f2010-03-30 11:12:18 +00009503 ping_trans_color.red*=0x0101;
9504 ping_trans_color.green*=0x0101;
9505 ping_trans_color.blue*=0x0101;
9506 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009507
9508 if (logging != MagickFalse)
9509 {
9510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9511 " to (%d,%d,%d)",
9512 (int) ping_trans_color.red,
9513 (int) ping_trans_color.green,
9514 (int) ping_trans_color.blue);
9515 }
cristy3ed852e2009-09-05 21:47:34 +00009516 }
9517 }
9518
cristy4383ec82011-01-05 15:42:32 +00009519 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9520 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009521
cristy3ed852e2009-09-05 21:47:34 +00009522 /*
9523 Adjust background and transparency samples in sub-8-bit grayscale files.
9524 */
glennrp5af765f2010-03-30 11:12:18 +00009525 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009526 PNG_COLOR_TYPE_GRAY)
9527 {
9528 png_uint_16
9529 maxval;
9530
cristy35ef8242010-06-03 16:24:13 +00009531 size_t
9532 one=1;
9533
cristy22ffd972010-06-03 16:51:47 +00009534 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009535
glennrp4f25bd02011-01-01 18:51:28 +00009536 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009537 {
cristy3ed852e2009-09-05 21:47:34 +00009538
glennrpa521b2f2010-10-29 04:11:03 +00009539 ping_background.gray=(png_uint_16)
glennrp847370c2011-07-05 17:37:15 +00009540 ((maxval/255.)*((GetPixelPacketIntensity(&image->background_color)))
9541 +.5);
cristy3ed852e2009-09-05 21:47:34 +00009542
9543 if (logging != MagickFalse)
9544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009545 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9547 " background_color index is %d",
9548 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009549
glennrp991d11d2010-11-12 21:55:28 +00009550 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009551 }
cristy3ed852e2009-09-05 21:47:34 +00009552
glennrp3e3e20f2011-06-09 04:21:43 +00009553 if (logging != MagickFalse)
9554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9555 " Scaling ping_trans_color.gray from %d",
9556 (int)ping_trans_color.gray);
9557
glennrp9be9b1c2011-06-09 12:21:45 +00009558 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009559 ping_trans_color.gray)+.5);
9560
9561 if (logging != MagickFalse)
9562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9563 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009564 }
glennrp17a14852010-05-10 03:01:59 +00009565
glennrp26f37912010-12-23 16:22:42 +00009566 if (ping_exclude_bKGD == MagickFalse)
9567 {
glennrp1273f7b2011-02-24 03:20:30 +00009568 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009569 {
9570 /*
9571 Identify which colormap entry is the background color.
9572 */
9573
glennrp17a14852010-05-10 03:01:59 +00009574 number_colors=image_colors;
9575
glennrpa521b2f2010-10-29 04:11:03 +00009576 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9577 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009578 break;
9579
9580 ping_background.index=(png_byte) i;
9581
glennrp3b51f0e2010-11-27 18:14:08 +00009582 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009583 {
9584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009585 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009586 }
glennrp0fe50b42010-11-16 03:52:51 +00009587
cristy13d07042010-11-21 20:56:18 +00009588 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009589 {
9590 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009591
9592 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009593 {
9594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9595 " background =(%d,%d,%d)",
9596 (int) ping_background.red,
9597 (int) ping_background.green,
9598 (int) ping_background.blue);
9599 }
9600 }
glennrpa521b2f2010-10-29 04:11:03 +00009601
glennrpd6bf1612010-12-17 17:28:54 +00009602 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009603 {
glennrp3b51f0e2010-11-27 18:14:08 +00009604 if (logging != MagickFalse)
9605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9606 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009607 ping_have_bKGD = MagickFalse;
9608 }
glennrp17a14852010-05-10 03:01:59 +00009609 }
glennrp26f37912010-12-23 16:22:42 +00009610 }
glennrp17a14852010-05-10 03:01:59 +00009611
cristy3ed852e2009-09-05 21:47:34 +00009612 if (logging != MagickFalse)
9613 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009614 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009615 /*
9616 Initialize compression level and filtering.
9617 */
9618 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009619 {
9620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9621 " Setting up deflate compression");
9622
9623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9624 " Compression buffer size: 32768");
9625 }
9626
cristy3ed852e2009-09-05 21:47:34 +00009627 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009628
cristy3ed852e2009-09-05 21:47:34 +00009629 if (logging != MagickFalse)
9630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9631 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009632
glennrp10d739e2011-06-29 18:00:52 +00009633 /* Untangle the "-quality" setting:
9634
9635 Undefined is 0; the default is used.
9636 Default is 75
9637
9638 10's digit:
9639
9640 0: Use Z_HUFFMAN_ONLY strategy with the
9641 zlib default compression level
9642
9643 1-9: the zlib compression level
9644
9645 1's digit:
9646
9647 0-4: the PNG filter method
9648
9649 5: libpng adaptive filtering if compression level > 5
9650 libpng filter type "none" if compression level <= 5
9651 or if image is grayscale or palette
9652
9653 6: libpng adaptive filtering
9654
9655 7: "LOCO" filtering (intrapixel differing) if writing
9656 a MNG, othewise "none". Did not work in IM-6.7.0-9
9657 and earlier because of a missing "else".
9658
9659 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009660 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009661
9662 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009663 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009664
9665 Note that using the -quality option, not all combinations of
9666 PNG filter type, zlib compression level, and zlib compression
9667 strategy are possible. This will be addressed soon in a
9668 release that accomodates "-define PNG:compression-strategy",
9669 etc.
9670
9671 */
9672
cristy3ed852e2009-09-05 21:47:34 +00009673 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9674 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009675
glennrp18682582011-06-30 18:11:47 +00009676 if (quality <= 9)
9677 {
9678 if (mng_info->write_png_compression_strategy == 0)
9679 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9680 }
9681
9682 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009683 {
9684 int
9685 level;
9686
cristybb503372010-05-27 20:51:26 +00009687 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009688
glennrp18682582011-06-30 18:11:47 +00009689 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009690 }
glennrp0fe50b42010-11-16 03:52:51 +00009691
glennrp18682582011-06-30 18:11:47 +00009692 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009693 {
glennrp18682582011-06-30 18:11:47 +00009694 if ((quality %10) == 8 || (quality %10) == 9)
9695 mng_info->write_png_compression_strategy=Z_RLE;
cristy3ed852e2009-09-05 21:47:34 +00009696 }
glennrp0fe50b42010-11-16 03:52:51 +00009697
glennrp18682582011-06-30 18:11:47 +00009698 if (mng_info->write_png_compression_filter == 0)
9699 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9700
cristy3ed852e2009-09-05 21:47:34 +00009701 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009702 {
glennrp18682582011-06-30 18:11:47 +00009703 if (mng_info->write_png_compression_level)
9704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9705 " Compression level: %d",
9706 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009707
glennrp18682582011-06-30 18:11:47 +00009708 if (mng_info->write_png_compression_strategy)
9709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9710 " Compression strategy: %d",
9711 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009712
glennrp18682582011-06-30 18:11:47 +00009713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9714 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009715
glennrp18682582011-06-30 18:11:47 +00009716 if (mng_info->write_png_compression_filter == PNG_ALL_FILTERS+1)
cristy3ed852e2009-09-05 21:47:34 +00009717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9718 " Base filter method: ADAPTIVE");
glennrp18682582011-06-30 18:11:47 +00009719 else if (mng_info->write_png_compression_filter == PNG_NO_FILTERS+1)
cristy3ed852e2009-09-05 21:47:34 +00009720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9721 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009722 else
9723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9724 " Base filter method: %d",
9725 (int) mng_info->write_png_compression_filter-1);
9726 }
glennrp2b013e42010-11-24 16:55:50 +00009727
glennrp18682582011-06-30 18:11:47 +00009728 if (mng_info->write_png_compression_level != 0)
9729 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9730
9731 if (mng_info->write_png_compression_filter == 6)
9732 {
9733 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9734 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9735 (quality < 50))
9736 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9737 else
9738 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9739 }
9740
9741 if (mng_info->write_png_compression_filter == 7 ||
9742 mng_info->write_png_compression_filter == 10)
9743 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9744
9745 else if (mng_info->write_png_compression_filter == 8)
9746 {
9747#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9748 if (mng_info->write_mng)
9749 {
9750 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9751 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9752 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9753 }
9754#endif
9755 png_set_filter(ping,PNG_FILTER_TYPE_BASE,0);
9756 }
9757
9758 else if (mng_info->write_png_compression_filter == 9)
9759 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9760
9761 else if (mng_info->write_png_compression_filter != 0)
9762 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9763 mng_info->write_png_compression_filter-1);
9764
9765 if (mng_info->write_png_compression_strategy != 0)
9766 png_set_compression_strategy(ping,
9767 mng_info->write_png_compression_strategy-1);
9768
cristy3ed852e2009-09-05 21:47:34 +00009769
glennrp823b55c2011-03-14 18:46:46 +00009770 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9771 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009772 {
9773 ResetImageProfileIterator(image);
9774 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009775 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009776 profile=GetImageProfile(image,name);
9777
9778 if (profile != (StringInfo *) NULL)
9779 {
glennrp5af765f2010-03-30 11:12:18 +00009780#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009781 if ((LocaleCompare(name,"ICC") == 0) ||
9782 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009783 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009784
9785 if (ping_exclude_iCCP == MagickFalse)
9786 {
9787 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009788#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009789 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009790#else
9791 (png_const_bytep) GetStringInfoDatum(profile),
9792#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009793 (png_uint_32) GetStringInfoLength(profile));
9794 }
glennrp26f37912010-12-23 16:22:42 +00009795 }
glennrp0fe50b42010-11-16 03:52:51 +00009796
glennrpc8cbc5d2011-01-01 00:12:34 +00009797 else
cristy3ed852e2009-09-05 21:47:34 +00009798#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009799 if (ping_exclude_zCCP == MagickFalse)
9800 {
glennrpcf002022011-01-30 02:38:15 +00009801 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009802 (unsigned char *) name,(unsigned char *) name,
9803 GetStringInfoDatum(profile),
9804 (png_uint_32) GetStringInfoLength(profile));
9805 }
9806 }
glennrp0b206f52011-01-07 04:55:32 +00009807
glennrpc8cbc5d2011-01-01 00:12:34 +00009808 if (logging != MagickFalse)
9809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9810 " Setting up text chunk with %s profile",name);
9811
9812 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009813 }
cristy3ed852e2009-09-05 21:47:34 +00009814 }
9815
9816#if defined(PNG_WRITE_sRGB_SUPPORTED)
9817 if ((mng_info->have_write_global_srgb == 0) &&
9818 ((image->rendering_intent != UndefinedIntent) ||
9819 (image->colorspace == sRGBColorspace)))
9820 {
glennrp26f37912010-12-23 16:22:42 +00009821 if (ping_exclude_sRGB == MagickFalse)
9822 {
9823 /*
9824 Note image rendering intent.
9825 */
9826 if (logging != MagickFalse)
9827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9828 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009829
glennrp26f37912010-12-23 16:22:42 +00009830 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009831 Magick_RenderingIntent_to_PNG_RenderingIntent(
9832 image->rendering_intent)));
glennrp0fe50b42010-11-16 03:52:51 +00009833
glennrp26f37912010-12-23 16:22:42 +00009834 if (ping_exclude_gAMA == MagickFalse)
9835 png_set_gAMA(ping,ping_info,0.45455);
9836 }
cristy3ed852e2009-09-05 21:47:34 +00009837 }
glennrp26f37912010-12-23 16:22:42 +00009838
glennrp5af765f2010-03-30 11:12:18 +00009839 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009840#endif
9841 {
glennrp2cc891a2010-12-24 13:44:32 +00009842 if (ping_exclude_gAMA == MagickFalse &&
9843 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009844 (image->gamma < .45 || image->gamma > .46)))
9845 {
cristy3ed852e2009-09-05 21:47:34 +00009846 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9847 {
9848 /*
9849 Note image gamma.
9850 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9851 */
9852 if (logging != MagickFalse)
9853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9854 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009855
cristy3ed852e2009-09-05 21:47:34 +00009856 png_set_gAMA(ping,ping_info,image->gamma);
9857 }
glennrp26f37912010-12-23 16:22:42 +00009858 }
glennrp2b013e42010-11-24 16:55:50 +00009859
glennrp26f37912010-12-23 16:22:42 +00009860 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009861 {
glennrp26f37912010-12-23 16:22:42 +00009862 if ((mng_info->have_write_global_chrm == 0) &&
9863 (image->chromaticity.red_primary.x != 0.0))
9864 {
9865 /*
9866 Note image chromaticity.
9867 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9868 */
9869 PrimaryInfo
9870 bp,
9871 gp,
9872 rp,
9873 wp;
cristy3ed852e2009-09-05 21:47:34 +00009874
glennrp26f37912010-12-23 16:22:42 +00009875 wp=image->chromaticity.white_point;
9876 rp=image->chromaticity.red_primary;
9877 gp=image->chromaticity.green_primary;
9878 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009879
glennrp26f37912010-12-23 16:22:42 +00009880 if (logging != MagickFalse)
9881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9882 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009883
glennrp26f37912010-12-23 16:22:42 +00009884 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9885 bp.x,bp.y);
9886 }
9887 }
cristy3ed852e2009-09-05 21:47:34 +00009888 }
glennrpdfd70802010-11-14 01:23:35 +00009889
glennrp5af765f2010-03-30 11:12:18 +00009890 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009891
9892 if (mng_info->write_mng)
9893 png_set_sig_bytes(ping,8);
9894
9895 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9896
glennrpd6bf1612010-12-17 17:28:54 +00009897 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009898 {
9899 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +00009900 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009901 {
glennrp5af765f2010-03-30 11:12:18 +00009902 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +00009903
glennrp5af765f2010-03-30 11:12:18 +00009904 if (ping_bit_depth < 8)
9905 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00009906 }
glennrp0fe50b42010-11-16 03:52:51 +00009907
cristy3ed852e2009-09-05 21:47:34 +00009908 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +00009909 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00009910 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009911 }
9912
glennrp0e8ea192010-12-24 18:00:33 +00009913 if (ping_need_colortype_warning != MagickFalse ||
9914 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00009915 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00009916 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00009917 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00009918 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +00009919 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +00009920 {
9921 if (logging != MagickFalse)
9922 {
glennrp0e8ea192010-12-24 18:00:33 +00009923 if (ping_need_colortype_warning != MagickFalse)
9924 {
9925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9926 " Image has transparency but tRNS chunk was excluded");
9927 }
9928
cristy3ed852e2009-09-05 21:47:34 +00009929 if (mng_info->write_png_depth)
9930 {
9931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9932 " Defined PNG:bit-depth=%u, Computed depth=%u",
9933 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00009934 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009935 }
glennrp0e8ea192010-12-24 18:00:33 +00009936
cristy3ed852e2009-09-05 21:47:34 +00009937 if (mng_info->write_png_colortype)
9938 {
9939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9940 " Defined PNG:color-type=%u, Computed color type=%u",
9941 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00009942 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009943 }
9944 }
glennrp0e8ea192010-12-24 18:00:33 +00009945
glennrp3bd2e412010-08-10 13:34:52 +00009946 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +00009947 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9948 }
9949
glennrp58e01762011-01-07 15:28:54 +00009950 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009951 {
9952 /* Add an opaque matte channel */
9953 image->matte = MagickTrue;
9954 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +00009955
glennrpb4a13412010-05-05 12:47:19 +00009956 if (logging != MagickFalse)
9957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9958 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00009959 }
9960
glennrp0e319732011-01-25 21:53:13 +00009961 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +00009962 {
glennrp991d11d2010-11-12 21:55:28 +00009963 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +00009964 {
glennrp991d11d2010-11-12 21:55:28 +00009965 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +00009966 if (logging != MagickFalse)
9967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9968 " Setting ping_have_tRNS=MagickTrue.");
9969 }
glennrpe9c26dc2010-05-30 01:56:35 +00009970 }
9971
cristy3ed852e2009-09-05 21:47:34 +00009972 if (logging != MagickFalse)
9973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9974 " Writing PNG header chunks");
9975
glennrp5af765f2010-03-30 11:12:18 +00009976 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9977 ping_bit_depth,ping_color_type,
9978 ping_interlace_method,ping_compression_method,
9979 ping_filter_method);
9980
glennrp39992b42010-11-14 00:03:43 +00009981 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9982 {
glennrpf09bded2011-01-08 01:15:59 +00009983 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009984
glennrp3b51f0e2010-11-27 18:14:08 +00009985 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +00009986 {
glennrp8640fb52010-11-23 15:48:26 +00009987 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +00009988 {
glennrpd6bf1612010-12-17 17:28:54 +00009989 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +00009990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009991 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9992 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009993 (int) palette[i].red,
9994 (int) palette[i].green,
9995 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +00009996 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009997 (int) ping_trans_alpha[i]);
9998 else
9999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010000 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010001 (int) i,
10002 (int) palette[i].red,
10003 (int) palette[i].green,
10004 (int) palette[i].blue);
10005 }
glennrp39992b42010-11-14 00:03:43 +000010006 }
glennrp39992b42010-11-14 00:03:43 +000010007 }
10008
glennrp26f37912010-12-23 16:22:42 +000010009 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010010 {
glennrp26f37912010-12-23 16:22:42 +000010011 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010012 {
glennrp26f37912010-12-23 16:22:42 +000010013 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010014 if (logging)
10015 {
10016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10017 " Setting up bKGD chunk");
10018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10019 " background color = (%d,%d,%d)",
10020 (int) ping_background.red,
10021 (int) ping_background.green,
10022 (int) ping_background.blue);
10023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10024 " index = %d, gray=%d",
10025 (int) ping_background.index,
10026 (int) ping_background.gray);
10027 }
10028 }
glennrp26f37912010-12-23 16:22:42 +000010029 }
10030
10031 if (ping_exclude_pHYs == MagickFalse)
10032 {
10033 if (ping_have_pHYs != MagickFalse)
10034 {
10035 png_set_pHYs(ping,ping_info,
10036 ping_pHYs_x_resolution,
10037 ping_pHYs_y_resolution,
10038 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010039
10040 if (logging)
10041 {
10042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10043 " Setting up pHYs chunk");
10044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10045 " x_resolution=%lu",
10046 (unsigned long) ping_pHYs_x_resolution);
10047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10048 " y_resolution=%lu",
10049 (unsigned long) ping_pHYs_y_resolution);
10050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10051 " unit_type=%lu",
10052 (unsigned long) ping_pHYs_unit_type);
10053 }
glennrp26f37912010-12-23 16:22:42 +000010054 }
glennrpdfd70802010-11-14 01:23:35 +000010055 }
10056
10057#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010058 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010059 {
glennrp26f37912010-12-23 16:22:42 +000010060 if (image->page.x || image->page.y)
10061 {
10062 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10063 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010064
glennrp26f37912010-12-23 16:22:42 +000010065 if (logging != MagickFalse)
10066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10067 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10068 (int) image->page.x, (int) image->page.y);
10069 }
glennrpdfd70802010-11-14 01:23:35 +000010070 }
10071#endif
10072
glennrpda8f3a72011-02-27 23:54:12 +000010073 if (mng_info->need_blob != MagickFalse)
10074 {
10075 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
10076 MagickFalse)
10077 png_error(ping,"WriteBlob Failed");
10078
10079 ping_have_blob=MagickTrue;
10080 }
10081
cristy3ed852e2009-09-05 21:47:34 +000010082 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010083
glennrp39992b42010-11-14 00:03:43 +000010084 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010085 {
glennrp3b51f0e2010-11-27 18:14:08 +000010086 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010087 {
10088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10089 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10090 }
10091
10092 if (ping_color_type == 3)
10093 (void) png_set_tRNS(ping, ping_info,
10094 ping_trans_alpha,
10095 ping_num_trans,
10096 NULL);
10097
10098 else
10099 {
10100 (void) png_set_tRNS(ping, ping_info,
10101 NULL,
10102 0,
10103 &ping_trans_color);
10104
glennrp3b51f0e2010-11-27 18:14:08 +000010105 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010106 {
10107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010108 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010109 (int) ping_trans_color.red,
10110 (int) ping_trans_color.green,
10111 (int) ping_trans_color.blue);
10112 }
10113 }
glennrp991d11d2010-11-12 21:55:28 +000010114 }
10115
cristy3ed852e2009-09-05 21:47:34 +000010116 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010117 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010118
cristy3ed852e2009-09-05 21:47:34 +000010119 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010120
cristy3ed852e2009-09-05 21:47:34 +000010121 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010122 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010123
glennrp26f37912010-12-23 16:22:42 +000010124 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010125 {
glennrp4f25bd02011-01-01 18:51:28 +000010126 if ((image->page.width != 0 && image->page.width != image->columns) ||
10127 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010128 {
10129 unsigned char
10130 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010131
glennrp26f37912010-12-23 16:22:42 +000010132 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10133 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010134 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010135 PNGLong(chunk+4,(png_uint_32) image->page.width);
10136 PNGLong(chunk+8,(png_uint_32) image->page.height);
10137 chunk[12]=0; /* unit = pixels */
10138 (void) WriteBlob(image,13,chunk);
10139 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10140 }
cristy3ed852e2009-09-05 21:47:34 +000010141 }
10142
10143#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010144 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010145#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010146 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010147#undef PNG_HAVE_IDAT
10148#endif
10149
10150 png_set_packing(ping);
10151 /*
10152 Allocate memory.
10153 */
10154 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010155 if (image_depth > 8)
10156 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010157 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010158 {
glennrpb4a13412010-05-05 12:47:19 +000010159 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010160 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010161 break;
glennrp0fe50b42010-11-16 03:52:51 +000010162
glennrpb4a13412010-05-05 12:47:19 +000010163 case PNG_COLOR_TYPE_GRAY_ALPHA:
10164 rowbytes*=2;
10165 break;
glennrp0fe50b42010-11-16 03:52:51 +000010166
glennrpb4a13412010-05-05 12:47:19 +000010167 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010168 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010169 break;
glennrp0fe50b42010-11-16 03:52:51 +000010170
glennrpb4a13412010-05-05 12:47:19 +000010171 default:
10172 break;
cristy3ed852e2009-09-05 21:47:34 +000010173 }
glennrp3b51f0e2010-11-27 18:14:08 +000010174
10175 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010176 {
10177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10178 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010179
glennrpb4a13412010-05-05 12:47:19 +000010180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010181 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010182 }
glennrpcf002022011-01-30 02:38:15 +000010183 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10184 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010185
glennrpcf002022011-01-30 02:38:15 +000010186 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010187 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010188
cristy3ed852e2009-09-05 21:47:34 +000010189 /*
10190 Initialize image scanlines.
10191 */
glennrp5af765f2010-03-30 11:12:18 +000010192 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010193 {
10194 /*
10195 PNG write failed.
10196 */
10197#ifdef PNG_DEBUG
10198 if (image_info->verbose)
10199 (void) printf("PNG write has failed.\n");
10200#endif
10201 png_destroy_write_struct(&ping,&ping_info);
10202 if (quantum_info != (QuantumInfo *) NULL)
10203 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010204 if (ping_pixels != (unsigned char *) NULL)
10205 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010206#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010207 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010208#endif
glennrpda8f3a72011-02-27 23:54:12 +000010209 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010210 (void) CloseBlob(image);
10211 image_info=DestroyImageInfo(image_info);
10212 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010213 return(MagickFalse);
10214 }
cristyed552522009-10-16 14:04:35 +000010215 quantum_info=AcquireQuantumInfo(image_info,image);
10216 if (quantum_info == (QuantumInfo *) NULL)
10217 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010218 quantum_info->format=UndefinedQuantumFormat;
10219 quantum_info->depth=image_depth;
10220 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010221
cristy3ed852e2009-09-05 21:47:34 +000010222 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010223 !mng_info->write_png32) &&
10224 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010225 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010226 image_matte == MagickFalse &&
10227 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010228 {
glennrp8bb3a022010-12-13 20:40:04 +000010229 /* Palette, Bilevel, or Opaque Monochrome */
cristy4c08aed2011-07-01 19:47:50 +000010230 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010231 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010232
cristy3ed852e2009-09-05 21:47:34 +000010233 quantum_info->depth=8;
10234 for (pass=0; pass < num_passes; pass++)
10235 {
10236 /*
10237 Convert PseudoClass image to a PNG monochrome image.
10238 */
cristybb503372010-05-27 20:51:26 +000010239 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010240 {
glennrpd71e86a2011-02-24 01:28:37 +000010241 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10243 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010244
cristy3ed852e2009-09-05 21:47:34 +000010245 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010246
cristy4c08aed2011-07-01 19:47:50 +000010247 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010248 break;
glennrp0fe50b42010-11-16 03:52:51 +000010249
cristy3ed852e2009-09-05 21:47:34 +000010250 if (mng_info->IsPalette)
10251 {
cristy4c08aed2011-07-01 19:47:50 +000010252 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010253 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010254 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10255 mng_info->write_png_depth &&
10256 mng_info->write_png_depth != old_bit_depth)
10257 {
10258 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010259 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010260 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010261 >> (8-old_bit_depth));
10262 }
10263 }
glennrp0fe50b42010-11-16 03:52:51 +000010264
cristy3ed852e2009-09-05 21:47:34 +000010265 else
10266 {
cristy4c08aed2011-07-01 19:47:50 +000010267 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010268 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010269 }
glennrp0fe50b42010-11-16 03:52:51 +000010270
cristy3ed852e2009-09-05 21:47:34 +000010271 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010272 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010273 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010274 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010275
glennrp3b51f0e2010-11-27 18:14:08 +000010276 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10278 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010279
glennrpcf002022011-01-30 02:38:15 +000010280 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010281 }
10282 if (image->previous == (Image *) NULL)
10283 {
10284 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10285 if (status == MagickFalse)
10286 break;
10287 }
10288 }
10289 }
glennrp0fe50b42010-11-16 03:52:51 +000010290
glennrp8bb3a022010-12-13 20:40:04 +000010291 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010292 {
glennrp0fe50b42010-11-16 03:52:51 +000010293 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010294 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010295 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010296 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010297 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010298 {
cristy4c08aed2011-07-01 19:47:50 +000010299 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010300 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010301
glennrp8bb3a022010-12-13 20:40:04 +000010302 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010303 {
glennrp8bb3a022010-12-13 20:40:04 +000010304
cristybb503372010-05-27 20:51:26 +000010305 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010306 {
10307 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010308
cristy4c08aed2011-07-01 19:47:50 +000010309 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010310 break;
glennrp2cc891a2010-12-24 13:44:32 +000010311
glennrp5af765f2010-03-30 11:12:18 +000010312 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010313 {
glennrp8bb3a022010-12-13 20:40:04 +000010314 if (mng_info->IsPalette)
cristy4c08aed2011-07-01 19:47:50 +000010315 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010316 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010317
glennrp8bb3a022010-12-13 20:40:04 +000010318 else
cristy4c08aed2011-07-01 19:47:50 +000010319 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010320 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010321
glennrp3b51f0e2010-11-27 18:14:08 +000010322 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010324 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010325 }
glennrp2cc891a2010-12-24 13:44:32 +000010326
glennrp8bb3a022010-12-13 20:40:04 +000010327 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10328 {
10329 if (logging != MagickFalse && y == 0)
10330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10331 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010332
cristy4c08aed2011-07-01 19:47:50 +000010333 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010334 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010335 }
glennrp2cc891a2010-12-24 13:44:32 +000010336
glennrp3b51f0e2010-11-27 18:14:08 +000010337 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010339 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010340
glennrpcf002022011-01-30 02:38:15 +000010341 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010342 }
glennrp2cc891a2010-12-24 13:44:32 +000010343
glennrp8bb3a022010-12-13 20:40:04 +000010344 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010345 {
glennrp8bb3a022010-12-13 20:40:04 +000010346 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10347 if (status == MagickFalse)
10348 break;
cristy3ed852e2009-09-05 21:47:34 +000010349 }
cristy3ed852e2009-09-05 21:47:34 +000010350 }
10351 }
glennrp8bb3a022010-12-13 20:40:04 +000010352
10353 else
10354 {
cristy4c08aed2011-07-01 19:47:50 +000010355 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010356 *p;
10357
10358 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010359 {
glennrp8bb3a022010-12-13 20:40:04 +000010360 if ((image_depth > 8) || (mng_info->write_png24 ||
10361 mng_info->write_png32 ||
10362 (!mng_info->write_png8 && !mng_info->IsPalette)))
10363 {
10364 for (y=0; y < (ssize_t) image->rows; y++)
10365 {
10366 p=GetVirtualPixels(image,0,y,image->columns,1,
10367 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010368
cristy4c08aed2011-07-01 19:47:50 +000010369 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010370 break;
glennrp2cc891a2010-12-24 13:44:32 +000010371
glennrp8bb3a022010-12-13 20:40:04 +000010372 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10373 {
10374 if (image->storage_class == DirectClass)
cristy4c08aed2011-07-01 19:47:50 +000010375 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010376 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010377
glennrp8bb3a022010-12-13 20:40:04 +000010378 else
cristy4c08aed2011-07-01 19:47:50 +000010379 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010380 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010381 }
glennrp2cc891a2010-12-24 13:44:32 +000010382
glennrp8bb3a022010-12-13 20:40:04 +000010383 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10384 {
cristy4c08aed2011-07-01 19:47:50 +000010385 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010386 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +000010387 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010388
glennrp8bb3a022010-12-13 20:40:04 +000010389 if (logging != MagickFalse && y == 0)
10390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10391 " Writing GRAY_ALPHA PNG pixels (3)");
10392 }
glennrp2cc891a2010-12-24 13:44:32 +000010393
glennrp8bb3a022010-12-13 20:40:04 +000010394 else if (image_matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +000010395 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010396 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010397
glennrp8bb3a022010-12-13 20:40:04 +000010398 else
cristy4c08aed2011-07-01 19:47:50 +000010399 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010400 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010401
glennrp8bb3a022010-12-13 20:40:04 +000010402 if (logging != MagickFalse && y == 0)
10403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10404 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010405
glennrpcf002022011-01-30 02:38:15 +000010406 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010407 }
10408 }
glennrp2cc891a2010-12-24 13:44:32 +000010409
glennrp8bb3a022010-12-13 20:40:04 +000010410 else
10411 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10412 mng_info->write_png32 ||
10413 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10414 {
10415 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10416 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10417 {
10418 if (logging != MagickFalse)
10419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10420 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010421
glennrp8bb3a022010-12-13 20:40:04 +000010422 quantum_info->depth=8;
10423 image_depth=8;
10424 }
glennrp2cc891a2010-12-24 13:44:32 +000010425
glennrp8bb3a022010-12-13 20:40:04 +000010426 for (y=0; y < (ssize_t) image->rows; y++)
10427 {
10428 if (logging != MagickFalse && y == 0)
10429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10430 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010431
glennrp770d1932011-03-06 22:11:17 +000010432 p=GetVirtualPixels(image,0,y,image->columns,1,
10433 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010434
cristy4c08aed2011-07-01 19:47:50 +000010435 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010436 break;
glennrp2cc891a2010-12-24 13:44:32 +000010437
glennrp8bb3a022010-12-13 20:40:04 +000010438 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010439 {
glennrp4bf89732011-03-21 13:48:28 +000010440 quantum_info->depth=image->depth;
10441
cristy4c08aed2011-07-01 19:47:50 +000010442 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010443 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +000010444 }
glennrp2cc891a2010-12-24 13:44:32 +000010445
glennrp8bb3a022010-12-13 20:40:04 +000010446 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10447 {
10448 if (logging != MagickFalse && y == 0)
10449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10450 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010451
cristy4c08aed2011-07-01 19:47:50 +000010452 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010453 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +000010454 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010455 }
glennrp2cc891a2010-12-24 13:44:32 +000010456
glennrp8bb3a022010-12-13 20:40:04 +000010457 else
glennrp8bb3a022010-12-13 20:40:04 +000010458 {
cristy4c08aed2011-07-01 19:47:50 +000010459 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +000010460 quantum_info,IndexQuantum,ping_pixels,&image->exception);
10461
10462 if (logging != MagickFalse && y <= 2)
10463 {
10464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010465 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010466
10467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10468 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10469 (int)ping_pixels[0],(int)ping_pixels[1]);
10470 }
glennrp8bb3a022010-12-13 20:40:04 +000010471 }
glennrpcf002022011-01-30 02:38:15 +000010472 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010473 }
10474 }
glennrp2cc891a2010-12-24 13:44:32 +000010475
glennrp8bb3a022010-12-13 20:40:04 +000010476 if (image->previous == (Image *) NULL)
10477 {
10478 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10479 if (status == MagickFalse)
10480 break;
10481 }
cristy3ed852e2009-09-05 21:47:34 +000010482 }
glennrp8bb3a022010-12-13 20:40:04 +000010483 }
10484 }
10485
cristyb32b90a2009-09-07 21:45:48 +000010486 if (quantum_info != (QuantumInfo *) NULL)
10487 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010488
10489 if (logging != MagickFalse)
10490 {
10491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010492 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010493
cristy3ed852e2009-09-05 21:47:34 +000010494 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010495 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010496
cristy3ed852e2009-09-05 21:47:34 +000010497 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010498 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010499
cristy3ed852e2009-09-05 21:47:34 +000010500 if (mng_info->write_png_depth)
10501 {
10502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10503 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
10504 }
glennrp0fe50b42010-11-16 03:52:51 +000010505
cristy3ed852e2009-09-05 21:47:34 +000010506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010507 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010508
cristy3ed852e2009-09-05 21:47:34 +000010509 if (mng_info->write_png_colortype)
10510 {
10511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10512 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
10513 }
glennrp0fe50b42010-11-16 03:52:51 +000010514
cristy3ed852e2009-09-05 21:47:34 +000010515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010516 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010517
cristy3ed852e2009-09-05 21:47:34 +000010518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010519 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010520 }
10521 /*
glennrpa0ed0092011-04-18 16:36:29 +000010522 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010523 */
glennrp823b55c2011-03-14 18:46:46 +000010524 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010525 {
glennrp26f37912010-12-23 16:22:42 +000010526 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010527 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010528 while (property != (const char *) NULL)
10529 {
10530 png_textp
10531 text;
glennrp2cc891a2010-12-24 13:44:32 +000010532
glennrp26f37912010-12-23 16:22:42 +000010533 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +000010534
10535 /* Don't write any "png:" properties; those are just for "identify" */
10536 if (LocaleNCompare(property,"png:",4) != 0 &&
10537
10538 /* Suppress density and units if we wrote a pHYs chunk */
10539 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010540 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010541 LocaleCompare(property,"units") != 0) &&
10542
10543 /* Suppress the IM-generated Date:create and Date:modify */
10544 (ping_exclude_date == MagickFalse ||
10545 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010546 {
glennrpc70af4a2011-03-07 00:08:23 +000010547 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010548 {
glennrpc70af4a2011-03-07 00:08:23 +000010549 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10550 text[0].key=(char *) property;
10551 text[0].text=(char *) value;
10552 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010553
glennrpc70af4a2011-03-07 00:08:23 +000010554 if (ping_exclude_tEXt != MagickFalse)
10555 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10556
10557 else if (ping_exclude_zTXt != MagickFalse)
10558 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10559
10560 else
glennrp26f37912010-12-23 16:22:42 +000010561 {
glennrpc70af4a2011-03-07 00:08:23 +000010562 text[0].compression=image_info->compression == NoCompression ||
10563 (image_info->compression == UndefinedCompression &&
10564 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10565 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010566 }
glennrp2cc891a2010-12-24 13:44:32 +000010567
glennrpc70af4a2011-03-07 00:08:23 +000010568 if (logging != MagickFalse)
10569 {
10570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10571 " Setting up text chunk");
10572
10573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10574 " keyword: %s",text[0].key);
10575 }
10576
10577 png_set_text(ping,ping_info,text,1);
10578 png_free(ping,text);
10579 }
glennrp26f37912010-12-23 16:22:42 +000010580 }
10581 property=GetNextImageProperty(image);
10582 }
cristy3ed852e2009-09-05 21:47:34 +000010583 }
10584
10585 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010586 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010587
10588 if (logging != MagickFalse)
10589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10590 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010591
cristy3ed852e2009-09-05 21:47:34 +000010592 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010593
cristy3ed852e2009-09-05 21:47:34 +000010594 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10595 {
10596 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010597 (ping_width != mng_info->page.width) ||
10598 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010599 {
10600 unsigned char
10601 chunk[32];
10602
10603 /*
10604 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10605 */
10606 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10607 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010608 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010609 chunk[4]=4;
10610 chunk[5]=0; /* frame name separator (no name) */
10611 chunk[6]=1; /* flag for changing delay, for next frame only */
10612 chunk[7]=0; /* flag for changing frame timeout */
10613 chunk[8]=1; /* flag for changing frame clipping for next frame */
10614 chunk[9]=0; /* flag for changing frame sync_id */
10615 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10616 chunk[14]=0; /* clipping boundaries delta type */
10617 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10618 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010619 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010620 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10621 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010622 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010623 (void) WriteBlob(image,31,chunk);
10624 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10625 mng_info->old_framing_mode=4;
10626 mng_info->framing_mode=1;
10627 }
glennrp0fe50b42010-11-16 03:52:51 +000010628
cristy3ed852e2009-09-05 21:47:34 +000010629 else
10630 mng_info->framing_mode=3;
10631 }
10632 if (mng_info->write_mng && !mng_info->need_fram &&
10633 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +000010634 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010635 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010636 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010637
cristy3ed852e2009-09-05 21:47:34 +000010638 /*
10639 Free PNG resources.
10640 */
glennrp5af765f2010-03-30 11:12:18 +000010641
cristy3ed852e2009-09-05 21:47:34 +000010642 png_destroy_write_struct(&ping,&ping_info);
10643
glennrpcf002022011-01-30 02:38:15 +000010644 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010645
10646#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010647 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010648#endif
10649
glennrpda8f3a72011-02-27 23:54:12 +000010650 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010651 (void) CloseBlob(image);
10652
10653 image_info=DestroyImageInfo(image_info);
10654 image=DestroyImage(image);
10655
10656 /* Store bit depth actually written */
10657 s[0]=(char) ping_bit_depth;
10658 s[1]='\0';
10659
10660 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
10661
cristy3ed852e2009-09-05 21:47:34 +000010662 if (logging != MagickFalse)
10663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10664 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010665
cristy3ed852e2009-09-05 21:47:34 +000010666 return(MagickTrue);
10667/* End write one PNG image */
10668}
10669
10670/*
10671%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10672% %
10673% %
10674% %
10675% W r i t e P N G I m a g e %
10676% %
10677% %
10678% %
10679%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10680%
10681% WritePNGImage() writes a Portable Network Graphics (PNG) or
10682% Multiple-image Network Graphics (MNG) image file.
10683%
10684% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10685%
10686% The format of the WritePNGImage method is:
10687%
10688% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
10689%
10690% A description of each parameter follows:
10691%
10692% o image_info: the image info.
10693%
10694% o image: The image.
10695%
10696% Returns MagickTrue on success, MagickFalse on failure.
10697%
10698% Communicating with the PNG encoder:
10699%
10700% While the datastream written is always in PNG format and normally would
10701% be given the "png" file extension, this method also writes the following
10702% pseudo-formats which are subsets of PNG:
10703%
glennrp5a39f372011-02-25 04:52:16 +000010704% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10705% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010706% is present, the tRNS chunk must only have values 0 and 255
10707% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010708% transparent). If other values are present they will be
10709% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010710% colors are present, they will be quantized to the 4-4-4-1,
10711% 3-3-3-1, or 3-3-2-1 palette.
10712%
10713% If you want better quantization or dithering of the colors
10714% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010715% PNG encoder. The pixels contain 8-bit indices even if
10716% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010717% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010718% PNG grayscale type might be slightly more efficient. Please
10719% note that writing to the PNG8 format may result in loss
10720% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010721%
10722% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10723% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010724% one of the colors as transparent. The only loss incurred
10725% is reduction of sample depth to 8. If the image has more
10726% than one transparent color, has semitransparent pixels, or
10727% has an opaque pixel with the same RGB components as the
10728% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010729%
10730% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10731% transparency is permitted, i.e., the alpha sample for
10732% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010733% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010734% The only loss in data is the reduction of the sample depth
10735% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010736%
10737% o -define: For more precise control of the PNG output, you can use the
10738% Image options "png:bit-depth" and "png:color-type". These
10739% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010740% from the application programming interfaces. The options
10741% are case-independent and are converted to lowercase before
10742% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010743%
10744% png:color-type can be 0, 2, 3, 4, or 6.
10745%
10746% When png:color-type is 0 (Grayscale), png:bit-depth can
10747% be 1, 2, 4, 8, or 16.
10748%
10749% When png:color-type is 2 (RGB), png:bit-depth can
10750% be 8 or 16.
10751%
10752% When png:color-type is 3 (Indexed), png:bit-depth can
10753% be 1, 2, 4, or 8. This refers to the number of bits
10754% used to store the index. The color samples always have
10755% bit-depth 8 in indexed PNG files.
10756%
10757% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10758% png:bit-depth can be 8 or 16.
10759%
glennrp5a39f372011-02-25 04:52:16 +000010760% If the image cannot be written without loss with the requested bit-depth
10761% and color-type, a PNG file will not be written, and the encoder will
10762% return MagickFalse.
10763%
cristy3ed852e2009-09-05 21:47:34 +000010764% Since image encoders should not be responsible for the "heavy lifting",
10765% the user should make sure that ImageMagick has already reduced the
10766% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010767% transparency prior to attempting to write the image with depth, color,
glennrp54dc0692011-07-01 19:02:13 +000010768% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010769%
cristy3ed852e2009-09-05 21:47:34 +000010770% Note that another definition, "png:bit-depth-written" exists, but it
10771% is not intended for external use. It is only used internally by the
10772% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10773%
10774% It is possible to request that the PNG encoder write previously-formatted
10775% ancillary chunks in the output PNG file, using the "-profile" commandline
10776% option as shown below or by setting the profile via a programming
10777% interface:
10778%
10779% -profile PNG-chunk-x:<file>
10780%
10781% where x is a location flag and <file> is a file containing the chunk
10782% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010783% This encoder will compute the chunk length and CRC, so those must not
10784% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010785%
10786% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10787% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10788% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010789% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010790%
glennrpbb8a7332010-11-13 15:17:35 +000010791% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010792%
glennrp3241bd02010-12-12 04:36:28 +000010793% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010794%
glennrpd6afd542010-11-19 01:53:05 +000010795% o 32-bit depth is reduced to 16.
10796% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10797% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010798% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010799% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010800% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010801% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10802% this can be done without loss and a larger bit depth N was not
10803% requested via the "-define PNG:bit-depth=N" option.
10804% o If matte channel is present but only one transparent color is
10805% present, RGB+tRNS is written instead of RGBA
10806% o Opaque matte channel is removed (or added, if color-type 4 or 6
10807% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010808%
cristy3ed852e2009-09-05 21:47:34 +000010809%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10810*/
10811static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10812 Image *image)
10813{
10814 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010815 excluding,
10816 logging,
10817 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010818 status;
10819
10820 MngInfo
10821 *mng_info;
10822
10823 const char
10824 *value;
10825
10826 int
glennrp21f0e622011-01-07 16:20:57 +000010827 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010828 source;
10829
cristy3ed852e2009-09-05 21:47:34 +000010830 /*
10831 Open image file.
10832 */
10833 assert(image_info != (const ImageInfo *) NULL);
10834 assert(image_info->signature == MagickSignature);
10835 assert(image != (Image *) NULL);
10836 assert(image->signature == MagickSignature);
10837 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010838 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010839 /*
10840 Allocate a MngInfo structure.
10841 */
10842 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010843 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010844
cristy3ed852e2009-09-05 21:47:34 +000010845 if (mng_info == (MngInfo *) NULL)
10846 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010847
cristy3ed852e2009-09-05 21:47:34 +000010848 /*
10849 Initialize members of the MngInfo structure.
10850 */
10851 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10852 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010853 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010854 have_mng_structure=MagickTrue;
10855
10856 /* See if user has requested a specific PNG subformat */
10857
10858 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10859 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10860 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10861
10862 if (mng_info->write_png8)
10863 {
glennrp9c1eb072010-06-06 22:19:15 +000010864 mng_info->write_png_colortype = /* 3 */ 4;
10865 mng_info->write_png_depth = 8;
10866 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010867 }
10868
10869 if (mng_info->write_png24)
10870 {
glennrp9c1eb072010-06-06 22:19:15 +000010871 mng_info->write_png_colortype = /* 2 */ 3;
10872 mng_info->write_png_depth = 8;
10873 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010874
glennrp9c1eb072010-06-06 22:19:15 +000010875 if (image->matte == MagickTrue)
10876 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010877
glennrp9c1eb072010-06-06 22:19:15 +000010878 else
10879 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010880
glennrp9c1eb072010-06-06 22:19:15 +000010881 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010882 }
10883
10884 if (mng_info->write_png32)
10885 {
glennrp9c1eb072010-06-06 22:19:15 +000010886 mng_info->write_png_colortype = /* 6 */ 7;
10887 mng_info->write_png_depth = 8;
10888 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010889
glennrp9c1eb072010-06-06 22:19:15 +000010890 if (image->matte == MagickTrue)
10891 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010892
glennrp9c1eb072010-06-06 22:19:15 +000010893 else
10894 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010895
glennrp9c1eb072010-06-06 22:19:15 +000010896 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010897 }
10898
10899 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000010900
cristy3ed852e2009-09-05 21:47:34 +000010901 if (value != (char *) NULL)
10902 {
10903 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010904 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010905
cristy3ed852e2009-09-05 21:47:34 +000010906 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010907 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000010908
cristy3ed852e2009-09-05 21:47:34 +000010909 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010910 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010911
cristy3ed852e2009-09-05 21:47:34 +000010912 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010913 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010914
cristy3ed852e2009-09-05 21:47:34 +000010915 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010916 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000010917
glennrpbb8a7332010-11-13 15:17:35 +000010918 else
10919 (void) ThrowMagickException(&image->exception,
10920 GetMagickModule(),CoderWarning,
10921 "ignoring invalid defined png:bit-depth",
10922 "=%s",value);
10923
cristy3ed852e2009-09-05 21:47:34 +000010924 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000010926 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010927 }
glennrp0fe50b42010-11-16 03:52:51 +000010928
cristy3ed852e2009-09-05 21:47:34 +000010929 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000010930
cristy3ed852e2009-09-05 21:47:34 +000010931 if (value != (char *) NULL)
10932 {
10933 /* We must store colortype+1 because 0 is a valid colortype */
10934 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010935 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010936
cristy3ed852e2009-09-05 21:47:34 +000010937 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010938 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000010939
cristy3ed852e2009-09-05 21:47:34 +000010940 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010941 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010942
cristy3ed852e2009-09-05 21:47:34 +000010943 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010944 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000010945
cristy3ed852e2009-09-05 21:47:34 +000010946 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010947 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000010948
glennrpbb8a7332010-11-13 15:17:35 +000010949 else
10950 (void) ThrowMagickException(&image->exception,
10951 GetMagickModule(),CoderWarning,
10952 "ignoring invalid defined png:color-type",
10953 "=%s",value);
10954
cristy3ed852e2009-09-05 21:47:34 +000010955 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010957 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010958 }
10959
glennrp0e8ea192010-12-24 18:00:33 +000010960 /* Check for chunks to be excluded:
10961 *
glennrp0dff56c2011-01-29 19:10:02 +000010962 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000010963 * listed in the "unused_chunks" array, above.
10964 *
10965 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10966 * define (in the image properties or in the image artifacts)
10967 * or via a mng_info member. For convenience, in addition
10968 * to or instead of a comma-separated list of chunks, the
10969 * "exclude-chunk" string can be simply "all" or "none".
10970 *
10971 * The exclude-chunk define takes priority over the mng_info.
10972 *
10973 * A "PNG:include-chunk" define takes priority over both the
10974 * mng_info and the "PNG:exclude-chunk" define. Like the
10975 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000010976 * well as a comma-separated list. Chunks that are unknown to
10977 * ImageMagick are always excluded, regardless of their "copy-safe"
10978 * status according to the PNG specification, and even if they
10979 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000010980 *
10981 * Finally, all chunks listed in the "unused_chunks" array are
10982 * automatically excluded, regardless of the other instructions
10983 * or lack thereof.
10984 *
10985 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10986 * will not be written and the gAMA chunk will only be written if it
10987 * is not between .45 and .46, or approximately (1.0/2.2).
10988 *
10989 * If you exclude tRNS and the image has transparency, the colortype
10990 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10991 *
10992 * The -strip option causes StripImage() to set the png:include-chunk
10993 * artifact to "none,gama".
10994 */
10995
glennrp26f37912010-12-23 16:22:42 +000010996 mng_info->ping_exclude_bKGD=MagickFalse;
10997 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010998 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010999 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11000 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011001 mng_info->ping_exclude_iCCP=MagickFalse;
11002 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11003 mng_info->ping_exclude_oFFs=MagickFalse;
11004 mng_info->ping_exclude_pHYs=MagickFalse;
11005 mng_info->ping_exclude_sRGB=MagickFalse;
11006 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011007 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011008 mng_info->ping_exclude_vpAg=MagickFalse;
11009 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11010 mng_info->ping_exclude_zTXt=MagickFalse;
11011
glennrp8d3d6e52011-04-19 04:39:51 +000011012 mng_info->ping_preserve_colormap=MagickFalse;
11013
11014 value=GetImageArtifact(image,"png:preserve-colormap");
11015 if (value == NULL)
11016 value=GetImageOption(image_info,"png:preserve-colormap");
11017 if (value != NULL)
11018 mng_info->ping_preserve_colormap=MagickTrue;
11019
glennrp18682582011-06-30 18:11:47 +000011020 /* Thes compression-level, compression-strategy, and compression-filter
11021 * defines take precedence over values from the -quality option.
11022 */
11023 value=GetImageArtifact(image,"png:compression-level");
11024 if (value == NULL)
11025 value=GetImageOption(image_info,"png:compression-level");
11026 if (value != NULL)
11027 {
glennrp18682582011-06-30 18:11:47 +000011028 /* We have to add 1 to everything because 0 is a valid input,
11029 * and we want to use 0 (the default) to mean undefined.
11030 */
11031 if (LocaleCompare(value,"0") == 0)
11032 mng_info->write_png_compression_level = 1;
11033
11034 if (LocaleCompare(value,"1") == 0)
11035 mng_info->write_png_compression_level = 2;
11036
11037 else if (LocaleCompare(value,"2") == 0)
11038 mng_info->write_png_compression_level = 3;
11039
11040 else if (LocaleCompare(value,"3") == 0)
11041 mng_info->write_png_compression_level = 4;
11042
11043 else if (LocaleCompare(value,"4") == 0)
11044 mng_info->write_png_compression_level = 5;
11045
11046 else if (LocaleCompare(value,"5") == 0)
11047 mng_info->write_png_compression_level = 6;
11048
11049 else if (LocaleCompare(value,"6") == 0)
11050 mng_info->write_png_compression_level = 7;
11051
11052 else if (LocaleCompare(value,"7") == 0)
11053 mng_info->write_png_compression_level = 8;
11054
11055 else if (LocaleCompare(value,"8") == 0)
11056 mng_info->write_png_compression_level = 9;
11057
11058 else if (LocaleCompare(value,"9") == 0)
11059 mng_info->write_png_compression_level = 10;
11060
11061 else
11062 (void) ThrowMagickException(&image->exception,
11063 GetMagickModule(),CoderWarning,
11064 "ignoring invalid defined png:compression-level",
11065 "=%s",value);
11066 }
11067
11068 value=GetImageArtifact(image,"png:compression-strategy");
11069 if (value == NULL)
11070 value=GetImageOption(image_info,"png:compression-strategy");
11071 if (value != NULL)
11072 {
11073
11074 if (LocaleCompare(value,"0") == 0)
11075 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11076
11077 else if (LocaleCompare(value,"1") == 0)
11078 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11079
11080 else if (LocaleCompare(value,"2") == 0)
11081 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11082
11083 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011084#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011085 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011086#else
11087 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11088#endif
glennrp18682582011-06-30 18:11:47 +000011089
11090 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011091#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011092 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011093#else
11094 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11095#endif
glennrp18682582011-06-30 18:11:47 +000011096
11097 else
11098 (void) ThrowMagickException(&image->exception,
11099 GetMagickModule(),CoderWarning,
11100 "ignoring invalid defined png:compression-strategy",
11101 "=%s",value);
11102 }
11103
11104 value=GetImageArtifact(image,"png:compression-filter");
11105 if (value == NULL)
11106 value=GetImageOption(image_info,"png:compression-filter");
11107 if (value != NULL)
11108 {
11109
11110 /* To do: combinations of filters allowed by libpng
11111 * masks 0x08 through 0xf8
11112 *
11113 * Implement this as a comma-separated list of 0,1,2,3,4,5
11114 * where 5 is a special case meaning PNG_ALL_FILTERS.
11115 */
11116
11117 if (LocaleCompare(value,"0") == 0)
11118 mng_info->write_png_compression_filter = 1;
11119
11120 if (LocaleCompare(value,"1") == 0)
11121 mng_info->write_png_compression_filter = 2;
11122
11123 else if (LocaleCompare(value,"2") == 0)
11124 mng_info->write_png_compression_filter = 3;
11125
11126 else if (LocaleCompare(value,"3") == 0)
11127 mng_info->write_png_compression_filter = 4;
11128
11129 else if (LocaleCompare(value,"4") == 0)
11130 mng_info->write_png_compression_filter = 5;
11131
11132 else if (LocaleCompare(value,"5") == 0)
11133 mng_info->write_png_compression_filter = 6;
11134
glennrp54dc0692011-07-01 19:02:13 +000011135 else if (LocaleCompare(value,"6") == 0)
11136 mng_info->write_png_compression_filter = 7;
11137
11138 else if (LocaleCompare(value,"7") == 0)
11139 mng_info->write_png_compression_filter = 8;
11140
11141 else if (LocaleCompare(value,"8") == 0)
11142 mng_info->write_png_compression_filter = 9;
11143
11144 else if (LocaleCompare(value,"9") == 0)
11145 mng_info->write_png_compression_filter = 10;
glennrp18682582011-06-30 18:11:47 +000011146
11147 else
11148 (void) ThrowMagickException(&image->exception,
11149 GetMagickModule(),CoderWarning,
11150 "ignoring invalid defined png:compression-filter",
11151 "=%s",value);
11152 }
11153
glennrp03812ae2010-12-24 01:31:34 +000011154 excluding=MagickFalse;
11155
glennrp5c7cf4e2010-12-24 00:30:00 +000011156 for (source=0; source<1; source++)
11157 {
11158 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011159 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011160 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011161
11162 if (value == NULL)
11163 value=GetImageArtifact(image,"png:exclude-chunks");
11164 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011165 else
glennrpacba0042010-12-24 14:27:26 +000011166 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011167 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011168
glennrpacba0042010-12-24 14:27:26 +000011169 if (value == NULL)
11170 value=GetImageOption(image_info,"png:exclude-chunks");
11171 }
11172
glennrp03812ae2010-12-24 01:31:34 +000011173 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011174 {
glennrp03812ae2010-12-24 01:31:34 +000011175
11176 size_t
11177 last;
11178
11179 excluding=MagickTrue;
11180
11181 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011182 {
11183 if (source == 0)
11184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11185 " png:exclude-chunk=%s found in image artifacts.\n", value);
11186 else
11187 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11188 " png:exclude-chunk=%s found in image properties.\n", value);
11189 }
glennrp03812ae2010-12-24 01:31:34 +000011190
11191 last=strlen(value);
11192
11193 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011194 {
glennrp03812ae2010-12-24 01:31:34 +000011195
11196 if (LocaleNCompare(value+i,"all",3) == 0)
11197 {
11198 mng_info->ping_exclude_bKGD=MagickTrue;
11199 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011200 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011201 mng_info->ping_exclude_EXIF=MagickTrue;
11202 mng_info->ping_exclude_gAMA=MagickTrue;
11203 mng_info->ping_exclude_iCCP=MagickTrue;
11204 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11205 mng_info->ping_exclude_oFFs=MagickTrue;
11206 mng_info->ping_exclude_pHYs=MagickTrue;
11207 mng_info->ping_exclude_sRGB=MagickTrue;
11208 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011209 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011210 mng_info->ping_exclude_vpAg=MagickTrue;
11211 mng_info->ping_exclude_zCCP=MagickTrue;
11212 mng_info->ping_exclude_zTXt=MagickTrue;
11213 i--;
11214 }
glennrp2cc891a2010-12-24 13:44:32 +000011215
glennrp03812ae2010-12-24 01:31:34 +000011216 if (LocaleNCompare(value+i,"none",4) == 0)
11217 {
11218 mng_info->ping_exclude_bKGD=MagickFalse;
11219 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011220 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011221 mng_info->ping_exclude_EXIF=MagickFalse;
11222 mng_info->ping_exclude_gAMA=MagickFalse;
11223 mng_info->ping_exclude_iCCP=MagickFalse;
11224 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11225 mng_info->ping_exclude_oFFs=MagickFalse;
11226 mng_info->ping_exclude_pHYs=MagickFalse;
11227 mng_info->ping_exclude_sRGB=MagickFalse;
11228 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011229 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011230 mng_info->ping_exclude_vpAg=MagickFalse;
11231 mng_info->ping_exclude_zCCP=MagickFalse;
11232 mng_info->ping_exclude_zTXt=MagickFalse;
11233 }
glennrp2cc891a2010-12-24 13:44:32 +000011234
glennrp03812ae2010-12-24 01:31:34 +000011235 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11236 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011237
glennrp03812ae2010-12-24 01:31:34 +000011238 if (LocaleNCompare(value+i,"chrm",4) == 0)
11239 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011240
glennrpa0ed0092011-04-18 16:36:29 +000011241 if (LocaleNCompare(value+i,"date",4) == 0)
11242 mng_info->ping_exclude_date=MagickTrue;
11243
glennrp03812ae2010-12-24 01:31:34 +000011244 if (LocaleNCompare(value+i,"exif",4) == 0)
11245 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011246
glennrp03812ae2010-12-24 01:31:34 +000011247 if (LocaleNCompare(value+i,"gama",4) == 0)
11248 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011249
glennrp03812ae2010-12-24 01:31:34 +000011250 if (LocaleNCompare(value+i,"iccp",4) == 0)
11251 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011252
glennrp03812ae2010-12-24 01:31:34 +000011253 /*
11254 if (LocaleNCompare(value+i,"itxt",4) == 0)
11255 mng_info->ping_exclude_iTXt=MagickTrue;
11256 */
glennrp2cc891a2010-12-24 13:44:32 +000011257
glennrp03812ae2010-12-24 01:31:34 +000011258 if (LocaleNCompare(value+i,"gama",4) == 0)
11259 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011260
glennrp03812ae2010-12-24 01:31:34 +000011261 if (LocaleNCompare(value+i,"offs",4) == 0)
11262 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011263
glennrp03812ae2010-12-24 01:31:34 +000011264 if (LocaleNCompare(value+i,"phys",4) == 0)
11265 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011266
glennrpa1e3b7b2010-12-24 16:37:33 +000011267 if (LocaleNCompare(value+i,"srgb",4) == 0)
11268 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011269
glennrp03812ae2010-12-24 01:31:34 +000011270 if (LocaleNCompare(value+i,"text",4) == 0)
11271 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011272
glennrpa1e3b7b2010-12-24 16:37:33 +000011273 if (LocaleNCompare(value+i,"trns",4) == 0)
11274 mng_info->ping_exclude_tRNS=MagickTrue;
11275
glennrp03812ae2010-12-24 01:31:34 +000011276 if (LocaleNCompare(value+i,"vpag",4) == 0)
11277 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011278
glennrp03812ae2010-12-24 01:31:34 +000011279 if (LocaleNCompare(value+i,"zccp",4) == 0)
11280 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011281
glennrp03812ae2010-12-24 01:31:34 +000011282 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11283 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011284
glennrp03812ae2010-12-24 01:31:34 +000011285 }
glennrpce91ed52010-12-23 22:37:49 +000011286 }
glennrp26f37912010-12-23 16:22:42 +000011287 }
11288
glennrp5c7cf4e2010-12-24 00:30:00 +000011289 for (source=0; source<1; source++)
11290 {
11291 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011292 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011293 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011294
11295 if (value == NULL)
11296 value=GetImageArtifact(image,"png:include-chunks");
11297 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011298 else
glennrpacba0042010-12-24 14:27:26 +000011299 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011300 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011301
glennrpacba0042010-12-24 14:27:26 +000011302 if (value == NULL)
11303 value=GetImageOption(image_info,"png:include-chunks");
11304 }
11305
glennrp03812ae2010-12-24 01:31:34 +000011306 if (value != NULL)
11307 {
11308 size_t
11309 last;
glennrp26f37912010-12-23 16:22:42 +000011310
glennrp03812ae2010-12-24 01:31:34 +000011311 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011312
glennrp03812ae2010-12-24 01:31:34 +000011313 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011314 {
11315 if (source == 0)
11316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11317 " png:include-chunk=%s found in image artifacts.\n", value);
11318 else
11319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11320 " png:include-chunk=%s found in image properties.\n", value);
11321 }
glennrp03812ae2010-12-24 01:31:34 +000011322
11323 last=strlen(value);
11324
11325 for (i=0; i<(int) last; i+=5)
11326 {
11327 if (LocaleNCompare(value+i,"all",3) == 0)
11328 {
11329 mng_info->ping_exclude_bKGD=MagickFalse;
11330 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011331 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011332 mng_info->ping_exclude_EXIF=MagickFalse;
11333 mng_info->ping_exclude_gAMA=MagickFalse;
11334 mng_info->ping_exclude_iCCP=MagickFalse;
11335 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11336 mng_info->ping_exclude_oFFs=MagickFalse;
11337 mng_info->ping_exclude_pHYs=MagickFalse;
11338 mng_info->ping_exclude_sRGB=MagickFalse;
11339 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011340 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011341 mng_info->ping_exclude_vpAg=MagickFalse;
11342 mng_info->ping_exclude_zCCP=MagickFalse;
11343 mng_info->ping_exclude_zTXt=MagickFalse;
11344 i--;
11345 }
glennrp2cc891a2010-12-24 13:44:32 +000011346
glennrp03812ae2010-12-24 01:31:34 +000011347 if (LocaleNCompare(value+i,"none",4) == 0)
11348 {
11349 mng_info->ping_exclude_bKGD=MagickTrue;
11350 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011351 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011352 mng_info->ping_exclude_EXIF=MagickTrue;
11353 mng_info->ping_exclude_gAMA=MagickTrue;
11354 mng_info->ping_exclude_iCCP=MagickTrue;
11355 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11356 mng_info->ping_exclude_oFFs=MagickTrue;
11357 mng_info->ping_exclude_pHYs=MagickTrue;
11358 mng_info->ping_exclude_sRGB=MagickTrue;
11359 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011360 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011361 mng_info->ping_exclude_vpAg=MagickTrue;
11362 mng_info->ping_exclude_zCCP=MagickTrue;
11363 mng_info->ping_exclude_zTXt=MagickTrue;
11364 }
glennrp2cc891a2010-12-24 13:44:32 +000011365
glennrp03812ae2010-12-24 01:31:34 +000011366 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11367 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011368
glennrp03812ae2010-12-24 01:31:34 +000011369 if (LocaleNCompare(value+i,"chrm",4) == 0)
11370 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011371
glennrpa0ed0092011-04-18 16:36:29 +000011372 if (LocaleNCompare(value+i,"date",4) == 0)
11373 mng_info->ping_exclude_date=MagickFalse;
11374
glennrp03812ae2010-12-24 01:31:34 +000011375 if (LocaleNCompare(value+i,"exif",4) == 0)
11376 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011377
glennrp03812ae2010-12-24 01:31:34 +000011378 if (LocaleNCompare(value+i,"gama",4) == 0)
11379 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011380
glennrp03812ae2010-12-24 01:31:34 +000011381 if (LocaleNCompare(value+i,"iccp",4) == 0)
11382 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011383
glennrp03812ae2010-12-24 01:31:34 +000011384 /*
11385 if (LocaleNCompare(value+i,"itxt",4) == 0)
11386 mng_info->ping_exclude_iTXt=MagickFalse;
11387 */
glennrp2cc891a2010-12-24 13:44:32 +000011388
glennrp03812ae2010-12-24 01:31:34 +000011389 if (LocaleNCompare(value+i,"gama",4) == 0)
11390 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011391
glennrp03812ae2010-12-24 01:31:34 +000011392 if (LocaleNCompare(value+i,"offs",4) == 0)
11393 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011394
glennrp03812ae2010-12-24 01:31:34 +000011395 if (LocaleNCompare(value+i,"phys",4) == 0)
11396 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011397
glennrpa1e3b7b2010-12-24 16:37:33 +000011398 if (LocaleNCompare(value+i,"srgb",4) == 0)
11399 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011400
glennrp03812ae2010-12-24 01:31:34 +000011401 if (LocaleNCompare(value+i,"text",4) == 0)
11402 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011403
glennrpa1e3b7b2010-12-24 16:37:33 +000011404 if (LocaleNCompare(value+i,"trns",4) == 0)
11405 mng_info->ping_exclude_tRNS=MagickFalse;
11406
glennrp03812ae2010-12-24 01:31:34 +000011407 if (LocaleNCompare(value+i,"vpag",4) == 0)
11408 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011409
glennrp03812ae2010-12-24 01:31:34 +000011410 if (LocaleNCompare(value+i,"zccp",4) == 0)
11411 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011412
glennrp03812ae2010-12-24 01:31:34 +000011413 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11414 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011415
glennrp03812ae2010-12-24 01:31:34 +000011416 }
glennrpce91ed52010-12-23 22:37:49 +000011417 }
glennrp26f37912010-12-23 16:22:42 +000011418 }
11419
glennrp03812ae2010-12-24 01:31:34 +000011420 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011421 {
11422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11423 " Chunks to be excluded from the output PNG:");
11424 if (mng_info->ping_exclude_bKGD != MagickFalse)
11425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11426 " bKGD");
11427 if (mng_info->ping_exclude_cHRM != MagickFalse)
11428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11429 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011430 if (mng_info->ping_exclude_date != MagickFalse)
11431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11432 " date");
glennrp26f37912010-12-23 16:22:42 +000011433 if (mng_info->ping_exclude_EXIF != MagickFalse)
11434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11435 " EXIF");
11436 if (mng_info->ping_exclude_gAMA != MagickFalse)
11437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11438 " gAMA");
11439 if (mng_info->ping_exclude_iCCP != MagickFalse)
11440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11441 " iCCP");
11442/*
11443 if (mng_info->ping_exclude_iTXt != MagickFalse)
11444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11445 " iTXt");
11446*/
11447 if (mng_info->ping_exclude_oFFs != MagickFalse)
11448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11449 " oFFs");
11450 if (mng_info->ping_exclude_pHYs != MagickFalse)
11451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11452 " pHYs");
11453 if (mng_info->ping_exclude_sRGB != MagickFalse)
11454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11455 " sRGB");
11456 if (mng_info->ping_exclude_tEXt != MagickFalse)
11457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11458 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011459 if (mng_info->ping_exclude_tRNS != MagickFalse)
11460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11461 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011462 if (mng_info->ping_exclude_vpAg != MagickFalse)
11463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11464 " vpAg");
11465 if (mng_info->ping_exclude_zCCP != MagickFalse)
11466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11467 " zCCP");
11468 if (mng_info->ping_exclude_zTXt != MagickFalse)
11469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11470 " zTXt");
11471 }
11472
glennrpb9cfe272010-12-21 15:08:06 +000011473 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011474
glennrpb9cfe272010-12-21 15:08:06 +000011475 status=WriteOnePNGImage(mng_info,image_info,image);
cristy3ed852e2009-09-05 21:47:34 +000011476
11477 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011478
cristy3ed852e2009-09-05 21:47:34 +000011479 if (logging != MagickFalse)
11480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011481
cristy3ed852e2009-09-05 21:47:34 +000011482 return(status);
11483}
11484
11485#if defined(JNG_SUPPORTED)
11486
11487/* Write one JNG image */
11488static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11489 const ImageInfo *image_info,Image *image)
11490{
11491 Image
11492 *jpeg_image;
11493
11494 ImageInfo
11495 *jpeg_image_info;
11496
11497 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011498 logging,
cristy3ed852e2009-09-05 21:47:34 +000011499 status;
11500
11501 size_t
11502 length;
11503
11504 unsigned char
11505 *blob,
11506 chunk[80],
11507 *p;
11508
11509 unsigned int
11510 jng_alpha_compression_method,
11511 jng_alpha_sample_depth,
11512 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011513 transparent;
11514
cristybb503372010-05-27 20:51:26 +000011515 size_t
cristy3ed852e2009-09-05 21:47:34 +000011516 jng_quality;
11517
11518 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011519 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011520
11521 blob=(unsigned char *) NULL;
11522 jpeg_image=(Image *) NULL;
11523 jpeg_image_info=(ImageInfo *) NULL;
11524
11525 status=MagickTrue;
11526 transparent=image_info->type==GrayscaleMatteType ||
11527 image_info->type==TrueColorMatteType;
11528 jng_color_type=10;
11529 jng_alpha_sample_depth=0;
11530 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11531 jng_alpha_compression_method=0;
11532
11533 if (image->matte != MagickFalse)
11534 {
11535 /* if any pixels are transparent */
11536 transparent=MagickTrue;
11537 if (image_info->compression==JPEGCompression)
11538 jng_alpha_compression_method=8;
11539 }
11540
11541 if (transparent)
11542 {
11543 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011544
cristy3ed852e2009-09-05 21:47:34 +000011545 /* Create JPEG blob, image, and image_info */
11546 if (logging != MagickFalse)
11547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +000011548 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011549
cristy3ed852e2009-09-05 21:47:34 +000011550 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011551
cristy3ed852e2009-09-05 21:47:34 +000011552 if (jpeg_image_info == (ImageInfo *) NULL)
11553 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011554
cristy3ed852e2009-09-05 21:47:34 +000011555 if (logging != MagickFalse)
11556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11557 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011558
cristy3ed852e2009-09-05 21:47:34 +000011559 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011560
cristy3ed852e2009-09-05 21:47:34 +000011561 if (jpeg_image == (Image *) NULL)
11562 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011563
cristy3ed852e2009-09-05 21:47:34 +000011564 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristy4c08aed2011-07-01 19:47:50 +000011565 status=SeparateImageChannel(jpeg_image,AlphaChannel);
cristy3ed852e2009-09-05 21:47:34 +000011566 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011567
cristy3ed852e2009-09-05 21:47:34 +000011568 if (jng_quality >= 1000)
11569 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000011570
cristy3ed852e2009-09-05 21:47:34 +000011571 else
11572 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000011573
cristy3ed852e2009-09-05 21:47:34 +000011574 jpeg_image_info->type=GrayscaleType;
11575 (void) SetImageType(jpeg_image,GrayscaleType);
11576 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011577 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011578 "%s",jpeg_image->filename);
11579 }
11580
11581 /* To do: check bit depth of PNG alpha channel */
11582
11583 /* Check if image is grayscale. */
11584 if (image_info->type != TrueColorMatteType && image_info->type !=
11585 TrueColorType && ImageIsGray(image))
11586 jng_color_type-=2;
11587
11588 if (transparent)
11589 {
11590 if (jng_alpha_compression_method==0)
11591 {
11592 const char
11593 *value;
11594
cristy4c08aed2011-07-01 19:47:50 +000011595 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011596 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11597 &image->exception);
11598 if (logging != MagickFalse)
11599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11600 " Creating PNG blob.");
11601 length=0;
11602
11603 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11604 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11605 jpeg_image_info->interlace=NoInterlace;
11606
11607 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11608 &image->exception);
11609
11610 /* Retrieve sample depth used */
11611 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
11612 if (value != (char *) NULL)
11613 jng_alpha_sample_depth= (unsigned int) value[0];
11614 }
11615 else
11616 {
cristy4c08aed2011-07-01 19:47:50 +000011617 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011618
11619 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11620 &image->exception);
11621
11622 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11623 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11624 jpeg_image_info->interlace=NoInterlace;
11625 if (logging != MagickFalse)
11626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11627 " Creating blob.");
11628 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000011629 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000011630 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011631
cristy3ed852e2009-09-05 21:47:34 +000011632 if (logging != MagickFalse)
11633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011634 " Successfully read jpeg_image into a blob, length=%.20g.",
11635 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011636
11637 }
11638 /* Destroy JPEG image and image_info */
11639 jpeg_image=DestroyImage(jpeg_image);
11640 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11641 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11642 }
11643
11644 /* Write JHDR chunk */
11645 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11646 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011647 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011648 PNGLong(chunk+4,(png_uint_32) image->columns);
11649 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011650 chunk[12]=jng_color_type;
11651 chunk[13]=8; /* sample depth */
11652 chunk[14]=8; /*jng_image_compression_method */
11653 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11654 chunk[16]=jng_alpha_sample_depth;
11655 chunk[17]=jng_alpha_compression_method;
11656 chunk[18]=0; /*jng_alpha_filter_method */
11657 chunk[19]=0; /*jng_alpha_interlace_method */
11658 (void) WriteBlob(image,20,chunk);
11659 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11660 if (logging != MagickFalse)
11661 {
11662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011663 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011664
cristy3ed852e2009-09-05 21:47:34 +000011665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011666 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011667
cristy3ed852e2009-09-05 21:47:34 +000011668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11669 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011670
cristy3ed852e2009-09-05 21:47:34 +000011671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11672 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011673
cristy3ed852e2009-09-05 21:47:34 +000011674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11675 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011676
cristy3ed852e2009-09-05 21:47:34 +000011677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11678 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011679
cristy3ed852e2009-09-05 21:47:34 +000011680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11681 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011682
cristy3ed852e2009-09-05 21:47:34 +000011683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11684 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011685
cristy3ed852e2009-09-05 21:47:34 +000011686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11687 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011688
cristy3ed852e2009-09-05 21:47:34 +000011689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11690 " JNG alpha interlace:%5d",0);
11691 }
11692
glennrp0fe50b42010-11-16 03:52:51 +000011693 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011694 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011695
11696 /*
11697 Write leading ancillary chunks
11698 */
11699
11700 if (transparent)
11701 {
11702 /*
11703 Write JNG bKGD chunk
11704 */
11705
11706 unsigned char
11707 blue,
11708 green,
11709 red;
11710
cristybb503372010-05-27 20:51:26 +000011711 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011712 num_bytes;
11713
11714 if (jng_color_type == 8 || jng_color_type == 12)
11715 num_bytes=6L;
11716 else
11717 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011718 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011719 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011720 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011721 red=ScaleQuantumToChar(image->background_color.red);
11722 green=ScaleQuantumToChar(image->background_color.green);
11723 blue=ScaleQuantumToChar(image->background_color.blue);
11724 *(chunk+4)=0;
11725 *(chunk+5)=red;
11726 *(chunk+6)=0;
11727 *(chunk+7)=green;
11728 *(chunk+8)=0;
11729 *(chunk+9)=blue;
11730 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11731 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11732 }
11733
11734 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11735 {
11736 /*
11737 Write JNG sRGB chunk
11738 */
11739 (void) WriteBlobMSBULong(image,1L);
11740 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011741 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011742
cristy3ed852e2009-09-05 21:47:34 +000011743 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011744 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011745 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011746 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011747
cristy3ed852e2009-09-05 21:47:34 +000011748 else
glennrpe610a072010-08-05 17:08:46 +000011749 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011750 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011751 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011752
cristy3ed852e2009-09-05 21:47:34 +000011753 (void) WriteBlob(image,5,chunk);
11754 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11755 }
11756 else
11757 {
11758 if (image->gamma != 0.0)
11759 {
11760 /*
11761 Write JNG gAMA chunk
11762 */
11763 (void) WriteBlobMSBULong(image,4L);
11764 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011765 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011766 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011767 (void) WriteBlob(image,8,chunk);
11768 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11769 }
glennrp0fe50b42010-11-16 03:52:51 +000011770
cristy3ed852e2009-09-05 21:47:34 +000011771 if ((mng_info->equal_chrms == MagickFalse) &&
11772 (image->chromaticity.red_primary.x != 0.0))
11773 {
11774 PrimaryInfo
11775 primary;
11776
11777 /*
11778 Write JNG cHRM chunk
11779 */
11780 (void) WriteBlobMSBULong(image,32L);
11781 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011782 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011783 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011784 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11785 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011786 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011787 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11788 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011789 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011790 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11791 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011792 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011793 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11794 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011795 (void) WriteBlob(image,36,chunk);
11796 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11797 }
11798 }
glennrp0fe50b42010-11-16 03:52:51 +000011799
cristy3ed852e2009-09-05 21:47:34 +000011800 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11801 {
11802 /*
11803 Write JNG pHYs chunk
11804 */
11805 (void) WriteBlobMSBULong(image,9L);
11806 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011807 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011808 if (image->units == PixelsPerInchResolution)
11809 {
cristy35ef8242010-06-03 16:24:13 +000011810 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011811 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011812
cristy35ef8242010-06-03 16:24:13 +000011813 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011814 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011815
cristy3ed852e2009-09-05 21:47:34 +000011816 chunk[12]=1;
11817 }
glennrp0fe50b42010-11-16 03:52:51 +000011818
cristy3ed852e2009-09-05 21:47:34 +000011819 else
11820 {
11821 if (image->units == PixelsPerCentimeterResolution)
11822 {
cristy35ef8242010-06-03 16:24:13 +000011823 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011824 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011825
cristy35ef8242010-06-03 16:24:13 +000011826 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011827 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011828
cristy3ed852e2009-09-05 21:47:34 +000011829 chunk[12]=1;
11830 }
glennrp0fe50b42010-11-16 03:52:51 +000011831
cristy3ed852e2009-09-05 21:47:34 +000011832 else
11833 {
cristy35ef8242010-06-03 16:24:13 +000011834 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11835 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011836 chunk[12]=0;
11837 }
11838 }
11839 (void) WriteBlob(image,13,chunk);
11840 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11841 }
11842
11843 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11844 {
11845 /*
11846 Write JNG oFFs chunk
11847 */
11848 (void) WriteBlobMSBULong(image,9L);
11849 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011850 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011851 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11852 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011853 chunk[12]=0;
11854 (void) WriteBlob(image,13,chunk);
11855 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11856 }
11857 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11858 {
11859 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11860 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011861 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011862 PNGLong(chunk+4,(png_uint_32) image->page.width);
11863 PNGLong(chunk+8,(png_uint_32) image->page.height);
11864 chunk[12]=0; /* unit = pixels */
11865 (void) WriteBlob(image,13,chunk);
11866 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11867 }
11868
11869
11870 if (transparent)
11871 {
11872 if (jng_alpha_compression_method==0)
11873 {
cristybb503372010-05-27 20:51:26 +000011874 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011875 i;
11876
cristybb503372010-05-27 20:51:26 +000011877 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011878 len;
11879
11880 /* Write IDAT chunk header */
11881 if (logging != MagickFalse)
11882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011883 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000011884 length);
cristy3ed852e2009-09-05 21:47:34 +000011885
11886 /* Copy IDAT chunks */
11887 len=0;
11888 p=blob+8;
cristybb503372010-05-27 20:51:26 +000011889 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000011890 {
11891 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11892 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000011893
cristy3ed852e2009-09-05 21:47:34 +000011894 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11895 {
11896 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000011897 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000011898 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000011899 (void) WriteBlob(image,(size_t) len+4,p);
11900 (void) WriteBlobMSBULong(image,
11901 crc32(0,p,(uInt) len+4));
11902 }
glennrp0fe50b42010-11-16 03:52:51 +000011903
cristy3ed852e2009-09-05 21:47:34 +000011904 else
11905 {
11906 if (logging != MagickFalse)
11907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011908 " Skipping %c%c%c%c chunk, length=%.20g.",
11909 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000011910 }
11911 p+=(8+len);
11912 }
11913 }
11914 else
11915 {
11916 /* Write JDAA chunk header */
11917 if (logging != MagickFalse)
11918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011919 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000011920 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011921 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000011922 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000011923 /* Write JDAT chunk(s) data */
11924 (void) WriteBlob(image,4,chunk);
11925 (void) WriteBlob(image,length,blob);
11926 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11927 (uInt) length));
11928 }
11929 blob=(unsigned char *) RelinquishMagickMemory(blob);
11930 }
11931
11932 /* Encode image as a JPEG blob */
11933 if (logging != MagickFalse)
11934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11935 " Creating jpeg_image_info.");
11936 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11937 if (jpeg_image_info == (ImageInfo *) NULL)
11938 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11939
11940 if (logging != MagickFalse)
11941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11942 " Creating jpeg_image.");
11943
11944 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11945 if (jpeg_image == (Image *) NULL)
11946 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11947 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11948
11949 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011950 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000011951 jpeg_image->filename);
11952
11953 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11954 &image->exception);
11955
11956 if (logging != MagickFalse)
11957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011958 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11959 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011960
11961 if (jng_color_type == 8 || jng_color_type == 12)
11962 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000011963
cristy3ed852e2009-09-05 21:47:34 +000011964 jpeg_image_info->quality=jng_quality % 1000;
11965 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11966 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000011967
cristy3ed852e2009-09-05 21:47:34 +000011968 if (logging != MagickFalse)
11969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11970 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000011971
cristy3ed852e2009-09-05 21:47:34 +000011972 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011973
cristy3ed852e2009-09-05 21:47:34 +000011974 if (logging != MagickFalse)
11975 {
11976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011977 " Successfully read jpeg_image into a blob, length=%.20g.",
11978 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011979
11980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011981 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000011982 }
glennrp0fe50b42010-11-16 03:52:51 +000011983
cristy3ed852e2009-09-05 21:47:34 +000011984 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000011985 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011986 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000011987 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000011988 (void) WriteBlob(image,4,chunk);
11989 (void) WriteBlob(image,length,blob);
11990 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11991
11992 jpeg_image=DestroyImage(jpeg_image);
11993 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11994 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11995 blob=(unsigned char *) RelinquishMagickMemory(blob);
11996
11997 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011998 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011999
12000 /* Write IEND chunk */
12001 (void) WriteBlobMSBULong(image,0L);
12002 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012003 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012004 (void) WriteBlob(image,4,chunk);
12005 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12006
12007 if (logging != MagickFalse)
12008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12009 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012010
cristy3ed852e2009-09-05 21:47:34 +000012011 return(status);
12012}
12013
12014
12015/*
12016%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12017% %
12018% %
12019% %
12020% W r i t e J N G I m a g e %
12021% %
12022% %
12023% %
12024%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12025%
12026% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12027%
12028% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12029%
12030% The format of the WriteJNGImage method is:
12031%
12032% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
12033%
12034% A description of each parameter follows:
12035%
12036% o image_info: the image info.
12037%
12038% o image: The image.
12039%
12040%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12041*/
12042static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
12043{
12044 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012045 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012046 logging,
cristy3ed852e2009-09-05 21:47:34 +000012047 status;
12048
12049 MngInfo
12050 *mng_info;
12051
cristy3ed852e2009-09-05 21:47:34 +000012052 /*
12053 Open image file.
12054 */
12055 assert(image_info != (const ImageInfo *) NULL);
12056 assert(image_info->signature == MagickSignature);
12057 assert(image != (Image *) NULL);
12058 assert(image->signature == MagickSignature);
12059 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012060 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012061 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12062 if (status == MagickFalse)
12063 return(status);
12064
12065 /*
12066 Allocate a MngInfo structure.
12067 */
12068 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012069 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012070 if (mng_info == (MngInfo *) NULL)
12071 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12072 /*
12073 Initialize members of the MngInfo structure.
12074 */
12075 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12076 mng_info->image=image;
12077 have_mng_structure=MagickTrue;
12078
12079 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12080
12081 status=WriteOneJNGImage(mng_info,image_info,image);
12082 (void) CloseBlob(image);
12083
12084 (void) CatchImageException(image);
12085 MngInfoFreeStruct(mng_info,&have_mng_structure);
12086 if (logging != MagickFalse)
12087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12088 return(status);
12089}
12090#endif
12091
12092
12093
12094static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12095{
12096 const char
12097 *option;
12098
12099 Image
12100 *next_image;
12101
12102 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012103 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012104 status;
12105
glennrp03812ae2010-12-24 01:31:34 +000012106 volatile MagickBooleanType
12107 logging;
12108
cristy3ed852e2009-09-05 21:47:34 +000012109 MngInfo
12110 *mng_info;
12111
12112 int
cristy3ed852e2009-09-05 21:47:34 +000012113 image_count,
12114 need_iterations,
12115 need_matte;
12116
12117 volatile int
12118#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12119 defined(PNG_MNG_FEATURES_SUPPORTED)
12120 need_local_plte,
12121#endif
12122 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012123 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012124 use_global_plte;
12125
cristybb503372010-05-27 20:51:26 +000012126 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012127 i;
12128
12129 unsigned char
12130 chunk[800];
12131
12132 volatile unsigned int
12133 write_jng,
12134 write_mng;
12135
cristybb503372010-05-27 20:51:26 +000012136 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012137 scene;
12138
cristybb503372010-05-27 20:51:26 +000012139 size_t
cristy3ed852e2009-09-05 21:47:34 +000012140 final_delay=0,
12141 initial_delay;
12142
glennrpd5045b42010-03-24 12:40:35 +000012143#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012144 if (image_info->verbose)
12145 printf("Your PNG library (libpng-%s) is rather old.\n",
12146 PNG_LIBPNG_VER_STRING);
12147#endif
12148
12149 /*
12150 Open image file.
12151 */
12152 assert(image_info != (const ImageInfo *) NULL);
12153 assert(image_info->signature == MagickSignature);
12154 assert(image != (Image *) NULL);
12155 assert(image->signature == MagickSignature);
12156 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012157 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012158 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12159 if (status == MagickFalse)
12160 return(status);
12161
12162 /*
12163 Allocate a MngInfo structure.
12164 */
12165 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012166 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012167 if (mng_info == (MngInfo *) NULL)
12168 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12169 /*
12170 Initialize members of the MngInfo structure.
12171 */
12172 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12173 mng_info->image=image;
12174 have_mng_structure=MagickTrue;
12175 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12176
12177 /*
12178 * See if user has requested a specific PNG subformat to be used
12179 * for all of the PNGs in the MNG being written, e.g.,
12180 *
12181 * convert *.png png8:animation.mng
12182 *
12183 * To do: check -define png:bit_depth and png:color_type as well,
12184 * or perhaps use mng:bit_depth and mng:color_type instead for
12185 * global settings.
12186 */
12187
12188 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12189 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12190 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12191
12192 write_jng=MagickFalse;
12193 if (image_info->compression == JPEGCompression)
12194 write_jng=MagickTrue;
12195
12196 mng_info->adjoin=image_info->adjoin &&
12197 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12198
cristy3ed852e2009-09-05 21:47:34 +000012199 if (logging != MagickFalse)
12200 {
12201 /* Log some info about the input */
12202 Image
12203 *p;
12204
12205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12206 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012207
cristy3ed852e2009-09-05 21:47:34 +000012208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012209 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012210
cristy3ed852e2009-09-05 21:47:34 +000012211 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12212 " Type: %d",image_info->type);
12213
12214 scene=0;
12215 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12216 {
12217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012218 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012219
cristy3ed852e2009-09-05 21:47:34 +000012220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012221 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012222
cristy3ed852e2009-09-05 21:47:34 +000012223 if (p->matte)
12224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12225 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012226
cristy3ed852e2009-09-05 21:47:34 +000012227 else
12228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12229 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012230
cristy3ed852e2009-09-05 21:47:34 +000012231 if (p->storage_class == PseudoClass)
12232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12233 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012234
cristy3ed852e2009-09-05 21:47:34 +000012235 else
12236 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12237 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012238
cristy3ed852e2009-09-05 21:47:34 +000012239 if (p->colors)
12240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012241 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012242
cristy3ed852e2009-09-05 21:47:34 +000012243 else
12244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12245 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012246
cristy3ed852e2009-09-05 21:47:34 +000012247 if (mng_info->adjoin == MagickFalse)
12248 break;
12249 }
12250 }
12251
cristy3ed852e2009-09-05 21:47:34 +000012252 use_global_plte=MagickFalse;
12253 all_images_are_gray=MagickFalse;
12254#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12255 need_local_plte=MagickTrue;
12256#endif
12257 need_defi=MagickFalse;
12258 need_matte=MagickFalse;
12259 mng_info->framing_mode=1;
12260 mng_info->old_framing_mode=1;
12261
12262 if (write_mng)
12263 if (image_info->page != (char *) NULL)
12264 {
12265 /*
12266 Determine image bounding box.
12267 */
12268 SetGeometry(image,&mng_info->page);
12269 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12270 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12271 }
12272 if (write_mng)
12273 {
12274 unsigned int
12275 need_geom;
12276
12277 unsigned short
12278 red,
12279 green,
12280 blue;
12281
12282 mng_info->page=image->page;
12283 need_geom=MagickTrue;
12284 if (mng_info->page.width || mng_info->page.height)
12285 need_geom=MagickFalse;
12286 /*
12287 Check all the scenes.
12288 */
12289 initial_delay=image->delay;
12290 need_iterations=MagickFalse;
12291 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12292 mng_info->equal_physs=MagickTrue,
12293 mng_info->equal_gammas=MagickTrue;
12294 mng_info->equal_srgbs=MagickTrue;
12295 mng_info->equal_backgrounds=MagickTrue;
12296 image_count=0;
12297#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12298 defined(PNG_MNG_FEATURES_SUPPORTED)
12299 all_images_are_gray=MagickTrue;
12300 mng_info->equal_palettes=MagickFalse;
12301 need_local_plte=MagickFalse;
12302#endif
12303 for (next_image=image; next_image != (Image *) NULL; )
12304 {
12305 if (need_geom)
12306 {
12307 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12308 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012309
cristy3ed852e2009-09-05 21:47:34 +000012310 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12311 mng_info->page.height=next_image->rows+next_image->page.y;
12312 }
glennrp0fe50b42010-11-16 03:52:51 +000012313
cristy3ed852e2009-09-05 21:47:34 +000012314 if (next_image->page.x || next_image->page.y)
12315 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012316
cristy3ed852e2009-09-05 21:47:34 +000012317 if (next_image->matte)
12318 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012319
cristy3ed852e2009-09-05 21:47:34 +000012320 if ((int) next_image->dispose >= BackgroundDispose)
12321 if (next_image->matte || next_image->page.x || next_image->page.y ||
12322 ((next_image->columns < mng_info->page.width) &&
12323 (next_image->rows < mng_info->page.height)))
12324 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012325
cristy3ed852e2009-09-05 21:47:34 +000012326 if (next_image->iterations)
12327 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012328
cristy3ed852e2009-09-05 21:47:34 +000012329 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012330
cristy3ed852e2009-09-05 21:47:34 +000012331 if (final_delay != initial_delay || final_delay > 1UL*
12332 next_image->ticks_per_second)
12333 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012334
cristy3ed852e2009-09-05 21:47:34 +000012335#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12336 defined(PNG_MNG_FEATURES_SUPPORTED)
12337 /*
12338 check for global palette possibility.
12339 */
12340 if (image->matte != MagickFalse)
12341 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012342
cristy3ed852e2009-09-05 21:47:34 +000012343 if (need_local_plte == 0)
12344 {
12345 if (ImageIsGray(image) == MagickFalse)
12346 all_images_are_gray=MagickFalse;
12347 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12348 if (use_global_plte == 0)
12349 use_global_plte=mng_info->equal_palettes;
12350 need_local_plte=!mng_info->equal_palettes;
12351 }
12352#endif
12353 if (GetNextImageInList(next_image) != (Image *) NULL)
12354 {
12355 if (next_image->background_color.red !=
12356 next_image->next->background_color.red ||
12357 next_image->background_color.green !=
12358 next_image->next->background_color.green ||
12359 next_image->background_color.blue !=
12360 next_image->next->background_color.blue)
12361 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012362
cristy3ed852e2009-09-05 21:47:34 +000012363 if (next_image->gamma != next_image->next->gamma)
12364 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012365
cristy3ed852e2009-09-05 21:47:34 +000012366 if (next_image->rendering_intent !=
12367 next_image->next->rendering_intent)
12368 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012369
cristy3ed852e2009-09-05 21:47:34 +000012370 if ((next_image->units != next_image->next->units) ||
12371 (next_image->x_resolution != next_image->next->x_resolution) ||
12372 (next_image->y_resolution != next_image->next->y_resolution))
12373 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012374
cristy3ed852e2009-09-05 21:47:34 +000012375 if (mng_info->equal_chrms)
12376 {
12377 if (next_image->chromaticity.red_primary.x !=
12378 next_image->next->chromaticity.red_primary.x ||
12379 next_image->chromaticity.red_primary.y !=
12380 next_image->next->chromaticity.red_primary.y ||
12381 next_image->chromaticity.green_primary.x !=
12382 next_image->next->chromaticity.green_primary.x ||
12383 next_image->chromaticity.green_primary.y !=
12384 next_image->next->chromaticity.green_primary.y ||
12385 next_image->chromaticity.blue_primary.x !=
12386 next_image->next->chromaticity.blue_primary.x ||
12387 next_image->chromaticity.blue_primary.y !=
12388 next_image->next->chromaticity.blue_primary.y ||
12389 next_image->chromaticity.white_point.x !=
12390 next_image->next->chromaticity.white_point.x ||
12391 next_image->chromaticity.white_point.y !=
12392 next_image->next->chromaticity.white_point.y)
12393 mng_info->equal_chrms=MagickFalse;
12394 }
12395 }
12396 image_count++;
12397 next_image=GetNextImageInList(next_image);
12398 }
12399 if (image_count < 2)
12400 {
12401 mng_info->equal_backgrounds=MagickFalse;
12402 mng_info->equal_chrms=MagickFalse;
12403 mng_info->equal_gammas=MagickFalse;
12404 mng_info->equal_srgbs=MagickFalse;
12405 mng_info->equal_physs=MagickFalse;
12406 use_global_plte=MagickFalse;
12407#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12408 need_local_plte=MagickTrue;
12409#endif
12410 need_iterations=MagickFalse;
12411 }
glennrp0fe50b42010-11-16 03:52:51 +000012412
cristy3ed852e2009-09-05 21:47:34 +000012413 if (mng_info->need_fram == MagickFalse)
12414 {
12415 /*
12416 Only certain framing rates 100/n are exactly representable without
12417 the FRAM chunk but we'll allow some slop in VLC files
12418 */
12419 if (final_delay == 0)
12420 {
12421 if (need_iterations != MagickFalse)
12422 {
12423 /*
12424 It's probably a GIF with loop; don't run it *too* fast.
12425 */
glennrp02617122010-07-28 13:07:35 +000012426 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012427 {
12428 final_delay=10;
12429 (void) ThrowMagickException(&image->exception,
12430 GetMagickModule(),CoderWarning,
12431 "input has zero delay between all frames; assuming",
12432 " 10 cs `%s'","");
12433 }
cristy3ed852e2009-09-05 21:47:34 +000012434 }
12435 else
12436 mng_info->ticks_per_second=0;
12437 }
12438 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012439 mng_info->ticks_per_second=(png_uint_32)
12440 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012441 if (final_delay > 50)
12442 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012443
cristy3ed852e2009-09-05 21:47:34 +000012444 if (final_delay > 75)
12445 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012446
cristy3ed852e2009-09-05 21:47:34 +000012447 if (final_delay > 125)
12448 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012449
cristy3ed852e2009-09-05 21:47:34 +000012450 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12451 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12452 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12453 1UL*image->ticks_per_second))
12454 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12455 }
glennrp0fe50b42010-11-16 03:52:51 +000012456
cristy3ed852e2009-09-05 21:47:34 +000012457 if (mng_info->need_fram != MagickFalse)
12458 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12459 /*
12460 If pseudocolor, we should also check to see if all the
12461 palettes are identical and write a global PLTE if they are.
12462 ../glennrp Feb 99.
12463 */
12464 /*
12465 Write the MNG version 1.0 signature and MHDR chunk.
12466 */
12467 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12468 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12469 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012470 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012471 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12472 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012473 PNGLong(chunk+12,mng_info->ticks_per_second);
12474 PNGLong(chunk+16,0L); /* layer count=unknown */
12475 PNGLong(chunk+20,0L); /* frame count=unknown */
12476 PNGLong(chunk+24,0L); /* play time=unknown */
12477 if (write_jng)
12478 {
12479 if (need_matte)
12480 {
12481 if (need_defi || mng_info->need_fram || use_global_plte)
12482 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012483
cristy3ed852e2009-09-05 21:47:34 +000012484 else
12485 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12486 }
glennrp0fe50b42010-11-16 03:52:51 +000012487
cristy3ed852e2009-09-05 21:47:34 +000012488 else
12489 {
12490 if (need_defi || mng_info->need_fram || use_global_plte)
12491 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012492
cristy3ed852e2009-09-05 21:47:34 +000012493 else
12494 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12495 }
12496 }
glennrp0fe50b42010-11-16 03:52:51 +000012497
cristy3ed852e2009-09-05 21:47:34 +000012498 else
12499 {
12500 if (need_matte)
12501 {
12502 if (need_defi || mng_info->need_fram || use_global_plte)
12503 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012504
cristy3ed852e2009-09-05 21:47:34 +000012505 else
12506 PNGLong(chunk+28,9L); /* simplicity=VLC */
12507 }
glennrp0fe50b42010-11-16 03:52:51 +000012508
cristy3ed852e2009-09-05 21:47:34 +000012509 else
12510 {
12511 if (need_defi || mng_info->need_fram || use_global_plte)
12512 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012513
cristy3ed852e2009-09-05 21:47:34 +000012514 else
12515 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12516 }
12517 }
12518 (void) WriteBlob(image,32,chunk);
12519 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12520 option=GetImageOption(image_info,"mng:need-cacheoff");
12521 if (option != (const char *) NULL)
12522 {
12523 size_t
12524 length;
12525
12526 /*
12527 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12528 */
12529 PNGType(chunk,mng_nEED);
12530 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012531 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012532 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012533 length+=4;
12534 (void) WriteBlob(image,length,chunk);
12535 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12536 }
12537 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12538 (GetNextImageInList(image) != (Image *) NULL) &&
12539 (image->iterations != 1))
12540 {
12541 /*
12542 Write MNG TERM chunk
12543 */
12544 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12545 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012546 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012547 chunk[4]=3; /* repeat animation */
12548 chunk[5]=0; /* show last frame when done */
12549 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12550 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012551
cristy3ed852e2009-09-05 21:47:34 +000012552 if (image->iterations == 0)
12553 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012554
cristy3ed852e2009-09-05 21:47:34 +000012555 else
12556 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012557
cristy3ed852e2009-09-05 21:47:34 +000012558 if (logging != MagickFalse)
12559 {
12560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012561 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12562 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012563
cristy3ed852e2009-09-05 21:47:34 +000012564 if (image->iterations == 0)
12565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012566 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012567
cristy3ed852e2009-09-05 21:47:34 +000012568 else
12569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012570 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012571 }
12572 (void) WriteBlob(image,14,chunk);
12573 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12574 }
12575 /*
12576 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12577 */
12578 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12579 mng_info->equal_srgbs)
12580 {
12581 /*
12582 Write MNG sRGB chunk
12583 */
12584 (void) WriteBlobMSBULong(image,1L);
12585 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012586 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012587
cristy3ed852e2009-09-05 21:47:34 +000012588 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012589 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012590 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012591 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012592
cristy3ed852e2009-09-05 21:47:34 +000012593 else
glennrpe610a072010-08-05 17:08:46 +000012594 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012595 Magick_RenderingIntent_to_PNG_RenderingIntent(
12596 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012597
cristy3ed852e2009-09-05 21:47:34 +000012598 (void) WriteBlob(image,5,chunk);
12599 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12600 mng_info->have_write_global_srgb=MagickTrue;
12601 }
glennrp0fe50b42010-11-16 03:52:51 +000012602
cristy3ed852e2009-09-05 21:47:34 +000012603 else
12604 {
12605 if (image->gamma && mng_info->equal_gammas)
12606 {
12607 /*
12608 Write MNG gAMA chunk
12609 */
12610 (void) WriteBlobMSBULong(image,4L);
12611 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012612 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012613 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012614 (void) WriteBlob(image,8,chunk);
12615 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12616 mng_info->have_write_global_gama=MagickTrue;
12617 }
12618 if (mng_info->equal_chrms)
12619 {
12620 PrimaryInfo
12621 primary;
12622
12623 /*
12624 Write MNG cHRM chunk
12625 */
12626 (void) WriteBlobMSBULong(image,32L);
12627 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012628 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012629 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012630 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12631 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012632 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012633 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12634 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012635 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012636 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12637 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012638 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012639 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12640 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012641 (void) WriteBlob(image,36,chunk);
12642 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12643 mng_info->have_write_global_chrm=MagickTrue;
12644 }
12645 }
12646 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
12647 {
12648 /*
12649 Write MNG pHYs chunk
12650 */
12651 (void) WriteBlobMSBULong(image,9L);
12652 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012653 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012654
cristy3ed852e2009-09-05 21:47:34 +000012655 if (image->units == PixelsPerInchResolution)
12656 {
cristy35ef8242010-06-03 16:24:13 +000012657 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012658 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012659
cristy35ef8242010-06-03 16:24:13 +000012660 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012661 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012662
cristy3ed852e2009-09-05 21:47:34 +000012663 chunk[12]=1;
12664 }
glennrp0fe50b42010-11-16 03:52:51 +000012665
cristy3ed852e2009-09-05 21:47:34 +000012666 else
12667 {
12668 if (image->units == PixelsPerCentimeterResolution)
12669 {
cristy35ef8242010-06-03 16:24:13 +000012670 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012671 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012672
cristy35ef8242010-06-03 16:24:13 +000012673 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012674 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012675
cristy3ed852e2009-09-05 21:47:34 +000012676 chunk[12]=1;
12677 }
glennrp0fe50b42010-11-16 03:52:51 +000012678
cristy3ed852e2009-09-05 21:47:34 +000012679 else
12680 {
cristy35ef8242010-06-03 16:24:13 +000012681 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
12682 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012683 chunk[12]=0;
12684 }
12685 }
12686 (void) WriteBlob(image,13,chunk);
12687 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12688 }
12689 /*
12690 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12691 or does not cover the entire frame.
12692 */
12693 if (write_mng && (image->matte || image->page.x > 0 ||
12694 image->page.y > 0 || (image->page.width &&
12695 (image->page.width+image->page.x < mng_info->page.width))
12696 || (image->page.height && (image->page.height+image->page.y
12697 < mng_info->page.height))))
12698 {
12699 (void) WriteBlobMSBULong(image,6L);
12700 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012701 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012702 red=ScaleQuantumToShort(image->background_color.red);
12703 green=ScaleQuantumToShort(image->background_color.green);
12704 blue=ScaleQuantumToShort(image->background_color.blue);
12705 PNGShort(chunk+4,red);
12706 PNGShort(chunk+6,green);
12707 PNGShort(chunk+8,blue);
12708 (void) WriteBlob(image,10,chunk);
12709 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12710 if (mng_info->equal_backgrounds)
12711 {
12712 (void) WriteBlobMSBULong(image,6L);
12713 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012714 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012715 (void) WriteBlob(image,10,chunk);
12716 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12717 }
12718 }
12719
12720#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12721 if ((need_local_plte == MagickFalse) &&
12722 (image->storage_class == PseudoClass) &&
12723 (all_images_are_gray == MagickFalse))
12724 {
cristybb503372010-05-27 20:51:26 +000012725 size_t
cristy3ed852e2009-09-05 21:47:34 +000012726 data_length;
12727
12728 /*
12729 Write MNG PLTE chunk
12730 */
12731 data_length=3*image->colors;
12732 (void) WriteBlobMSBULong(image,data_length);
12733 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012734 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012735
cristybb503372010-05-27 20:51:26 +000012736 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012737 {
12738 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
12739 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
12740 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
12741 }
glennrp0fe50b42010-11-16 03:52:51 +000012742
cristy3ed852e2009-09-05 21:47:34 +000012743 (void) WriteBlob(image,data_length+4,chunk);
12744 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12745 mng_info->have_write_global_plte=MagickTrue;
12746 }
12747#endif
12748 }
12749 scene=0;
12750 mng_info->delay=0;
12751#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12752 defined(PNG_MNG_FEATURES_SUPPORTED)
12753 mng_info->equal_palettes=MagickFalse;
12754#endif
12755 do
12756 {
12757 if (mng_info->adjoin)
12758 {
12759#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12760 defined(PNG_MNG_FEATURES_SUPPORTED)
12761 /*
12762 If we aren't using a global palette for the entire MNG, check to
12763 see if we can use one for two or more consecutive images.
12764 */
12765 if (need_local_plte && use_global_plte && !all_images_are_gray)
12766 {
12767 if (mng_info->IsPalette)
12768 {
12769 /*
12770 When equal_palettes is true, this image has the same palette
12771 as the previous PseudoClass image
12772 */
12773 mng_info->have_write_global_plte=mng_info->equal_palettes;
12774 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12775 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12776 {
12777 /*
12778 Write MNG PLTE chunk
12779 */
cristybb503372010-05-27 20:51:26 +000012780 size_t
cristy3ed852e2009-09-05 21:47:34 +000012781 data_length;
12782
12783 data_length=3*image->colors;
12784 (void) WriteBlobMSBULong(image,data_length);
12785 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012786 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012787
cristybb503372010-05-27 20:51:26 +000012788 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012789 {
12790 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12791 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12792 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12793 }
glennrp0fe50b42010-11-16 03:52:51 +000012794
cristy3ed852e2009-09-05 21:47:34 +000012795 (void) WriteBlob(image,data_length+4,chunk);
12796 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12797 (uInt) (data_length+4)));
12798 mng_info->have_write_global_plte=MagickTrue;
12799 }
12800 }
12801 else
12802 mng_info->have_write_global_plte=MagickFalse;
12803 }
12804#endif
12805 if (need_defi)
12806 {
cristybb503372010-05-27 20:51:26 +000012807 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012808 previous_x,
12809 previous_y;
12810
12811 if (scene)
12812 {
12813 previous_x=mng_info->page.x;
12814 previous_y=mng_info->page.y;
12815 }
12816 else
12817 {
12818 previous_x=0;
12819 previous_y=0;
12820 }
12821 mng_info->page=image->page;
12822 if ((mng_info->page.x != previous_x) ||
12823 (mng_info->page.y != previous_y))
12824 {
12825 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12826 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012827 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012828 chunk[4]=0; /* object 0 MSB */
12829 chunk[5]=0; /* object 0 LSB */
12830 chunk[6]=0; /* visible */
12831 chunk[7]=0; /* abstract */
12832 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12833 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12834 (void) WriteBlob(image,16,chunk);
12835 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12836 }
12837 }
12838 }
12839
12840 mng_info->write_mng=write_mng;
12841
12842 if ((int) image->dispose >= 3)
12843 mng_info->framing_mode=3;
12844
12845 if (mng_info->need_fram && mng_info->adjoin &&
12846 ((image->delay != mng_info->delay) ||
12847 (mng_info->framing_mode != mng_info->old_framing_mode)))
12848 {
12849 if (image->delay == mng_info->delay)
12850 {
12851 /*
12852 Write a MNG FRAM chunk with the new framing mode.
12853 */
12854 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12855 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012856 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012857 chunk[4]=(unsigned char) mng_info->framing_mode;
12858 (void) WriteBlob(image,5,chunk);
12859 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12860 }
12861 else
12862 {
12863 /*
12864 Write a MNG FRAM chunk with the delay.
12865 */
12866 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12867 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012868 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012869 chunk[4]=(unsigned char) mng_info->framing_mode;
12870 chunk[5]=0; /* frame name separator (no name) */
12871 chunk[6]=2; /* flag for changing default delay */
12872 chunk[7]=0; /* flag for changing frame timeout */
12873 chunk[8]=0; /* flag for changing frame clipping */
12874 chunk[9]=0; /* flag for changing frame sync_id */
12875 PNGLong(chunk+10,(png_uint_32)
12876 ((mng_info->ticks_per_second*
12877 image->delay)/MagickMax(image->ticks_per_second,1)));
12878 (void) WriteBlob(image,14,chunk);
12879 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000012880 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000012881 }
12882 mng_info->old_framing_mode=mng_info->framing_mode;
12883 }
12884
12885#if defined(JNG_SUPPORTED)
12886 if (image_info->compression == JPEGCompression)
12887 {
12888 ImageInfo
12889 *write_info;
12890
12891 if (logging != MagickFalse)
12892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12893 " Writing JNG object.");
12894 /* To do: specify the desired alpha compression method. */
12895 write_info=CloneImageInfo(image_info);
12896 write_info->compression=UndefinedCompression;
12897 status=WriteOneJNGImage(mng_info,write_info,image);
12898 write_info=DestroyImageInfo(write_info);
12899 }
12900 else
12901#endif
12902 {
12903 if (logging != MagickFalse)
12904 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12905 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000012906
glennrpb9cfe272010-12-21 15:08:06 +000012907 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000012908 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000012909
12910 /* We don't want any ancillary chunks written */
12911 mng_info->ping_exclude_bKGD=MagickTrue;
12912 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000012913 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012914 mng_info->ping_exclude_EXIF=MagickTrue;
12915 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012916 mng_info->ping_exclude_iCCP=MagickTrue;
12917 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12918 mng_info->ping_exclude_oFFs=MagickTrue;
12919 mng_info->ping_exclude_pHYs=MagickTrue;
12920 mng_info->ping_exclude_sRGB=MagickTrue;
12921 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000012922 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012923 mng_info->ping_exclude_vpAg=MagickTrue;
12924 mng_info->ping_exclude_zCCP=MagickTrue;
12925 mng_info->ping_exclude_zTXt=MagickTrue;
12926
cristy3ed852e2009-09-05 21:47:34 +000012927 status=WriteOnePNGImage(mng_info,image_info,image);
12928 }
12929
12930 if (status == MagickFalse)
12931 {
12932 MngInfoFreeStruct(mng_info,&have_mng_structure);
12933 (void) CloseBlob(image);
12934 return(MagickFalse);
12935 }
12936 (void) CatchImageException(image);
12937 if (GetNextImageInList(image) == (Image *) NULL)
12938 break;
12939 image=SyncNextImageInList(image);
12940 status=SetImageProgress(image,SaveImagesTag,scene++,
12941 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000012942
cristy3ed852e2009-09-05 21:47:34 +000012943 if (status == MagickFalse)
12944 break;
glennrp0fe50b42010-11-16 03:52:51 +000012945
cristy3ed852e2009-09-05 21:47:34 +000012946 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000012947
cristy3ed852e2009-09-05 21:47:34 +000012948 if (write_mng)
12949 {
12950 while (GetPreviousImageInList(image) != (Image *) NULL)
12951 image=GetPreviousImageInList(image);
12952 /*
12953 Write the MEND chunk.
12954 */
12955 (void) WriteBlobMSBULong(image,0x00000000L);
12956 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000012957 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000012958 (void) WriteBlob(image,4,chunk);
12959 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12960 }
12961 /*
12962 Relinquish resources.
12963 */
12964 (void) CloseBlob(image);
12965 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012966
cristy3ed852e2009-09-05 21:47:34 +000012967 if (logging != MagickFalse)
12968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012969
cristy3ed852e2009-09-05 21:47:34 +000012970 return(MagickTrue);
12971}
glennrpd5045b42010-03-24 12:40:35 +000012972#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000012973
cristy3ed852e2009-09-05 21:47:34 +000012974static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12975{
12976 image=image;
12977 printf("Your PNG library is too old: You have libpng-%s\n",
12978 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000012979
cristy3ed852e2009-09-05 21:47:34 +000012980 ThrowBinaryException(CoderError,"PNG library is too old",
12981 image_info->filename);
12982}
glennrp39992b42010-11-14 00:03:43 +000012983
cristy3ed852e2009-09-05 21:47:34 +000012984static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12985{
12986 return(WritePNGImage(image_info,image));
12987}
glennrpd5045b42010-03-24 12:40:35 +000012988#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000012989#endif