blob: 20c186649e94647b1c1dbee8ce6215f520656e59 [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"
cristy510d06a2011-07-06 23:43:54 +000054#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000055#include "MagickCore/constitute.h"
56#include "MagickCore/enhance.h"
57#include "MagickCore/exception.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/geometry.h"
60#include "MagickCore/histogram.h"
61#include "MagickCore/image.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/layer.h"
64#include "MagickCore/list.h"
65#include "MagickCore/log.h"
66#include "MagickCore/MagickCore.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/module.h"
69#include "MagickCore/monitor.h"
70#include "MagickCore/monitor-private.h"
71#include "MagickCore/option.h"
72#include "MagickCore/pixel.h"
73#include "MagickCore/pixel-accessor.h"
74#include "MagickCore/profile.h"
75#include "MagickCore/property.h"
76#include "MagickCore/quantum-private.h"
77#include "MagickCore/resource_.h"
78#include "MagickCore/semaphore.h"
79#include "MagickCore/quantum-private.h"
80#include "MagickCore/static.h"
81#include "MagickCore/statistic.h"
82#include "MagickCore/string_.h"
83#include "MagickCore/string-private.h"
84#include "MagickCore/transform.h"
85#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000086#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp286a6352009-11-09 02:58:50 +000087
glennrp7ef138c2009-11-10 13:50:20 +000088/* Suppress libpng pedantic warnings that were added in
89 * libpng-1.2.41 and libpng-1.4.0. If you are working on
glennrpfaa852b2010-03-30 12:17:00 +000090 * migration to libpng-1.5, remove these defines and then
glennrp7ef138c2009-11-10 13:50:20 +000091 * fix any code that generates warnings.
92 */
glennrp991e92a2010-01-28 03:09:00 +000093/* #define PNG_DEPRECATED Use of this function is deprecated */
glennrpfaa852b2010-03-30 12:17:00 +000094/* #define PNG_USE_RESULT The result of this function must be checked */
95/* #define PNG_NORETURN This function does not return */
96/* #define PNG_ALLOCATED The result of the function is new memory */
glennrp8371ecc2011-02-19 19:15:38 +000097/* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
glennrp5b927342011-04-14 14:31:48 +000098
99/* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
cristy75cfe702011-04-14 14:27:17 +0000100#define PNG_PTR_NORETURN
glennrp286a6352009-11-09 02:58:50 +0000101
cristy3ed852e2009-09-05 21:47:34 +0000102#include "png.h"
103#include "zlib.h"
104
105/* ImageMagick differences */
106#define first_scene scene
107
glennrpd5045b42010-03-24 12:40:35 +0000108#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000109/*
110 Optional declarations. Define or undefine them as you like.
111*/
112/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
113
114/*
115 Features under construction. Define these to work on them.
116*/
117#undef MNG_OBJECT_BUFFERS
118#undef MNG_BASI_SUPPORTED
119#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
120#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
cristy3ed852e2009-09-05 21:47:34 +0000121#if defined(MAGICKCORE_JPEG_DELEGATE)
122# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
123#endif
124#if !defined(RGBColorMatchExact)
125#define IsPNGColorEqual(color,target) \
glennrp8e045c82011-04-27 16:40:27 +0000126 (((color).red == (target).red) && \
127 ((color).green == (target).green) && \
128 ((color).blue == (target).blue))
cristy3ed852e2009-09-05 21:47:34 +0000129#endif
130
glennrp8e58efd2011-05-20 12:16:29 +0000131/* Macros for left-bit-replication to ensure that pixels
132 * and PixelPackets all have the image->depth, and for use
133 * in PNG8 quantization.
134 */
135
136
137/* LBR01: Replicate top bit */
138
glennrp05001c32011-08-06 13:04:16 +0000139#define LBR01PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000140 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
141 0 : QuantumRange);
142
glennrp91d99252011-06-25 14:30:13 +0000143#define LBR01PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000144 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
145 0 : QuantumRange);
146
glennrp91d99252011-06-25 14:30:13 +0000147#define LBR01PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000148 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
149 0 : QuantumRange);
150
cristy4c08aed2011-07-01 19:47:50 +0000151#define LBR01PacketAlpha(pixelpacket) \
152 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000153 0 : QuantumRange);
154
glennrp91d99252011-06-25 14:30:13 +0000155#define LBR01PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000156 { \
glennrp05001c32011-08-06 13:04:16 +0000157 LBR01PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000158 LBR01PacketGreen((pixelpacket)); \
159 LBR01PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000160 }
glennrp8e58efd2011-05-20 12:16:29 +0000161
glennrp91d99252011-06-25 14:30:13 +0000162#define LBR01PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000163 { \
glennrp91d99252011-06-25 14:30:13 +0000164 LBR01PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000165 LBR01PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000166 }
glennrp8e58efd2011-05-20 12:16:29 +0000167
cristyef618312011-06-25 12:26:44 +0000168#define LBR01PixelRed(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000169 (ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000170 0 : QuantumRange);
171
glennrp54cf7972011-08-06 14:28:09 +0000172#define LBR01PixelGreen(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000173 (ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000174 0 : QuantumRange);
175
glennrp54cf7972011-08-06 14:28:09 +0000176#define LBR01PixelBlue(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000177 (ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000178 0 : QuantumRange);
179
glennrp54cf7972011-08-06 14:28:09 +0000180#define LBR01PixelAlpha(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000181 (ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000182 0 : QuantumRange);
183
glennrp54cf7972011-08-06 14:28:09 +0000184#define LBR01PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000185 { \
cristyef618312011-06-25 12:26:44 +0000186 LBR01PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000187 LBR01PixelGreen((pixel)); \
188 LBR01PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000189 }
glennrp8e58efd2011-05-20 12:16:29 +0000190
glennrp54cf7972011-08-06 14:28:09 +0000191#define LBR01PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000192 { \
glennrp54cf7972011-08-06 14:28:09 +0000193 LBR01PixelRGB((pixel)); \
194 LBR01PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000195 }
glennrp8e58efd2011-05-20 12:16:29 +0000196
197/* LBR02: Replicate top 2 bits */
198
glennrp05001c32011-08-06 13:04:16 +0000199#define LBR02PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000200 { \
201 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
202 (pixelpacket).red=ScaleCharToQuantum( \
203 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
204 }
glennrp91d99252011-06-25 14:30:13 +0000205#define LBR02PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000206 { \
207 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
208 (pixelpacket).green=ScaleCharToQuantum( \
209 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
210 }
glennrp91d99252011-06-25 14:30:13 +0000211#define LBR02PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000212 { \
213 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
214 (pixelpacket).blue=ScaleCharToQuantum( \
215 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
216 }
cristy4c08aed2011-07-01 19:47:50 +0000217#define LBR02PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000218 { \
cristy4c08aed2011-07-01 19:47:50 +0000219 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
220 (pixelpacket).alpha=ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000221 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
222 }
223
glennrp91d99252011-06-25 14:30:13 +0000224#define LBR02PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000225 { \
glennrp05001c32011-08-06 13:04:16 +0000226 LBR02PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000227 LBR02PacketGreen((pixelpacket)); \
228 LBR02PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000229 }
glennrp8e58efd2011-05-20 12:16:29 +0000230
glennrp91d99252011-06-25 14:30:13 +0000231#define LBR02PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000232 { \
glennrp91d99252011-06-25 14:30:13 +0000233 LBR02PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000234 LBR02PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000235 }
glennrp8e58efd2011-05-20 12:16:29 +0000236
cristyef618312011-06-25 12:26:44 +0000237#define LBR02PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000238 { \
cristy4c08aed2011-07-01 19:47:50 +0000239 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000240 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000241 SetPixelRed(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000242 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
243 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000244 }
glennrp54cf7972011-08-06 14:28:09 +0000245#define LBR02PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000246 { \
cristy4c08aed2011-07-01 19:47:50 +0000247 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000248 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000249 SetPixelGreen(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000250 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
251 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000252 }
glennrp54cf7972011-08-06 14:28:09 +0000253#define LBR02PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000254 { \
255 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000256 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
257 SetPixelBlue(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000258 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
259 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000260 }
glennrp54cf7972011-08-06 14:28:09 +0000261#define LBR02PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000262 { \
263 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000264 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
265 SetPixelAlpha(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000266 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
267 (pixel) ); \
glennrp8e58efd2011-05-20 12:16:29 +0000268 }
269
glennrp54cf7972011-08-06 14:28:09 +0000270#define LBR02PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000271 { \
cristyef618312011-06-25 12:26:44 +0000272 LBR02PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000273 LBR02PixelGreen((pixel)); \
274 LBR02PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000275 }
glennrp8e58efd2011-05-20 12:16:29 +0000276
glennrp54cf7972011-08-06 14:28:09 +0000277#define LBR02PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000278 { \
glennrp54cf7972011-08-06 14:28:09 +0000279 LBR02PixelRGB((pixel)); \
280 LBR02PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000281 }
glennrp8e58efd2011-05-20 12:16:29 +0000282
283/* LBR03: Replicate top 3 bits (only used with opaque pixels during
284 PNG8 quantization) */
285
glennrp05001c32011-08-06 13:04:16 +0000286#define LBR03PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000287 { \
288 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
289 (pixelpacket).red=ScaleCharToQuantum( \
290 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
291 }
glennrp91d99252011-06-25 14:30:13 +0000292#define LBR03PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000293 { \
294 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
295 (pixelpacket).green=ScaleCharToQuantum( \
296 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
297 }
glennrp91d99252011-06-25 14:30:13 +0000298#define LBR03PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000299 { \
300 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
301 (pixelpacket).blue=ScaleCharToQuantum( \
302 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
303 }
304
glennrp91d99252011-06-25 14:30:13 +0000305#define LBR03PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000306 { \
glennrp05001c32011-08-06 13:04:16 +0000307 LBR03PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000308 LBR03PacketGreen((pixelpacket)); \
309 LBR03PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000310 }
glennrp8e58efd2011-05-20 12:16:29 +0000311
cristyef618312011-06-25 12:26:44 +0000312#define LBR03PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000313 { \
cristy4c08aed2011-07-01 19:47:50 +0000314 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000315 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000316 SetPixelRed(image, ScaleCharToQuantum( \
317 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000318 }
cristy4c08aed2011-07-01 19:47:50 +0000319#define LBR03Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000320 { \
cristy4c08aed2011-07-01 19:47:50 +0000321 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000322 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000323 SetPixelGreen(image, ScaleCharToQuantum( \
324 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000325 }
cristy4c08aed2011-07-01 19:47:50 +0000326#define LBR03Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000327 { \
cristy4c08aed2011-07-01 19:47:50 +0000328 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000329 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000330 SetPixelBlue(image, ScaleCharToQuantum( \
331 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000332 }
333
cristy4c08aed2011-07-01 19:47:50 +0000334#define LBR03RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000335 { \
cristyef618312011-06-25 12:26:44 +0000336 LBR03PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000337 LBR03Green((pixel)); \
338 LBR03Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000339 }
glennrp8e58efd2011-05-20 12:16:29 +0000340
341/* LBR04: Replicate top 4 bits */
342
glennrp05001c32011-08-06 13:04:16 +0000343#define LBR04PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000344 { \
345 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
346 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
347 }
glennrp91d99252011-06-25 14:30:13 +0000348#define LBR04PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000349 { \
350 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
351 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
352 }
glennrp91d99252011-06-25 14:30:13 +0000353#define LBR04PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000354 { \
355 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
356 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
357 }
cristy4c08aed2011-07-01 19:47:50 +0000358#define LBR04PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000359 { \
cristy4c08aed2011-07-01 19:47:50 +0000360 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
361 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
glennrp8e58efd2011-05-20 12:16:29 +0000362 }
363
glennrp91d99252011-06-25 14:30:13 +0000364#define LBR04PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000365 { \
glennrp05001c32011-08-06 13:04:16 +0000366 LBR04PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000367 LBR04PacketGreen((pixelpacket)); \
368 LBR04PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000369 }
glennrp8e58efd2011-05-20 12:16:29 +0000370
glennrp91d99252011-06-25 14:30:13 +0000371#define LBR04PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000372 { \
glennrp91d99252011-06-25 14:30:13 +0000373 LBR04PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000374 LBR04PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000375 }
glennrp8e58efd2011-05-20 12:16:29 +0000376
cristyef618312011-06-25 12:26:44 +0000377#define LBR04PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000378 { \
cristy4c08aed2011-07-01 19:47:50 +0000379 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000380 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000381 SetPixelRed(image,\
382 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000383 }
glennrp54cf7972011-08-06 14:28:09 +0000384#define LBR04PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000385 { \
cristy4c08aed2011-07-01 19:47:50 +0000386 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000387 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000388 SetPixelGreen(image,\
389 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000390 }
glennrp54cf7972011-08-06 14:28:09 +0000391#define LBR04PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000392 { \
393 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000394 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
395 SetPixelBlue(image,\
396 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000397 }
glennrp54cf7972011-08-06 14:28:09 +0000398#define LBR04PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000399 { \
400 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000401 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
402 SetPixelAlpha(image,\
403 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000404 }
405
glennrp54cf7972011-08-06 14:28:09 +0000406#define LBR04PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000407 { \
cristyef618312011-06-25 12:26:44 +0000408 LBR04PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000409 LBR04PixelGreen((pixel)); \
410 LBR04PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000411 }
glennrp8e58efd2011-05-20 12:16:29 +0000412
glennrp54cf7972011-08-06 14:28:09 +0000413#define LBR04PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000414 { \
glennrp54cf7972011-08-06 14:28:09 +0000415 LBR04PixelRGB((pixel)); \
416 LBR04PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000417 }
glennrp8e58efd2011-05-20 12:16:29 +0000418
419
420/* LBR08: Replicate top 8 bits */
421
glennrp05001c32011-08-06 13:04:16 +0000422#define LBR08PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000423 { \
424 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
425 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
426 }
glennrp91d99252011-06-25 14:30:13 +0000427#define LBR08PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000428 { \
429 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
430 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
431 }
glennrp91d99252011-06-25 14:30:13 +0000432#define LBR08PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000433 { \
434 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
435 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
436 }
cristy4c08aed2011-07-01 19:47:50 +0000437#define LBR08PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000438 { \
cristy4c08aed2011-07-01 19:47:50 +0000439 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
440 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000441 }
442
glennrp91d99252011-06-25 14:30:13 +0000443#define LBR08PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000444 { \
glennrp05001c32011-08-06 13:04:16 +0000445 LBR08PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000446 LBR08PacketGreen((pixelpacket)); \
447 LBR08PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000448 }
glennrp8e58efd2011-05-20 12:16:29 +0000449
glennrp91d99252011-06-25 14:30:13 +0000450#define LBR08PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000451 { \
glennrp91d99252011-06-25 14:30:13 +0000452 LBR08PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000453 LBR08PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000454 }
glennrp8e58efd2011-05-20 12:16:29 +0000455
cristyef618312011-06-25 12:26:44 +0000456#define LBR08PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000457 { \
458 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000459 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
460 SetPixelRed(image,\
461 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000462 }
glennrp54cf7972011-08-06 14:28:09 +0000463#define LBR08PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000464 { \
465 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000466 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
467 SetPixelGreen(image,\
468 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000469 }
glennrp54cf7972011-08-06 14:28:09 +0000470#define LBR08PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000471 { \
472 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000473 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
474 SetPixelBlue(image,\
475 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000476 }
glennrp54cf7972011-08-06 14:28:09 +0000477#define LBR08PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000478 { \
479 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000480 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
481 SetPixelAlpha(image,\
482 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000483 }
484
glennrp54cf7972011-08-06 14:28:09 +0000485#define LBR08PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000486 { \
cristyef618312011-06-25 12:26:44 +0000487 LBR08PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000488 LBR08PixelGreen((pixel)); \
489 LBR08PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000490 }
glennrp8e58efd2011-05-20 12:16:29 +0000491
glennrp54cf7972011-08-06 14:28:09 +0000492#define LBR08PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000493 { \
glennrp54cf7972011-08-06 14:28:09 +0000494 LBR08PixelRGB((pixel)); \
495 LBR08PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000496 }
glennrp8e58efd2011-05-20 12:16:29 +0000497
498
499/* LBR16: Replicate top 16 bits */
500
glennrp05001c32011-08-06 13:04:16 +0000501#define LBR16PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000502 { \
503 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
504 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
505 }
glennrp91d99252011-06-25 14:30:13 +0000506#define LBR16PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000507 { \
508 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
509 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
510 }
glennrp91d99252011-06-25 14:30:13 +0000511#define LBR16PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000512 { \
513 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
514 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
515 }
cristy4c08aed2011-07-01 19:47:50 +0000516#define LBR16PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000517 { \
cristy4c08aed2011-07-01 19:47:50 +0000518 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
519 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000520 }
521
glennrp91d99252011-06-25 14:30:13 +0000522#define LBR16PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000523 { \
glennrp05001c32011-08-06 13:04:16 +0000524 LBR16PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000525 LBR16PacketGreen((pixelpacket)); \
526 LBR16PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000527 }
glennrp8e58efd2011-05-20 12:16:29 +0000528
glennrp91d99252011-06-25 14:30:13 +0000529#define LBR16PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000530 { \
glennrp91d99252011-06-25 14:30:13 +0000531 LBR16PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000532 LBR16PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000533 }
glennrp8e58efd2011-05-20 12:16:29 +0000534
cristyef618312011-06-25 12:26:44 +0000535#define LBR16PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000536 { \
537 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000538 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
539 SetPixelRed(image,\
540 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000541 }
glennrp54cf7972011-08-06 14:28:09 +0000542#define LBR16PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000543 { \
544 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000545 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
546 SetPixelGreen(image,\
547 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000548 }
glennrp54cf7972011-08-06 14:28:09 +0000549#define LBR16PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000550 { \
551 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000552 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
553 SetPixelBlue(image,\
554 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000555 }
glennrp54cf7972011-08-06 14:28:09 +0000556#define LBR16PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000557 { \
558 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000559 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
560 SetPixelAlpha(image,\
561 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000562 }
563
glennrp54cf7972011-08-06 14:28:09 +0000564#define LBR16PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000565 { \
cristyef618312011-06-25 12:26:44 +0000566 LBR16PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000567 LBR16PixelGreen((pixel)); \
568 LBR16PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000569 }
glennrp8e58efd2011-05-20 12:16:29 +0000570
glennrp54cf7972011-08-06 14:28:09 +0000571#define LBR16PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000572 { \
glennrp54cf7972011-08-06 14:28:09 +0000573 LBR16PixelRGB((pixel)); \
574 LBR16PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000575 }
glennrp8e58efd2011-05-20 12:16:29 +0000576
cristy3ed852e2009-09-05 21:47:34 +0000577/*
578 Establish thread safety.
579 setjmp/longjmp is claimed to be safe on these platforms:
580 setjmp/longjmp is alleged to be unsafe on these platforms:
581*/
582#ifndef SETJMP_IS_THREAD_SAFE
583#define PNG_SETJMP_NOT_THREAD_SAFE
584#endif
585
586#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
587static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000588 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000589#endif
590
591/*
592 This temporary until I set up malloc'ed object attributes array.
593 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
594 waste more memory.
595*/
596#define MNG_MAX_OBJECTS 256
597
598/*
599 If this not defined, spec is interpreted strictly. If it is
600 defined, an attempt will be made to recover from some errors,
601 including
602 o global PLTE too short
603*/
604#undef MNG_LOOSE
605
606/*
607 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
608 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
609 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
610 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
611 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
612 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
613 will be enabled by default in libpng-1.2.0.
614*/
cristy3ed852e2009-09-05 21:47:34 +0000615#ifdef PNG_MNG_FEATURES_SUPPORTED
616# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
617# define PNG_READ_EMPTY_PLTE_SUPPORTED
618# endif
619# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
620# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
621# endif
622#endif
623
624/*
cristybb503372010-05-27 20:51:26 +0000625 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000626 This macro is only defined in libpng-1.0.3 and later.
627 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
628*/
629#ifndef PNG_UINT_31_MAX
630#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
631#endif
632
633/*
634 Constant strings for known chunk types. If you need to add a chunk,
635 add a string holding the name here. To make the code more
636 portable, we use ASCII numbers like this, not characters.
637*/
638
639static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
640static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
641static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
642static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
643static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
644static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
645static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
646static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
647static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
648static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
649static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
650static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
651static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
652static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
653static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
654static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
655static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
656static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
657static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
658static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
659static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
660static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
661static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
662static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
663static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
664static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
665static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
666static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
667static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
668static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
669static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
670static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
671static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
672static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
673
674#if defined(JNG_SUPPORTED)
675static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
676static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
677static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
678static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
679static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
680static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
681#endif
682
683/*
684Other known chunks that are not yet supported by ImageMagick:
685static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
686static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
687static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
688static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
689static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
690static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
691static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
692static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
693*/
694
695typedef struct _MngBox
696{
cristy8182b072010-05-30 20:10:53 +0000697 long
cristy3ed852e2009-09-05 21:47:34 +0000698 left,
699 right,
700 top,
701 bottom;
702} MngBox;
703
704typedef struct _MngPair
705{
cristy8182b072010-05-30 20:10:53 +0000706 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000707 a,
708 b;
709} MngPair;
710
711#ifdef MNG_OBJECT_BUFFERS
712typedef struct _MngBuffer
713{
714
cristybb503372010-05-27 20:51:26 +0000715 size_t
cristy3ed852e2009-09-05 21:47:34 +0000716 height,
717 width;
718
719 Image
720 *image;
721
722 png_color
723 plte[256];
724
725 int
726 reference_count;
727
728 unsigned char
729 alpha_sample_depth,
730 compression_method,
731 color_type,
732 concrete,
733 filter_method,
734 frozen,
735 image_type,
736 interlace_method,
737 pixel_sample_depth,
738 plte_length,
739 sample_depth,
740 viewable;
741} MngBuffer;
742#endif
743
744typedef struct _MngInfo
745{
746
747#ifdef MNG_OBJECT_BUFFERS
748 MngBuffer
749 *ob[MNG_MAX_OBJECTS];
750#endif
751
752 Image *
753 image;
754
755 RectangleInfo
756 page;
757
758 int
759 adjoin,
760#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
761 bytes_in_read_buffer,
762 found_empty_plte,
763#endif
764 equal_backgrounds,
765 equal_chrms,
766 equal_gammas,
767#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
768 defined(PNG_MNG_FEATURES_SUPPORTED)
769 equal_palettes,
770#endif
771 equal_physs,
772 equal_srgbs,
773 framing_mode,
774 have_global_bkgd,
775 have_global_chrm,
776 have_global_gama,
777 have_global_phys,
778 have_global_sbit,
779 have_global_srgb,
780 have_saved_bkgd_index,
781 have_write_global_chrm,
782 have_write_global_gama,
783 have_write_global_plte,
784 have_write_global_srgb,
785 need_fram,
786 object_id,
787 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000788 saved_bkgd_index;
789
790 int
791 new_number_colors;
792
cristybb503372010-05-27 20:51:26 +0000793 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000794 image_found,
795 loop_count[256],
796 loop_iteration[256],
797 scenes_found,
798 x_off[MNG_MAX_OBJECTS],
799 y_off[MNG_MAX_OBJECTS];
800
801 MngBox
802 clip,
803 frame,
804 image_box,
805 object_clip[MNG_MAX_OBJECTS];
806
807 unsigned char
808 /* These flags could be combined into one byte */
809 exists[MNG_MAX_OBJECTS],
810 frozen[MNG_MAX_OBJECTS],
811 loop_active[256],
812 invisible[MNG_MAX_OBJECTS],
813 viewable[MNG_MAX_OBJECTS];
814
815 MagickOffsetType
816 loop_jump[256];
817
818 png_colorp
819 global_plte;
820
821 png_color_8
822 global_sbit;
823
824 png_byte
825#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
826 read_buffer[8],
827#endif
828 global_trns[256];
829
830 float
831 global_gamma;
832
833 ChromaticityInfo
834 global_chrm;
835
836 RenderingIntent
837 global_srgb_intent;
838
cristy35ef8242010-06-03 16:24:13 +0000839 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000840 delay,
841 global_plte_length,
842 global_trns_length,
843 global_x_pixels_per_unit,
844 global_y_pixels_per_unit,
845 mng_width,
846 mng_height,
847 ticks_per_second;
848
glennrpb9cfe272010-12-21 15:08:06 +0000849 MagickBooleanType
850 need_blob;
851
cristy3ed852e2009-09-05 21:47:34 +0000852 unsigned int
853 IsPalette,
854 global_phys_unit_type,
855 basi_warning,
856 clon_warning,
857 dhdr_warning,
858 jhdr_warning,
859 magn_warning,
860 past_warning,
861 phyg_warning,
862 phys_warning,
863 sbit_warning,
864 show_warning,
865 mng_type,
866 write_mng,
867 write_png_colortype,
868 write_png_depth,
glennrp18682582011-06-30 18:11:47 +0000869 write_png_compression_level,
870 write_png_compression_strategy,
871 write_png_compression_filter,
cristy3ed852e2009-09-05 21:47:34 +0000872 write_png8,
873 write_png24,
874 write_png32;
875
876#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000877 size_t
cristy3ed852e2009-09-05 21:47:34 +0000878 basi_width,
879 basi_height;
880
881 unsigned int
882 basi_depth,
883 basi_color_type,
884 basi_compression_method,
885 basi_filter_type,
886 basi_interlace_method,
887 basi_red,
888 basi_green,
889 basi_blue,
890 basi_alpha,
891 basi_viewable;
892#endif
893
894 png_uint_16
895 magn_first,
896 magn_last,
897 magn_mb,
898 magn_ml,
899 magn_mr,
900 magn_mt,
901 magn_mx,
902 magn_my,
903 magn_methx,
904 magn_methy;
905
906 PixelPacket
907 mng_global_bkgd;
908
glennrp26f37912010-12-23 16:22:42 +0000909 /* Added at version 6.6.6-7 */
910 MagickBooleanType
911 ping_exclude_bKGD,
912 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000913 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000914 ping_exclude_EXIF,
915 ping_exclude_gAMA,
916 ping_exclude_iCCP,
917 /* ping_exclude_iTXt, */
918 ping_exclude_oFFs,
919 ping_exclude_pHYs,
920 ping_exclude_sRGB,
921 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000922 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000923 ping_exclude_vpAg,
924 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000925 ping_exclude_zTXt,
926 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000927
cristy3ed852e2009-09-05 21:47:34 +0000928} MngInfo;
929#endif /* VER */
930
931/*
932 Forward declarations.
933*/
934static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000935 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000936
cristy3ed852e2009-09-05 21:47:34 +0000937static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000938 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000939
cristy3ed852e2009-09-05 21:47:34 +0000940#if defined(JNG_SUPPORTED)
941static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000942 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000943#endif
944
glennrp0c3e06b2010-11-19 13:45:02 +0000945#if PNG_LIBPNG_VER > 10011
946
glennrpfd05d622011-02-25 04:10:33 +0000947
glennrp0c3e06b2010-11-19 13:45:02 +0000948#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
949static MagickBooleanType
glennrpfd05d622011-02-25 04:10:33 +0000950LosslessReduceDepthOK(Image *image)
glennrp0c3e06b2010-11-19 13:45:02 +0000951{
glennrp67b9c1a2011-04-22 18:47:36 +0000952 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
953 *
954 * This is true if the high byte and the next highest byte of
955 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000956 * are equal to each other. We check this by seeing if the samples
957 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000958 *
959 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000960 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000961 */
962
glennrp3faa9a32011-04-23 14:00:25 +0000963#define QuantumToCharToQuantumEqQuantum(quantum) \
964 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
965
glennrp0c3e06b2010-11-19 13:45:02 +0000966 MagickBooleanType
967 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000968
glennrp03e11f62011-04-22 13:30:16 +0000969 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000970 {
971
cristy4c08aed2011-07-01 19:47:50 +0000972 const Quantum
glennrp0c3e06b2010-11-19 13:45:02 +0000973 *p;
974
975 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000976 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
977 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
978 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
979 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000980
981 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
982 {
983 int indx;
984
985 for (indx=0; indx < (ssize_t) image->colors; indx++)
986 {
glennrp3faa9a32011-04-23 14:00:25 +0000987 ok_to_reduce=(
988 QuantumToCharToQuantumEqQuantum(
989 image->colormap[indx].red) &&
990 QuantumToCharToQuantumEqQuantum(
991 image->colormap[indx].green) &&
992 QuantumToCharToQuantumEqQuantum(
993 image->colormap[indx].blue)) ?
994 MagickTrue : MagickFalse;
995
glennrp0c3e06b2010-11-19 13:45:02 +0000996 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000997 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000998 }
999 }
1000
1001 if ((ok_to_reduce != MagickFalse) &&
1002 (image->storage_class != PseudoClass))
1003 {
1004 ssize_t
1005 y;
1006
1007 register ssize_t
1008 x;
1009
1010 for (y=0; y < (ssize_t) image->rows; y++)
1011 {
1012 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1013
cristy4c08aed2011-07-01 19:47:50 +00001014 if (p == (const Quantum *) NULL)
glennrp0c3e06b2010-11-19 13:45:02 +00001015 {
1016 ok_to_reduce = MagickFalse;
1017 break;
1018 }
1019
1020 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1021 {
glennrp3faa9a32011-04-23 14:00:25 +00001022 ok_to_reduce=
cristy4c08aed2011-07-01 19:47:50 +00001023 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1024 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1025 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
glennrp3faa9a32011-04-23 14:00:25 +00001026 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001027
1028 if (ok_to_reduce == MagickFalse)
1029 break;
1030
cristyed231572011-07-14 02:18:59 +00001031 p+=GetPixelChannels(image);
glennrp0c3e06b2010-11-19 13:45:02 +00001032 }
glennrp8640fb52010-11-23 15:48:26 +00001033 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +00001034 break;
1035 }
1036 }
1037
1038 if (ok_to_reduce != MagickFalse)
1039 {
glennrp0c3e06b2010-11-19 13:45:02 +00001040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001041 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +00001042 }
glennrpa6a06632011-01-19 15:15:34 +00001043 else
1044 {
1045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001046 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +00001047 }
glennrp0c3e06b2010-11-19 13:45:02 +00001048 }
1049
1050 return ok_to_reduce;
1051}
1052#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1053
glennrpe610a072010-08-05 17:08:46 +00001054static int
glennrpcf002022011-01-30 02:38:15 +00001055Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +00001056{
glennrpe610a072010-08-05 17:08:46 +00001057 switch (intent)
1058 {
1059 case PerceptualIntent:
1060 return 0;
glennrp0fe50b42010-11-16 03:52:51 +00001061
glennrpe610a072010-08-05 17:08:46 +00001062 case RelativeIntent:
1063 return 1;
glennrp0fe50b42010-11-16 03:52:51 +00001064
glennrpe610a072010-08-05 17:08:46 +00001065 case SaturationIntent:
1066 return 2;
glennrp0fe50b42010-11-16 03:52:51 +00001067
glennrpe610a072010-08-05 17:08:46 +00001068 case AbsoluteIntent:
1069 return 3;
glennrp0fe50b42010-11-16 03:52:51 +00001070
glennrpe610a072010-08-05 17:08:46 +00001071 default:
1072 return -1;
1073 }
1074}
1075
1076static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +00001077Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +00001078{
glennrpcf002022011-01-30 02:38:15 +00001079 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001080 {
1081 case 0:
1082 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001083
glennrpe610a072010-08-05 17:08:46 +00001084 case 1:
1085 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001086
glennrpe610a072010-08-05 17:08:46 +00001087 case 2:
1088 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001089
glennrpe610a072010-08-05 17:08:46 +00001090 case 3:
1091 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001092
glennrpe610a072010-08-05 17:08:46 +00001093 default:
1094 return UndefinedIntent;
1095 }
1096}
1097
cristybb503372010-05-27 20:51:26 +00001098static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001099{
1100 if (x > y)
1101 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001102
cristy3ed852e2009-09-05 21:47:34 +00001103 return(y);
1104}
glennrp0c3e06b2010-11-19 13:45:02 +00001105
cristybb503372010-05-27 20:51:26 +00001106static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001107{
1108 if (x < y)
1109 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001110
cristy3ed852e2009-09-05 21:47:34 +00001111 return(y);
1112}
glennrp0c3e06b2010-11-19 13:45:02 +00001113
cristy3ed852e2009-09-05 21:47:34 +00001114
1115/*
1116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1117% %
1118% %
1119% %
1120% I m a g e I s G r a y %
1121% %
1122% %
1123% %
1124%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1125% %
cristy4c08aed2011-07-01 19:47:50 +00001126% Like IsImageGray except does not change DirectClass to PseudoClass %
cristy3ed852e2009-09-05 21:47:34 +00001127% %
1128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1129*/
1130static MagickBooleanType ImageIsGray(Image *image)
1131{
cristy4c08aed2011-07-01 19:47:50 +00001132 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001133 *p;
1134
cristybb503372010-05-27 20:51:26 +00001135 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001136 i,
1137 x,
1138 y;
1139
1140 assert(image != (Image *) NULL);
1141 assert(image->signature == MagickSignature);
1142 if (image->debug != MagickFalse)
1143 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1144
1145 if (image->storage_class == PseudoClass)
1146 {
cristybb503372010-05-27 20:51:26 +00001147 for (i=0; i < (ssize_t) image->colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00001148 if (IsPixelPacketGray(image->colormap+i) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001149 return(MagickFalse);
1150 return(MagickTrue);
1151 }
cristybb503372010-05-27 20:51:26 +00001152 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001153 {
1154 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00001155 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001156 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +00001157 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001158 {
cristy4c08aed2011-07-01 19:47:50 +00001159 if (IsPixelGray(image,p) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001160 return(MagickFalse);
cristyed231572011-07-14 02:18:59 +00001161 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001162 }
1163 }
1164 return(MagickTrue);
1165}
glennrpd5045b42010-03-24 12:40:35 +00001166#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001167#endif /* MAGICKCORE_PNG_DELEGATE */
1168
1169/*
1170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1171% %
1172% %
1173% %
1174% I s M N G %
1175% %
1176% %
1177% %
1178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1179%
1180% IsMNG() returns MagickTrue if the image format type, identified by the
1181% magick string, is MNG.
1182%
1183% The format of the IsMNG method is:
1184%
1185% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1186%
1187% A description of each parameter follows:
1188%
1189% o magick: compare image format pattern against these bytes.
1190%
1191% o length: Specifies the length of the magick string.
1192%
1193%
1194*/
1195static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1196{
1197 if (length < 8)
1198 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001199
cristy3ed852e2009-09-05 21:47:34 +00001200 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1201 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001202
cristy3ed852e2009-09-05 21:47:34 +00001203 return(MagickFalse);
1204}
1205
1206/*
1207%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1208% %
1209% %
1210% %
1211% I s J N G %
1212% %
1213% %
1214% %
1215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1216%
1217% IsJNG() returns MagickTrue if the image format type, identified by the
1218% magick string, is JNG.
1219%
1220% The format of the IsJNG method is:
1221%
1222% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1223%
1224% A description of each parameter follows:
1225%
1226% o magick: compare image format pattern against these bytes.
1227%
1228% o length: Specifies the length of the magick string.
1229%
1230%
1231*/
1232static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1233{
1234 if (length < 8)
1235 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001236
cristy3ed852e2009-09-05 21:47:34 +00001237 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1238 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001239
cristy3ed852e2009-09-05 21:47:34 +00001240 return(MagickFalse);
1241}
1242
1243/*
1244%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245% %
1246% %
1247% %
1248% I s P N G %
1249% %
1250% %
1251% %
1252%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1253%
1254% IsPNG() returns MagickTrue if the image format type, identified by the
1255% magick string, is PNG.
1256%
1257% The format of the IsPNG method is:
1258%
1259% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1260%
1261% A description of each parameter follows:
1262%
1263% o magick: compare image format pattern against these bytes.
1264%
1265% o length: Specifies the length of the magick string.
1266%
1267*/
1268static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1269{
1270 if (length < 8)
1271 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001272
cristy3ed852e2009-09-05 21:47:34 +00001273 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1274 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001275
cristy3ed852e2009-09-05 21:47:34 +00001276 return(MagickFalse);
1277}
1278
1279#if defined(MAGICKCORE_PNG_DELEGATE)
1280#if defined(__cplusplus) || defined(c_plusplus)
1281extern "C" {
1282#endif
1283
glennrpd5045b42010-03-24 12:40:35 +00001284#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001285static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001286{
1287 unsigned char
1288 buffer[4];
1289
1290 assert(image != (Image *) NULL);
1291 assert(image->signature == MagickSignature);
1292 buffer[0]=(unsigned char) (value >> 24);
1293 buffer[1]=(unsigned char) (value >> 16);
1294 buffer[2]=(unsigned char) (value >> 8);
1295 buffer[3]=(unsigned char) value;
1296 return((size_t) WriteBlob(image,4,buffer));
1297}
1298
1299static void PNGLong(png_bytep p,png_uint_32 value)
1300{
1301 *p++=(png_byte) ((value >> 24) & 0xff);
1302 *p++=(png_byte) ((value >> 16) & 0xff);
1303 *p++=(png_byte) ((value >> 8) & 0xff);
1304 *p++=(png_byte) (value & 0xff);
1305}
1306
glennrpa521b2f2010-10-29 04:11:03 +00001307#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001308static void PNGsLong(png_bytep p,png_int_32 value)
1309{
1310 *p++=(png_byte) ((value >> 24) & 0xff);
1311 *p++=(png_byte) ((value >> 16) & 0xff);
1312 *p++=(png_byte) ((value >> 8) & 0xff);
1313 *p++=(png_byte) (value & 0xff);
1314}
glennrpa521b2f2010-10-29 04:11:03 +00001315#endif
cristy3ed852e2009-09-05 21:47:34 +00001316
1317static void PNGShort(png_bytep p,png_uint_16 value)
1318{
1319 *p++=(png_byte) ((value >> 8) & 0xff);
1320 *p++=(png_byte) (value & 0xff);
1321}
1322
1323static void PNGType(png_bytep p,png_bytep type)
1324{
1325 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1326}
1327
glennrp03812ae2010-12-24 01:31:34 +00001328static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1329 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001330{
1331 if (logging != MagickFalse)
1332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001333 " Writing %c%c%c%c chunk, length: %.20g",
1334 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001335}
glennrpd5045b42010-03-24 12:40:35 +00001336#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001337
1338#if defined(__cplusplus) || defined(c_plusplus)
1339}
1340#endif
1341
glennrpd5045b42010-03-24 12:40:35 +00001342#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001343/*
1344%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1345% %
1346% %
1347% %
1348% R e a d P N G I m a g e %
1349% %
1350% %
1351% %
1352%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1353%
1354% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1355% Multiple-image Network Graphics (MNG) image file and returns it. It
1356% allocates the memory necessary for the new Image structure and returns a
1357% pointer to the new image or set of images.
1358%
1359% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1360%
1361% The format of the ReadPNGImage method is:
1362%
1363% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1364%
1365% A description of each parameter follows:
1366%
1367% o image_info: the image info.
1368%
1369% o exception: return any errors or warnings in this structure.
1370%
1371% To do, more or less in chronological order (as of version 5.5.2,
1372% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1373%
1374% Get 16-bit cheap transparency working.
1375%
1376% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1377%
1378% Preserve all unknown and not-yet-handled known chunks found in input
1379% PNG file and copy them into output PNG files according to the PNG
1380% copying rules.
1381%
1382% (At this point, PNG encoding should be in full MNG compliance)
1383%
1384% Provide options for choice of background to use when the MNG BACK
1385% chunk is not present or is not mandatory (i.e., leave transparent,
1386% user specified, MNG BACK, PNG bKGD)
1387%
1388% Implement LOOP/ENDL [done, but could do discretionary loops more
1389% efficiently by linking in the duplicate frames.].
1390%
1391% Decode and act on the MHDR simplicity profile (offer option to reject
1392% files or attempt to process them anyway when the profile isn't LC or VLC).
1393%
1394% Upgrade to full MNG without Delta-PNG.
1395%
1396% o BACK [done a while ago except for background image ID]
1397% o MOVE [done 15 May 1999]
1398% o CLIP [done 15 May 1999]
1399% o DISC [done 19 May 1999]
1400% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1401% o SEEK [partially done 19 May 1999 (discard function only)]
1402% o SHOW
1403% o PAST
1404% o BASI
1405% o MNG-level tEXt/iTXt/zTXt
1406% o pHYg
1407% o pHYs
1408% o sBIT
1409% o bKGD
1410% o iTXt (wait for libpng implementation).
1411%
1412% Use the scene signature to discover when an identical scene is
1413% being reused, and just point to the original image->exception instead
1414% of storing another set of pixels. This not specific to MNG
1415% but could be applied generally.
1416%
1417% Upgrade to full MNG with Delta-PNG.
1418%
1419% JNG tEXt/iTXt/zTXt
1420%
1421% We will not attempt to read files containing the CgBI chunk.
1422% They are really Xcode files meant for display on the iPhone.
1423% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001424% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001425% since irretrievable loss of color data has occurred due to the
1426% use of premultiplied alpha.
1427*/
1428
1429#if defined(__cplusplus) || defined(c_plusplus)
1430extern "C" {
1431#endif
1432
1433/*
1434 This the function that does the actual reading of data. It is
1435 the same as the one supplied in libpng, except that it receives the
1436 datastream from the ReadBlob() function instead of standard input.
1437*/
1438static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1439{
1440 Image
1441 *image;
1442
1443 image=(Image *) png_get_io_ptr(png_ptr);
1444 if (length)
1445 {
1446 png_size_t
1447 check;
1448
1449 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1450 if (check != length)
1451 {
1452 char
1453 msg[MaxTextExtent];
1454
cristy3b6fd2e2011-05-20 12:53:50 +00001455 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001456 "Expected %.20g bytes; found %.20g bytes",(double) length,
1457 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001458 png_warning(png_ptr,msg);
1459 png_error(png_ptr,"Read Exception");
1460 }
1461 }
1462}
1463
1464#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1465 !defined(PNG_MNG_FEATURES_SUPPORTED)
1466/* We use mng_get_data() instead of png_get_data() if we have a libpng
1467 * older than libpng-1.0.3a, which was the first to allow the empty
1468 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1469 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1470 * encountered after an empty PLTE, so we have to look ahead for bKGD
1471 * chunks and remove them from the datastream that is passed to libpng,
1472 * and store their contents for later use.
1473 */
1474static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1475{
1476 MngInfo
1477 *mng_info;
1478
1479 Image
1480 *image;
1481
1482 png_size_t
1483 check;
1484
cristybb503372010-05-27 20:51:26 +00001485 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001486 i;
1487
1488 i=0;
1489 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1490 image=(Image *) mng_info->image;
1491 while (mng_info->bytes_in_read_buffer && length)
1492 {
1493 data[i]=mng_info->read_buffer[i];
1494 mng_info->bytes_in_read_buffer--;
1495 length--;
1496 i++;
1497 }
1498 if (length)
1499 {
1500 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001501
cristy3ed852e2009-09-05 21:47:34 +00001502 if (check != length)
1503 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001504
cristy3ed852e2009-09-05 21:47:34 +00001505 if (length == 4)
1506 {
1507 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1508 (data[3] == 0))
1509 {
1510 check=(png_size_t) ReadBlob(image,(size_t) length,
1511 (char *) mng_info->read_buffer);
1512 mng_info->read_buffer[4]=0;
1513 mng_info->bytes_in_read_buffer=4;
1514 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1515 mng_info->found_empty_plte=MagickTrue;
1516 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1517 {
1518 mng_info->found_empty_plte=MagickFalse;
1519 mng_info->have_saved_bkgd_index=MagickFalse;
1520 }
1521 }
glennrp0fe50b42010-11-16 03:52:51 +00001522
cristy3ed852e2009-09-05 21:47:34 +00001523 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1524 (data[3] == 1))
1525 {
1526 check=(png_size_t) ReadBlob(image,(size_t) length,
1527 (char *) mng_info->read_buffer);
1528 mng_info->read_buffer[4]=0;
1529 mng_info->bytes_in_read_buffer=4;
1530 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1531 if (mng_info->found_empty_plte)
1532 {
1533 /*
1534 Skip the bKGD data byte and CRC.
1535 */
1536 check=(png_size_t)
1537 ReadBlob(image,5,(char *) mng_info->read_buffer);
1538 check=(png_size_t) ReadBlob(image,(size_t) length,
1539 (char *) mng_info->read_buffer);
1540 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1541 mng_info->have_saved_bkgd_index=MagickTrue;
1542 mng_info->bytes_in_read_buffer=0;
1543 }
1544 }
1545 }
1546 }
1547}
1548#endif
1549
1550static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1551{
1552 Image
1553 *image;
1554
1555 image=(Image *) png_get_io_ptr(png_ptr);
1556 if (length)
1557 {
1558 png_size_t
1559 check;
1560
cristybb503372010-05-27 20:51:26 +00001561 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001562
cristy3ed852e2009-09-05 21:47:34 +00001563 if (check != length)
1564 png_error(png_ptr,"WriteBlob Failed");
1565 }
1566}
1567
1568static void png_flush_data(png_structp png_ptr)
1569{
1570 (void) png_ptr;
1571}
1572
1573#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1574static int PalettesAreEqual(Image *a,Image *b)
1575{
cristybb503372010-05-27 20:51:26 +00001576 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001577 i;
1578
1579 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1580 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001581
cristy3ed852e2009-09-05 21:47:34 +00001582 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1583 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001584
cristy3ed852e2009-09-05 21:47:34 +00001585 if (a->colors != b->colors)
1586 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001587
cristybb503372010-05-27 20:51:26 +00001588 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001589 {
1590 if ((a->colormap[i].red != b->colormap[i].red) ||
1591 (a->colormap[i].green != b->colormap[i].green) ||
1592 (a->colormap[i].blue != b->colormap[i].blue))
1593 return((int) MagickFalse);
1594 }
glennrp0fe50b42010-11-16 03:52:51 +00001595
cristy3ed852e2009-09-05 21:47:34 +00001596 return((int) MagickTrue);
1597}
1598#endif
1599
1600static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1601{
1602 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1603 mng_info->exists[i] && !mng_info->frozen[i])
1604 {
1605#ifdef MNG_OBJECT_BUFFERS
1606 if (mng_info->ob[i] != (MngBuffer *) NULL)
1607 {
1608 if (mng_info->ob[i]->reference_count > 0)
1609 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001610
cristy3ed852e2009-09-05 21:47:34 +00001611 if (mng_info->ob[i]->reference_count == 0)
1612 {
1613 if (mng_info->ob[i]->image != (Image *) NULL)
1614 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001615
cristy3ed852e2009-09-05 21:47:34 +00001616 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1617 }
1618 }
1619 mng_info->ob[i]=(MngBuffer *) NULL;
1620#endif
1621 mng_info->exists[i]=MagickFalse;
1622 mng_info->invisible[i]=MagickFalse;
1623 mng_info->viewable[i]=MagickFalse;
1624 mng_info->frozen[i]=MagickFalse;
1625 mng_info->x_off[i]=0;
1626 mng_info->y_off[i]=0;
1627 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001628 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001629 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001630 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001631 }
1632}
1633
glennrp21f0e622011-01-07 16:20:57 +00001634static void MngInfoFreeStruct(MngInfo *mng_info,
1635 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001636{
glennrp21f0e622011-01-07 16:20:57 +00001637 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001638 {
cristybb503372010-05-27 20:51:26 +00001639 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001640 i;
1641
1642 for (i=1; i < MNG_MAX_OBJECTS; i++)
1643 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001644
cristy3ed852e2009-09-05 21:47:34 +00001645 if (mng_info->global_plte != (png_colorp) NULL)
1646 mng_info->global_plte=(png_colorp)
1647 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001648
cristy3ed852e2009-09-05 21:47:34 +00001649 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1650 *have_mng_structure=MagickFalse;
1651 }
1652}
1653
1654static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1655{
1656 MngBox
1657 box;
1658
1659 box=box1;
1660 if (box.left < box2.left)
1661 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001662
cristy3ed852e2009-09-05 21:47:34 +00001663 if (box.top < box2.top)
1664 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001665
cristy3ed852e2009-09-05 21:47:34 +00001666 if (box.right > box2.right)
1667 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001668
cristy3ed852e2009-09-05 21:47:34 +00001669 if (box.bottom > box2.bottom)
1670 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001671
cristy3ed852e2009-09-05 21:47:34 +00001672 return box;
1673}
1674
1675static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1676{
1677 MngBox
1678 box;
1679
1680 /*
1681 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1682 */
cristybb503372010-05-27 20:51:26 +00001683 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1684 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1685 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1686 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001687 if (delta_type != 0)
1688 {
1689 box.left+=previous_box.left;
1690 box.right+=previous_box.right;
1691 box.top+=previous_box.top;
1692 box.bottom+=previous_box.bottom;
1693 }
glennrp0fe50b42010-11-16 03:52:51 +00001694
cristy3ed852e2009-09-05 21:47:34 +00001695 return(box);
1696}
1697
1698static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1699 unsigned char *p)
1700{
1701 MngPair
1702 pair;
1703 /*
cristybb503372010-05-27 20:51:26 +00001704 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001705 */
cristy8182b072010-05-30 20:10:53 +00001706 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1707 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001708
cristy3ed852e2009-09-05 21:47:34 +00001709 if (delta_type != 0)
1710 {
1711 pair.a+=previous_pair.a;
1712 pair.b+=previous_pair.b;
1713 }
glennrp0fe50b42010-11-16 03:52:51 +00001714
cristy3ed852e2009-09-05 21:47:34 +00001715 return(pair);
1716}
1717
cristy8182b072010-05-30 20:10:53 +00001718static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001719{
cristy8182b072010-05-30 20:10:53 +00001720 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001721}
1722
glennrpcf002022011-01-30 02:38:15 +00001723static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001724{
1725 Image
1726 *image;
1727
1728 image=(Image *) png_get_error_ptr(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001729
cristy3ed852e2009-09-05 21:47:34 +00001730 if (image->debug != MagickFalse)
1731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1732 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001733
cristy3ed852e2009-09-05 21:47:34 +00001734 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1735 message,"`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001736
glennrpe4017e32011-01-08 17:16:09 +00001737#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001738 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1739 * are building with libpng-1.4.x and can be ignored.
1740 */
cristy3ed852e2009-09-05 21:47:34 +00001741 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001742#else
1743 png_longjmp(ping,1);
1744#endif
cristy3ed852e2009-09-05 21:47:34 +00001745}
1746
glennrpcf002022011-01-30 02:38:15 +00001747static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001748{
1749 Image
1750 *image;
1751
1752 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1753 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001754
cristy3ed852e2009-09-05 21:47:34 +00001755 image=(Image *) png_get_error_ptr(ping);
1756 if (image->debug != MagickFalse)
1757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristycc23b9a2010-05-09 22:37:43 +00001758 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001759
cristy3ed852e2009-09-05 21:47:34 +00001760 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1761 message,"`%s'",image->filename);
1762}
1763
1764#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001765static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001766{
1767#if (PNG_LIBPNG_VER < 10011)
1768 png_voidp
1769 ret;
1770
1771 png_ptr=png_ptr;
1772 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001773
cristy3ed852e2009-09-05 21:47:34 +00001774 if (ret == NULL)
1775 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001776
cristy3ed852e2009-09-05 21:47:34 +00001777 return(ret);
1778#else
1779 png_ptr=png_ptr;
1780 return((png_voidp) AcquireMagickMemory((size_t) size));
1781#endif
1782}
1783
1784/*
1785 Free a pointer. It is removed from the list at the same time.
1786*/
glennrpcf002022011-01-30 02:38:15 +00001787static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001788{
1789 png_ptr=png_ptr;
1790 ptr=RelinquishMagickMemory(ptr);
1791 return((png_free_ptr) NULL);
1792}
1793#endif
1794
1795#if defined(__cplusplus) || defined(c_plusplus)
1796}
1797#endif
1798
1799static int
glennrpcf002022011-01-30 02:38:15 +00001800Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristy3ed852e2009-09-05 21:47:34 +00001801 png_textp text,int ii)
1802{
cristybb503372010-05-27 20:51:26 +00001803 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001804 i;
1805
1806 register unsigned char
1807 *dp;
1808
1809 register png_charp
1810 sp;
1811
1812 png_uint_32
1813 length,
1814 nibbles;
1815
1816 StringInfo
1817 *profile;
1818
glennrp0c3e06b2010-11-19 13:45:02 +00001819 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001820 unhex[103]={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,0, 0,0,0,0,0,0,0,0,0,0,
1822 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1823 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1824 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1825 13,14,15};
1826
1827 sp=text[ii].text+1;
1828 /* look for newline */
1829 while (*sp != '\n')
1830 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001831
cristy3ed852e2009-09-05 21:47:34 +00001832 /* look for length */
1833 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1834 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001835
cristyf2f27272009-12-17 14:48:46 +00001836 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001837
glennrp97f90e22011-02-22 05:47:58 +00001838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1839 " length: %lu",(unsigned long) length);
1840
cristy3ed852e2009-09-05 21:47:34 +00001841 while (*sp != ' ' && *sp != '\n')
1842 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001843
cristy3ed852e2009-09-05 21:47:34 +00001844 /* allocate space */
1845 if (length == 0)
1846 {
1847 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1848 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1849 return(MagickFalse);
1850 }
glennrp0fe50b42010-11-16 03:52:51 +00001851
cristy8723e4b2011-09-01 13:11:19 +00001852 profile=BlobToStringInfo((const void *) NULL,length);
glennrp0fe50b42010-11-16 03:52:51 +00001853
cristy3ed852e2009-09-05 21:47:34 +00001854 if (profile == (StringInfo *) NULL)
1855 {
1856 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1857 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1858 "unable to copy profile");
1859 return(MagickFalse);
1860 }
glennrp0fe50b42010-11-16 03:52:51 +00001861
cristy3ed852e2009-09-05 21:47:34 +00001862 /* copy profile, skipping white space and column 1 "=" signs */
1863 dp=GetStringInfoDatum(profile);
1864 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001865
cristybb503372010-05-27 20:51:26 +00001866 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001867 {
1868 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1869 {
1870 if (*sp == '\0')
1871 {
1872 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1873 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1874 profile=DestroyStringInfo(profile);
1875 return(MagickFalse);
1876 }
1877 sp++;
1878 }
glennrp0fe50b42010-11-16 03:52:51 +00001879
cristy3ed852e2009-09-05 21:47:34 +00001880 if (i%2 == 0)
1881 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001882
cristy3ed852e2009-09-05 21:47:34 +00001883 else
1884 (*dp++)+=unhex[(int) *sp++];
1885 }
1886 /*
1887 We have already read "Raw profile type.
1888 */
1889 (void) SetImageProfile(image,&text[ii].key[17],profile);
1890 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001891
cristy3ed852e2009-09-05 21:47:34 +00001892 if (image_info->verbose)
1893 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001894
cristy3ed852e2009-09-05 21:47:34 +00001895 return MagickTrue;
1896}
1897
1898#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1899static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1900{
1901 Image
1902 *image;
1903
1904
1905 /* The unknown chunk structure contains the chunk data:
1906 png_byte name[5];
1907 png_byte *data;
1908 png_size_t size;
1909
1910 Note that libpng has already taken care of the CRC handling.
1911 */
1912
1913
1914 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1915 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1916 return(0); /* Did not recognize */
1917
1918 /* recognized vpAg */
1919
1920 if (chunk->size != 9)
1921 return(-1); /* Error return */
1922
1923 if (chunk->data[8] != 0)
1924 return(0); /* ImageMagick requires pixel units */
1925
1926 image=(Image *) png_get_user_chunk_ptr(ping);
1927
cristybb503372010-05-27 20:51:26 +00001928 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001929 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001930
cristybb503372010-05-27 20:51:26 +00001931 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001932 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1933
1934 /* Return one of the following: */
1935 /* return(-n); chunk had an error */
1936 /* return(0); did not recognize */
1937 /* return(n); success */
1938
1939 return(1);
1940
1941}
1942#endif
1943
1944/*
1945%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1946% %
1947% %
1948% %
1949% R e a d O n e P N G I m a g e %
1950% %
1951% %
1952% %
1953%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1954%
1955% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1956% (minus the 8-byte signature) and returns it. It allocates the memory
1957% necessary for the new Image structure and returns a pointer to the new
1958% image.
1959%
1960% The format of the ReadOnePNGImage method is:
1961%
1962% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1963% ExceptionInfo *exception)
1964%
1965% A description of each parameter follows:
1966%
1967% o mng_info: Specifies a pointer to a MngInfo structure.
1968%
1969% o image_info: the image info.
1970%
1971% o exception: return any errors or warnings in this structure.
1972%
1973*/
1974static Image *ReadOnePNGImage(MngInfo *mng_info,
1975 const ImageInfo *image_info, ExceptionInfo *exception)
1976{
1977 /* Read one PNG image */
1978
glennrpcc95c3f2011-04-18 16:46:48 +00001979 /* To do: Read the tIME chunk into the date:modify property */
1980 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1981
cristy3ed852e2009-09-05 21:47:34 +00001982 Image
1983 *image;
1984
1985 int
glennrp4eb39312011-03-30 21:34:55 +00001986 intent,
glennrpcb395ac2011-03-30 19:50:23 +00001987 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00001988 num_text,
glennrp4eb39312011-03-30 21:34:55 +00001989 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00001990 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00001991 pass,
1992 ping_bit_depth,
1993 ping_color_type,
1994 ping_interlace_method,
1995 ping_compression_method,
1996 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00001997 ping_num_trans,
1998 unit_type;
1999
2000 double
2001 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002002
cristy5c4e2582011-09-11 19:21:03 +00002003 PixelLongPacket
glennrpa6a06632011-01-19 15:15:34 +00002004 transparent_color;
2005
cristy3ed852e2009-09-05 21:47:34 +00002006 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002007 logging,
cristy3ed852e2009-09-05 21:47:34 +00002008 status;
2009
glennrpfaa852b2010-03-30 12:17:00 +00002010 png_bytep
2011 ping_trans_alpha;
2012
2013 png_color_16p
2014 ping_background,
2015 ping_trans_color;
2016
cristy3ed852e2009-09-05 21:47:34 +00002017 png_info
2018 *end_info,
2019 *ping_info;
2020
2021 png_struct
2022 *ping;
2023
2024 png_textp
2025 text;
2026
glennrpfaa852b2010-03-30 12:17:00 +00002027 png_uint_32
2028 ping_height,
2029 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002030 ping_rowbytes,
2031 x_resolution,
2032 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002033
cristy3ed852e2009-09-05 21:47:34 +00002034 QuantumInfo
2035 *quantum_info;
2036
2037 unsigned char
glennrpcf002022011-01-30 02:38:15 +00002038 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002039
cristybb503372010-05-27 20:51:26 +00002040 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002041 y;
2042
2043 register unsigned char
2044 *p;
2045
cristybb503372010-05-27 20:51:26 +00002046 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002047 i,
2048 x;
2049
cristy4c08aed2011-07-01 19:47:50 +00002050 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002051 *q;
2052
2053 size_t
glennrp39992b42010-11-14 00:03:43 +00002054 length,
cristy3ed852e2009-09-05 21:47:34 +00002055 row_offset;
2056
cristyeb3b22a2011-03-31 20:16:11 +00002057 ssize_t
2058 j;
2059
cristy3ed852e2009-09-05 21:47:34 +00002060#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2061 png_byte unused_chunks[]=
2062 {
2063 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2064 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2065 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2066 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2067 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2068 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2069 };
2070#endif
2071
2072 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002073 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002074
2075#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002076 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002077#endif
2078
glennrp25c1e2b2010-03-25 01:39:56 +00002079#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002080 if (image_info->verbose)
2081 printf("Your PNG library (libpng-%s) is rather old.\n",
2082 PNG_LIBPNG_VER_STRING);
2083#endif
2084
glennrp61b4c952009-11-10 20:40:41 +00002085#if (PNG_LIBPNG_VER >= 10400)
2086# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2087 if (image_info->verbose)
2088 {
2089 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2090 PNG_LIBPNG_VER_STRING);
2091 printf("Please update it.\n");
2092 }
2093# endif
2094#endif
2095
2096
cristyed552522009-10-16 14:04:35 +00002097 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002098 image=mng_info->image;
2099
glennrpa6a06632011-01-19 15:15:34 +00002100 if (logging != MagickFalse)
2101 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2102 " image->matte=%d",(int) image->matte);
2103
glennrp0e319732011-01-25 21:53:13 +00002104 /* Set to an out-of-range color unless tRNS chunk is present */
2105 transparent_color.red=65537;
2106 transparent_color.green=65537;
2107 transparent_color.blue=65537;
cristy4c08aed2011-07-01 19:47:50 +00002108 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002109
glennrpcb395ac2011-03-30 19:50:23 +00002110 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002111 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002112 num_raw_profiles = 0;
2113
cristy3ed852e2009-09-05 21:47:34 +00002114 /*
2115 Allocate the PNG structures
2116 */
2117#ifdef PNG_USER_MEM_SUPPORTED
2118 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
glennrpcf002022011-01-30 02:38:15 +00002119 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2120 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002121#else
2122 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00002123 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002124#endif
2125 if (ping == (png_struct *) NULL)
2126 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002127
cristy3ed852e2009-09-05 21:47:34 +00002128 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002129
cristy3ed852e2009-09-05 21:47:34 +00002130 if (ping_info == (png_info *) NULL)
2131 {
2132 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2133 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2134 }
glennrp0fe50b42010-11-16 03:52:51 +00002135
cristy3ed852e2009-09-05 21:47:34 +00002136 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002137
cristy3ed852e2009-09-05 21:47:34 +00002138 if (end_info == (png_info *) NULL)
2139 {
2140 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2141 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2142 }
glennrp0fe50b42010-11-16 03:52:51 +00002143
glennrpcf002022011-01-30 02:38:15 +00002144 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002145
glennrpfaa852b2010-03-30 12:17:00 +00002146 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002147 {
2148 /*
2149 PNG image is corrupt.
2150 */
2151 png_destroy_read_struct(&ping,&ping_info,&end_info);
2152#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002153 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002154#endif
2155 if (logging != MagickFalse)
2156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2157 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002158
cristy3ed852e2009-09-05 21:47:34 +00002159 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002160 {
2161 InheritException(exception,&image->exception);
2162 image->columns=0;
2163 }
glennrp0fe50b42010-11-16 03:52:51 +00002164
cristy3ed852e2009-09-05 21:47:34 +00002165 return(GetFirstImageInList(image));
2166 }
2167 /*
2168 Prepare PNG for reading.
2169 */
glennrpfaa852b2010-03-30 12:17:00 +00002170
cristy3ed852e2009-09-05 21:47:34 +00002171 mng_info->image_found++;
2172 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002173
cristy3ed852e2009-09-05 21:47:34 +00002174 if (LocaleCompare(image_info->magick,"MNG") == 0)
2175 {
2176#if defined(PNG_MNG_FEATURES_SUPPORTED)
2177 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2178 png_set_read_fn(ping,image,png_get_data);
2179#else
2180#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2181 png_permit_empty_plte(ping,MagickTrue);
2182 png_set_read_fn(ping,image,png_get_data);
2183#else
2184 mng_info->image=image;
2185 mng_info->bytes_in_read_buffer=0;
2186 mng_info->found_empty_plte=MagickFalse;
2187 mng_info->have_saved_bkgd_index=MagickFalse;
2188 png_set_read_fn(ping,mng_info,mng_get_data);
2189#endif
2190#endif
2191 }
glennrp0fe50b42010-11-16 03:52:51 +00002192
cristy3ed852e2009-09-05 21:47:34 +00002193 else
2194 png_set_read_fn(ping,image,png_get_data);
2195
2196#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2197 /* Ignore unused chunks and all unknown chunks except for vpAg */
2198 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2199 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2200 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2201 (int)sizeof(unused_chunks)/5);
2202 /* Callback for other unknown chunks */
2203 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2204#endif
2205
glennrp991e92a2010-01-28 03:09:00 +00002206#if (PNG_LIBPNG_VER < 10400)
2207# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2208 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002209 /* Disable thread-unsafe features of pnggccrd */
2210 if (png_access_version_number() >= 10200)
2211 {
2212 png_uint_32 mmx_disable_mask=0;
2213 png_uint_32 asm_flags;
2214
2215 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2216 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2217 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2218 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2219 asm_flags=png_get_asm_flags(ping);
2220 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2221 }
glennrp991e92a2010-01-28 03:09:00 +00002222# endif
cristy3ed852e2009-09-05 21:47:34 +00002223#endif
2224
2225 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002226
2227 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2228 &ping_bit_depth,&ping_color_type,
2229 &ping_interlace_method,&ping_compression_method,
2230 &ping_filter_method);
2231
2232 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2233 &ping_trans_color);
2234
2235 (void) png_get_bKGD(ping, ping_info, &ping_background);
2236
2237 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002238 {
glennrpfaa852b2010-03-30 12:17:00 +00002239 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2240 {
2241 png_set_packing(ping);
2242 ping_bit_depth = 8;
2243 }
cristy3ed852e2009-09-05 21:47:34 +00002244 }
glennrpfaa852b2010-03-30 12:17:00 +00002245
2246 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002247 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002248 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00002249 if (logging != MagickFalse)
2250 {
2251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002252 " PNG width: %.20g, height: %.20g",
2253 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002254
cristy3ed852e2009-09-05 21:47:34 +00002255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2256 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002257 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002258
cristy3ed852e2009-09-05 21:47:34 +00002259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2260 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002261 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002262
cristy3ed852e2009-09-05 21:47:34 +00002263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2264 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002265 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002266 }
2267
glennrpfaa852b2010-03-30 12:17:00 +00002268#ifdef PNG_READ_iCCP_SUPPORTED
2269 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002270 {
2271 int
2272 compression;
2273
glennrpe4017e32011-01-08 17:16:09 +00002274#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002275 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002276 info;
2277#else
2278 png_bytep
2279 info;
2280#endif
2281
2282 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002283 name;
2284
2285 png_uint_32
2286 profile_length;
2287
2288 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2289 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002290
cristy3ed852e2009-09-05 21:47:34 +00002291 if (profile_length != 0)
2292 {
2293 StringInfo
2294 *profile;
2295
2296 if (logging != MagickFalse)
2297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2298 " Reading PNG iCCP chunk.");
cristye8f8f382011-09-01 13:32:37 +00002299 profile=BlobToStringInfo(info,profile_length);
2300 if (profile == (StringInfo *) NULL)
2301 {
2302 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2303 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2304 "unable to copy profile");
cristyad5b0852011-09-08 02:01:21 +00002305 return((Image *) NULL);
cristye8f8f382011-09-01 13:32:37 +00002306 }
cristy3ed852e2009-09-05 21:47:34 +00002307 SetStringInfoDatum(profile,(const unsigned char *) info);
2308 (void) SetImageProfile(image,"icc",profile);
2309 profile=DestroyStringInfo(profile);
2310 }
2311 }
2312#endif
2313#if defined(PNG_READ_sRGB_SUPPORTED)
2314 {
cristy3ed852e2009-09-05 21:47:34 +00002315 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00002316 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2317 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00002318
cristy3ed852e2009-09-05 21:47:34 +00002319 if (png_get_sRGB(ping,ping_info,&intent))
2320 {
glennrpcf002022011-01-30 02:38:15 +00002321 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2322 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002323
cristy3ed852e2009-09-05 21:47:34 +00002324 if (logging != MagickFalse)
2325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002326 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002327 }
2328 }
2329#endif
2330 {
glennrpfaa852b2010-03-30 12:17:00 +00002331 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2332 if (mng_info->have_global_gama)
2333 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002334
cristy3ed852e2009-09-05 21:47:34 +00002335 if (png_get_gAMA(ping,ping_info,&file_gamma))
2336 {
2337 image->gamma=(float) file_gamma;
2338 if (logging != MagickFalse)
2339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2340 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2341 }
2342 }
glennrpfaa852b2010-03-30 12:17:00 +00002343 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2344 {
2345 if (mng_info->have_global_chrm != MagickFalse)
2346 {
2347 (void) png_set_cHRM(ping,ping_info,
2348 mng_info->global_chrm.white_point.x,
2349 mng_info->global_chrm.white_point.y,
2350 mng_info->global_chrm.red_primary.x,
2351 mng_info->global_chrm.red_primary.y,
2352 mng_info->global_chrm.green_primary.x,
2353 mng_info->global_chrm.green_primary.y,
2354 mng_info->global_chrm.blue_primary.x,
2355 mng_info->global_chrm.blue_primary.y);
2356 }
2357 }
glennrp0fe50b42010-11-16 03:52:51 +00002358
glennrpfaa852b2010-03-30 12:17:00 +00002359 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002360 {
2361 (void) png_get_cHRM(ping,ping_info,
2362 &image->chromaticity.white_point.x,
2363 &image->chromaticity.white_point.y,
2364 &image->chromaticity.red_primary.x,
2365 &image->chromaticity.red_primary.y,
2366 &image->chromaticity.green_primary.x,
2367 &image->chromaticity.green_primary.y,
2368 &image->chromaticity.blue_primary.x,
2369 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002370
cristy3ed852e2009-09-05 21:47:34 +00002371 if (logging != MagickFalse)
2372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2373 " Reading PNG cHRM chunk.");
2374 }
glennrp0fe50b42010-11-16 03:52:51 +00002375
glennrpe610a072010-08-05 17:08:46 +00002376 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002377 {
glennrpe610a072010-08-05 17:08:46 +00002378 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002379 Magick_RenderingIntent_to_PNG_RenderingIntent
2380 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00002381 png_set_gAMA(ping,ping_info,0.45455f);
2382 png_set_cHRM(ping,ping_info,
2383 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2384 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002385 }
cristy3ed852e2009-09-05 21:47:34 +00002386#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002387 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002388 {
cristy905ef802011-02-23 00:29:18 +00002389 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2390 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002391
cristy3ed852e2009-09-05 21:47:34 +00002392 if (logging != MagickFalse)
2393 if (image->page.x || image->page.y)
2394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002395 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2396 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002397 }
2398#endif
2399#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002400 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2401 {
2402 if (mng_info->have_global_phys)
2403 {
2404 png_set_pHYs(ping,ping_info,
2405 mng_info->global_x_pixels_per_unit,
2406 mng_info->global_y_pixels_per_unit,
2407 mng_info->global_phys_unit_type);
2408 }
2409 }
2410
2411 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002412 {
cristy3ed852e2009-09-05 21:47:34 +00002413 /*
2414 Set image resolution.
2415 */
2416 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002417 &unit_type);
2418 image->x_resolution=(double) x_resolution;
2419 image->y_resolution=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002420
cristy3ed852e2009-09-05 21:47:34 +00002421 if (unit_type == PNG_RESOLUTION_METER)
2422 {
2423 image->units=PixelsPerCentimeterResolution;
2424 image->x_resolution=(double) x_resolution/100.0;
2425 image->y_resolution=(double) y_resolution/100.0;
2426 }
glennrp0fe50b42010-11-16 03:52:51 +00002427
cristy3ed852e2009-09-05 21:47:34 +00002428 if (logging != MagickFalse)
2429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002430 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2431 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002432 }
cristy3ed852e2009-09-05 21:47:34 +00002433#endif
glennrp823b55c2011-03-14 18:46:46 +00002434
glennrpfaa852b2010-03-30 12:17:00 +00002435 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002436 {
2437 int
2438 number_colors;
2439
2440 png_colorp
2441 palette;
2442
2443 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002444
cristy3ed852e2009-09-05 21:47:34 +00002445 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002446 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002447 {
2448 if (mng_info->global_plte_length)
2449 {
2450 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2451 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002452
glennrpfaa852b2010-03-30 12:17:00 +00002453 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002454 if (mng_info->global_trns_length)
2455 {
2456 if (mng_info->global_trns_length >
2457 mng_info->global_plte_length)
2458 (void) ThrowMagickException(&image->exception,
2459 GetMagickModule(),CoderError,
2460 "global tRNS has more entries than global PLTE",
2461 "`%s'",image_info->filename);
2462 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2463 (int) mng_info->global_trns_length,NULL);
2464 }
glennrpbfd9e612011-04-22 14:02:20 +00002465#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002466 if (
2467#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2468 mng_info->have_saved_bkgd_index ||
2469#endif
glennrpfaa852b2010-03-30 12:17:00 +00002470 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002471 {
2472 png_color_16
2473 background;
2474
2475#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2476 if (mng_info->have_saved_bkgd_index)
2477 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002478#endif
glennrpfaa852b2010-03-30 12:17:00 +00002479 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2480 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002481
cristy3ed852e2009-09-05 21:47:34 +00002482 background.red=(png_uint_16)
2483 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002484
cristy3ed852e2009-09-05 21:47:34 +00002485 background.green=(png_uint_16)
2486 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002487
cristy3ed852e2009-09-05 21:47:34 +00002488 background.blue=(png_uint_16)
2489 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002490
glennrpc6c391a2011-04-27 02:23:56 +00002491 background.gray=(png_uint_16)
2492 mng_info->global_plte[background.index].green;
2493
cristy3ed852e2009-09-05 21:47:34 +00002494 png_set_bKGD(ping,ping_info,&background);
2495 }
2496#endif
2497 }
2498 else
2499 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2500 CoderError,"No global PLTE in file","`%s'",
2501 image_info->filename);
2502 }
2503 }
2504
glennrpbfd9e612011-04-22 14:02:20 +00002505#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002506 if (mng_info->have_global_bkgd &&
2507 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002508 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002509
glennrpfaa852b2010-03-30 12:17:00 +00002510 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002511 {
glennrpbfd9e612011-04-22 14:02:20 +00002512 unsigned int
2513 bkgd_scale;
2514
cristy3ed852e2009-09-05 21:47:34 +00002515 /*
2516 Set image background color.
2517 */
2518 if (logging != MagickFalse)
2519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2520 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002521
glennrpbfd9e612011-04-22 14:02:20 +00002522 /* Scale background components to 16-bit, then scale
2523 * to quantum depth
2524 */
2525 if (logging != MagickFalse)
2526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2527 " raw ping_background=(%d,%d,%d).",ping_background->red,
2528 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002529
glennrpbfd9e612011-04-22 14:02:20 +00002530 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002531
glennrpbfd9e612011-04-22 14:02:20 +00002532 if (ping_bit_depth == 1)
2533 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002534
glennrpbfd9e612011-04-22 14:02:20 +00002535 else if (ping_bit_depth == 2)
2536 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002537
glennrpbfd9e612011-04-22 14:02:20 +00002538 else if (ping_bit_depth == 4)
2539 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002540
glennrpbfd9e612011-04-22 14:02:20 +00002541 if (ping_bit_depth <= 8)
2542 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002543
glennrpbfd9e612011-04-22 14:02:20 +00002544 ping_background->red *= bkgd_scale;
2545 ping_background->green *= bkgd_scale;
2546 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002547
glennrpbfd9e612011-04-22 14:02:20 +00002548 if (logging != MagickFalse)
2549 {
glennrp2cbb4482010-06-02 04:37:24 +00002550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2551 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002552
glennrp2cbb4482010-06-02 04:37:24 +00002553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2554 " ping_background=(%d,%d,%d).",ping_background->red,
2555 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002556 }
glennrp2cbb4482010-06-02 04:37:24 +00002557
glennrpbfd9e612011-04-22 14:02:20 +00002558 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002559 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002560
glennrpbfd9e612011-04-22 14:02:20 +00002561 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002562 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002563
glennrpbfd9e612011-04-22 14:02:20 +00002564 image->background_color.blue=
2565 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002566
cristy4c08aed2011-07-01 19:47:50 +00002567 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002568
glennrpbfd9e612011-04-22 14:02:20 +00002569 if (logging != MagickFalse)
2570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2571 " image->background_color=(%.20g,%.20g,%.20g).",
2572 (double) image->background_color.red,
2573 (double) image->background_color.green,
2574 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002575 }
glennrpbfd9e612011-04-22 14:02:20 +00002576#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002577
glennrpfaa852b2010-03-30 12:17:00 +00002578 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002579 {
2580 /*
glennrpa6a06632011-01-19 15:15:34 +00002581 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002582 */
2583 int
2584 max_sample;
2585
cristy35ef8242010-06-03 16:24:13 +00002586 size_t
2587 one=1;
2588
cristy3ed852e2009-09-05 21:47:34 +00002589 if (logging != MagickFalse)
2590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2591 " Reading PNG tRNS chunk.");
2592
cristyf9cca6a2010-06-04 23:49:28 +00002593 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002594
glennrpfaa852b2010-03-30 12:17:00 +00002595 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2596 (int)ping_trans_color->gray > max_sample) ||
2597 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2598 ((int)ping_trans_color->red > max_sample ||
2599 (int)ping_trans_color->green > max_sample ||
2600 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002601 {
2602 if (logging != MagickFalse)
2603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2604 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002605 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002606 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002607 image->matte=MagickFalse;
2608 }
2609 else
2610 {
glennrpa6a06632011-01-19 15:15:34 +00002611 int
2612 scale_to_short;
2613
2614 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2615
2616 /* Scale transparent_color to short */
2617 transparent_color.red= scale_to_short*ping_trans_color->red;
2618 transparent_color.green= scale_to_short*ping_trans_color->green;
2619 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy4c08aed2011-07-01 19:47:50 +00002620 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002621
glennrpfaa852b2010-03-30 12:17:00 +00002622 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002623 {
glennrp0f111982010-07-07 20:18:33 +00002624 if (logging != MagickFalse)
2625 {
2626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2627 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002628
glennrp0f111982010-07-07 20:18:33 +00002629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00002630 " scaled graylevel is %d.",transparent_color.alpha);
glennrp0f111982010-07-07 20:18:33 +00002631 }
cristy4c08aed2011-07-01 19:47:50 +00002632 transparent_color.red=transparent_color.alpha;
2633 transparent_color.green=transparent_color.alpha;
2634 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002635 }
2636 }
2637 }
2638#if defined(PNG_READ_sBIT_SUPPORTED)
2639 if (mng_info->have_global_sbit)
2640 {
glennrpfaa852b2010-03-30 12:17:00 +00002641 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002642 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2643 }
2644#endif
2645 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002646
cristy3ed852e2009-09-05 21:47:34 +00002647 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002648
2649 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2650
cristy3ed852e2009-09-05 21:47:34 +00002651 /*
2652 Initialize image structure.
2653 */
2654 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002655 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002656 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002657 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002658 if (mng_info->mng_type == 0)
2659 {
glennrpfaa852b2010-03-30 12:17:00 +00002660 mng_info->mng_width=ping_width;
2661 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002662 mng_info->frame=mng_info->image_box;
2663 mng_info->clip=mng_info->image_box;
2664 }
glennrp0fe50b42010-11-16 03:52:51 +00002665
cristy3ed852e2009-09-05 21:47:34 +00002666 else
2667 {
2668 image->page.y=mng_info->y_off[mng_info->object_id];
2669 }
glennrp0fe50b42010-11-16 03:52:51 +00002670
cristy3ed852e2009-09-05 21:47:34 +00002671 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002672 image->columns=ping_width;
2673 image->rows=ping_height;
2674 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002675 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002676 {
cristybefe4d22010-06-07 01:18:58 +00002677 size_t
2678 one;
2679
cristy3ed852e2009-09-05 21:47:34 +00002680 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002681 one=1;
2682 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002683#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2684 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002685 image->colors=256;
2686#else
2687 if (image->colors > 65536L)
2688 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002689#endif
glennrpfaa852b2010-03-30 12:17:00 +00002690 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002691 {
2692 int
2693 number_colors;
2694
2695 png_colorp
2696 palette;
2697
2698 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002699 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002700
cristy3ed852e2009-09-05 21:47:34 +00002701 if (logging != MagickFalse)
2702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2703 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2704 }
2705 }
2706
2707 if (image->storage_class == PseudoClass)
2708 {
2709 /*
2710 Initialize image colormap.
2711 */
cristy018f07f2011-09-04 21:15:19 +00002712 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002713 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002714
glennrpfaa852b2010-03-30 12:17:00 +00002715 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002716 {
2717 int
2718 number_colors;
2719
2720 png_colorp
2721 palette;
2722
2723 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002724
glennrp6af6cf12011-04-22 13:05:16 +00002725 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002726 {
2727 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2728 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2729 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2730 }
glennrp6af6cf12011-04-22 13:05:16 +00002731
glennrp67b9c1a2011-04-22 18:47:36 +00002732 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002733 {
2734 image->colormap[i].red=0;
2735 image->colormap[i].green=0;
2736 image->colormap[i].blue=0;
2737 }
cristy3ed852e2009-09-05 21:47:34 +00002738 }
glennrp0fe50b42010-11-16 03:52:51 +00002739
cristy3ed852e2009-09-05 21:47:34 +00002740 else
2741 {
cristybb503372010-05-27 20:51:26 +00002742 size_t
cristy3ed852e2009-09-05 21:47:34 +00002743 scale;
2744
glennrpfaa852b2010-03-30 12:17:00 +00002745 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002746
cristy3ed852e2009-09-05 21:47:34 +00002747 if (scale < 1)
2748 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002749
cristybb503372010-05-27 20:51:26 +00002750 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002751 {
2752 image->colormap[i].red=(Quantum) (i*scale);
2753 image->colormap[i].green=(Quantum) (i*scale);
2754 image->colormap[i].blue=(Quantum) (i*scale);
2755 }
2756 }
2757 }
glennrp147bc912011-03-30 18:47:21 +00002758
glennrpcb395ac2011-03-30 19:50:23 +00002759 /* Set some properties for reporting by "identify" */
2760 {
glennrp147bc912011-03-30 18:47:21 +00002761 char
2762 msg[MaxTextExtent];
2763
2764 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2765 ping_interlace_method in value */
2766
cristy3b6fd2e2011-05-20 12:53:50 +00002767 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002768 "%d, %d",(int) ping_width, (int) ping_height);
2769 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
glennrp147bc912011-03-30 18:47:21 +00002770
cristy3b6fd2e2011-05-20 12:53:50 +00002771 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
glennrp147bc912011-03-30 18:47:21 +00002772 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2773
cristy3b6fd2e2011-05-20 12:53:50 +00002774 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
glennrp147bc912011-03-30 18:47:21 +00002775 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2776
cristy3b6fd2e2011-05-20 12:53:50 +00002777 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002778 (int) ping_interlace_method);
2779 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
glennrpcb395ac2011-03-30 19:50:23 +00002780 }
glennrp147bc912011-03-30 18:47:21 +00002781
cristy3ed852e2009-09-05 21:47:34 +00002782 /*
2783 Read image scanlines.
2784 */
2785 if (image->delay != 0)
2786 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002787
glennrp0ca69b12010-07-26 01:57:52 +00002788 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002789 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2790 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002791 {
2792 if (logging != MagickFalse)
2793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002794 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002795 mng_info->scenes_found-1);
2796 png_destroy_read_struct(&ping,&ping_info,&end_info);
2797#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002798 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002799#endif
2800 if (logging != MagickFalse)
2801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2802 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002803
cristy3ed852e2009-09-05 21:47:34 +00002804 return(image);
2805 }
glennrp0fe50b42010-11-16 03:52:51 +00002806
cristy3ed852e2009-09-05 21:47:34 +00002807 if (logging != MagickFalse)
2808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2809 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002810
cristy3ed852e2009-09-05 21:47:34 +00002811 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002812 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2813 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002814
cristy3ed852e2009-09-05 21:47:34 +00002815 else
glennrpcf002022011-01-30 02:38:15 +00002816 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2817 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002818
glennrpcf002022011-01-30 02:38:15 +00002819 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002820 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002821
cristy3ed852e2009-09-05 21:47:34 +00002822 if (logging != MagickFalse)
2823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2824 " Converting PNG pixels to pixel packets");
2825 /*
2826 Convert PNG pixels to pixel packets.
2827 */
glennrpfaa852b2010-03-30 12:17:00 +00002828 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002829 {
2830 /*
2831 PNG image is corrupt.
2832 */
2833 png_destroy_read_struct(&ping,&ping_info,&end_info);
2834#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002835 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002836#endif
2837 if (quantum_info != (QuantumInfo *) NULL)
2838 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002839
glennrpcf002022011-01-30 02:38:15 +00002840 if (ping_pixels != (unsigned char *) NULL)
2841 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002842
cristy3ed852e2009-09-05 21:47:34 +00002843 if (logging != MagickFalse)
2844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2845 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002846
cristy3ed852e2009-09-05 21:47:34 +00002847 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002848 {
2849 InheritException(exception,&image->exception);
2850 image->columns=0;
2851 }
glennrp0fe50b42010-11-16 03:52:51 +00002852
cristy3ed852e2009-09-05 21:47:34 +00002853 return(GetFirstImageInList(image));
2854 }
glennrp0fe50b42010-11-16 03:52:51 +00002855
cristyed552522009-10-16 14:04:35 +00002856 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002857
cristyed552522009-10-16 14:04:35 +00002858 if (quantum_info == (QuantumInfo *) NULL)
2859 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002860
glennrpc8cbc5d2011-01-01 00:12:34 +00002861 {
2862
2863 MagickBooleanType
2864 found_transparent_pixel;
2865
2866 found_transparent_pixel=MagickFalse;
2867
cristy3ed852e2009-09-05 21:47:34 +00002868 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002869 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002870 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002871 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002872 /*
2873 Convert image to DirectClass pixel packets.
2874 */
glennrp67b9c1a2011-04-22 18:47:36 +00002875#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2876 int
2877 depth;
2878
2879 depth=(ssize_t) ping_bit_depth;
2880#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002881 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2882 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2883 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2884 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002885
glennrpc8cbc5d2011-01-01 00:12:34 +00002886 for (y=0; y < (ssize_t) image->rows; y++)
2887 {
2888 if (num_passes > 1)
2889 row_offset=ping_rowbytes*y;
2890
2891 else
2892 row_offset=0;
2893
glennrpcf002022011-01-30 02:38:15 +00002894 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002895 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2896
cristyacd2ed22011-08-30 01:44:23 +00002897 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00002898 break;
2899
glennrpc8cbc5d2011-01-01 00:12:34 +00002900 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2901 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002902 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002903
2904 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2905 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002906 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002907
2908 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2909 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002910 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002911
2912 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2913 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002914 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002915
2916 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2917 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002918 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002919
glennrpc8cbc5d2011-01-01 00:12:34 +00002920 if (found_transparent_pixel == MagickFalse)
2921 {
2922 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002923 if (y== 0 && logging != MagickFalse)
2924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2925 " Looking for cheap transparent pixel");
2926
glennrpc8cbc5d2011-01-01 00:12:34 +00002927 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2928 {
glennrp5aa37f62011-01-02 03:07:57 +00002929 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2930 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy4c08aed2011-07-01 19:47:50 +00002931 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00002932 {
glennrpa6a06632011-01-19 15:15:34 +00002933 if (logging != MagickFalse)
2934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2935 " ...got one.");
2936
glennrpc8cbc5d2011-01-01 00:12:34 +00002937 found_transparent_pixel = MagickTrue;
2938 break;
2939 }
glennrp4f25bd02011-01-01 18:51:28 +00002940 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2941 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp847370c2011-07-05 17:37:15 +00002942 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2943 transparent_color.red &&
2944 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2945 transparent_color.green &&
2946 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2947 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002948 {
glennrpa6a06632011-01-19 15:15:34 +00002949 if (logging != MagickFalse)
2950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2951 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002952 found_transparent_pixel = MagickTrue;
2953 break;
2954 }
cristyed231572011-07-14 02:18:59 +00002955 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00002956 }
2957 }
2958
2959 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2960 {
2961 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2962 image->rows);
2963
2964 if (status == MagickFalse)
2965 break;
2966 }
2967 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2968 break;
2969 }
2970
2971 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2972 {
2973 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00002974 if (status == MagickFalse)
2975 break;
2976 }
cristy3ed852e2009-09-05 21:47:34 +00002977 }
cristy3ed852e2009-09-05 21:47:34 +00002978 }
glennrp0fe50b42010-11-16 03:52:51 +00002979
cristy3ed852e2009-09-05 21:47:34 +00002980 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00002981
cristy3ed852e2009-09-05 21:47:34 +00002982 for (pass=0; pass < num_passes; pass++)
2983 {
2984 Quantum
2985 *quantum_scanline;
2986
2987 register Quantum
2988 *r;
2989
2990 /*
2991 Convert grayscale image to PseudoClass pixel packets.
2992 */
glennrpc17d96f2011-06-27 01:20:11 +00002993 if (logging != MagickFalse)
2994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2995 " Converting grayscale pixels to pixel packets");
cristy4c08aed2011-07-01 19:47:50 +00002996
glennrpfaa852b2010-03-30 12:17:00 +00002997 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00002998 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002999
cristy3ed852e2009-09-05 21:47:34 +00003000 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3001 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003002
cristy3ed852e2009-09-05 21:47:34 +00003003 if (quantum_scanline == (Quantum *) NULL)
3004 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003005
cristybb503372010-05-27 20:51:26 +00003006 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003007 {
3008 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003009 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003010
cristy3ed852e2009-09-05 21:47:34 +00003011 else
3012 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003013
glennrpcf002022011-01-30 02:38:15 +00003014 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00003015 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003016
cristyacd2ed22011-08-30 01:44:23 +00003017 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003018 break;
glennrp0fe50b42010-11-16 03:52:51 +00003019
glennrpcf002022011-01-30 02:38:15 +00003020 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003021 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003022
glennrpfaa852b2010-03-30 12:17:00 +00003023 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003024 {
3025 case 1:
3026 {
cristybb503372010-05-27 20:51:26 +00003027 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003028 bit;
3029
cristybb503372010-05-27 20:51:26 +00003030 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003031 {
3032 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003033 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003034 p++;
3035 }
glennrp0fe50b42010-11-16 03:52:51 +00003036
cristy3ed852e2009-09-05 21:47:34 +00003037 if ((image->columns % 8) != 0)
3038 {
cristybb503372010-05-27 20:51:26 +00003039 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003040 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003041 }
glennrp0fe50b42010-11-16 03:52:51 +00003042
cristy3ed852e2009-09-05 21:47:34 +00003043 break;
3044 }
glennrp47b9dd52010-11-24 18:12:06 +00003045
cristy3ed852e2009-09-05 21:47:34 +00003046 case 2:
3047 {
cristybb503372010-05-27 20:51:26 +00003048 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003049 {
glennrpa18d5bc2011-04-23 14:51:34 +00003050 *r++=(*p >> 6) & 0x03;
3051 *r++=(*p >> 4) & 0x03;
3052 *r++=(*p >> 2) & 0x03;
3053 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003054 }
glennrp0fe50b42010-11-16 03:52:51 +00003055
cristy3ed852e2009-09-05 21:47:34 +00003056 if ((image->columns % 4) != 0)
3057 {
cristybb503372010-05-27 20:51:26 +00003058 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003059 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003060 }
glennrp0fe50b42010-11-16 03:52:51 +00003061
cristy3ed852e2009-09-05 21:47:34 +00003062 break;
3063 }
glennrp47b9dd52010-11-24 18:12:06 +00003064
cristy3ed852e2009-09-05 21:47:34 +00003065 case 4:
3066 {
cristybb503372010-05-27 20:51:26 +00003067 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003068 {
glennrpa18d5bc2011-04-23 14:51:34 +00003069 *r++=(*p >> 4) & 0x0f;
3070 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003071 }
glennrp0fe50b42010-11-16 03:52:51 +00003072
cristy3ed852e2009-09-05 21:47:34 +00003073 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003074 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003075
cristy3ed852e2009-09-05 21:47:34 +00003076 break;
3077 }
glennrp47b9dd52010-11-24 18:12:06 +00003078
cristy3ed852e2009-09-05 21:47:34 +00003079 case 8:
3080 {
glennrpfaa852b2010-03-30 12:17:00 +00003081 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003082 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003083 {
glennrpa18d5bc2011-04-23 14:51:34 +00003084 *r++=*p++;
cristy4c08aed2011-07-01 19:47:50 +00003085 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3086 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003087 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003088 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003089 }
glennrp0fe50b42010-11-16 03:52:51 +00003090
cristy3ed852e2009-09-05 21:47:34 +00003091 else
cristybb503372010-05-27 20:51:26 +00003092 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003093 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003094
cristy3ed852e2009-09-05 21:47:34 +00003095 break;
3096 }
glennrp47b9dd52010-11-24 18:12:06 +00003097
cristy3ed852e2009-09-05 21:47:34 +00003098 case 16:
3099 {
cristybb503372010-05-27 20:51:26 +00003100 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003101 {
glennrpc17d96f2011-06-27 01:20:11 +00003102#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003103 size_t
3104 quantum;
3105
3106 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003107 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003108
3109 else
glennrpc17d96f2011-06-27 01:20:11 +00003110 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003111
glennrp58f77c72011-04-23 14:09:09 +00003112 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003113 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003114 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003115
3116 if (ping_color_type == 4)
3117 {
glennrpc17d96f2011-06-27 01:20:11 +00003118 if (image->colors > 256)
3119 quantum=((*p++) << 8);
3120 else
3121 quantum=0;
3122
glennrp58f77c72011-04-23 14:09:09 +00003123 quantum|=(*p++);
cristy4c08aed2011-07-01 19:47:50 +00003124 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3125 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003126 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003127 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003128 }
glennrp58f77c72011-04-23 14:09:09 +00003129
3130#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3131 *r++=(*p++);
3132 p++; /* strip low byte */
3133
3134 if (ping_color_type == 4)
3135 {
cristy4c08aed2011-07-01 19:47:50 +00003136 SetPixelAlpha(image,*p++,q);
3137 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003138 found_transparent_pixel = MagickTrue;
3139 p++;
cristyed231572011-07-14 02:18:59 +00003140 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003141 }
cristy3ed852e2009-09-05 21:47:34 +00003142#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003143 }
glennrp47b9dd52010-11-24 18:12:06 +00003144
cristy3ed852e2009-09-05 21:47:34 +00003145 break;
3146 }
glennrp47b9dd52010-11-24 18:12:06 +00003147
cristy3ed852e2009-09-05 21:47:34 +00003148 default:
3149 break;
3150 }
glennrp3faa9a32011-04-23 14:00:25 +00003151
cristy3ed852e2009-09-05 21:47:34 +00003152 /*
3153 Transfer image scanline.
3154 */
3155 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003156
cristy4c08aed2011-07-01 19:47:50 +00003157 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3158
cristyacd2ed22011-08-30 01:44:23 +00003159 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00003160 break;
cristybb503372010-05-27 20:51:26 +00003161 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00003162 {
3163 SetPixelIndex(image,*r++,q);
cristyed231572011-07-14 02:18:59 +00003164 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00003165 }
glennrp0fe50b42010-11-16 03:52:51 +00003166
cristy3ed852e2009-09-05 21:47:34 +00003167 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3168 break;
glennrp0fe50b42010-11-16 03:52:51 +00003169
cristy7a287bf2010-02-14 02:18:09 +00003170 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3171 {
cristycee97112010-05-28 00:44:52 +00003172 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003173 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003174
cristy7a287bf2010-02-14 02:18:09 +00003175 if (status == MagickFalse)
3176 break;
3177 }
cristy3ed852e2009-09-05 21:47:34 +00003178 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003179
cristy7a287bf2010-02-14 02:18:09 +00003180 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003181 {
3182 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003183
cristy3ed852e2009-09-05 21:47:34 +00003184 if (status == MagickFalse)
3185 break;
3186 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003187
cristy3ed852e2009-09-05 21:47:34 +00003188 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3189 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003190
3191 image->matte=found_transparent_pixel;
3192
3193 if (logging != MagickFalse)
3194 {
3195 if (found_transparent_pixel != MagickFalse)
3196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3197 " Found transparent pixel");
3198 else
glennrp5aa37f62011-01-02 03:07:57 +00003199 {
3200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3201 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003202
glennrp5aa37f62011-01-02 03:07:57 +00003203 ping_color_type&=0x03;
3204 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003205 }
3206 }
3207
cristyb32b90a2009-09-07 21:45:48 +00003208 if (quantum_info != (QuantumInfo *) NULL)
3209 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00003210
cristy5c6f7892010-05-05 22:53:29 +00003211 if (image->storage_class == PseudoClass)
3212 {
cristyaeb2cbc2010-05-07 13:28:58 +00003213 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003214 matte;
3215
3216 matte=image->matte;
3217 image->matte=MagickFalse;
3218 (void) SyncImage(image);
cristyaeb2cbc2010-05-07 13:28:58 +00003219 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003220 }
glennrp47b9dd52010-11-24 18:12:06 +00003221
glennrp4eb39312011-03-30 21:34:55 +00003222 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003223
3224 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003225 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003226 {
3227 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003228 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003229 image->colors=2;
3230 (void) SetImageBackgroundColor(image);
3231#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003232 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003233#endif
3234 if (logging != MagickFalse)
3235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3236 " exit ReadOnePNGImage() early.");
3237 return(image);
3238 }
glennrp47b9dd52010-11-24 18:12:06 +00003239
glennrpfaa852b2010-03-30 12:17:00 +00003240 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003241 {
3242 ClassType
3243 storage_class;
3244
3245 /*
3246 Image has a transparent background.
3247 */
3248 storage_class=image->storage_class;
3249 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003250
glennrp3c218112010-11-27 15:31:26 +00003251/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003252
glennrp0fe50b42010-11-16 03:52:51 +00003253 if (storage_class == PseudoClass)
3254 {
3255 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003256 {
glennrp0fe50b42010-11-16 03:52:51 +00003257 for (x=0; x < ping_num_trans; x++)
3258 {
cristy4c08aed2011-07-01 19:47:50 +00003259 image->colormap[x].alpha =
3260 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003261 }
glennrpc11cf6a2010-03-20 16:46:19 +00003262 }
glennrp47b9dd52010-11-24 18:12:06 +00003263
glennrp0fe50b42010-11-16 03:52:51 +00003264 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3265 {
3266 for (x=0; x < (int) image->colors; x++)
3267 {
3268 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy4c08aed2011-07-01 19:47:50 +00003269 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003270 {
cristy4c08aed2011-07-01 19:47:50 +00003271 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003272 }
3273 }
3274 }
3275 (void) SyncImage(image);
3276 }
glennrp47b9dd52010-11-24 18:12:06 +00003277
glennrpa6a06632011-01-19 15:15:34 +00003278#if 1 /* Should have already been done above, but glennrp problem P10
3279 * needs this.
3280 */
glennrp0fe50b42010-11-16 03:52:51 +00003281 else
3282 {
3283 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003284 {
glennrp0fe50b42010-11-16 03:52:51 +00003285 image->storage_class=storage_class;
3286 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3287
cristyacd2ed22011-08-30 01:44:23 +00003288 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003289 break;
3290
glennrp0fe50b42010-11-16 03:52:51 +00003291
glennrpa6a06632011-01-19 15:15:34 +00003292 /* Caution: on a Q8 build, this does not distinguish between
3293 * 16-bit colors that differ only in the low byte
3294 */
glennrp0fe50b42010-11-16 03:52:51 +00003295 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3296 {
glennrp847370c2011-07-05 17:37:15 +00003297 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3298 transparent_color.red &&
3299 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3300 transparent_color.green &&
3301 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3302 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003303 {
cristy4c08aed2011-07-01 19:47:50 +00003304 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003305 }
glennrp0fe50b42010-11-16 03:52:51 +00003306
glennrp67b9c1a2011-04-22 18:47:36 +00003307#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003308 else
glennrp4f25bd02011-01-01 18:51:28 +00003309 {
cristy4c08aed2011-07-01 19:47:50 +00003310 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003311 }
glennrpa6a06632011-01-19 15:15:34 +00003312#endif
glennrp0fe50b42010-11-16 03:52:51 +00003313
cristyed231572011-07-14 02:18:59 +00003314 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003315 }
3316
3317 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3318 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003319 }
glennrp0fe50b42010-11-16 03:52:51 +00003320 }
glennrpa6a06632011-01-19 15:15:34 +00003321#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003322
cristy3ed852e2009-09-05 21:47:34 +00003323 image->storage_class=DirectClass;
3324 }
glennrp3c218112010-11-27 15:31:26 +00003325
cristyb40fc462010-08-08 00:49:49 +00003326 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3327 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3328 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00003329
cristyeb3b22a2011-03-31 20:16:11 +00003330 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003331 {
3332 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003333 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3334 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003335 else
glennrpa0ed0092011-04-18 16:36:29 +00003336 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3337 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003338
glennrp4eb39312011-03-30 21:34:55 +00003339 if (status != MagickFalse)
3340 for (i=0; i < (ssize_t) num_text; i++)
3341 {
3342 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003343
glennrp4eb39312011-03-30 21:34:55 +00003344 if (logging != MagickFalse)
3345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3346 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003347
glennrp4eb39312011-03-30 21:34:55 +00003348 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003349 {
glennrp4eb39312011-03-30 21:34:55 +00003350 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
3351 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003352 }
glennrp0fe50b42010-11-16 03:52:51 +00003353
glennrp4eb39312011-03-30 21:34:55 +00003354 else
3355 {
3356 char
3357 *value;
3358
3359 length=text[i].text_length;
3360 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3361 sizeof(*value));
3362 if (value == (char *) NULL)
3363 {
3364 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3365 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3366 image->filename);
3367 break;
3368 }
3369 *value='\0';
3370 (void) ConcatenateMagickString(value,text[i].text,length+2);
3371
3372 /* Don't save "density" or "units" property if we have a pHYs
3373 * chunk
3374 */
3375 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3376 (LocaleCompare(text[i].key,"density") != 0 &&
3377 LocaleCompare(text[i].key,"units") != 0))
3378 (void) SetImageProperty(image,text[i].key,value);
3379
3380 if (logging != MagickFalse)
3381 {
3382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3383 " length: %lu",(unsigned long) length);
3384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3385 " Keyword: %s",text[i].key);
3386 }
3387
3388 value=DestroyString(value);
3389 }
3390 }
3391 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003392 }
glennrp3c218112010-11-27 15:31:26 +00003393
cristy3ed852e2009-09-05 21:47:34 +00003394#ifdef MNG_OBJECT_BUFFERS
3395 /*
3396 Store the object if necessary.
3397 */
3398 if (object_id && !mng_info->frozen[object_id])
3399 {
3400 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3401 {
3402 /*
3403 create a new object buffer.
3404 */
3405 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003406 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003407
cristy3ed852e2009-09-05 21:47:34 +00003408 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3409 {
3410 mng_info->ob[object_id]->image=(Image *) NULL;
3411 mng_info->ob[object_id]->reference_count=1;
3412 }
3413 }
glennrp47b9dd52010-11-24 18:12:06 +00003414
cristy3ed852e2009-09-05 21:47:34 +00003415 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3416 mng_info->ob[object_id]->frozen)
3417 {
3418 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3419 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3420 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3421 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003422
cristy3ed852e2009-09-05 21:47:34 +00003423 if (mng_info->ob[object_id]->frozen)
3424 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3425 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3426 "`%s'",image->filename);
3427 }
glennrp0fe50b42010-11-16 03:52:51 +00003428
cristy3ed852e2009-09-05 21:47:34 +00003429 else
3430 {
cristy3ed852e2009-09-05 21:47:34 +00003431
3432 if (mng_info->ob[object_id]->image != (Image *) NULL)
3433 mng_info->ob[object_id]->image=DestroyImage
3434 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003435
cristy3ed852e2009-09-05 21:47:34 +00003436 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3437 &image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00003438
cristy3ed852e2009-09-05 21:47:34 +00003439 if (mng_info->ob[object_id]->image != (Image *) NULL)
3440 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003441
cristy3ed852e2009-09-05 21:47:34 +00003442 else
3443 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3444 ResourceLimitError,"Cloning image for object buffer failed",
3445 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003446
glennrpfaa852b2010-03-30 12:17:00 +00003447 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003448 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003449
glennrpfaa852b2010-03-30 12:17:00 +00003450 mng_info->ob[object_id]->width=ping_width;
3451 mng_info->ob[object_id]->height=ping_height;
3452 mng_info->ob[object_id]->color_type=ping_color_type;
3453 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3454 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3455 mng_info->ob[object_id]->compression_method=
3456 ping_compression_method;
3457 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003458
glennrpfaa852b2010-03-30 12:17:00 +00003459 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003460 {
3461 int
3462 number_colors;
3463
3464 png_colorp
3465 plte;
3466
3467 /*
3468 Copy the PLTE to the object buffer.
3469 */
3470 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3471 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003472
cristy3ed852e2009-09-05 21:47:34 +00003473 for (i=0; i < number_colors; i++)
3474 {
3475 mng_info->ob[object_id]->plte[i]=plte[i];
3476 }
3477 }
glennrp47b9dd52010-11-24 18:12:06 +00003478
cristy3ed852e2009-09-05 21:47:34 +00003479 else
3480 mng_info->ob[object_id]->plte_length=0;
3481 }
3482 }
3483#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003484
3485 /* Set image->matte to MagickTrue if the input colortype supports
3486 * alpha or if a valid tRNS chunk is present, no matter whether there
3487 * is actual transparency present.
3488 */
3489 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3490 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3491 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3492 MagickTrue : MagickFalse;
3493
glennrpcb395ac2011-03-30 19:50:23 +00003494 /* Set more properties for identify to retrieve */
3495 {
3496 char
3497 msg[MaxTextExtent];
3498
glennrp4eb39312011-03-30 21:34:55 +00003499 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003500 {
3501 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003502 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003503 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrpcb395ac2011-03-30 19:50:23 +00003504 (void) SetImageProperty(image,"PNG:text ",msg);
3505 }
3506
3507 if (num_raw_profiles != 0)
3508 {
cristy3b6fd2e2011-05-20 12:53:50 +00003509 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003510 "%d were found", num_raw_profiles);
3511 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3512 }
3513
glennrpcb395ac2011-03-30 19:50:23 +00003514 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003515 {
cristy3b6fd2e2011-05-20 12:53:50 +00003516 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003517 "chunk was found (see Chromaticity, above)");
3518 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3519 }
glennrpcb395ac2011-03-30 19:50:23 +00003520
3521 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003522 {
cristy3b6fd2e2011-05-20 12:53:50 +00003523 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003524 "chunk was found (see Background color, above)");
3525 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3526 }
3527
cristy3b6fd2e2011-05-20 12:53:50 +00003528 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003529 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003530
3531 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3532 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3533
glennrpcb395ac2011-03-30 19:50:23 +00003534 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3535 (void) SetImageProperty(image,"PNG:tRNS ",msg);
glennrp4eb39312011-03-30 21:34:55 +00003536
3537#if defined(PNG_sRGB_SUPPORTED)
3538 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3539 {
cristy3b6fd2e2011-05-20 12:53:50 +00003540 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003541 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003542 (int) intent);
3543 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3544 }
3545#endif
3546
3547 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3548 {
cristy3b6fd2e2011-05-20 12:53:50 +00003549 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003550 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003551 file_gamma);
3552 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3553 }
3554
3555#if defined(PNG_pHYs_SUPPORTED)
3556 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3557 {
cristy3b6fd2e2011-05-20 12:53:50 +00003558 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003559 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003560 (double) x_resolution,(double) y_resolution, unit_type);
3561 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3562 }
3563#endif
3564
3565#if defined(PNG_oFFs_SUPPORTED)
3566 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3567 {
cristy3b6fd2e2011-05-20 12:53:50 +00003568 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003569 (double) image->page.x,(double) image->page.y);
3570 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3571 }
3572#endif
3573
glennrp07523c72011-03-31 18:12:10 +00003574 if ((image->page.width != 0 && image->page.width != image->columns) ||
3575 (image->page.height != 0 && image->page.height != image->rows))
3576 {
cristy3b6fd2e2011-05-20 12:53:50 +00003577 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003578 "width=%.20g, height=%.20g",
3579 (double) image->page.width,(double) image->page.height);
3580 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3581 }
glennrpcb395ac2011-03-30 19:50:23 +00003582 }
3583
cristy3ed852e2009-09-05 21:47:34 +00003584 /*
3585 Relinquish resources.
3586 */
3587 png_destroy_read_struct(&ping,&ping_info,&end_info);
3588
glennrpcf002022011-01-30 02:38:15 +00003589 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003590#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003591 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003592#endif
3593
3594 if (logging != MagickFalse)
3595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3596 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003597
cristy3ed852e2009-09-05 21:47:34 +00003598 return(image);
3599
3600/* end of reading one PNG image */
3601}
3602
3603static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3604{
3605 Image
3606 *image,
3607 *previous;
3608
3609 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003610 have_mng_structure,
3611 logging,
cristy3ed852e2009-09-05 21:47:34 +00003612 status;
3613
3614 MngInfo
3615 *mng_info;
3616
3617 char
3618 magic_number[MaxTextExtent];
3619
cristy3ed852e2009-09-05 21:47:34 +00003620 ssize_t
3621 count;
3622
3623 /*
3624 Open image file.
3625 */
3626 assert(image_info != (const ImageInfo *) NULL);
3627 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003628
cristy3ed852e2009-09-05 21:47:34 +00003629 if (image_info->debug != MagickFalse)
3630 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3631 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003632
cristy3ed852e2009-09-05 21:47:34 +00003633 assert(exception != (ExceptionInfo *) NULL);
3634 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003635 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003636 image=AcquireImage(image_info);
3637 mng_info=(MngInfo *) NULL;
3638 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003639
cristy3ed852e2009-09-05 21:47:34 +00003640 if (status == MagickFalse)
3641 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003642
cristy3ed852e2009-09-05 21:47:34 +00003643 /*
3644 Verify PNG signature.
3645 */
3646 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003647
glennrpdde35db2011-02-21 12:06:32 +00003648 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003649 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003650
cristy3ed852e2009-09-05 21:47:34 +00003651 /*
3652 Allocate a MngInfo structure.
3653 */
3654 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003655 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003656
cristy3ed852e2009-09-05 21:47:34 +00003657 if (mng_info == (MngInfo *) NULL)
3658 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003659
cristy3ed852e2009-09-05 21:47:34 +00003660 /*
3661 Initialize members of the MngInfo structure.
3662 */
3663 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3664 mng_info->image=image;
3665 have_mng_structure=MagickTrue;
3666
3667 previous=image;
3668 image=ReadOnePNGImage(mng_info,image_info,exception);
3669 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003670
cristy3ed852e2009-09-05 21:47:34 +00003671 if (image == (Image *) NULL)
3672 {
3673 if (previous != (Image *) NULL)
3674 {
3675 if (previous->signature != MagickSignature)
3676 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003677
cristy3ed852e2009-09-05 21:47:34 +00003678 (void) CloseBlob(previous);
3679 (void) DestroyImageList(previous);
3680 }
glennrp0fe50b42010-11-16 03:52:51 +00003681
cristy3ed852e2009-09-05 21:47:34 +00003682 if (logging != MagickFalse)
3683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3684 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003685
cristy3ed852e2009-09-05 21:47:34 +00003686 return((Image *) NULL);
3687 }
glennrp47b9dd52010-11-24 18:12:06 +00003688
cristy3ed852e2009-09-05 21:47:34 +00003689 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003690
cristy3ed852e2009-09-05 21:47:34 +00003691 if ((image->columns == 0) || (image->rows == 0))
3692 {
3693 if (logging != MagickFalse)
3694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3695 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003696
cristy3ed852e2009-09-05 21:47:34 +00003697 ThrowReaderException(CorruptImageError,"CorruptImage");
3698 }
glennrp47b9dd52010-11-24 18:12:06 +00003699
cristy3ed852e2009-09-05 21:47:34 +00003700 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3701 {
cristy018f07f2011-09-04 21:15:19 +00003702 (void) SetImageType(image,TrueColorType,exception);
cristy3ed852e2009-09-05 21:47:34 +00003703 image->matte=MagickFalse;
3704 }
glennrp0fe50b42010-11-16 03:52:51 +00003705
cristy3ed852e2009-09-05 21:47:34 +00003706 if (LocaleCompare(image_info->magick,"PNG32") == 0)
cristy018f07f2011-09-04 21:15:19 +00003707 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003708
cristy3ed852e2009-09-05 21:47:34 +00003709 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3711 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3712 (double) image->page.width,(double) image->page.height,
3713 (double) image->page.x,(double) image->page.y);
3714
3715 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003717
cristy3ed852e2009-09-05 21:47:34 +00003718 return(image);
3719}
3720
3721
3722
3723#if defined(JNG_SUPPORTED)
3724/*
3725%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3726% %
3727% %
3728% %
3729% R e a d O n e J N G I m a g e %
3730% %
3731% %
3732% %
3733%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3734%
3735% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3736% (minus the 8-byte signature) and returns it. It allocates the memory
3737% necessary for the new Image structure and returns a pointer to the new
3738% image.
3739%
3740% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3741%
3742% The format of the ReadOneJNGImage method is:
3743%
3744% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3745% ExceptionInfo *exception)
3746%
3747% A description of each parameter follows:
3748%
3749% o mng_info: Specifies a pointer to a MngInfo structure.
3750%
3751% o image_info: the image info.
3752%
3753% o exception: return any errors or warnings in this structure.
3754%
3755*/
3756static Image *ReadOneJNGImage(MngInfo *mng_info,
3757 const ImageInfo *image_info, ExceptionInfo *exception)
3758{
3759 Image
3760 *alpha_image,
3761 *color_image,
3762 *image,
3763 *jng_image;
3764
3765 ImageInfo
3766 *alpha_image_info,
3767 *color_image_info;
3768
cristy4383ec82011-01-05 15:42:32 +00003769 MagickBooleanType
3770 logging;
3771
cristybb503372010-05-27 20:51:26 +00003772 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003773 y;
3774
3775 MagickBooleanType
3776 status;
3777
3778 png_uint_32
3779 jng_height,
3780 jng_width;
3781
3782 png_byte
3783 jng_color_type,
3784 jng_image_sample_depth,
3785 jng_image_compression_method,
3786 jng_image_interlace_method,
3787 jng_alpha_sample_depth,
3788 jng_alpha_compression_method,
3789 jng_alpha_filter_method,
3790 jng_alpha_interlace_method;
3791
cristy4c08aed2011-07-01 19:47:50 +00003792 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003793 *s;
3794
cristybb503372010-05-27 20:51:26 +00003795 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003796 i,
3797 x;
3798
cristy4c08aed2011-07-01 19:47:50 +00003799 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003800 *q;
3801
3802 register unsigned char
3803 *p;
3804
3805 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003806 read_JSEP,
3807 reading_idat,
3808 skip_to_iend;
3809
cristybb503372010-05-27 20:51:26 +00003810 size_t
cristy3ed852e2009-09-05 21:47:34 +00003811 length;
3812
3813 jng_alpha_compression_method=0;
3814 jng_alpha_sample_depth=8;
3815 jng_color_type=0;
3816 jng_height=0;
3817 jng_width=0;
3818 alpha_image=(Image *) NULL;
3819 color_image=(Image *) NULL;
3820 alpha_image_info=(ImageInfo *) NULL;
3821 color_image_info=(ImageInfo *) NULL;
3822
3823 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003824 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003825
3826 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003827
cristy4c08aed2011-07-01 19:47:50 +00003828 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003829 {
3830 /*
3831 Allocate next image structure.
3832 */
3833 if (logging != MagickFalse)
3834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3835 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003836
cristy3ed852e2009-09-05 21:47:34 +00003837 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00003838
cristy3ed852e2009-09-05 21:47:34 +00003839 if (GetNextImageInList(image) == (Image *) NULL)
3840 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003841
cristy3ed852e2009-09-05 21:47:34 +00003842 image=SyncNextImageInList(image);
3843 }
3844 mng_info->image=image;
3845
3846 /*
3847 Signature bytes have already been read.
3848 */
3849
3850 read_JSEP=MagickFalse;
3851 reading_idat=MagickFalse;
3852 skip_to_iend=MagickFalse;
3853 for (;;)
3854 {
3855 char
3856 type[MaxTextExtent];
3857
3858 unsigned char
3859 *chunk;
3860
3861 unsigned int
3862 count;
3863
3864 /*
3865 Read a new JNG chunk.
3866 */
3867 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3868 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003869
cristy3ed852e2009-09-05 21:47:34 +00003870 if (status == MagickFalse)
3871 break;
glennrp0fe50b42010-11-16 03:52:51 +00003872
cristy3ed852e2009-09-05 21:47:34 +00003873 type[0]='\0';
3874 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3875 length=ReadBlobMSBLong(image);
3876 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3877
3878 if (logging != MagickFalse)
3879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003880 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3881 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003882
3883 if (length > PNG_UINT_31_MAX || count == 0)
3884 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003885
cristy3ed852e2009-09-05 21:47:34 +00003886 p=NULL;
3887 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003888
cristy3ed852e2009-09-05 21:47:34 +00003889 if (length)
3890 {
3891 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003892
cristy3ed852e2009-09-05 21:47:34 +00003893 if (chunk == (unsigned char *) NULL)
3894 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003895
cristybb503372010-05-27 20:51:26 +00003896 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003897 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003898
cristy3ed852e2009-09-05 21:47:34 +00003899 p=chunk;
3900 }
glennrp47b9dd52010-11-24 18:12:06 +00003901
cristy3ed852e2009-09-05 21:47:34 +00003902 (void) ReadBlobMSBLong(image); /* read crc word */
3903
3904 if (skip_to_iend)
3905 {
3906 if (length)
3907 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003908
cristy3ed852e2009-09-05 21:47:34 +00003909 continue;
3910 }
3911
3912 if (memcmp(type,mng_JHDR,4) == 0)
3913 {
3914 if (length == 16)
3915 {
cristybb503372010-05-27 20:51:26 +00003916 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003917 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003918 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003919 (p[6] << 8) | p[7]);
3920 jng_color_type=p[8];
3921 jng_image_sample_depth=p[9];
3922 jng_image_compression_method=p[10];
3923 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003924
cristy3ed852e2009-09-05 21:47:34 +00003925 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3926 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003927
cristy3ed852e2009-09-05 21:47:34 +00003928 jng_alpha_sample_depth=p[12];
3929 jng_alpha_compression_method=p[13];
3930 jng_alpha_filter_method=p[14];
3931 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003932
cristy3ed852e2009-09-05 21:47:34 +00003933 if (logging != MagickFalse)
3934 {
3935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003936 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003937
cristy3ed852e2009-09-05 21:47:34 +00003938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003939 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003940
cristy3ed852e2009-09-05 21:47:34 +00003941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3942 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003943
cristy3ed852e2009-09-05 21:47:34 +00003944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3945 " jng_image_sample_depth: %3d",
3946 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003947
cristy3ed852e2009-09-05 21:47:34 +00003948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3949 " jng_image_compression_method:%3d",
3950 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003951
cristy3ed852e2009-09-05 21:47:34 +00003952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3953 " jng_image_interlace_method: %3d",
3954 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003955
cristy3ed852e2009-09-05 21:47:34 +00003956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3957 " jng_alpha_sample_depth: %3d",
3958 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003959
cristy3ed852e2009-09-05 21:47:34 +00003960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3961 " jng_alpha_compression_method:%3d",
3962 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003963
cristy3ed852e2009-09-05 21:47:34 +00003964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3965 " jng_alpha_filter_method: %3d",
3966 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00003967
cristy3ed852e2009-09-05 21:47:34 +00003968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3969 " jng_alpha_interlace_method: %3d",
3970 jng_alpha_interlace_method);
3971 }
3972 }
glennrp47b9dd52010-11-24 18:12:06 +00003973
cristy3ed852e2009-09-05 21:47:34 +00003974 if (length)
3975 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003976
cristy3ed852e2009-09-05 21:47:34 +00003977 continue;
3978 }
3979
3980
3981 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3982 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3983 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3984 {
3985 /*
3986 o create color_image
3987 o open color_blob, attached to color_image
3988 o if (color type has alpha)
3989 open alpha_blob, attached to alpha_image
3990 */
3991
cristy73bd4a52010-10-05 11:24:23 +00003992 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003993
cristy3ed852e2009-09-05 21:47:34 +00003994 if (color_image_info == (ImageInfo *) NULL)
3995 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003996
cristy3ed852e2009-09-05 21:47:34 +00003997 GetImageInfo(color_image_info);
3998 color_image=AcquireImage(color_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003999
cristy3ed852e2009-09-05 21:47:34 +00004000 if (color_image == (Image *) NULL)
4001 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4002
4003 if (logging != MagickFalse)
4004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4005 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004006
cristy3ed852e2009-09-05 21:47:34 +00004007 (void) AcquireUniqueFilename(color_image->filename);
4008 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4009 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004010
cristy3ed852e2009-09-05 21:47:34 +00004011 if (status == MagickFalse)
4012 return((Image *) NULL);
4013
4014 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4015 {
4016 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004017 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004018
cristy3ed852e2009-09-05 21:47:34 +00004019 if (alpha_image_info == (ImageInfo *) NULL)
4020 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004021
cristy3ed852e2009-09-05 21:47:34 +00004022 GetImageInfo(alpha_image_info);
4023 alpha_image=AcquireImage(alpha_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00004024
cristy3ed852e2009-09-05 21:47:34 +00004025 if (alpha_image == (Image *) NULL)
4026 {
4027 alpha_image=DestroyImage(alpha_image);
4028 ThrowReaderException(ResourceLimitError,
4029 "MemoryAllocationFailed");
4030 }
glennrp0fe50b42010-11-16 03:52:51 +00004031
cristy3ed852e2009-09-05 21:47:34 +00004032 if (logging != MagickFalse)
4033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4034 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004035
cristy3ed852e2009-09-05 21:47:34 +00004036 (void) AcquireUniqueFilename(alpha_image->filename);
4037 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4038 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004039
cristy3ed852e2009-09-05 21:47:34 +00004040 if (status == MagickFalse)
4041 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004042
cristy3ed852e2009-09-05 21:47:34 +00004043 if (jng_alpha_compression_method == 0)
4044 {
4045 unsigned char
4046 data[18];
4047
4048 if (logging != MagickFalse)
4049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4050 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004051
cristy3ed852e2009-09-05 21:47:34 +00004052 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4053 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004054
cristy3ed852e2009-09-05 21:47:34 +00004055 (void) WriteBlobMSBULong(alpha_image,13L);
4056 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004057 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004058 PNGLong(data+4,jng_width);
4059 PNGLong(data+8,jng_height);
4060 data[12]=jng_alpha_sample_depth;
4061 data[13]=0; /* color_type gray */
4062 data[14]=0; /* compression method 0 */
4063 data[15]=0; /* filter_method 0 */
4064 data[16]=0; /* interlace_method 0 */
4065 (void) WriteBlob(alpha_image,17,data);
4066 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4067 }
4068 }
4069 reading_idat=MagickTrue;
4070 }
4071
4072 if (memcmp(type,mng_JDAT,4) == 0)
4073 {
glennrp47b9dd52010-11-24 18:12:06 +00004074 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004075
4076 if (logging != MagickFalse)
4077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4078 " Copying JDAT chunk data to color_blob.");
4079
4080 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004081
cristy3ed852e2009-09-05 21:47:34 +00004082 if (length)
4083 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004084
cristy3ed852e2009-09-05 21:47:34 +00004085 continue;
4086 }
4087
4088 if (memcmp(type,mng_IDAT,4) == 0)
4089 {
4090 png_byte
4091 data[5];
4092
glennrp47b9dd52010-11-24 18:12:06 +00004093 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004094
4095 if (image_info->ping == MagickFalse)
4096 {
4097 if (logging != MagickFalse)
4098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4099 " Copying IDAT chunk data to alpha_blob.");
4100
cristybb503372010-05-27 20:51:26 +00004101 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004102 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004103 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004104 (void) WriteBlob(alpha_image,4,data);
4105 (void) WriteBlob(alpha_image,length,chunk);
4106 (void) WriteBlobMSBULong(alpha_image,
4107 crc32(crc32(0,data,4),chunk,(uInt) length));
4108 }
glennrp0fe50b42010-11-16 03:52:51 +00004109
cristy3ed852e2009-09-05 21:47:34 +00004110 if (length)
4111 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004112
cristy3ed852e2009-09-05 21:47:34 +00004113 continue;
4114 }
4115
4116 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4117 {
glennrp47b9dd52010-11-24 18:12:06 +00004118 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004119
4120 if (image_info->ping == MagickFalse)
4121 {
4122 if (logging != MagickFalse)
4123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4124 " Copying JDAA chunk data to alpha_blob.");
4125
4126 (void) WriteBlob(alpha_image,length,chunk);
4127 }
glennrp0fe50b42010-11-16 03:52:51 +00004128
cristy3ed852e2009-09-05 21:47:34 +00004129 if (length)
4130 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004131
cristy3ed852e2009-09-05 21:47:34 +00004132 continue;
4133 }
4134
4135 if (memcmp(type,mng_JSEP,4) == 0)
4136 {
4137 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004138
cristy3ed852e2009-09-05 21:47:34 +00004139 if (length)
4140 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004141
cristy3ed852e2009-09-05 21:47:34 +00004142 continue;
4143 }
4144
4145 if (memcmp(type,mng_bKGD,4) == 0)
4146 {
4147 if (length == 2)
4148 {
4149 image->background_color.red=ScaleCharToQuantum(p[1]);
4150 image->background_color.green=image->background_color.red;
4151 image->background_color.blue=image->background_color.red;
4152 }
glennrp0fe50b42010-11-16 03:52:51 +00004153
cristy3ed852e2009-09-05 21:47:34 +00004154 if (length == 6)
4155 {
4156 image->background_color.red=ScaleCharToQuantum(p[1]);
4157 image->background_color.green=ScaleCharToQuantum(p[3]);
4158 image->background_color.blue=ScaleCharToQuantum(p[5]);
4159 }
glennrp0fe50b42010-11-16 03:52:51 +00004160
cristy3ed852e2009-09-05 21:47:34 +00004161 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4162 continue;
4163 }
4164
4165 if (memcmp(type,mng_gAMA,4) == 0)
4166 {
4167 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004168 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004169
cristy3ed852e2009-09-05 21:47:34 +00004170 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4171 continue;
4172 }
4173
4174 if (memcmp(type,mng_cHRM,4) == 0)
4175 {
4176 if (length == 32)
4177 {
cristy8182b072010-05-30 20:10:53 +00004178 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4179 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4180 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4181 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4182 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4183 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4184 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4185 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004186 }
glennrp47b9dd52010-11-24 18:12:06 +00004187
cristy3ed852e2009-09-05 21:47:34 +00004188 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4189 continue;
4190 }
4191
4192 if (memcmp(type,mng_sRGB,4) == 0)
4193 {
4194 if (length == 1)
4195 {
glennrpe610a072010-08-05 17:08:46 +00004196 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004197 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004198 image->gamma=0.45455f;
4199 image->chromaticity.red_primary.x=0.6400f;
4200 image->chromaticity.red_primary.y=0.3300f;
4201 image->chromaticity.green_primary.x=0.3000f;
4202 image->chromaticity.green_primary.y=0.6000f;
4203 image->chromaticity.blue_primary.x=0.1500f;
4204 image->chromaticity.blue_primary.y=0.0600f;
4205 image->chromaticity.white_point.x=0.3127f;
4206 image->chromaticity.white_point.y=0.3290f;
4207 }
glennrp47b9dd52010-11-24 18:12:06 +00004208
cristy3ed852e2009-09-05 21:47:34 +00004209 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4210 continue;
4211 }
4212
4213 if (memcmp(type,mng_oFFs,4) == 0)
4214 {
4215 if (length > 8)
4216 {
glennrp5eae7602011-02-22 15:21:32 +00004217 image->page.x=(ssize_t) mng_get_long(p);
4218 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004219
cristy3ed852e2009-09-05 21:47:34 +00004220 if ((int) p[8] != 0)
4221 {
4222 image->page.x/=10000;
4223 image->page.y/=10000;
4224 }
4225 }
glennrp47b9dd52010-11-24 18:12:06 +00004226
cristy3ed852e2009-09-05 21:47:34 +00004227 if (length)
4228 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004229
cristy3ed852e2009-09-05 21:47:34 +00004230 continue;
4231 }
4232
4233 if (memcmp(type,mng_pHYs,4) == 0)
4234 {
4235 if (length > 8)
4236 {
cristy8182b072010-05-30 20:10:53 +00004237 image->x_resolution=(double) mng_get_long(p);
4238 image->y_resolution=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004239 if ((int) p[8] == PNG_RESOLUTION_METER)
4240 {
4241 image->units=PixelsPerCentimeterResolution;
4242 image->x_resolution=image->x_resolution/100.0f;
4243 image->y_resolution=image->y_resolution/100.0f;
4244 }
4245 }
glennrp0fe50b42010-11-16 03:52:51 +00004246
cristy3ed852e2009-09-05 21:47:34 +00004247 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4248 continue;
4249 }
4250
4251#if 0
4252 if (memcmp(type,mng_iCCP,4) == 0)
4253 {
glennrpfd05d622011-02-25 04:10:33 +00004254 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004255 if (length)
4256 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004257
cristy3ed852e2009-09-05 21:47:34 +00004258 continue;
4259 }
4260#endif
4261
4262 if (length)
4263 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4264
4265 if (memcmp(type,mng_IEND,4))
4266 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004267
cristy3ed852e2009-09-05 21:47:34 +00004268 break;
4269 }
4270
4271
4272 /* IEND found */
4273
4274 /*
4275 Finish up reading image data:
4276
4277 o read main image from color_blob.
4278
4279 o close color_blob.
4280
4281 o if (color_type has alpha)
4282 if alpha_encoding is PNG
4283 read secondary image from alpha_blob via ReadPNG
4284 if alpha_encoding is JPEG
4285 read secondary image from alpha_blob via ReadJPEG
4286
4287 o close alpha_blob.
4288
4289 o copy intensity of secondary image into
cristy4c08aed2011-07-01 19:47:50 +00004290 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004291
4292 o destroy the secondary image.
4293 */
4294
4295 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004296
cristy3ed852e2009-09-05 21:47:34 +00004297 if (logging != MagickFalse)
4298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4299 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004300
cristy3b6fd2e2011-05-20 12:53:50 +00004301 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004302 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004303
cristy3ed852e2009-09-05 21:47:34 +00004304 color_image_info->ping=MagickFalse; /* To do: avoid this */
4305 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004306
cristy3ed852e2009-09-05 21:47:34 +00004307 if (jng_image == (Image *) NULL)
4308 return((Image *) NULL);
4309
4310 (void) RelinquishUniqueFileResource(color_image->filename);
4311 color_image=DestroyImage(color_image);
4312 color_image_info=DestroyImageInfo(color_image_info);
4313
4314 if (jng_image == (Image *) NULL)
4315 return((Image *) NULL);
4316
4317 if (logging != MagickFalse)
4318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4319 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004320
cristy3ed852e2009-09-05 21:47:34 +00004321 image->rows=jng_height;
4322 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004323
cristybb503372010-05-27 20:51:26 +00004324 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004325 {
4326 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
4327 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004328 for (x=(ssize_t) image->columns; x != 0; x--)
4329 {
4330 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4331 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4332 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004333 q+=GetPixelChannels(image);
4334 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004335 }
glennrp47b9dd52010-11-24 18:12:06 +00004336
cristy3ed852e2009-09-05 21:47:34 +00004337 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4338 break;
4339 }
glennrp0fe50b42010-11-16 03:52:51 +00004340
cristy3ed852e2009-09-05 21:47:34 +00004341 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004342
cristy3ed852e2009-09-05 21:47:34 +00004343 if (image_info->ping == MagickFalse)
4344 {
4345 if (jng_color_type >= 12)
4346 {
4347 if (jng_alpha_compression_method == 0)
4348 {
4349 png_byte
4350 data[5];
4351 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4352 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004353 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004354 (void) WriteBlob(alpha_image,4,data);
4355 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4356 }
glennrp0fe50b42010-11-16 03:52:51 +00004357
cristy3ed852e2009-09-05 21:47:34 +00004358 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004359
cristy3ed852e2009-09-05 21:47:34 +00004360 if (logging != MagickFalse)
4361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00004362 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004363
cristy3b6fd2e2011-05-20 12:53:50 +00004364 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004365 "%s",alpha_image->filename);
4366
4367 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004368
cristy3ed852e2009-09-05 21:47:34 +00004369 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004370 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004371 {
4372 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristy4c08aed2011-07-01 19:47:50 +00004373 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +00004374 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004375
cristy3ed852e2009-09-05 21:47:34 +00004376 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00004377 for (x=(ssize_t) image->columns; x != 0; x--)
4378 {
4379 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004380 q+=GetPixelChannels(image);
4381 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004382 }
glennrp0fe50b42010-11-16 03:52:51 +00004383
cristy3ed852e2009-09-05 21:47:34 +00004384 else
cristy4c08aed2011-07-01 19:47:50 +00004385 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004386 {
cristy4c08aed2011-07-01 19:47:50 +00004387 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4388 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004389 image->matte=MagickTrue;
cristyed231572011-07-14 02:18:59 +00004390 q+=GetPixelChannels(image);
4391 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004392 }
glennrp0fe50b42010-11-16 03:52:51 +00004393
cristy3ed852e2009-09-05 21:47:34 +00004394 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4395 break;
4396 }
4397 (void) RelinquishUniqueFileResource(alpha_image->filename);
4398 alpha_image=DestroyImage(alpha_image);
4399 alpha_image_info=DestroyImageInfo(alpha_image_info);
4400 if (jng_image != (Image *) NULL)
4401 jng_image=DestroyImage(jng_image);
4402 }
4403 }
4404
glennrp47b9dd52010-11-24 18:12:06 +00004405 /* Read the JNG image. */
4406
cristy3ed852e2009-09-05 21:47:34 +00004407 if (mng_info->mng_type == 0)
4408 {
4409 mng_info->mng_width=jng_width;
4410 mng_info->mng_height=jng_height;
4411 }
glennrp0fe50b42010-11-16 03:52:51 +00004412
cristy3ed852e2009-09-05 21:47:34 +00004413 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004414 {
4415 image->page.width=jng_width;
4416 image->page.height=jng_height;
4417 }
4418
cristy3ed852e2009-09-05 21:47:34 +00004419 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004420 {
4421 image->page.x=mng_info->x_off[mng_info->object_id];
4422 image->page.y=mng_info->y_off[mng_info->object_id];
4423 }
4424
cristy3ed852e2009-09-05 21:47:34 +00004425 else
glennrp0fe50b42010-11-16 03:52:51 +00004426 {
4427 image->page.y=mng_info->y_off[mng_info->object_id];
4428 }
4429
cristy3ed852e2009-09-05 21:47:34 +00004430 mng_info->image_found++;
4431 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4432 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004433
cristy3ed852e2009-09-05 21:47:34 +00004434 if (logging != MagickFalse)
4435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4436 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004437
cristy3ed852e2009-09-05 21:47:34 +00004438 return(image);
4439}
4440
4441/*
4442%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4443% %
4444% %
4445% %
4446% R e a d J N G I m a g e %
4447% %
4448% %
4449% %
4450%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4451%
4452% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4453% (including the 8-byte signature) and returns it. It allocates the memory
4454% necessary for the new Image structure and returns a pointer to the new
4455% image.
4456%
4457% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4458%
4459% The format of the ReadJNGImage method is:
4460%
4461% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4462% *exception)
4463%
4464% A description of each parameter follows:
4465%
4466% o image_info: the image info.
4467%
4468% o exception: return any errors or warnings in this structure.
4469%
4470*/
4471
4472static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4473{
4474 Image
4475 *image,
4476 *previous;
4477
4478 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004479 have_mng_structure,
4480 logging,
cristy3ed852e2009-09-05 21:47:34 +00004481 status;
4482
4483 MngInfo
4484 *mng_info;
4485
4486 char
4487 magic_number[MaxTextExtent];
4488
cristy3ed852e2009-09-05 21:47:34 +00004489 size_t
4490 count;
4491
4492 /*
4493 Open image file.
4494 */
4495 assert(image_info != (const ImageInfo *) NULL);
4496 assert(image_info->signature == MagickSignature);
4497 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4498 assert(exception != (ExceptionInfo *) NULL);
4499 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004500 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004501 image=AcquireImage(image_info);
4502 mng_info=(MngInfo *) NULL;
4503 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004504
cristy3ed852e2009-09-05 21:47:34 +00004505 if (status == MagickFalse)
4506 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004507
cristy3ed852e2009-09-05 21:47:34 +00004508 if (LocaleCompare(image_info->magick,"JNG") != 0)
4509 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004510
glennrp47b9dd52010-11-24 18:12:06 +00004511 /* Verify JNG signature. */
4512
cristy3ed852e2009-09-05 21:47:34 +00004513 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004514
glennrp3b8763e2011-02-21 12:08:18 +00004515 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004516 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004517
glennrp47b9dd52010-11-24 18:12:06 +00004518 /* Allocate a MngInfo structure. */
4519
cristy3ed852e2009-09-05 21:47:34 +00004520 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004521 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004522
cristy3ed852e2009-09-05 21:47:34 +00004523 if (mng_info == (MngInfo *) NULL)
4524 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004525
glennrp47b9dd52010-11-24 18:12:06 +00004526 /* Initialize members of the MngInfo structure. */
4527
cristy3ed852e2009-09-05 21:47:34 +00004528 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4529 have_mng_structure=MagickTrue;
4530
4531 mng_info->image=image;
4532 previous=image;
4533 image=ReadOneJNGImage(mng_info,image_info,exception);
4534 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004535
cristy3ed852e2009-09-05 21:47:34 +00004536 if (image == (Image *) NULL)
4537 {
4538 if (IsImageObject(previous) != MagickFalse)
4539 {
4540 (void) CloseBlob(previous);
4541 (void) DestroyImageList(previous);
4542 }
glennrp0fe50b42010-11-16 03:52:51 +00004543
cristy3ed852e2009-09-05 21:47:34 +00004544 if (logging != MagickFalse)
4545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4546 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004547
cristy3ed852e2009-09-05 21:47:34 +00004548 return((Image *) NULL);
4549 }
4550 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004551
cristy3ed852e2009-09-05 21:47:34 +00004552 if (image->columns == 0 || image->rows == 0)
4553 {
4554 if (logging != MagickFalse)
4555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4556 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004557
cristy3ed852e2009-09-05 21:47:34 +00004558 ThrowReaderException(CorruptImageError,"CorruptImage");
4559 }
glennrp0fe50b42010-11-16 03:52:51 +00004560
cristy3ed852e2009-09-05 21:47:34 +00004561 if (logging != MagickFalse)
4562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004563
cristy3ed852e2009-09-05 21:47:34 +00004564 return(image);
4565}
4566#endif
4567
4568static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4569{
4570 char
4571 page_geometry[MaxTextExtent];
4572
4573 Image
4574 *image,
4575 *previous;
4576
cristy4383ec82011-01-05 15:42:32 +00004577 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004578 logging,
4579 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004580
cristy3ed852e2009-09-05 21:47:34 +00004581 volatile int
4582 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004583 object_id,
4584 term_chunk_found,
4585 skip_to_iend;
4586
cristybb503372010-05-27 20:51:26 +00004587 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004588 image_count=0;
4589
4590 MagickBooleanType
4591 status;
4592
4593 MagickOffsetType
4594 offset;
4595
4596 MngInfo
4597 *mng_info;
4598
4599 MngBox
4600 default_fb,
4601 fb,
4602 previous_fb;
4603
4604#if defined(MNG_INSERT_LAYERS)
4605 PixelPacket
4606 mng_background_color;
4607#endif
4608
4609 register unsigned char
4610 *p;
4611
cristybb503372010-05-27 20:51:26 +00004612 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004613 i;
4614
4615 size_t
4616 count;
4617
cristybb503372010-05-27 20:51:26 +00004618 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004619 loop_level;
4620
4621 volatile short
4622 skipping_loop;
4623
4624#if defined(MNG_INSERT_LAYERS)
4625 unsigned int
4626 mandatory_back=0;
4627#endif
4628
4629 volatile unsigned int
4630#ifdef MNG_OBJECT_BUFFERS
4631 mng_background_object=0,
4632#endif
4633 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4634
cristybb503372010-05-27 20:51:26 +00004635 size_t
cristy3ed852e2009-09-05 21:47:34 +00004636 default_frame_timeout,
4637 frame_timeout,
4638#if defined(MNG_INSERT_LAYERS)
4639 image_height,
4640 image_width,
4641#endif
4642 length;
4643
glennrp38ea0832010-06-02 18:50:28 +00004644 /* These delays are all measured in image ticks_per_second,
4645 * not in MNG ticks_per_second
4646 */
cristybb503372010-05-27 20:51:26 +00004647 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004648 default_frame_delay,
4649 final_delay,
4650 final_image_delay,
4651 frame_delay,
4652#if defined(MNG_INSERT_LAYERS)
4653 insert_layers,
4654#endif
4655 mng_iterations=1,
4656 simplicity=0,
4657 subframe_height=0,
4658 subframe_width=0;
4659
4660 previous_fb.top=0;
4661 previous_fb.bottom=0;
4662 previous_fb.left=0;
4663 previous_fb.right=0;
4664 default_fb.top=0;
4665 default_fb.bottom=0;
4666 default_fb.left=0;
4667 default_fb.right=0;
4668
glennrp47b9dd52010-11-24 18:12:06 +00004669 /* Open image file. */
4670
cristy3ed852e2009-09-05 21:47:34 +00004671 assert(image_info != (const ImageInfo *) NULL);
4672 assert(image_info->signature == MagickSignature);
4673 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4674 assert(exception != (ExceptionInfo *) NULL);
4675 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004676 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004677 image=AcquireImage(image_info);
4678 mng_info=(MngInfo *) NULL;
4679 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004680
cristy3ed852e2009-09-05 21:47:34 +00004681 if (status == MagickFalse)
4682 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004683
cristy3ed852e2009-09-05 21:47:34 +00004684 first_mng_object=MagickFalse;
4685 skipping_loop=(-1);
4686 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004687
4688 /* Allocate a MngInfo structure. */
4689
cristy73bd4a52010-10-05 11:24:23 +00004690 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004691
cristy3ed852e2009-09-05 21:47:34 +00004692 if (mng_info == (MngInfo *) NULL)
4693 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004694
glennrp47b9dd52010-11-24 18:12:06 +00004695 /* Initialize members of the MngInfo structure. */
4696
cristy3ed852e2009-09-05 21:47:34 +00004697 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4698 mng_info->image=image;
4699 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004700
4701 if (LocaleCompare(image_info->magick,"MNG") == 0)
4702 {
4703 char
4704 magic_number[MaxTextExtent];
4705
glennrp47b9dd52010-11-24 18:12:06 +00004706 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004707 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4708 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4709 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004710
4711 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004712 for (i=0; i < MNG_MAX_OBJECTS; i++)
4713 {
cristybb503372010-05-27 20:51:26 +00004714 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4715 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004716 }
4717 mng_info->exists[0]=MagickTrue;
4718 }
glennrp47b9dd52010-11-24 18:12:06 +00004719
cristy3ed852e2009-09-05 21:47:34 +00004720 first_mng_object=MagickTrue;
4721 mng_type=0;
4722#if defined(MNG_INSERT_LAYERS)
4723 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4724#endif
4725 default_frame_delay=0;
4726 default_frame_timeout=0;
4727 frame_delay=0;
4728 final_delay=1;
4729 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4730 object_id=0;
4731 skip_to_iend=MagickFalse;
4732 term_chunk_found=MagickFalse;
4733 mng_info->framing_mode=1;
4734#if defined(MNG_INSERT_LAYERS)
4735 mandatory_back=MagickFalse;
4736#endif
4737#if defined(MNG_INSERT_LAYERS)
4738 mng_background_color=image->background_color;
4739#endif
4740 default_fb=mng_info->frame;
4741 previous_fb=mng_info->frame;
4742 do
4743 {
4744 char
4745 type[MaxTextExtent];
4746
4747 if (LocaleCompare(image_info->magick,"MNG") == 0)
4748 {
4749 unsigned char
4750 *chunk;
4751
4752 /*
4753 Read a new chunk.
4754 */
4755 type[0]='\0';
4756 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4757 length=ReadBlobMSBLong(image);
4758 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4759
4760 if (logging != MagickFalse)
4761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004762 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4763 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004764
4765 if (length > PNG_UINT_31_MAX)
4766 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004767
cristy3ed852e2009-09-05 21:47:34 +00004768 if (count == 0)
4769 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004770
cristy3ed852e2009-09-05 21:47:34 +00004771 p=NULL;
4772 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004773
cristy3ed852e2009-09-05 21:47:34 +00004774 if (length)
4775 {
4776 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004777
cristy3ed852e2009-09-05 21:47:34 +00004778 if (chunk == (unsigned char *) NULL)
4779 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004780
cristybb503372010-05-27 20:51:26 +00004781 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004782 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004783
cristy3ed852e2009-09-05 21:47:34 +00004784 p=chunk;
4785 }
glennrp0fe50b42010-11-16 03:52:51 +00004786
cristy3ed852e2009-09-05 21:47:34 +00004787 (void) ReadBlobMSBLong(image); /* read crc word */
4788
4789#if !defined(JNG_SUPPORTED)
4790 if (memcmp(type,mng_JHDR,4) == 0)
4791 {
4792 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004793
cristy3ed852e2009-09-05 21:47:34 +00004794 if (mng_info->jhdr_warning == 0)
4795 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4796 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004797
cristy3ed852e2009-09-05 21:47:34 +00004798 mng_info->jhdr_warning++;
4799 }
4800#endif
4801 if (memcmp(type,mng_DHDR,4) == 0)
4802 {
4803 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004804
cristy3ed852e2009-09-05 21:47:34 +00004805 if (mng_info->dhdr_warning == 0)
4806 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4807 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004808
cristy3ed852e2009-09-05 21:47:34 +00004809 mng_info->dhdr_warning++;
4810 }
4811 if (memcmp(type,mng_MEND,4) == 0)
4812 break;
glennrp47b9dd52010-11-24 18:12:06 +00004813
cristy3ed852e2009-09-05 21:47:34 +00004814 if (skip_to_iend)
4815 {
4816 if (memcmp(type,mng_IEND,4) == 0)
4817 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004818
cristy3ed852e2009-09-05 21:47:34 +00004819 if (length)
4820 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004821
cristy3ed852e2009-09-05 21:47:34 +00004822 if (logging != MagickFalse)
4823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4824 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004825
cristy3ed852e2009-09-05 21:47:34 +00004826 continue;
4827 }
glennrp0fe50b42010-11-16 03:52:51 +00004828
cristy3ed852e2009-09-05 21:47:34 +00004829 if (memcmp(type,mng_MHDR,4) == 0)
4830 {
cristybb503372010-05-27 20:51:26 +00004831 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004832 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004833
cristybb503372010-05-27 20:51:26 +00004834 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004835 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004836
cristy3ed852e2009-09-05 21:47:34 +00004837 if (logging != MagickFalse)
4838 {
4839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004840 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004842 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004843 }
glennrp0fe50b42010-11-16 03:52:51 +00004844
cristy3ed852e2009-09-05 21:47:34 +00004845 p+=8;
cristy8182b072010-05-30 20:10:53 +00004846 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004847
cristy3ed852e2009-09-05 21:47:34 +00004848 if (mng_info->ticks_per_second == 0)
4849 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004850
cristy3ed852e2009-09-05 21:47:34 +00004851 else
4852 default_frame_delay=1UL*image->ticks_per_second/
4853 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004854
cristy3ed852e2009-09-05 21:47:34 +00004855 frame_delay=default_frame_delay;
4856 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004857
cristy3ed852e2009-09-05 21:47:34 +00004858 if (length > 16)
4859 {
4860 p+=16;
cristy8182b072010-05-30 20:10:53 +00004861 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004862 }
glennrp0fe50b42010-11-16 03:52:51 +00004863
cristy3ed852e2009-09-05 21:47:34 +00004864 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004865
cristy3ed852e2009-09-05 21:47:34 +00004866 if ((simplicity != 0) && ((simplicity | 11) == 11))
4867 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004868
cristy3ed852e2009-09-05 21:47:34 +00004869 if ((simplicity != 0) && ((simplicity | 9) == 9))
4870 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004871
cristy3ed852e2009-09-05 21:47:34 +00004872#if defined(MNG_INSERT_LAYERS)
4873 if (mng_type != 3)
4874 insert_layers=MagickTrue;
4875#endif
cristy4c08aed2011-07-01 19:47:50 +00004876 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004877 {
glennrp47b9dd52010-11-24 18:12:06 +00004878 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004879 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00004880
cristy3ed852e2009-09-05 21:47:34 +00004881 if (GetNextImageInList(image) == (Image *) NULL)
4882 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004883
cristy3ed852e2009-09-05 21:47:34 +00004884 image=SyncNextImageInList(image);
4885 mng_info->image=image;
4886 }
4887
4888 if ((mng_info->mng_width > 65535L) ||
4889 (mng_info->mng_height > 65535L))
4890 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004891
cristy3b6fd2e2011-05-20 12:53:50 +00004892 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004893 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004894 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004895
cristy3ed852e2009-09-05 21:47:34 +00004896 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004897 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004898 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004899 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004900 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004901
cristy3ed852e2009-09-05 21:47:34 +00004902 for (i=0; i < MNG_MAX_OBJECTS; i++)
4903 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004904
cristy3ed852e2009-09-05 21:47:34 +00004905 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4906 continue;
4907 }
4908
4909 if (memcmp(type,mng_TERM,4) == 0)
4910 {
4911 int
4912 repeat=0;
4913
4914
4915 if (length)
4916 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004917
cristy3ed852e2009-09-05 21:47:34 +00004918 if (repeat == 3)
4919 {
cristy8182b072010-05-30 20:10:53 +00004920 final_delay=(png_uint_32) mng_get_long(&p[2]);
4921 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004922
cristy3ed852e2009-09-05 21:47:34 +00004923 if (mng_iterations == PNG_UINT_31_MAX)
4924 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004925
cristy3ed852e2009-09-05 21:47:34 +00004926 image->iterations=mng_iterations;
4927 term_chunk_found=MagickTrue;
4928 }
glennrp0fe50b42010-11-16 03:52:51 +00004929
cristy3ed852e2009-09-05 21:47:34 +00004930 if (logging != MagickFalse)
4931 {
4932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4933 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004934
cristy3ed852e2009-09-05 21:47:34 +00004935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004936 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004937
cristy3ed852e2009-09-05 21:47:34 +00004938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004939 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004940 }
glennrp0fe50b42010-11-16 03:52:51 +00004941
cristy3ed852e2009-09-05 21:47:34 +00004942 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4943 continue;
4944 }
4945 if (memcmp(type,mng_DEFI,4) == 0)
4946 {
4947 if (mng_type == 3)
4948 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4949 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4950 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004951
cristy3ed852e2009-09-05 21:47:34 +00004952 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004953
cristy3ed852e2009-09-05 21:47:34 +00004954 if (mng_type == 2 && object_id != 0)
4955 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4956 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4957 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004958
cristy3ed852e2009-09-05 21:47:34 +00004959 if (object_id > MNG_MAX_OBJECTS)
4960 {
4961 /*
4962 Instead ofsuing a warning we should allocate a larger
4963 MngInfo structure and continue.
4964 */
4965 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4966 CoderError,"object id too large","`%s'",image->filename);
4967 object_id=MNG_MAX_OBJECTS;
4968 }
glennrp0fe50b42010-11-16 03:52:51 +00004969
cristy3ed852e2009-09-05 21:47:34 +00004970 if (mng_info->exists[object_id])
4971 if (mng_info->frozen[object_id])
4972 {
4973 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4974 (void) ThrowMagickException(&image->exception,
4975 GetMagickModule(),CoderError,
4976 "DEFI cannot redefine a frozen MNG object","`%s'",
4977 image->filename);
4978 continue;
4979 }
glennrp0fe50b42010-11-16 03:52:51 +00004980
cristy3ed852e2009-09-05 21:47:34 +00004981 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004982
cristy3ed852e2009-09-05 21:47:34 +00004983 if (length > 2)
4984 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00004985
cristy3ed852e2009-09-05 21:47:34 +00004986 /*
4987 Extract object offset info.
4988 */
4989 if (length > 11)
4990 {
glennrp0fe50b42010-11-16 03:52:51 +00004991 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4992 (p[5] << 16) | (p[6] << 8) | p[7]);
4993
4994 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4995 (p[9] << 16) | (p[10] << 8) | p[11]);
4996
cristy3ed852e2009-09-05 21:47:34 +00004997 if (logging != MagickFalse)
4998 {
4999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005000 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005001 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005002
cristy3ed852e2009-09-05 21:47:34 +00005003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005004 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005005 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005006 }
5007 }
glennrp0fe50b42010-11-16 03:52:51 +00005008
cristy3ed852e2009-09-05 21:47:34 +00005009 /*
5010 Extract object clipping info.
5011 */
5012 if (length > 27)
5013 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5014 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005015
cristy3ed852e2009-09-05 21:47:34 +00005016 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5017 continue;
5018 }
5019 if (memcmp(type,mng_bKGD,4) == 0)
5020 {
5021 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005022
cristy3ed852e2009-09-05 21:47:34 +00005023 if (length > 5)
5024 {
5025 mng_info->mng_global_bkgd.red=
5026 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005027
cristy3ed852e2009-09-05 21:47:34 +00005028 mng_info->mng_global_bkgd.green=
5029 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005030
cristy3ed852e2009-09-05 21:47:34 +00005031 mng_info->mng_global_bkgd.blue=
5032 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005033
cristy3ed852e2009-09-05 21:47:34 +00005034 mng_info->have_global_bkgd=MagickTrue;
5035 }
glennrp0fe50b42010-11-16 03:52:51 +00005036
cristy3ed852e2009-09-05 21:47:34 +00005037 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5038 continue;
5039 }
5040 if (memcmp(type,mng_BACK,4) == 0)
5041 {
5042#if defined(MNG_INSERT_LAYERS)
5043 if (length > 6)
5044 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005045
cristy3ed852e2009-09-05 21:47:34 +00005046 else
5047 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005048
cristy3ed852e2009-09-05 21:47:34 +00005049 if (mandatory_back && length > 5)
5050 {
5051 mng_background_color.red=
5052 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005053
cristy3ed852e2009-09-05 21:47:34 +00005054 mng_background_color.green=
5055 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005056
cristy3ed852e2009-09-05 21:47:34 +00005057 mng_background_color.blue=
5058 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005059
cristy4c08aed2011-07-01 19:47:50 +00005060 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005061 }
glennrp0fe50b42010-11-16 03:52:51 +00005062
cristy3ed852e2009-09-05 21:47:34 +00005063#ifdef MNG_OBJECT_BUFFERS
5064 if (length > 8)
5065 mng_background_object=(p[7] << 8) | p[8];
5066#endif
5067#endif
5068 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5069 continue;
5070 }
glennrp47b9dd52010-11-24 18:12:06 +00005071
cristy3ed852e2009-09-05 21:47:34 +00005072 if (memcmp(type,mng_PLTE,4) == 0)
5073 {
glennrp47b9dd52010-11-24 18:12:06 +00005074 /* Read global PLTE. */
5075
cristy3ed852e2009-09-05 21:47:34 +00005076 if (length && (length < 769))
5077 {
5078 if (mng_info->global_plte == (png_colorp) NULL)
5079 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5080 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005081
cristybb503372010-05-27 20:51:26 +00005082 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005083 {
5084 mng_info->global_plte[i].red=p[3*i];
5085 mng_info->global_plte[i].green=p[3*i+1];
5086 mng_info->global_plte[i].blue=p[3*i+2];
5087 }
glennrp0fe50b42010-11-16 03:52:51 +00005088
cristy35ef8242010-06-03 16:24:13 +00005089 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005090 }
5091#ifdef MNG_LOOSE
5092 for ( ; i < 256; i++)
5093 {
5094 mng_info->global_plte[i].red=i;
5095 mng_info->global_plte[i].green=i;
5096 mng_info->global_plte[i].blue=i;
5097 }
glennrp0fe50b42010-11-16 03:52:51 +00005098
cristy3ed852e2009-09-05 21:47:34 +00005099 if (length)
5100 mng_info->global_plte_length=256;
5101#endif
5102 else
5103 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005104
cristy3ed852e2009-09-05 21:47:34 +00005105 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5106 continue;
5107 }
glennrp47b9dd52010-11-24 18:12:06 +00005108
cristy3ed852e2009-09-05 21:47:34 +00005109 if (memcmp(type,mng_tRNS,4) == 0)
5110 {
5111 /* read global tRNS */
5112
5113 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005114 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005115 mng_info->global_trns[i]=p[i];
5116
5117#ifdef MNG_LOOSE
5118 for ( ; i < 256; i++)
5119 mng_info->global_trns[i]=255;
5120#endif
cristy12560f32010-06-03 16:51:08 +00005121 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005122 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5123 continue;
5124 }
5125 if (memcmp(type,mng_gAMA,4) == 0)
5126 {
5127 if (length == 4)
5128 {
cristybb503372010-05-27 20:51:26 +00005129 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005130 igamma;
5131
cristy8182b072010-05-30 20:10:53 +00005132 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005133 mng_info->global_gamma=((float) igamma)*0.00001;
5134 mng_info->have_global_gama=MagickTrue;
5135 }
glennrp0fe50b42010-11-16 03:52:51 +00005136
cristy3ed852e2009-09-05 21:47:34 +00005137 else
5138 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005139
cristy3ed852e2009-09-05 21:47:34 +00005140 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5141 continue;
5142 }
5143
5144 if (memcmp(type,mng_cHRM,4) == 0)
5145 {
glennrp47b9dd52010-11-24 18:12:06 +00005146 /* Read global cHRM */
5147
cristy3ed852e2009-09-05 21:47:34 +00005148 if (length == 32)
5149 {
cristy8182b072010-05-30 20:10:53 +00005150 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5151 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5152 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005153 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005154 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005155 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005156 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005157 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005158 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005159 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005160 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005161 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005162 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005163 mng_info->have_global_chrm=MagickTrue;
5164 }
5165 else
5166 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005167
cristy3ed852e2009-09-05 21:47:34 +00005168 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5169 continue;
5170 }
glennrp47b9dd52010-11-24 18:12:06 +00005171
cristy3ed852e2009-09-05 21:47:34 +00005172 if (memcmp(type,mng_sRGB,4) == 0)
5173 {
5174 /*
5175 Read global sRGB.
5176 */
5177 if (length)
5178 {
glennrpe610a072010-08-05 17:08:46 +00005179 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005180 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005181 mng_info->have_global_srgb=MagickTrue;
5182 }
5183 else
5184 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005185
cristy3ed852e2009-09-05 21:47:34 +00005186 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5187 continue;
5188 }
glennrp47b9dd52010-11-24 18:12:06 +00005189
cristy3ed852e2009-09-05 21:47:34 +00005190 if (memcmp(type,mng_iCCP,4) == 0)
5191 {
glennrpfd05d622011-02-25 04:10:33 +00005192 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005193
5194 /*
5195 Read global iCCP.
5196 */
5197 if (length)
5198 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005199
cristy3ed852e2009-09-05 21:47:34 +00005200 continue;
5201 }
glennrp47b9dd52010-11-24 18:12:06 +00005202
cristy3ed852e2009-09-05 21:47:34 +00005203 if (memcmp(type,mng_FRAM,4) == 0)
5204 {
5205 if (mng_type == 3)
5206 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5207 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5208 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005209
cristy3ed852e2009-09-05 21:47:34 +00005210 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5211 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005212
cristy3ed852e2009-09-05 21:47:34 +00005213 frame_delay=default_frame_delay;
5214 frame_timeout=default_frame_timeout;
5215 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005216
cristy3ed852e2009-09-05 21:47:34 +00005217 if (length)
5218 if (p[0])
5219 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005220
cristy3ed852e2009-09-05 21:47:34 +00005221 if (logging != MagickFalse)
5222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5223 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005224
cristy3ed852e2009-09-05 21:47:34 +00005225 if (length > 6)
5226 {
glennrp47b9dd52010-11-24 18:12:06 +00005227 /* Note the delay and frame clipping boundaries. */
5228
cristy3ed852e2009-09-05 21:47:34 +00005229 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005230
cristybb503372010-05-27 20:51:26 +00005231 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005232 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005233
cristy3ed852e2009-09-05 21:47:34 +00005234 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005235
cristybb503372010-05-27 20:51:26 +00005236 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005237 {
5238 int
5239 change_delay,
5240 change_timeout,
5241 change_clipping;
5242
5243 change_delay=(*p++);
5244 change_timeout=(*p++);
5245 change_clipping=(*p++);
5246 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005247
cristy3ed852e2009-09-05 21:47:34 +00005248 if (change_delay)
5249 {
cristy8182b072010-05-30 20:10:53 +00005250 frame_delay=1UL*image->ticks_per_second*
5251 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005252
cristy8182b072010-05-30 20:10:53 +00005253 if (mng_info->ticks_per_second != 0)
5254 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005255
glennrpbb010dd2010-06-01 13:07:15 +00005256 else
5257 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005258
cristy3ed852e2009-09-05 21:47:34 +00005259 if (change_delay == 2)
5260 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005261
cristy3ed852e2009-09-05 21:47:34 +00005262 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005263
cristy3ed852e2009-09-05 21:47:34 +00005264 if (logging != MagickFalse)
5265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005266 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005267 }
glennrp47b9dd52010-11-24 18:12:06 +00005268
cristy3ed852e2009-09-05 21:47:34 +00005269 if (change_timeout)
5270 {
glennrpbb010dd2010-06-01 13:07:15 +00005271 frame_timeout=1UL*image->ticks_per_second*
5272 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005273
glennrpbb010dd2010-06-01 13:07:15 +00005274 if (mng_info->ticks_per_second != 0)
5275 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005276
glennrpbb010dd2010-06-01 13:07:15 +00005277 else
5278 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005279
cristy3ed852e2009-09-05 21:47:34 +00005280 if (change_delay == 2)
5281 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005282
cristy3ed852e2009-09-05 21:47:34 +00005283 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005284
cristy3ed852e2009-09-05 21:47:34 +00005285 if (logging != MagickFalse)
5286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005287 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005288 }
glennrp47b9dd52010-11-24 18:12:06 +00005289
cristy3ed852e2009-09-05 21:47:34 +00005290 if (change_clipping)
5291 {
5292 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5293 p+=17;
5294 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005295
cristy3ed852e2009-09-05 21:47:34 +00005296 if (logging != MagickFalse)
5297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005298 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005299 (double) fb.left,(double) fb.right,(double) fb.top,
5300 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005301
cristy3ed852e2009-09-05 21:47:34 +00005302 if (change_clipping == 2)
5303 default_fb=fb;
5304 }
5305 }
5306 }
5307 mng_info->clip=fb;
5308 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005309
cristybb503372010-05-27 20:51:26 +00005310 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005311 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005312
cristybb503372010-05-27 20:51:26 +00005313 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005314 -mng_info->clip.top);
5315 /*
5316 Insert a background layer behind the frame if framing_mode is 4.
5317 */
5318#if defined(MNG_INSERT_LAYERS)
5319 if (logging != MagickFalse)
5320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005321 " subframe_width=%.20g, subframe_height=%.20g",(double)
5322 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005323
cristy3ed852e2009-09-05 21:47:34 +00005324 if (insert_layers && (mng_info->framing_mode == 4) &&
5325 (subframe_width) && (subframe_height))
5326 {
glennrp47b9dd52010-11-24 18:12:06 +00005327 /* Allocate next image structure. */
cristy4c08aed2011-07-01 19:47:50 +00005328 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005329 {
5330 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005331
cristy3ed852e2009-09-05 21:47:34 +00005332 if (GetNextImageInList(image) == (Image *) NULL)
5333 {
5334 image=DestroyImageList(image);
5335 MngInfoFreeStruct(mng_info,&have_mng_structure);
5336 return((Image *) NULL);
5337 }
glennrp47b9dd52010-11-24 18:12:06 +00005338
cristy3ed852e2009-09-05 21:47:34 +00005339 image=SyncNextImageInList(image);
5340 }
glennrp0fe50b42010-11-16 03:52:51 +00005341
cristy3ed852e2009-09-05 21:47:34 +00005342 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005343
cristy3ed852e2009-09-05 21:47:34 +00005344 if (term_chunk_found)
5345 {
5346 image->start_loop=MagickTrue;
5347 image->iterations=mng_iterations;
5348 term_chunk_found=MagickFalse;
5349 }
glennrp0fe50b42010-11-16 03:52:51 +00005350
cristy3ed852e2009-09-05 21:47:34 +00005351 else
5352 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005353
cristy3ed852e2009-09-05 21:47:34 +00005354 image->columns=subframe_width;
5355 image->rows=subframe_height;
5356 image->page.width=subframe_width;
5357 image->page.height=subframe_height;
5358 image->page.x=mng_info->clip.left;
5359 image->page.y=mng_info->clip.top;
5360 image->background_color=mng_background_color;
5361 image->matte=MagickFalse;
5362 image->delay=0;
5363 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005364
cristy3ed852e2009-09-05 21:47:34 +00005365 if (logging != MagickFalse)
5366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005367 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005368 (double) mng_info->clip.left,(double) mng_info->clip.right,
5369 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005370 }
5371#endif
5372 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5373 continue;
5374 }
5375 if (memcmp(type,mng_CLIP,4) == 0)
5376 {
5377 unsigned int
5378 first_object,
5379 last_object;
5380
5381 /*
5382 Read CLIP.
5383 */
5384 first_object=(p[0] << 8) | p[1];
5385 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005386
cristy3ed852e2009-09-05 21:47:34 +00005387 for (i=(int) first_object; i <= (int) last_object; i++)
5388 {
5389 if (mng_info->exists[i] && !mng_info->frozen[i])
5390 {
5391 MngBox
5392 box;
5393
5394 box=mng_info->object_clip[i];
5395 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5396 }
5397 }
glennrp47b9dd52010-11-24 18:12:06 +00005398
cristy3ed852e2009-09-05 21:47:34 +00005399 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5400 continue;
5401 }
5402 if (memcmp(type,mng_SAVE,4) == 0)
5403 {
5404 for (i=1; i < MNG_MAX_OBJECTS; i++)
5405 if (mng_info->exists[i])
5406 {
5407 mng_info->frozen[i]=MagickTrue;
5408#ifdef MNG_OBJECT_BUFFERS
5409 if (mng_info->ob[i] != (MngBuffer *) NULL)
5410 mng_info->ob[i]->frozen=MagickTrue;
5411#endif
5412 }
glennrp0fe50b42010-11-16 03:52:51 +00005413
cristy3ed852e2009-09-05 21:47:34 +00005414 if (length)
5415 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005416
cristy3ed852e2009-09-05 21:47:34 +00005417 continue;
5418 }
5419
5420 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5421 {
glennrp47b9dd52010-11-24 18:12:06 +00005422 /* Read DISC or SEEK. */
5423
cristy3ed852e2009-09-05 21:47:34 +00005424 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5425 {
5426 for (i=1; i < MNG_MAX_OBJECTS; i++)
5427 MngInfoDiscardObject(mng_info,i);
5428 }
glennrp0fe50b42010-11-16 03:52:51 +00005429
cristy3ed852e2009-09-05 21:47:34 +00005430 else
5431 {
cristybb503372010-05-27 20:51:26 +00005432 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005433 j;
5434
cristybb503372010-05-27 20:51:26 +00005435 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005436 {
5437 i=p[j] << 8 | p[j+1];
5438 MngInfoDiscardObject(mng_info,i);
5439 }
5440 }
glennrp0fe50b42010-11-16 03:52:51 +00005441
cristy3ed852e2009-09-05 21:47:34 +00005442 if (length)
5443 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005444
cristy3ed852e2009-09-05 21:47:34 +00005445 continue;
5446 }
glennrp47b9dd52010-11-24 18:12:06 +00005447
cristy3ed852e2009-09-05 21:47:34 +00005448 if (memcmp(type,mng_MOVE,4) == 0)
5449 {
cristybb503372010-05-27 20:51:26 +00005450 size_t
cristy3ed852e2009-09-05 21:47:34 +00005451 first_object,
5452 last_object;
5453
glennrp47b9dd52010-11-24 18:12:06 +00005454 /* read MOVE */
5455
cristy3ed852e2009-09-05 21:47:34 +00005456 first_object=(p[0] << 8) | p[1];
5457 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005458 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005459 {
5460 if (mng_info->exists[i] && !mng_info->frozen[i])
5461 {
5462 MngPair
5463 new_pair;
5464
5465 MngPair
5466 old_pair;
5467
5468 old_pair.a=mng_info->x_off[i];
5469 old_pair.b=mng_info->y_off[i];
5470 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5471 mng_info->x_off[i]=new_pair.a;
5472 mng_info->y_off[i]=new_pair.b;
5473 }
5474 }
glennrp47b9dd52010-11-24 18:12:06 +00005475
cristy3ed852e2009-09-05 21:47:34 +00005476 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5477 continue;
5478 }
5479
5480 if (memcmp(type,mng_LOOP,4) == 0)
5481 {
cristybb503372010-05-27 20:51:26 +00005482 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005483 loop_level=chunk[0];
5484 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005485
5486 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005487 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005488
cristy3ed852e2009-09-05 21:47:34 +00005489 if (logging != MagickFalse)
5490 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005491 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5492 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005493
cristy3ed852e2009-09-05 21:47:34 +00005494 if (loop_iters == 0)
5495 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005496
cristy3ed852e2009-09-05 21:47:34 +00005497 else
5498 {
5499 mng_info->loop_jump[loop_level]=TellBlob(image);
5500 mng_info->loop_count[loop_level]=loop_iters;
5501 }
glennrp0fe50b42010-11-16 03:52:51 +00005502
cristy3ed852e2009-09-05 21:47:34 +00005503 mng_info->loop_iteration[loop_level]=0;
5504 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5505 continue;
5506 }
glennrp47b9dd52010-11-24 18:12:06 +00005507
cristy3ed852e2009-09-05 21:47:34 +00005508 if (memcmp(type,mng_ENDL,4) == 0)
5509 {
5510 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005511
cristy3ed852e2009-09-05 21:47:34 +00005512 if (skipping_loop > 0)
5513 {
5514 if (skipping_loop == loop_level)
5515 {
5516 /*
5517 Found end of zero-iteration loop.
5518 */
5519 skipping_loop=(-1);
5520 mng_info->loop_active[loop_level]=0;
5521 }
5522 }
glennrp47b9dd52010-11-24 18:12:06 +00005523
cristy3ed852e2009-09-05 21:47:34 +00005524 else
5525 {
5526 if (mng_info->loop_active[loop_level] == 1)
5527 {
5528 mng_info->loop_count[loop_level]--;
5529 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005530
cristy3ed852e2009-09-05 21:47:34 +00005531 if (logging != MagickFalse)
5532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005533 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005534 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005535 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005536
cristy3ed852e2009-09-05 21:47:34 +00005537 if (mng_info->loop_count[loop_level] != 0)
5538 {
5539 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5540 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005541
cristy3ed852e2009-09-05 21:47:34 +00005542 if (offset < 0)
5543 ThrowReaderException(CorruptImageError,
5544 "ImproperImageHeader");
5545 }
glennrp47b9dd52010-11-24 18:12:06 +00005546
cristy3ed852e2009-09-05 21:47:34 +00005547 else
5548 {
5549 short
5550 last_level;
5551
5552 /*
5553 Finished loop.
5554 */
5555 mng_info->loop_active[loop_level]=0;
5556 last_level=(-1);
5557 for (i=0; i < loop_level; i++)
5558 if (mng_info->loop_active[i] == 1)
5559 last_level=(short) i;
5560 loop_level=last_level;
5561 }
5562 }
5563 }
glennrp47b9dd52010-11-24 18:12:06 +00005564
cristy3ed852e2009-09-05 21:47:34 +00005565 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5566 continue;
5567 }
glennrp47b9dd52010-11-24 18:12:06 +00005568
cristy3ed852e2009-09-05 21:47:34 +00005569 if (memcmp(type,mng_CLON,4) == 0)
5570 {
5571 if (mng_info->clon_warning == 0)
5572 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5573 CoderError,"CLON is not implemented yet","`%s'",
5574 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005575
cristy3ed852e2009-09-05 21:47:34 +00005576 mng_info->clon_warning++;
5577 }
glennrp47b9dd52010-11-24 18:12:06 +00005578
cristy3ed852e2009-09-05 21:47:34 +00005579 if (memcmp(type,mng_MAGN,4) == 0)
5580 {
5581 png_uint_16
5582 magn_first,
5583 magn_last,
5584 magn_mb,
5585 magn_ml,
5586 magn_mr,
5587 magn_mt,
5588 magn_mx,
5589 magn_my,
5590 magn_methx,
5591 magn_methy;
5592
5593 if (length > 1)
5594 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005595
cristy3ed852e2009-09-05 21:47:34 +00005596 else
5597 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005598
cristy3ed852e2009-09-05 21:47:34 +00005599 if (length > 3)
5600 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005601
cristy3ed852e2009-09-05 21:47:34 +00005602 else
5603 magn_last=magn_first;
5604#ifndef MNG_OBJECT_BUFFERS
5605 if (magn_first || magn_last)
5606 if (mng_info->magn_warning == 0)
5607 {
5608 (void) ThrowMagickException(&image->exception,
5609 GetMagickModule(),CoderError,
5610 "MAGN is not implemented yet for nonzero objects",
5611 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005612
cristy3ed852e2009-09-05 21:47:34 +00005613 mng_info->magn_warning++;
5614 }
5615#endif
5616 if (length > 4)
5617 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005618
cristy3ed852e2009-09-05 21:47:34 +00005619 else
5620 magn_methx=0;
5621
5622 if (length > 6)
5623 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005624
cristy3ed852e2009-09-05 21:47:34 +00005625 else
5626 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005627
cristy3ed852e2009-09-05 21:47:34 +00005628 if (magn_mx == 0)
5629 magn_mx=1;
5630
5631 if (length > 8)
5632 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005633
cristy3ed852e2009-09-05 21:47:34 +00005634 else
5635 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005636
cristy3ed852e2009-09-05 21:47:34 +00005637 if (magn_my == 0)
5638 magn_my=1;
5639
5640 if (length > 10)
5641 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005642
cristy3ed852e2009-09-05 21:47:34 +00005643 else
5644 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005645
cristy3ed852e2009-09-05 21:47:34 +00005646 if (magn_ml == 0)
5647 magn_ml=1;
5648
5649 if (length > 12)
5650 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005651
cristy3ed852e2009-09-05 21:47:34 +00005652 else
5653 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005654
cristy3ed852e2009-09-05 21:47:34 +00005655 if (magn_mr == 0)
5656 magn_mr=1;
5657
5658 if (length > 14)
5659 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005660
cristy3ed852e2009-09-05 21:47:34 +00005661 else
5662 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005663
cristy3ed852e2009-09-05 21:47:34 +00005664 if (magn_mt == 0)
5665 magn_mt=1;
5666
5667 if (length > 16)
5668 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005669
cristy3ed852e2009-09-05 21:47:34 +00005670 else
5671 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005672
cristy3ed852e2009-09-05 21:47:34 +00005673 if (magn_mb == 0)
5674 magn_mb=1;
5675
5676 if (length > 17)
5677 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005678
cristy3ed852e2009-09-05 21:47:34 +00005679 else
5680 magn_methy=magn_methx;
5681
glennrp47b9dd52010-11-24 18:12:06 +00005682
cristy3ed852e2009-09-05 21:47:34 +00005683 if (magn_methx > 5 || magn_methy > 5)
5684 if (mng_info->magn_warning == 0)
5685 {
5686 (void) ThrowMagickException(&image->exception,
5687 GetMagickModule(),CoderError,
5688 "Unknown MAGN method in MNG datastream","`%s'",
5689 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005690
cristy3ed852e2009-09-05 21:47:34 +00005691 mng_info->magn_warning++;
5692 }
5693#ifdef MNG_OBJECT_BUFFERS
5694 /* Magnify existing objects in the range magn_first to magn_last */
5695#endif
5696 if (magn_first == 0 || magn_last == 0)
5697 {
5698 /* Save the magnification factors for object 0 */
5699 mng_info->magn_mb=magn_mb;
5700 mng_info->magn_ml=magn_ml;
5701 mng_info->magn_mr=magn_mr;
5702 mng_info->magn_mt=magn_mt;
5703 mng_info->magn_mx=magn_mx;
5704 mng_info->magn_my=magn_my;
5705 mng_info->magn_methx=magn_methx;
5706 mng_info->magn_methy=magn_methy;
5707 }
5708 }
glennrp47b9dd52010-11-24 18:12:06 +00005709
cristy3ed852e2009-09-05 21:47:34 +00005710 if (memcmp(type,mng_PAST,4) == 0)
5711 {
5712 if (mng_info->past_warning == 0)
5713 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5714 CoderError,"PAST is not implemented yet","`%s'",
5715 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005716
cristy3ed852e2009-09-05 21:47:34 +00005717 mng_info->past_warning++;
5718 }
glennrp47b9dd52010-11-24 18:12:06 +00005719
cristy3ed852e2009-09-05 21:47:34 +00005720 if (memcmp(type,mng_SHOW,4) == 0)
5721 {
5722 if (mng_info->show_warning == 0)
5723 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5724 CoderError,"SHOW is not implemented yet","`%s'",
5725 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005726
cristy3ed852e2009-09-05 21:47:34 +00005727 mng_info->show_warning++;
5728 }
glennrp47b9dd52010-11-24 18:12:06 +00005729
cristy3ed852e2009-09-05 21:47:34 +00005730 if (memcmp(type,mng_sBIT,4) == 0)
5731 {
5732 if (length < 4)
5733 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005734
cristy3ed852e2009-09-05 21:47:34 +00005735 else
5736 {
5737 mng_info->global_sbit.gray=p[0];
5738 mng_info->global_sbit.red=p[0];
5739 mng_info->global_sbit.green=p[1];
5740 mng_info->global_sbit.blue=p[2];
5741 mng_info->global_sbit.alpha=p[3];
5742 mng_info->have_global_sbit=MagickTrue;
5743 }
5744 }
5745 if (memcmp(type,mng_pHYs,4) == 0)
5746 {
5747 if (length > 8)
5748 {
5749 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005750 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005751 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005752 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005753 mng_info->global_phys_unit_type=p[8];
5754 mng_info->have_global_phys=MagickTrue;
5755 }
glennrp47b9dd52010-11-24 18:12:06 +00005756
cristy3ed852e2009-09-05 21:47:34 +00005757 else
5758 mng_info->have_global_phys=MagickFalse;
5759 }
5760 if (memcmp(type,mng_pHYg,4) == 0)
5761 {
5762 if (mng_info->phyg_warning == 0)
5763 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5764 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005765
cristy3ed852e2009-09-05 21:47:34 +00005766 mng_info->phyg_warning++;
5767 }
5768 if (memcmp(type,mng_BASI,4) == 0)
5769 {
5770 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005771
cristy3ed852e2009-09-05 21:47:34 +00005772 if (mng_info->basi_warning == 0)
5773 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5774 CoderError,"BASI is not implemented yet","`%s'",
5775 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005776
cristy3ed852e2009-09-05 21:47:34 +00005777 mng_info->basi_warning++;
5778#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005779 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005780 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005781 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005782 (p[6] << 8) | p[7]);
5783 basi_color_type=p[8];
5784 basi_compression_method=p[9];
5785 basi_filter_type=p[10];
5786 basi_interlace_method=p[11];
5787 if (length > 11)
5788 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005789
cristy3ed852e2009-09-05 21:47:34 +00005790 else
5791 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005792
cristy3ed852e2009-09-05 21:47:34 +00005793 if (length > 13)
5794 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005795
cristy3ed852e2009-09-05 21:47:34 +00005796 else
5797 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005798
cristy3ed852e2009-09-05 21:47:34 +00005799 if (length > 15)
5800 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005801
cristy3ed852e2009-09-05 21:47:34 +00005802 else
5803 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005804
cristy3ed852e2009-09-05 21:47:34 +00005805 if (length > 17)
5806 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005807
cristy3ed852e2009-09-05 21:47:34 +00005808 else
5809 {
5810 if (basi_sample_depth == 16)
5811 basi_alpha=65535L;
5812 else
5813 basi_alpha=255;
5814 }
glennrp47b9dd52010-11-24 18:12:06 +00005815
cristy3ed852e2009-09-05 21:47:34 +00005816 if (length > 19)
5817 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005818
cristy3ed852e2009-09-05 21:47:34 +00005819 else
5820 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005821
cristy3ed852e2009-09-05 21:47:34 +00005822#endif
5823 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5824 continue;
5825 }
glennrp47b9dd52010-11-24 18:12:06 +00005826
cristy3ed852e2009-09-05 21:47:34 +00005827 if (memcmp(type,mng_IHDR,4)
5828#if defined(JNG_SUPPORTED)
5829 && memcmp(type,mng_JHDR,4)
5830#endif
5831 )
5832 {
5833 /* Not an IHDR or JHDR chunk */
5834 if (length)
5835 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005836
cristy3ed852e2009-09-05 21:47:34 +00005837 continue;
5838 }
5839/* Process IHDR */
5840 if (logging != MagickFalse)
5841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5842 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005843
cristy3ed852e2009-09-05 21:47:34 +00005844 mng_info->exists[object_id]=MagickTrue;
5845 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005846
cristy3ed852e2009-09-05 21:47:34 +00005847 if (mng_info->invisible[object_id])
5848 {
5849 if (logging != MagickFalse)
5850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5851 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005852
cristy3ed852e2009-09-05 21:47:34 +00005853 skip_to_iend=MagickTrue;
5854 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5855 continue;
5856 }
5857#if defined(MNG_INSERT_LAYERS)
5858 if (length < 8)
5859 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005860
cristy8182b072010-05-30 20:10:53 +00005861 image_width=(size_t) mng_get_long(p);
5862 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005863#endif
5864 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5865
5866 /*
5867 Insert a transparent background layer behind the entire animation
5868 if it is not full screen.
5869 */
5870#if defined(MNG_INSERT_LAYERS)
5871 if (insert_layers && mng_type && first_mng_object)
5872 {
5873 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5874 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005875 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005876 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005877 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005878 {
cristy4c08aed2011-07-01 19:47:50 +00005879 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005880 {
5881 /*
5882 Allocate next image structure.
5883 */
5884 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005885
cristy3ed852e2009-09-05 21:47:34 +00005886 if (GetNextImageInList(image) == (Image *) NULL)
5887 {
5888 image=DestroyImageList(image);
5889 MngInfoFreeStruct(mng_info,&have_mng_structure);
5890 return((Image *) NULL);
5891 }
glennrp47b9dd52010-11-24 18:12:06 +00005892
cristy3ed852e2009-09-05 21:47:34 +00005893 image=SyncNextImageInList(image);
5894 }
5895 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005896
cristy3ed852e2009-09-05 21:47:34 +00005897 if (term_chunk_found)
5898 {
5899 image->start_loop=MagickTrue;
5900 image->iterations=mng_iterations;
5901 term_chunk_found=MagickFalse;
5902 }
glennrp47b9dd52010-11-24 18:12:06 +00005903
cristy3ed852e2009-09-05 21:47:34 +00005904 else
5905 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005906
5907 /* Make a background rectangle. */
5908
cristy3ed852e2009-09-05 21:47:34 +00005909 image->delay=0;
5910 image->columns=mng_info->mng_width;
5911 image->rows=mng_info->mng_height;
5912 image->page.width=mng_info->mng_width;
5913 image->page.height=mng_info->mng_height;
5914 image->page.x=0;
5915 image->page.y=0;
5916 image->background_color=mng_background_color;
5917 (void) SetImageBackgroundColor(image);
5918 if (logging != MagickFalse)
5919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005920 " Inserted transparent background layer, W=%.20g, H=%.20g",
5921 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005922 }
5923 }
5924 /*
5925 Insert a background layer behind the upcoming image if
5926 framing_mode is 3, and we haven't already inserted one.
5927 */
5928 if (insert_layers && (mng_info->framing_mode == 3) &&
5929 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5930 (simplicity & 0x08)))
5931 {
cristy4c08aed2011-07-01 19:47:50 +00005932 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005933 {
5934 /*
5935 Allocate next image structure.
5936 */
5937 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005938
cristy3ed852e2009-09-05 21:47:34 +00005939 if (GetNextImageInList(image) == (Image *) NULL)
5940 {
5941 image=DestroyImageList(image);
5942 MngInfoFreeStruct(mng_info,&have_mng_structure);
5943 return((Image *) NULL);
5944 }
glennrp47b9dd52010-11-24 18:12:06 +00005945
cristy3ed852e2009-09-05 21:47:34 +00005946 image=SyncNextImageInList(image);
5947 }
glennrp0fe50b42010-11-16 03:52:51 +00005948
cristy3ed852e2009-09-05 21:47:34 +00005949 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005950
cristy3ed852e2009-09-05 21:47:34 +00005951 if (term_chunk_found)
5952 {
5953 image->start_loop=MagickTrue;
5954 image->iterations=mng_iterations;
5955 term_chunk_found=MagickFalse;
5956 }
glennrp0fe50b42010-11-16 03:52:51 +00005957
cristy3ed852e2009-09-05 21:47:34 +00005958 else
5959 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005960
cristy3ed852e2009-09-05 21:47:34 +00005961 image->delay=0;
5962 image->columns=subframe_width;
5963 image->rows=subframe_height;
5964 image->page.width=subframe_width;
5965 image->page.height=subframe_height;
5966 image->page.x=mng_info->clip.left;
5967 image->page.y=mng_info->clip.top;
5968 image->background_color=mng_background_color;
5969 image->matte=MagickFalse;
5970 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005971
cristy3ed852e2009-09-05 21:47:34 +00005972 if (logging != MagickFalse)
5973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005974 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005975 (double) mng_info->clip.left,(double) mng_info->clip.right,
5976 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005977 }
5978#endif /* MNG_INSERT_LAYERS */
5979 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005980
cristy4c08aed2011-07-01 19:47:50 +00005981 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005982 {
5983 /*
5984 Allocate next image structure.
5985 */
5986 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005987
cristy3ed852e2009-09-05 21:47:34 +00005988 if (GetNextImageInList(image) == (Image *) NULL)
5989 {
5990 image=DestroyImageList(image);
5991 MngInfoFreeStruct(mng_info,&have_mng_structure);
5992 return((Image *) NULL);
5993 }
glennrp47b9dd52010-11-24 18:12:06 +00005994
cristy3ed852e2009-09-05 21:47:34 +00005995 image=SyncNextImageInList(image);
5996 }
5997 mng_info->image=image;
5998 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5999 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006000
cristy3ed852e2009-09-05 21:47:34 +00006001 if (status == MagickFalse)
6002 break;
glennrp0fe50b42010-11-16 03:52:51 +00006003
cristy3ed852e2009-09-05 21:47:34 +00006004 if (term_chunk_found)
6005 {
6006 image->start_loop=MagickTrue;
6007 term_chunk_found=MagickFalse;
6008 }
glennrp0fe50b42010-11-16 03:52:51 +00006009
cristy3ed852e2009-09-05 21:47:34 +00006010 else
6011 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006012
cristy3ed852e2009-09-05 21:47:34 +00006013 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6014 {
6015 image->delay=frame_delay;
6016 frame_delay=default_frame_delay;
6017 }
glennrp0fe50b42010-11-16 03:52:51 +00006018
cristy3ed852e2009-09-05 21:47:34 +00006019 else
6020 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006021
cristy3ed852e2009-09-05 21:47:34 +00006022 image->page.width=mng_info->mng_width;
6023 image->page.height=mng_info->mng_height;
6024 image->page.x=mng_info->x_off[object_id];
6025 image->page.y=mng_info->y_off[object_id];
6026 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006027
cristy3ed852e2009-09-05 21:47:34 +00006028 /*
6029 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6030 */
glennrp47b9dd52010-11-24 18:12:06 +00006031
cristy3ed852e2009-09-05 21:47:34 +00006032 if (logging != MagickFalse)
6033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6034 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6035 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006036
cristybb503372010-05-27 20:51:26 +00006037 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006038
cristy3ed852e2009-09-05 21:47:34 +00006039 if (offset < 0)
6040 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6041 }
6042
6043 previous=image;
6044 mng_info->image=image;
6045 mng_info->mng_type=mng_type;
6046 mng_info->object_id=object_id;
6047
6048 if (memcmp(type,mng_IHDR,4) == 0)
6049 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006050
cristy3ed852e2009-09-05 21:47:34 +00006051#if defined(JNG_SUPPORTED)
6052 else
6053 image=ReadOneJNGImage(mng_info,image_info,exception);
6054#endif
6055
6056 if (image == (Image *) NULL)
6057 {
6058 if (IsImageObject(previous) != MagickFalse)
6059 {
6060 (void) DestroyImageList(previous);
6061 (void) CloseBlob(previous);
6062 }
glennrp47b9dd52010-11-24 18:12:06 +00006063
cristy3ed852e2009-09-05 21:47:34 +00006064 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 if (image->columns == 0 || image->rows == 0)
6069 {
6070 (void) CloseBlob(image);
6071 image=DestroyImageList(image);
6072 MngInfoFreeStruct(mng_info,&have_mng_structure);
6073 return((Image *) NULL);
6074 }
glennrp0fe50b42010-11-16 03:52:51 +00006075
cristy3ed852e2009-09-05 21:47:34 +00006076 mng_info->image=image;
6077
6078 if (mng_type)
6079 {
6080 MngBox
6081 crop_box;
6082
6083 if (mng_info->magn_methx || mng_info->magn_methy)
6084 {
6085 png_uint_32
6086 magnified_height,
6087 magnified_width;
6088
6089 if (logging != MagickFalse)
6090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6091 " Processing MNG MAGN chunk");
6092
6093 if (mng_info->magn_methx == 1)
6094 {
6095 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006096
cristy3ed852e2009-09-05 21:47:34 +00006097 if (image->columns > 1)
6098 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006099
cristy3ed852e2009-09-05 21:47:34 +00006100 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006101 magnified_width += (png_uint_32)
6102 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006103 }
glennrp47b9dd52010-11-24 18:12:06 +00006104
cristy3ed852e2009-09-05 21:47:34 +00006105 else
6106 {
cristy4e5bc842010-06-09 13:56:01 +00006107 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006108
cristy3ed852e2009-09-05 21:47:34 +00006109 if (image->columns > 1)
6110 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006111
cristy3ed852e2009-09-05 21:47:34 +00006112 if (image->columns > 2)
6113 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006114
cristy3ed852e2009-09-05 21:47:34 +00006115 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006116 magnified_width += (png_uint_32)
6117 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006118 }
glennrp47b9dd52010-11-24 18:12:06 +00006119
cristy3ed852e2009-09-05 21:47:34 +00006120 if (mng_info->magn_methy == 1)
6121 {
6122 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006123
cristy3ed852e2009-09-05 21:47:34 +00006124 if (image->rows > 1)
6125 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006126
cristy3ed852e2009-09-05 21:47:34 +00006127 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006128 magnified_height += (png_uint_32)
6129 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006130 }
glennrp47b9dd52010-11-24 18:12:06 +00006131
cristy3ed852e2009-09-05 21:47:34 +00006132 else
6133 {
cristy4e5bc842010-06-09 13:56:01 +00006134 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006135
cristy3ed852e2009-09-05 21:47:34 +00006136 if (image->rows > 1)
6137 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006138
cristy3ed852e2009-09-05 21:47:34 +00006139 if (image->rows > 2)
6140 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006141
cristy3ed852e2009-09-05 21:47:34 +00006142 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006143 magnified_height += (png_uint_32)
6144 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006145 }
glennrp47b9dd52010-11-24 18:12:06 +00006146
cristy3ed852e2009-09-05 21:47:34 +00006147 if (magnified_height > image->rows ||
6148 magnified_width > image->columns)
6149 {
6150 Image
6151 *large_image;
6152
6153 int
6154 yy;
6155
cristy4c08aed2011-07-01 19:47:50 +00006156 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006157 *next,
6158 *prev;
6159
6160 png_uint_16
6161 magn_methx,
6162 magn_methy;
6163
cristy4c08aed2011-07-01 19:47:50 +00006164 ssize_t
6165 m,
6166 y;
6167
6168 register Quantum
6169 *n,
6170 *q;
6171
6172 register ssize_t
6173 x;
6174
glennrp47b9dd52010-11-24 18:12:06 +00006175 /* Allocate next image structure. */
6176
cristy3ed852e2009-09-05 21:47:34 +00006177 if (logging != MagickFalse)
6178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6179 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006180
cristy3ed852e2009-09-05 21:47:34 +00006181 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00006182
cristy3ed852e2009-09-05 21:47:34 +00006183 if (GetNextImageInList(image) == (Image *) NULL)
6184 {
6185 image=DestroyImageList(image);
6186 MngInfoFreeStruct(mng_info,&have_mng_structure);
6187 return((Image *) NULL);
6188 }
6189
6190 large_image=SyncNextImageInList(image);
6191
6192 large_image->columns=magnified_width;
6193 large_image->rows=magnified_height;
6194
6195 magn_methx=mng_info->magn_methx;
6196 magn_methy=mng_info->magn_methy;
6197
glennrp3faa9a32011-04-23 14:00:25 +00006198#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006199#define QM unsigned short
6200 if (magn_methx != 1 || magn_methy != 1)
6201 {
6202 /*
6203 Scale pixels to unsigned shorts to prevent
6204 overflow of intermediate values of interpolations
6205 */
cristybb503372010-05-27 20:51:26 +00006206 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006207 {
6208 q=GetAuthenticPixels(image,0,y,image->columns,1,
6209 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006210
cristybb503372010-05-27 20:51:26 +00006211 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006212 {
cristy4c08aed2011-07-01 19:47:50 +00006213 SetPixelRed(image,ScaleQuantumToShort(
6214 GetPixelRed(image,q)),q);
6215 SetPixelGreen(image,ScaleQuantumToShort(
6216 GetPixelGreen(image,q)),q);
6217 SetPixelBlue(image,ScaleQuantumToShort(
6218 GetPixelBlue(image,q)),q);
6219 SetPixelAlpha(image,ScaleQuantumToShort(
6220 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006221 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006222 }
glennrp47b9dd52010-11-24 18:12:06 +00006223
cristy3ed852e2009-09-05 21:47:34 +00006224 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6225 break;
6226 }
6227 }
6228#else
6229#define QM Quantum
6230#endif
6231
6232 if (image->matte != MagickFalse)
6233 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006234
cristy3ed852e2009-09-05 21:47:34 +00006235 else
6236 {
cristy4c08aed2011-07-01 19:47:50 +00006237 large_image->background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00006238 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006239
cristy3ed852e2009-09-05 21:47:34 +00006240 if (magn_methx == 4)
6241 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006242
cristy3ed852e2009-09-05 21:47:34 +00006243 if (magn_methx == 5)
6244 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006245
cristy3ed852e2009-09-05 21:47:34 +00006246 if (magn_methy == 4)
6247 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006248
cristy3ed852e2009-09-05 21:47:34 +00006249 if (magn_methy == 5)
6250 magn_methy=3;
6251 }
6252
6253 /* magnify the rows into the right side of the large image */
6254
6255 if (logging != MagickFalse)
6256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006257 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006258 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006259 yy=0;
6260 length=(size_t) image->columns;
cristy4c08aed2011-07-01 19:47:50 +00006261 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6262 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006263
cristy4c08aed2011-07-01 19:47:50 +00006264 if ((prev == (Quantum *) NULL) ||
6265 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006266 {
6267 image=DestroyImageList(image);
6268 MngInfoFreeStruct(mng_info,&have_mng_structure);
6269 ThrowReaderException(ResourceLimitError,
6270 "MemoryAllocationFailed");
6271 }
glennrp47b9dd52010-11-24 18:12:06 +00006272
cristy3ed852e2009-09-05 21:47:34 +00006273 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6274 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006275
cristybb503372010-05-27 20:51:26 +00006276 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006277 {
6278 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006279 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006280
cristybb503372010-05-27 20:51:26 +00006281 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6282 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006283
cristybb503372010-05-27 20:51:26 +00006284 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6285 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006286
cristybb503372010-05-27 20:51:26 +00006287 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006288 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006289
cristy3ed852e2009-09-05 21:47:34 +00006290 else
cristybb503372010-05-27 20:51:26 +00006291 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006292
cristy3ed852e2009-09-05 21:47:34 +00006293 n=prev;
6294 prev=next;
6295 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006296
cristybb503372010-05-27 20:51:26 +00006297 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006298 {
6299 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6300 exception);
6301 (void) CopyMagickMemory(next,n,length);
6302 }
glennrp47b9dd52010-11-24 18:12:06 +00006303
cristy3ed852e2009-09-05 21:47:34 +00006304 for (i=0; i < m; i++, yy++)
6305 {
cristy4c08aed2011-07-01 19:47:50 +00006306 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006307 *pixels;
6308
cristybb503372010-05-27 20:51:26 +00006309 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006310 pixels=prev;
6311 n=next;
6312 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006313 1,exception);
cristy3ed852e2009-09-05 21:47:34 +00006314 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00006315
cristybb503372010-05-27 20:51:26 +00006316 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006317 {
glennrpfd05d622011-02-25 04:10:33 +00006318 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006319 /*
6320 if (image->storage_class == PseudoClass)
6321 {
6322 }
6323 */
6324
6325 if (magn_methy <= 1)
6326 {
glennrpbb4f99d2011-05-22 11:13:17 +00006327 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006328 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
glennrp847370c2011-07-05 17:37:15 +00006329 SetPixelGreen(large_image,GetPixelGreen(image,
6330 pixels),q);
6331 SetPixelBlue(large_image,GetPixelBlue(image,
6332 pixels),q);
6333 SetPixelAlpha(large_image,GetPixelAlpha(image,
6334 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006335 }
glennrp47b9dd52010-11-24 18:12:06 +00006336
cristy3ed852e2009-09-05 21:47:34 +00006337 else if (magn_methy == 2 || magn_methy == 4)
6338 {
6339 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006340 {
glennrp847370c2011-07-05 17:37:15 +00006341 SetPixelRed(large_image,GetPixelRed(image,
6342 pixels),q);
6343 SetPixelGreen(large_image,GetPixelGreen(image,
6344 pixels),q);
6345 SetPixelBlue(large_image,GetPixelBlue(image,
6346 pixels),q);
6347 SetPixelAlpha(large_image,GetPixelAlpha(image,
6348 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006349 }
glennrp47b9dd52010-11-24 18:12:06 +00006350
cristy3ed852e2009-09-05 21:47:34 +00006351 else
6352 {
6353 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006354 SetPixelRed(large_image,((QM) (((ssize_t)
6355 (2*i*(GetPixelRed(image,n)
6356 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006357 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006358 +GetPixelRed(image,pixels)))),q);
6359 SetPixelGreen(large_image,((QM) (((ssize_t)
6360 (2*i*(GetPixelGreen(image,n)
6361 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006362 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006363 +GetPixelGreen(image,pixels)))),q);
6364 SetPixelBlue(large_image,((QM) (((ssize_t)
6365 (2*i*(GetPixelBlue(image,n)
6366 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006367 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006368 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006369
cristy3ed852e2009-09-05 21:47:34 +00006370 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006371 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6372 (2*i*(GetPixelAlpha(image,n)
6373 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006374 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006375 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006376 }
glennrp47b9dd52010-11-24 18:12:06 +00006377
cristy3ed852e2009-09-05 21:47:34 +00006378 if (magn_methy == 4)
6379 {
6380 /* Replicate nearest */
6381 if (i <= ((m+1) << 1))
glennrp847370c2011-07-05 17:37:15 +00006382 SetPixelAlpha(large_image,GetPixelAlpha(image,
6383 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006384 else
glennrp847370c2011-07-05 17:37:15 +00006385 SetPixelAlpha(large_image,GetPixelAlpha(image,
6386 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006387 }
6388 }
glennrp47b9dd52010-11-24 18:12:06 +00006389
cristy3ed852e2009-09-05 21:47:34 +00006390 else /* if (magn_methy == 3 || magn_methy == 5) */
6391 {
6392 /* Replicate nearest */
6393 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006394 {
glennrp847370c2011-07-05 17:37:15 +00006395 SetPixelRed(large_image,GetPixelRed(image,
6396 pixels),q);
6397 SetPixelGreen(large_image,GetPixelGreen(image,
6398 pixels),q);
6399 SetPixelBlue(large_image,GetPixelBlue(image,
6400 pixels),q);
6401 SetPixelAlpha(large_image,GetPixelAlpha(image,
6402 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006403 }
glennrp47b9dd52010-11-24 18:12:06 +00006404
cristy3ed852e2009-09-05 21:47:34 +00006405 else
glennrpbb4f99d2011-05-22 11:13:17 +00006406 {
cristy4c08aed2011-07-01 19:47:50 +00006407 SetPixelRed(large_image,GetPixelRed(image,n),q);
glennrp847370c2011-07-05 17:37:15 +00006408 SetPixelGreen(large_image,GetPixelGreen(image,n),
6409 q);
6410 SetPixelBlue(large_image,GetPixelBlue(image,n),
6411 q);
6412 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6413 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006414 }
glennrp47b9dd52010-11-24 18:12:06 +00006415
cristy3ed852e2009-09-05 21:47:34 +00006416 if (magn_methy == 5)
6417 {
cristy4c08aed2011-07-01 19:47:50 +00006418 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6419 (GetPixelAlpha(image,n)
6420 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006421 +m))/((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006422 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006423 }
6424 }
cristyed231572011-07-14 02:18:59 +00006425 n+=GetPixelChannels(image);
6426 q+=GetPixelChannels(large_image);
6427 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006428 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006429
cristy3ed852e2009-09-05 21:47:34 +00006430 if (SyncAuthenticPixels(large_image,exception) == 0)
6431 break;
glennrp47b9dd52010-11-24 18:12:06 +00006432
cristy3ed852e2009-09-05 21:47:34 +00006433 } /* i */
6434 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006435
cristy4c08aed2011-07-01 19:47:50 +00006436 prev=(Quantum *) RelinquishMagickMemory(prev);
6437 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006438
6439 length=image->columns;
6440
6441 if (logging != MagickFalse)
6442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6443 " Delete original image");
6444
6445 DeleteImageFromList(&image);
6446
6447 image=large_image;
6448
6449 mng_info->image=image;
6450
6451 /* magnify the columns */
6452 if (logging != MagickFalse)
6453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006454 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006455
cristybb503372010-05-27 20:51:26 +00006456 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006457 {
cristy4c08aed2011-07-01 19:47:50 +00006458 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006459 *pixels;
6460
6461 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristyed231572011-07-14 02:18:59 +00006462 pixels=q+(image->columns-length)*GetPixelChannels(image);
6463 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006464
cristybb503372010-05-27 20:51:26 +00006465 for (x=(ssize_t) (image->columns-length);
6466 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006467 {
cristyed231572011-07-14 02:18:59 +00006468 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006469
cristybb503372010-05-27 20:51:26 +00006470 if (x == (ssize_t) (image->columns-length))
6471 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006472
cristybb503372010-05-27 20:51:26 +00006473 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6474 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006475
cristybb503372010-05-27 20:51:26 +00006476 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6477 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006478
cristybb503372010-05-27 20:51:26 +00006479 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006480 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006481
cristy3ed852e2009-09-05 21:47:34 +00006482 else
cristybb503372010-05-27 20:51:26 +00006483 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006484
cristy3ed852e2009-09-05 21:47:34 +00006485 for (i=0; i < m; i++)
6486 {
6487 if (magn_methx <= 1)
6488 {
6489 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006490 SetPixelRed(image,GetPixelRed(image,pixels),q);
6491 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6492 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6493 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006494 }
glennrp47b9dd52010-11-24 18:12:06 +00006495
cristy3ed852e2009-09-05 21:47:34 +00006496 else if (magn_methx == 2 || magn_methx == 4)
6497 {
6498 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006499 {
cristy4c08aed2011-07-01 19:47:50 +00006500 SetPixelRed(image,GetPixelRed(image,pixels),q);
6501 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6502 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6503 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006504 }
glennrp47b9dd52010-11-24 18:12:06 +00006505
cristyed231572011-07-14 02:18:59 +00006506 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006507 else
6508 {
6509 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006510 SetPixelRed(image,(QM) ((2*i*(
6511 GetPixelRed(image,n)
6512 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006513 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006514 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006515
cristy4c08aed2011-07-01 19:47:50 +00006516 SetPixelGreen(image,(QM) ((2*i*(
6517 GetPixelGreen(image,n)
6518 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006519 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006520 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006521
cristy4c08aed2011-07-01 19:47:50 +00006522 SetPixelBlue(image,(QM) ((2*i*(
6523 GetPixelBlue(image,n)
6524 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006525 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006526 GetPixelBlue(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006527 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006528 SetPixelAlpha(image,(QM) ((2*i*(
6529 GetPixelAlpha(image,n)
6530 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006531 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006532 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006533 }
glennrp47b9dd52010-11-24 18:12:06 +00006534
cristy3ed852e2009-09-05 21:47:34 +00006535 if (magn_methx == 4)
6536 {
6537 /* Replicate nearest */
6538 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006539 {
cristy4c08aed2011-07-01 19:47:50 +00006540 SetPixelAlpha(image,
6541 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006542 }
cristy3ed852e2009-09-05 21:47:34 +00006543 else
glennrpbb4f99d2011-05-22 11:13:17 +00006544 {
cristy4c08aed2011-07-01 19:47:50 +00006545 SetPixelAlpha(image,
6546 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006547 }
cristy3ed852e2009-09-05 21:47:34 +00006548 }
6549 }
glennrp47b9dd52010-11-24 18:12:06 +00006550
cristy3ed852e2009-09-05 21:47:34 +00006551 else /* if (magn_methx == 3 || magn_methx == 5) */
6552 {
6553 /* Replicate nearest */
6554 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006555 {
cristy4c08aed2011-07-01 19:47:50 +00006556 SetPixelRed(image,GetPixelRed(image,pixels),q);
6557 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6558 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6559 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006560 }
glennrp47b9dd52010-11-24 18:12:06 +00006561
cristy3ed852e2009-09-05 21:47:34 +00006562 else
glennrpbb4f99d2011-05-22 11:13:17 +00006563 {
cristy4c08aed2011-07-01 19:47:50 +00006564 SetPixelRed(image,GetPixelRed(image,n),q);
6565 SetPixelGreen(image,GetPixelGreen(image,n),q);
6566 SetPixelBlue(image,GetPixelBlue(image,n),q);
6567 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006568 }
glennrp47b9dd52010-11-24 18:12:06 +00006569
cristy3ed852e2009-09-05 21:47:34 +00006570 if (magn_methx == 5)
6571 {
6572 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006573 SetPixelAlpha(image,
6574 (QM) ((2*i*( GetPixelAlpha(image,n)
6575 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006576 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006577 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006578 }
6579 }
cristyed231572011-07-14 02:18:59 +00006580 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006581 }
cristyed231572011-07-14 02:18:59 +00006582 n+=GetPixelChannels(image);
6583 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006584 }
glennrp47b9dd52010-11-24 18:12:06 +00006585
cristy3ed852e2009-09-05 21:47:34 +00006586 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6587 break;
6588 }
glennrp3faa9a32011-04-23 14:00:25 +00006589#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006590 if (magn_methx != 1 || magn_methy != 1)
6591 {
6592 /*
6593 Rescale pixels to Quantum
6594 */
cristybb503372010-05-27 20:51:26 +00006595 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006596 {
6597 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006598
cristybb503372010-05-27 20:51:26 +00006599 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006600 {
cristy4c08aed2011-07-01 19:47:50 +00006601 SetPixelRed(image,ScaleShortToQuantum(
6602 GetPixelRed(image,q)),q);
6603 SetPixelGreen(image,ScaleShortToQuantum(
6604 GetPixelGreen(image,q)),q);
6605 SetPixelBlue(image,ScaleShortToQuantum(
6606 GetPixelBlue(image,q)),q);
6607 SetPixelAlpha(image,ScaleShortToQuantum(
6608 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006609 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006610 }
glennrp47b9dd52010-11-24 18:12:06 +00006611
cristy3ed852e2009-09-05 21:47:34 +00006612 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6613 break;
6614 }
6615 }
6616#endif
6617 if (logging != MagickFalse)
6618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6619 " Finished MAGN processing");
6620 }
6621 }
6622
6623 /*
6624 Crop_box is with respect to the upper left corner of the MNG.
6625 */
6626 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6627 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6628 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6629 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6630 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6631 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6632 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6633 if ((crop_box.left != (mng_info->image_box.left
6634 +mng_info->x_off[object_id])) ||
6635 (crop_box.right != (mng_info->image_box.right
6636 +mng_info->x_off[object_id])) ||
6637 (crop_box.top != (mng_info->image_box.top
6638 +mng_info->y_off[object_id])) ||
6639 (crop_box.bottom != (mng_info->image_box.bottom
6640 +mng_info->y_off[object_id])))
6641 {
6642 if (logging != MagickFalse)
6643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6644 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006645
cristy3ed852e2009-09-05 21:47:34 +00006646 if ((crop_box.left < crop_box.right) &&
6647 (crop_box.top < crop_box.bottom))
6648 {
6649 Image
6650 *im;
6651
6652 RectangleInfo
6653 crop_info;
6654
6655 /*
6656 Crop_info is with respect to the upper left corner of
6657 the image.
6658 */
6659 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6660 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006661 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6662 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006663 image->page.width=image->columns;
6664 image->page.height=image->rows;
6665 image->page.x=0;
6666 image->page.y=0;
6667 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006668
cristy3ed852e2009-09-05 21:47:34 +00006669 if (im != (Image *) NULL)
6670 {
6671 image->columns=im->columns;
6672 image->rows=im->rows;
6673 im=DestroyImage(im);
6674 image->page.width=image->columns;
6675 image->page.height=image->rows;
6676 image->page.x=crop_box.left;
6677 image->page.y=crop_box.top;
6678 }
6679 }
glennrp47b9dd52010-11-24 18:12:06 +00006680
cristy3ed852e2009-09-05 21:47:34 +00006681 else
6682 {
6683 /*
6684 No pixels in crop area. The MNG spec still requires
6685 a layer, though, so make a single transparent pixel in
6686 the top left corner.
6687 */
6688 image->columns=1;
6689 image->rows=1;
6690 image->colors=2;
6691 (void) SetImageBackgroundColor(image);
6692 image->page.width=1;
6693 image->page.height=1;
6694 image->page.x=0;
6695 image->page.y=0;
6696 }
6697 }
6698#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6699 image=mng_info->image;
6700#endif
6701 }
6702
glennrp2b013e42010-11-24 16:55:50 +00006703#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6704 /* PNG does not handle depths greater than 16 so reduce it even
6705 * if lossy
6706 */
6707 if (image->depth > 16)
6708 image->depth=16;
6709#endif
6710
glennrp3faa9a32011-04-23 14:00:25 +00006711#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006712 if (LosslessReduceDepthOK(image) != MagickFalse)
6713 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006714#endif
glennrpd6afd542010-11-19 01:53:05 +00006715
cristy3ed852e2009-09-05 21:47:34 +00006716 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006717
cristy3ed852e2009-09-05 21:47:34 +00006718 if (image_info->number_scenes != 0)
6719 {
6720 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006721 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006722 break;
6723 }
glennrpd6afd542010-11-19 01:53:05 +00006724
cristy3ed852e2009-09-05 21:47:34 +00006725 if (logging != MagickFalse)
6726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6727 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006728
cristy3ed852e2009-09-05 21:47:34 +00006729 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006730
cristy3ed852e2009-09-05 21:47:34 +00006731 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006732
cristy3ed852e2009-09-05 21:47:34 +00006733 if (logging != MagickFalse)
6734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6735 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006736
cristy3ed852e2009-09-05 21:47:34 +00006737#if defined(MNG_INSERT_LAYERS)
6738 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6739 (mng_info->mng_height))
6740 {
6741 /*
6742 Insert a background layer if nothing else was found.
6743 */
6744 if (logging != MagickFalse)
6745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6746 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006747
cristy4c08aed2011-07-01 19:47:50 +00006748 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006749 {
6750 /*
6751 Allocate next image structure.
6752 */
6753 AcquireNextImage(image_info,image);
6754 if (GetNextImageInList(image) == (Image *) NULL)
6755 {
6756 image=DestroyImageList(image);
6757 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006758
cristy3ed852e2009-09-05 21:47:34 +00006759 if (logging != MagickFalse)
6760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6761 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006762
cristy3ed852e2009-09-05 21:47:34 +00006763 return((Image *) NULL);
6764 }
6765 image=SyncNextImageInList(image);
6766 }
6767 image->columns=mng_info->mng_width;
6768 image->rows=mng_info->mng_height;
6769 image->page.width=mng_info->mng_width;
6770 image->page.height=mng_info->mng_height;
6771 image->page.x=0;
6772 image->page.y=0;
6773 image->background_color=mng_background_color;
6774 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006775
cristy3ed852e2009-09-05 21:47:34 +00006776 if (image_info->ping == MagickFalse)
6777 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006778
cristy3ed852e2009-09-05 21:47:34 +00006779 mng_info->image_found++;
6780 }
6781#endif
6782 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006783
cristy3ed852e2009-09-05 21:47:34 +00006784 if (mng_iterations == 1)
6785 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006786
cristy3ed852e2009-09-05 21:47:34 +00006787 while (GetPreviousImageInList(image) != (Image *) NULL)
6788 {
6789 image_count++;
6790 if (image_count > 10*mng_info->image_found)
6791 {
6792 if (logging != MagickFalse)
6793 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006794
cristy3ed852e2009-09-05 21:47:34 +00006795 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6796 CoderError,"Linked list is corrupted, beginning of list not found",
6797 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006798
cristy3ed852e2009-09-05 21:47:34 +00006799 return((Image *) NULL);
6800 }
glennrp0fe50b42010-11-16 03:52:51 +00006801
cristy3ed852e2009-09-05 21:47:34 +00006802 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006803
cristy3ed852e2009-09-05 21:47:34 +00006804 if (GetNextImageInList(image) == (Image *) NULL)
6805 {
6806 if (logging != MagickFalse)
6807 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006808
cristy3ed852e2009-09-05 21:47:34 +00006809 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6810 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6811 image_info->filename);
6812 }
6813 }
glennrp47b9dd52010-11-24 18:12:06 +00006814
cristy3ed852e2009-09-05 21:47:34 +00006815 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6816 GetNextImageInList(image) ==
6817 (Image *) NULL)
6818 {
6819 if (logging != MagickFalse)
6820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6821 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006822
cristy3ed852e2009-09-05 21:47:34 +00006823 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6824 CoderError,"image->next for first image is NULL but shouldn't be.",
6825 "`%s'",image_info->filename);
6826 }
glennrp47b9dd52010-11-24 18:12:06 +00006827
cristy3ed852e2009-09-05 21:47:34 +00006828 if (mng_info->image_found == 0)
6829 {
6830 if (logging != MagickFalse)
6831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6832 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006833
cristy3ed852e2009-09-05 21:47:34 +00006834 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6835 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006836
cristy3ed852e2009-09-05 21:47:34 +00006837 if (image != (Image *) NULL)
6838 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006839
cristy3ed852e2009-09-05 21:47:34 +00006840 MngInfoFreeStruct(mng_info,&have_mng_structure);
6841 return((Image *) NULL);
6842 }
6843
6844 if (mng_info->ticks_per_second)
6845 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6846 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006847
cristy3ed852e2009-09-05 21:47:34 +00006848 else
6849 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006850
cristy3ed852e2009-09-05 21:47:34 +00006851 /* Find final nonzero image delay */
6852 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006853
cristy3ed852e2009-09-05 21:47:34 +00006854 while (GetNextImageInList(image) != (Image *) NULL)
6855 {
6856 if (image->delay)
6857 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006858
cristy3ed852e2009-09-05 21:47:34 +00006859 image=GetNextImageInList(image);
6860 }
glennrp0fe50b42010-11-16 03:52:51 +00006861
cristy3ed852e2009-09-05 21:47:34 +00006862 if (final_delay < final_image_delay)
6863 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006864
cristy3ed852e2009-09-05 21:47:34 +00006865 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006866
cristy3ed852e2009-09-05 21:47:34 +00006867 if (logging != MagickFalse)
6868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006869 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6870 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006871
cristy3ed852e2009-09-05 21:47:34 +00006872 if (logging != MagickFalse)
6873 {
6874 int
6875 scene;
6876
6877 scene=0;
6878 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006879
cristy3ed852e2009-09-05 21:47:34 +00006880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6881 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006882
cristy3ed852e2009-09-05 21:47:34 +00006883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006884 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006885
cristy3ed852e2009-09-05 21:47:34 +00006886 while (GetNextImageInList(image) != (Image *) NULL)
6887 {
6888 image=GetNextImageInList(image);
6889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006890 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006891 }
6892 }
6893
6894 image=GetFirstImageInList(image);
6895#ifdef MNG_COALESCE_LAYERS
6896 if (insert_layers)
6897 {
6898 Image
6899 *next_image,
6900 *next;
6901
cristybb503372010-05-27 20:51:26 +00006902 size_t
cristy3ed852e2009-09-05 21:47:34 +00006903 scene;
6904
6905 if (logging != MagickFalse)
6906 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006907
cristy3ed852e2009-09-05 21:47:34 +00006908 scene=image->scene;
6909 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006910
cristy3ed852e2009-09-05 21:47:34 +00006911 if (next_image == (Image *) NULL)
6912 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006913
cristy3ed852e2009-09-05 21:47:34 +00006914 image=DestroyImageList(image);
6915 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006916
cristy3ed852e2009-09-05 21:47:34 +00006917 for (next=image; next != (Image *) NULL; next=next_image)
6918 {
6919 next->page.width=mng_info->mng_width;
6920 next->page.height=mng_info->mng_height;
6921 next->page.x=0;
6922 next->page.y=0;
6923 next->scene=scene++;
6924 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006925
cristy3ed852e2009-09-05 21:47:34 +00006926 if (next_image == (Image *) NULL)
6927 break;
glennrp47b9dd52010-11-24 18:12:06 +00006928
cristy3ed852e2009-09-05 21:47:34 +00006929 if (next->delay == 0)
6930 {
6931 scene--;
6932 next_image->previous=GetPreviousImageInList(next);
6933 if (GetPreviousImageInList(next) == (Image *) NULL)
6934 image=next_image;
6935 else
6936 next->previous->next=next_image;
6937 next=DestroyImage(next);
6938 }
6939 }
6940 }
6941#endif
6942
6943 while (GetNextImageInList(image) != (Image *) NULL)
6944 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006945
cristy3ed852e2009-09-05 21:47:34 +00006946 image->dispose=BackgroundDispose;
6947
6948 if (logging != MagickFalse)
6949 {
6950 int
6951 scene;
6952
6953 scene=0;
6954 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006955
cristy3ed852e2009-09-05 21:47:34 +00006956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6957 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006958
cristy3ed852e2009-09-05 21:47:34 +00006959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006960 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6961 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006962
cristy3ed852e2009-09-05 21:47:34 +00006963 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006964 {
6965 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006966
cristyf2faecf2010-05-28 19:19:36 +00006967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006968 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6969 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006970 }
6971 }
glennrp47b9dd52010-11-24 18:12:06 +00006972
cristy3ed852e2009-09-05 21:47:34 +00006973 image=GetFirstImageInList(image);
6974 MngInfoFreeStruct(mng_info,&have_mng_structure);
6975 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006976
cristy3ed852e2009-09-05 21:47:34 +00006977 if (logging != MagickFalse)
6978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006979
cristy3ed852e2009-09-05 21:47:34 +00006980 return(GetFirstImageInList(image));
6981}
glennrp25c1e2b2010-03-25 01:39:56 +00006982#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006983static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6984{
6985 printf("Your PNG library is too old: You have libpng-%s\n",
6986 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006987
cristy3ed852e2009-09-05 21:47:34 +00006988 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6989 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006990
cristy3ed852e2009-09-05 21:47:34 +00006991 return(Image *) NULL;
6992}
glennrp47b9dd52010-11-24 18:12:06 +00006993
cristy3ed852e2009-09-05 21:47:34 +00006994static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6995{
6996 return(ReadPNGImage(image_info,exception));
6997}
glennrp25c1e2b2010-03-25 01:39:56 +00006998#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006999#endif
7000
7001/*
7002%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7003% %
7004% %
7005% %
7006% R e g i s t e r P N G I m a g e %
7007% %
7008% %
7009% %
7010%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7011%
7012% RegisterPNGImage() adds properties for the PNG image format to
7013% the list of supported formats. The properties include the image format
7014% tag, a method to read and/or write the format, whether the format
7015% supports the saving of more than one frame to the same file or blob,
7016% whether the format supports native in-memory I/O, and a brief
7017% description of the format.
7018%
7019% The format of the RegisterPNGImage method is:
7020%
cristybb503372010-05-27 20:51:26 +00007021% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007022%
7023*/
cristybb503372010-05-27 20:51:26 +00007024ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007025{
7026 char
7027 version[MaxTextExtent];
7028
7029 MagickInfo
7030 *entry;
7031
7032 static const char
7033 *PNGNote=
7034 {
7035 "See http://www.libpng.org/ for details about the PNG format."
7036 },
glennrp47b9dd52010-11-24 18:12:06 +00007037
cristy3ed852e2009-09-05 21:47:34 +00007038 *JNGNote=
7039 {
7040 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7041 "format."
7042 },
glennrp47b9dd52010-11-24 18:12:06 +00007043
cristy3ed852e2009-09-05 21:47:34 +00007044 *MNGNote=
7045 {
7046 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7047 "format."
7048 };
7049
7050 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007051
cristy3ed852e2009-09-05 21:47:34 +00007052#if defined(PNG_LIBPNG_VER_STRING)
7053 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7054 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007055
cristy3ed852e2009-09-05 21:47:34 +00007056 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7057 {
7058 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7059 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7060 MaxTextExtent);
7061 }
7062#endif
glennrp47b9dd52010-11-24 18:12:06 +00007063
cristy3ed852e2009-09-05 21:47:34 +00007064 entry=SetMagickInfo("MNG");
7065 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007066
cristy3ed852e2009-09-05 21:47:34 +00007067#if defined(MAGICKCORE_PNG_DELEGATE)
7068 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7069 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7070#endif
glennrp47b9dd52010-11-24 18:12:06 +00007071
cristy3ed852e2009-09-05 21:47:34 +00007072 entry->magick=(IsImageFormatHandler *) IsMNG;
7073 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007074
cristy3ed852e2009-09-05 21:47:34 +00007075 if (*version != '\0')
7076 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007077
cristy3ed852e2009-09-05 21:47:34 +00007078 entry->module=ConstantString("PNG");
7079 entry->note=ConstantString(MNGNote);
7080 (void) RegisterMagickInfo(entry);
7081
7082 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007083
cristy3ed852e2009-09-05 21:47:34 +00007084#if defined(MAGICKCORE_PNG_DELEGATE)
7085 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7086 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7087#endif
glennrp47b9dd52010-11-24 18:12:06 +00007088
cristy3ed852e2009-09-05 21:47:34 +00007089 entry->magick=(IsImageFormatHandler *) IsPNG;
7090 entry->adjoin=MagickFalse;
7091 entry->description=ConstantString("Portable Network Graphics");
7092 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007093
cristy3ed852e2009-09-05 21:47:34 +00007094 if (*version != '\0')
7095 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007096
cristy3ed852e2009-09-05 21:47:34 +00007097 entry->note=ConstantString(PNGNote);
7098 (void) RegisterMagickInfo(entry);
7099
7100 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007101
cristy3ed852e2009-09-05 21:47:34 +00007102#if defined(MAGICKCORE_PNG_DELEGATE)
7103 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7104 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7105#endif
glennrp47b9dd52010-11-24 18:12:06 +00007106
cristy3ed852e2009-09-05 21:47:34 +00007107 entry->magick=(IsImageFormatHandler *) IsPNG;
7108 entry->adjoin=MagickFalse;
7109 entry->description=ConstantString(
7110 "8-bit indexed with optional binary transparency");
7111 entry->module=ConstantString("PNG");
7112 (void) RegisterMagickInfo(entry);
7113
7114 entry=SetMagickInfo("PNG24");
7115 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007116
cristy3ed852e2009-09-05 21:47:34 +00007117#if defined(ZLIB_VERSION)
7118 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7119 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007120
cristy3ed852e2009-09-05 21:47:34 +00007121 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7122 {
7123 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7124 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7125 }
7126#endif
glennrp47b9dd52010-11-24 18:12:06 +00007127
cristy3ed852e2009-09-05 21:47:34 +00007128 if (*version != '\0')
7129 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007130
cristy3ed852e2009-09-05 21:47:34 +00007131#if defined(MAGICKCORE_PNG_DELEGATE)
7132 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7133 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7134#endif
glennrp47b9dd52010-11-24 18:12:06 +00007135
cristy3ed852e2009-09-05 21:47:34 +00007136 entry->magick=(IsImageFormatHandler *) IsPNG;
7137 entry->adjoin=MagickFalse;
7138 entry->description=ConstantString("opaque 24-bit RGB");
7139 entry->module=ConstantString("PNG");
7140 (void) RegisterMagickInfo(entry);
7141
7142 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007143
cristy3ed852e2009-09-05 21:47:34 +00007144#if defined(MAGICKCORE_PNG_DELEGATE)
7145 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7146 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7147#endif
glennrp47b9dd52010-11-24 18:12:06 +00007148
cristy3ed852e2009-09-05 21:47:34 +00007149 entry->magick=(IsImageFormatHandler *) IsPNG;
7150 entry->adjoin=MagickFalse;
7151 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7152 entry->module=ConstantString("PNG");
7153 (void) RegisterMagickInfo(entry);
7154
7155 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007156
cristy3ed852e2009-09-05 21:47:34 +00007157#if defined(JNG_SUPPORTED)
7158#if defined(MAGICKCORE_PNG_DELEGATE)
7159 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7160 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7161#endif
7162#endif
glennrp47b9dd52010-11-24 18:12:06 +00007163
cristy3ed852e2009-09-05 21:47:34 +00007164 entry->magick=(IsImageFormatHandler *) IsJNG;
7165 entry->adjoin=MagickFalse;
7166 entry->description=ConstantString("JPEG Network Graphics");
7167 entry->module=ConstantString("PNG");
7168 entry->note=ConstantString(JNGNote);
7169 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007170
cristy18b17442009-10-25 18:36:48 +00007171#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007172 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007173#endif
glennrp47b9dd52010-11-24 18:12:06 +00007174
cristy3ed852e2009-09-05 21:47:34 +00007175 return(MagickImageCoderSignature);
7176}
7177
7178/*
7179%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7180% %
7181% %
7182% %
7183% U n r e g i s t e r P N G I m a g e %
7184% %
7185% %
7186% %
7187%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7188%
7189% UnregisterPNGImage() removes format registrations made by the
7190% PNG module from the list of supported formats.
7191%
7192% The format of the UnregisterPNGImage method is:
7193%
7194% UnregisterPNGImage(void)
7195%
7196*/
7197ModuleExport void UnregisterPNGImage(void)
7198{
7199 (void) UnregisterMagickInfo("MNG");
7200 (void) UnregisterMagickInfo("PNG");
7201 (void) UnregisterMagickInfo("PNG8");
7202 (void) UnregisterMagickInfo("PNG24");
7203 (void) UnregisterMagickInfo("PNG32");
7204 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007205
cristy3ed852e2009-09-05 21:47:34 +00007206#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007207 if (ping_semaphore != (SemaphoreInfo *) NULL)
7208 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007209#endif
7210}
7211
7212#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007213#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007214/*
7215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7216% %
7217% %
7218% %
7219% W r i t e M N G I m a g e %
7220% %
7221% %
7222% %
7223%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7224%
7225% WriteMNGImage() writes an image in the Portable Network Graphics
7226% Group's "Multiple-image Network Graphics" encoded image format.
7227%
7228% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7229%
7230% The format of the WriteMNGImage method is:
7231%
cristy1e178e72011-08-28 19:44:34 +00007232% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7233% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007234%
7235% A description of each parameter follows.
7236%
7237% o image_info: the image info.
7238%
7239% o image: The image.
7240%
cristy1e178e72011-08-28 19:44:34 +00007241% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007242%
7243% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7244% "To do" under ReadPNGImage):
7245%
cristy3ed852e2009-09-05 21:47:34 +00007246% Preserve all unknown and not-yet-handled known chunks found in input
7247% PNG file and copy them into output PNG files according to the PNG
7248% copying rules.
7249%
7250% Write the iCCP chunk at MNG level when (icc profile length > 0)
7251%
7252% Improve selection of color type (use indexed-colour or indexed-colour
7253% with tRNS when 256 or fewer unique RGBA values are present).
7254%
7255% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7256% This will be complicated if we limit ourselves to generating MNG-LC
7257% files. For now we ignore disposal method 3 and simply overlay the next
7258% image on it.
7259%
7260% Check for identical PLTE's or PLTE/tRNS combinations and use a
7261% global MNG PLTE or PLTE/tRNS combination when appropriate.
7262% [mostly done 15 June 1999 but still need to take care of tRNS]
7263%
7264% Check for identical sRGB and replace with a global sRGB (and remove
7265% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7266% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7267% local gAMA/cHRM with local sRGB if appropriate).
7268%
7269% Check for identical sBIT chunks and write global ones.
7270%
7271% Provide option to skip writing the signature tEXt chunks.
7272%
7273% Use signatures to detect identical objects and reuse the first
7274% instance of such objects instead of writing duplicate objects.
7275%
7276% Use a smaller-than-32k value of compression window size when
7277% appropriate.
7278%
7279% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7280% ancillary text chunks and save profiles.
7281%
7282% Provide an option to force LC files (to ensure exact framing rate)
7283% instead of VLC.
7284%
7285% Provide an option to force VLC files instead of LC, even when offsets
7286% are present. This will involve expanding the embedded images with a
7287% transparent region at the top and/or left.
7288*/
7289
cristy3ed852e2009-09-05 21:47:34 +00007290static void
glennrpcf002022011-01-30 02:38:15 +00007291Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007292 png_info *ping_info, unsigned char *profile_type, unsigned char
7293 *profile_description, unsigned char *profile_data, png_uint_32 length)
7294{
cristy3ed852e2009-09-05 21:47:34 +00007295 png_textp
7296 text;
7297
cristybb503372010-05-27 20:51:26 +00007298 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007299 i;
7300
7301 unsigned char
7302 *sp;
7303
7304 png_charp
7305 dp;
7306
7307 png_uint_32
7308 allocated_length,
7309 description_length;
7310
7311 unsigned char
7312 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007313
cristy3ed852e2009-09-05 21:47:34 +00007314 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7315 return;
7316
7317 if (image_info->verbose)
7318 {
glennrp0fe50b42010-11-16 03:52:51 +00007319 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7320 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007321 }
glennrp0fe50b42010-11-16 03:52:51 +00007322
cristy3ed852e2009-09-05 21:47:34 +00007323 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7324 description_length=(png_uint_32) strlen((const char *) profile_description);
7325 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7326 + description_length);
7327 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7328 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7329 text[0].key[0]='\0';
7330 (void) ConcatenateMagickString(text[0].key,
7331 "Raw profile type ",MaxTextExtent);
7332 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7333 sp=profile_data;
7334 dp=text[0].text;
7335 *dp++='\n';
7336 (void) CopyMagickString(dp,(const char *) profile_description,
7337 allocated_length);
7338 dp+=description_length;
7339 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007340 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007341 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007342 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007343
cristybb503372010-05-27 20:51:26 +00007344 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007345 {
7346 if (i%36 == 0)
7347 *dp++='\n';
7348 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7349 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7350 }
glennrp47b9dd52010-11-24 18:12:06 +00007351
cristy3ed852e2009-09-05 21:47:34 +00007352 *dp++='\n';
7353 *dp='\0';
7354 text[0].text_length=(png_size_t) (dp-text[0].text);
7355 text[0].compression=image_info->compression == NoCompression ||
7356 (image_info->compression == UndefinedCompression &&
7357 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007358
cristy3ed852e2009-09-05 21:47:34 +00007359 if (text[0].text_length <= allocated_length)
7360 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007361
cristy3ed852e2009-09-05 21:47:34 +00007362 png_free(ping,text[0].text);
7363 png_free(ping,text[0].key);
7364 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007365}
7366
glennrpcf002022011-01-30 02:38:15 +00007367static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007368 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007369{
7370 char
7371 *name;
7372
7373 const StringInfo
7374 *profile;
7375
7376 unsigned char
7377 *data;
7378
7379 png_uint_32 length;
7380
7381 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007382
7383 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7384 {
cristy3ed852e2009-09-05 21:47:34 +00007385 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007386
cristy3ed852e2009-09-05 21:47:34 +00007387 if (profile != (const StringInfo *) NULL)
7388 {
7389 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007390 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007391
glennrp47b9dd52010-11-24 18:12:06 +00007392 if (LocaleNCompare(name,string,11) == 0)
7393 {
7394 if (logging != MagickFalse)
7395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7396 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007397
glennrpcf002022011-01-30 02:38:15 +00007398 ping_profile=CloneStringInfo(profile);
7399 data=GetStringInfoDatum(ping_profile),
7400 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007401 data[4]=data[3];
7402 data[3]=data[2];
7403 data[2]=data[1];
7404 data[1]=data[0];
7405 (void) WriteBlobMSBULong(image,length-5); /* data length */
7406 (void) WriteBlob(image,length-1,data+1);
7407 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007408 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007409 }
cristy3ed852e2009-09-05 21:47:34 +00007410 }
glennrp47b9dd52010-11-24 18:12:06 +00007411
cristy3ed852e2009-09-05 21:47:34 +00007412 name=GetNextImageProfile(image);
7413 }
glennrp47b9dd52010-11-24 18:12:06 +00007414
cristy3ed852e2009-09-05 21:47:34 +00007415 return(MagickTrue);
7416}
7417
glennrpb9cfe272010-12-21 15:08:06 +00007418
cristy3ed852e2009-09-05 21:47:34 +00007419/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007420static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +00007421 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007422{
7423 Image
7424 *image;
7425
7426 ImageInfo
7427 *image_info;
7428
cristy3ed852e2009-09-05 21:47:34 +00007429 char
7430 s[2];
7431
7432 const char
7433 *name,
7434 *property,
7435 *value;
7436
7437 const StringInfo
7438 *profile;
7439
cristy3ed852e2009-09-05 21:47:34 +00007440 int
cristy3ed852e2009-09-05 21:47:34 +00007441 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007442 pass;
7443
glennrpe9c26dc2010-05-30 01:56:35 +00007444 png_byte
7445 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007446
glennrp39992b42010-11-14 00:03:43 +00007447 png_color
7448 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007449
glennrp5af765f2010-03-30 11:12:18 +00007450 png_color_16
7451 ping_background,
7452 ping_trans_color;
7453
cristy3ed852e2009-09-05 21:47:34 +00007454 png_info
7455 *ping_info;
7456
7457 png_struct
7458 *ping;
7459
glennrp5af765f2010-03-30 11:12:18 +00007460 png_uint_32
7461 ping_height,
7462 ping_width;
7463
cristybb503372010-05-27 20:51:26 +00007464 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007465 y;
7466
7467 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007468 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007469 logging,
glennrp58e01762011-01-07 15:28:54 +00007470 matte,
7471
glennrpda8f3a72011-02-27 23:54:12 +00007472 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007473 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007474 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007475 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007476 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007477 ping_have_bKGD,
7478 ping_have_pHYs,
7479 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007480
7481 ping_exclude_bKGD,
7482 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007483 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007484 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007485 ping_exclude_gAMA,
7486 ping_exclude_iCCP,
7487 /* ping_exclude_iTXt, */
7488 ping_exclude_oFFs,
7489 ping_exclude_pHYs,
7490 ping_exclude_sRGB,
7491 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007492 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007493 ping_exclude_vpAg,
7494 ping_exclude_zCCP, /* hex-encoded iCCP */
7495 ping_exclude_zTXt,
7496
glennrp8d3d6e52011-04-19 04:39:51 +00007497 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007498 ping_need_colortype_warning,
7499
glennrp82b3c532011-03-22 19:20:54 +00007500 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007501 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007502 tried_333,
7503 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007504
7505 QuantumInfo
7506 *quantum_info;
7507
cristybb503372010-05-27 20:51:26 +00007508 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007509 i,
7510 x;
7511
7512 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007513 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007514
glennrp5af765f2010-03-30 11:12:18 +00007515 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007516 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007517 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007518 ping_color_type,
7519 ping_interlace_method,
7520 ping_compression_method,
7521 ping_filter_method,
7522 ping_num_trans;
7523
cristybb503372010-05-27 20:51:26 +00007524 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007525 image_depth,
7526 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007527
cristybb503372010-05-27 20:51:26 +00007528 size_t
cristy3ed852e2009-09-05 21:47:34 +00007529 quality,
7530 rowbytes,
7531 save_image_depth;
7532
glennrpdfd70802010-11-14 01:23:35 +00007533 int
glennrpfd05d622011-02-25 04:10:33 +00007534 j,
glennrpf09bded2011-01-08 01:15:59 +00007535 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007536 number_opaque,
7537 number_semitransparent,
7538 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007539 ping_pHYs_unit_type;
7540
7541 png_uint_32
7542 ping_pHYs_x_resolution,
7543 ping_pHYs_y_resolution;
7544
cristy3ed852e2009-09-05 21:47:34 +00007545 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007546 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007547
glennrpb9cfe272010-12-21 15:08:06 +00007548 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7549 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007550 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007551 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007552
cristy3ed852e2009-09-05 21:47:34 +00007553#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007554 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007555#endif
7556
glennrp5af765f2010-03-30 11:12:18 +00007557 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007558 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007559 ping_color_type=0,
7560 ping_interlace_method=0,
7561 ping_compression_method=0,
7562 ping_filter_method=0,
7563 ping_num_trans = 0;
7564
7565 ping_background.red = 0;
7566 ping_background.green = 0;
7567 ping_background.blue = 0;
7568 ping_background.gray = 0;
7569 ping_background.index = 0;
7570
7571 ping_trans_color.red=0;
7572 ping_trans_color.green=0;
7573 ping_trans_color.blue=0;
7574 ping_trans_color.gray=0;
7575
glennrpdfd70802010-11-14 01:23:35 +00007576 ping_pHYs_unit_type = 0;
7577 ping_pHYs_x_resolution = 0;
7578 ping_pHYs_y_resolution = 0;
7579
glennrpda8f3a72011-02-27 23:54:12 +00007580 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007581 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007582 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007583 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007584 ping_have_bKGD=MagickFalse;
7585 ping_have_pHYs=MagickFalse;
7586 ping_have_tRNS=MagickFalse;
7587
glennrp0e8ea192010-12-24 18:00:33 +00007588 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7589 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007590 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007591 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007592 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007593 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7594 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7595 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7596 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7597 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7598 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007599 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007600 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7601 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7602 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7603
glennrp8d3d6e52011-04-19 04:39:51 +00007604 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007605 ping_need_colortype_warning = MagickFalse;
7606
cristy0d57eec2011-09-04 22:13:56 +00007607 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7608 * i.e., eliminate the ICC profile and set image->rendering_intent.
7609 * Note that this will not involve any changes to the actual pixels
7610 * but merely passes information to applications that read the resulting
7611 * PNG image.
7612 */
7613 if (ping_exclude_sRGB == MagickFalse)
7614 {
7615 char
7616 *name;
7617
7618 const StringInfo
7619 *profile;
7620
7621 ResetImageProfileIterator(image);
7622 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7623 {
7624 profile=GetImageProfile(image,name);
7625
7626 if (profile != (StringInfo *) NULL)
7627 {
7628 if ((LocaleCompare(name,"ICC") == 0) ||
glennrp29a106e2011-09-06 17:11:42 +00007629 (LocaleCompare(name,"ICM") == 0))
7630 {
cristy0d57eec2011-09-04 22:13:56 +00007631 unsigned char
7632 *data;
7633
7634 png_uint_32
7635 length;
7636
glennrp29a106e2011-09-06 17:11:42 +00007637 length=(png_uint_32) GetStringInfoLength(profile);
7638
7639 if (length == 3144)
cristy0d57eec2011-09-04 22:13:56 +00007640 {
glennrp29a106e2011-09-06 17:11:42 +00007641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7642 " got a 3144-byte ICC profile (potentially sRGB)");
cristy0d57eec2011-09-04 22:13:56 +00007643
glennrp29a106e2011-09-06 17:11:42 +00007644 data=GetStringInfoDatum(profile);
7645
7646 if (data[52]=='s' && data[53]=='R' &&
7647 data[54]=='G' && data[55]=='B')
7648 {
cristy0d57eec2011-09-04 22:13:56 +00007649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp29a106e2011-09-06 17:11:42 +00007650 " It is the HP-Microsoft sRGB)");
cristye9ac4c32011-09-26 18:47:22 +00007651 if (image->rendering_intent==UndefinedIntent)
glennrp29a106e2011-09-06 17:11:42 +00007652 image->rendering_intent=PerceptualIntent;
7653 }
7654 else
7655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7656 " It is not sRGB (%c%c%c%c)",data[52],
7657 data[53],data[54],data[55]);
7658
cristy0d57eec2011-09-04 22:13:56 +00007659 }
glennrpdeaaa1f2011-09-30 19:30:49 +00007660 else if (length == 60960)
7661 {
7662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7663 " got a 60960-byte ICC profile (potentially sRGB)");
7664
7665 data=GetStringInfoDatum(profile);
7666
7667 if (data[269]=='s' && data[271]=='R' &&
7668 data[273]=='G' && data[275]=='B')
7669 {
7670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7671 " It is the ICC v4 sRGB)");
7672 if (image->rendering_intent==UndefinedIntent)
7673 image->rendering_intent=PerceptualIntent;
7674 }
7675 else
7676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7677 " It is not sRGB (%c%c%c%c)",
7678 data[269],data[271],data[273],data[275]);
7679
7680 }
glennrp29a106e2011-09-06 17:11:42 +00007681 else if (length == 3052)
7682 {
7683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7684 " got a 3052-byte ICC profile (potentially sRGB)");
7685
7686 data=GetStringInfoDatum(profile);
7687
7688 if (data[336]=='s' && data[337]=='R' &&
7689 data[338]=='G' && data[339]=='B')
7690 {
7691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7692 " It is the ICC no-black sRGB)");
cristye9ac4c32011-09-26 18:47:22 +00007693 if (image->rendering_intent==UndefinedIntent)
glennrp29a106e2011-09-06 17:11:42 +00007694 image->rendering_intent=PerceptualIntent;
7695 }
7696 else
7697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpdeaaa1f2011-09-30 19:30:49 +00007698 " It is not sRGB (%c%c%c%c)",data[336],
7699 data[337],data[338],data[339]);
glennrp29a106e2011-09-06 17:11:42 +00007700
7701 }
7702 else
7703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7704 " got a %lu-byte ICC profile",
7705 (unsigned long) length);
7706 }
cristy0d57eec2011-09-04 22:13:56 +00007707 }
7708 name=GetNextImageProfile(image);
7709 }
7710 }
7711
glennrp8bb3a022010-12-13 20:40:04 +00007712 number_opaque = 0;
7713 number_semitransparent = 0;
7714 number_transparent = 0;
7715
glennrpfd05d622011-02-25 04:10:33 +00007716 if (logging != MagickFalse)
7717 {
7718 if (image->storage_class == UndefinedClass)
7719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7720 " storage_class=UndefinedClass");
7721 if (image->storage_class == DirectClass)
7722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7723 " storage_class=DirectClass");
7724 if (image->storage_class == PseudoClass)
7725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7726 " storage_class=PseudoClass");
7727 }
glennrp28af3712011-04-06 18:07:30 +00007728
glennrp7e65e932011-08-19 02:31:16 +00007729 if (image->storage_class == PseudoClass &&
7730 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7731 (mng_info->write_png_colortype != 0 &&
7732 mng_info->write_png_colortype != 4)))
7733 {
7734 (void) SyncImage(image);
7735 image->storage_class = DirectClass;
7736 }
7737
glennrpc6c391a2011-04-27 02:23:56 +00007738 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007739 {
glennrpc6c391a2011-04-27 02:23:56 +00007740 if (image->storage_class != PseudoClass && image->colormap != NULL)
7741 {
7742 /* Free the bogus colormap; it can cause trouble later */
7743 if (logging != MagickFalse)
7744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7745 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00007746 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00007747 image->colormap=NULL;
7748 }
glennrp28af3712011-04-06 18:07:30 +00007749 }
glennrpbb4f99d2011-05-22 11:13:17 +00007750
cristy510d06a2011-07-06 23:43:54 +00007751 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007752 (void) TransformImageColorspace(image,RGBColorspace);
glennrp0fe50b42010-11-16 03:52:51 +00007753
glennrp3241bd02010-12-12 04:36:28 +00007754 /*
7755 Sometimes we get PseudoClass images whose RGB values don't match
7756 the colors in the colormap. This code syncs the RGB values.
7757 */
7758 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7759 (void) SyncImage(image);
7760
glennrpa6a06632011-01-19 15:15:34 +00007761#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7762 if (image->depth > 8)
7763 {
7764 if (logging != MagickFalse)
7765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7766 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7767
7768 image->depth=8;
7769 }
7770#endif
7771
glennrp8e58efd2011-05-20 12:16:29 +00007772 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007773 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7774 {
cristy4c08aed2011-07-01 19:47:50 +00007775 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007776 *r;
7777
7778 ExceptionInfo
7779 *exception;
7780
7781 exception=(&image->exception);
7782
7783 if (image->depth > 8)
7784 {
7785#if MAGICKCORE_QUANTUM_DEPTH > 16
7786 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007787 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007788
7789 for (y=0; y < (ssize_t) image->rows; y++)
7790 {
7791 r=GetAuthenticPixels(image,0,y,image->columns,1,
7792 exception);
7793
cristy4c08aed2011-07-01 19:47:50 +00007794 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007795 break;
7796
7797 for (x=0; x < (ssize_t) image->columns; x++)
7798 {
glennrp54cf7972011-08-06 14:28:09 +00007799 LBR16PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007800 r++;
7801 }
glennrpbb4f99d2011-05-22 11:13:17 +00007802
glennrp8e58efd2011-05-20 12:16:29 +00007803 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7804 break;
7805 }
7806
7807 if (image->storage_class == PseudoClass && image->colormap != NULL)
7808 {
cristy3e08f112011-05-24 13:19:30 +00007809 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007810 {
glennrp91d99252011-06-25 14:30:13 +00007811 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007812 }
7813 }
7814#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7815 }
7816
7817 else if (image->depth > 4)
7818 {
7819#if MAGICKCORE_QUANTUM_DEPTH > 8
7820 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007821 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007822
7823 for (y=0; y < (ssize_t) image->rows; y++)
7824 {
7825 r=GetAuthenticPixels(image,0,y,image->columns,1,
7826 exception);
7827
cristy4c08aed2011-07-01 19:47:50 +00007828 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007829 break;
7830
7831 for (x=0; x < (ssize_t) image->columns; x++)
7832 {
glennrp54cf7972011-08-06 14:28:09 +00007833 LBR08PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007834 r++;
7835 }
glennrpbb4f99d2011-05-22 11:13:17 +00007836
glennrp8e58efd2011-05-20 12:16:29 +00007837 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7838 break;
7839 }
7840
7841 if (image->storage_class == PseudoClass && image->colormap != NULL)
7842 {
cristy3e08f112011-05-24 13:19:30 +00007843 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007844 {
glennrp91d99252011-06-25 14:30:13 +00007845 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007846 }
7847 }
7848#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7849 }
7850 else
7851 if (image->depth > 2)
7852 {
7853 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007854 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007855
7856 for (y=0; y < (ssize_t) image->rows; y++)
7857 {
7858 r=GetAuthenticPixels(image,0,y,image->columns,1,
7859 exception);
7860
cristy4c08aed2011-07-01 19:47:50 +00007861 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007862 break;
7863
7864 for (x=0; x < (ssize_t) image->columns; x++)
7865 {
glennrp54cf7972011-08-06 14:28:09 +00007866 LBR04PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007867 r++;
7868 }
glennrpbb4f99d2011-05-22 11:13:17 +00007869
glennrp8e58efd2011-05-20 12:16:29 +00007870 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7871 break;
7872 }
7873
7874 if (image->storage_class == PseudoClass && image->colormap != NULL)
7875 {
cristy3e08f112011-05-24 13:19:30 +00007876 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007877 {
glennrp91d99252011-06-25 14:30:13 +00007878 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007879 }
7880 }
7881 }
7882
7883 else if (image->depth > 1)
7884 {
7885 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007886 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007887
7888 for (y=0; y < (ssize_t) image->rows; y++)
7889 {
7890 r=GetAuthenticPixels(image,0,y,image->columns,1,
7891 exception);
7892
cristy4c08aed2011-07-01 19:47:50 +00007893 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007894 break;
7895
7896 for (x=0; x < (ssize_t) image->columns; x++)
7897 {
glennrp54cf7972011-08-06 14:28:09 +00007898 LBR02PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007899 r++;
7900 }
glennrpbb4f99d2011-05-22 11:13:17 +00007901
glennrp8e58efd2011-05-20 12:16:29 +00007902 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7903 break;
7904 }
7905
7906 if (image->storage_class == PseudoClass && image->colormap != NULL)
7907 {
cristy3e08f112011-05-24 13:19:30 +00007908 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007909 {
glennrp91d99252011-06-25 14:30:13 +00007910 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007911 }
7912 }
7913 }
7914 else
7915 {
7916 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007917 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007918
7919 for (y=0; y < (ssize_t) image->rows; y++)
7920 {
7921 r=GetAuthenticPixels(image,0,y,image->columns,1,
7922 exception);
7923
cristy4c08aed2011-07-01 19:47:50 +00007924 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007925 break;
7926
7927 for (x=0; x < (ssize_t) image->columns; x++)
7928 {
glennrp54cf7972011-08-06 14:28:09 +00007929 LBR01PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007930 r++;
7931 }
glennrpbb4f99d2011-05-22 11:13:17 +00007932
glennrp8e58efd2011-05-20 12:16:29 +00007933 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7934 break;
7935 }
7936
7937 if (image->storage_class == PseudoClass && image->colormap != NULL)
7938 {
cristy3e08f112011-05-24 13:19:30 +00007939 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007940 {
glennrp91d99252011-06-25 14:30:13 +00007941 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007942 }
7943 }
7944 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007945 }
7946
glennrp67b9c1a2011-04-22 18:47:36 +00007947 /* To do: set to next higher multiple of 8 */
7948 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007949 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007950
glennrp2b013e42010-11-24 16:55:50 +00007951#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7952 /* PNG does not handle depths greater than 16 so reduce it even
7953 * if lossy
7954 */
glennrp8e58efd2011-05-20 12:16:29 +00007955 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007956 image->depth=16;
7957#endif
7958
glennrp3faa9a32011-04-23 14:00:25 +00007959#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007960 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007961 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007962 image->depth = 8;
7963#endif
7964
glennrpc8c2f062011-02-25 19:00:33 +00007965 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007966 * we reduce the transparency to binary and run again, then if there
7967 * 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 +00007968 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7969 * palette. Then (To do) we take care of a final reduction that is only
7970 * needed if there are still 256 colors present and one of them has both
7971 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007972 */
glennrp82b3c532011-03-22 19:20:54 +00007973
glennrp8ca51ad2011-05-12 21:22:32 +00007974 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007975 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007976 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007977
glennrp8ca51ad2011-05-12 21:22:32 +00007978 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007979 {
7980 /* BUILD_PALETTE
7981 *
7982 * Sometimes we get DirectClass images that have 256 colors or fewer.
7983 * This code will build a colormap.
7984 *
7985 * Also, sometimes we get PseudoClass images with an out-of-date
7986 * colormap. This code will replace the colormap with a new one.
7987 * Sometimes we get PseudoClass images that have more than 256 colors.
7988 * This code will delete the colormap and change the image to
7989 * DirectClass.
7990 *
cristy4c08aed2011-07-01 19:47:50 +00007991 * If image->matte is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00007992 * even though it sometimes contains left-over non-opaque values.
7993 *
7994 * Also we gather some information (number of opaque, transparent,
7995 * and semitransparent pixels, and whether the image has any non-gray
7996 * pixels or only black-and-white pixels) that we might need later.
7997 *
7998 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7999 * we need to check for bogus non-opaque values, at least.
8000 */
glennrp3c218112010-11-27 15:31:26 +00008001
glennrpd71e86a2011-02-24 01:28:37 +00008002 ExceptionInfo
8003 *exception;
glennrp3c218112010-11-27 15:31:26 +00008004
glennrpd71e86a2011-02-24 01:28:37 +00008005 int
8006 n;
glennrp3c218112010-11-27 15:31:26 +00008007
glennrpd71e86a2011-02-24 01:28:37 +00008008 PixelPacket
8009 opaque[260],
8010 semitransparent[260],
8011 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00008012
cristy4c08aed2011-07-01 19:47:50 +00008013 register const Quantum
8014 *s;
glennrp8bb3a022010-12-13 20:40:04 +00008015
cristy4c08aed2011-07-01 19:47:50 +00008016 register Quantum
8017 *q,
glennrpfd05d622011-02-25 04:10:33 +00008018 *r;
8019
glennrpd71e86a2011-02-24 01:28:37 +00008020 if (logging != MagickFalse)
8021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8022 " Enter BUILD_PALETTE:");
8023
8024 if (logging != MagickFalse)
8025 {
glennrp03812ae2010-12-24 01:31:34 +00008026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008027 " image->columns=%.20g",(double) image->columns);
8028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8029 " image->rows=%.20g",(double) image->rows);
8030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8031 " image->matte=%.20g",(double) image->matte);
8032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8033 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008034
glennrpfd05d622011-02-25 04:10:33 +00008035 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008036 {
8037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008038 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008040 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008041
glennrpd71e86a2011-02-24 01:28:37 +00008042 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008043 {
glennrpd71e86a2011-02-24 01:28:37 +00008044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8045 " %d (%d,%d,%d,%d)",
8046 (int) i,
8047 (int) image->colormap[i].red,
8048 (int) image->colormap[i].green,
8049 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008050 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008051 }
glennrp2cc891a2010-12-24 13:44:32 +00008052
glennrpd71e86a2011-02-24 01:28:37 +00008053 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8054 {
8055 if (i > 255)
8056 {
8057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8058 " %d (%d,%d,%d,%d)",
8059 (int) i,
8060 (int) image->colormap[i].red,
8061 (int) image->colormap[i].green,
8062 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008063 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008064 }
8065 }
glennrp03812ae2010-12-24 01:31:34 +00008066 }
glennrp7ddcc222010-12-11 05:01:05 +00008067
glennrpd71e86a2011-02-24 01:28:37 +00008068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8069 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008070
glennrpd71e86a2011-02-24 01:28:37 +00008071 if (image->colors == 0)
8072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8073 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008074
glennrp8d3d6e52011-04-19 04:39:51 +00008075 if (ping_preserve_colormap == MagickFalse)
8076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8077 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008078 }
8079
8080 exception=(&image->exception);
8081
8082 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008083 number_opaque = 0;
8084 number_semitransparent = 0;
8085 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008086
8087 for (y=0; y < (ssize_t) image->rows; y++)
8088 {
8089 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8090
cristyacd2ed22011-08-30 01:44:23 +00008091 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008092 break;
8093
8094 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008095 {
glennrp4737d522011-04-29 03:33:42 +00008096 if (image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008097 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008098 {
8099 if (number_opaque < 259)
8100 {
8101 if (number_opaque == 0)
8102 {
cristy4c08aed2011-07-01 19:47:50 +00008103 GetPixelPacket(image, q, opaque);
8104 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008105 number_opaque=1;
8106 }
glennrp2cc891a2010-12-24 13:44:32 +00008107
glennrpd71e86a2011-02-24 01:28:37 +00008108 for (i=0; i< (ssize_t) number_opaque; i++)
8109 {
cristy4c08aed2011-07-01 19:47:50 +00008110 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008111 break;
8112 }
glennrp7ddcc222010-12-11 05:01:05 +00008113
glennrpd71e86a2011-02-24 01:28:37 +00008114 if (i == (ssize_t) number_opaque &&
8115 number_opaque < 259)
8116 {
8117 number_opaque++;
cristy4c08aed2011-07-01 19:47:50 +00008118 GetPixelPacket(image, q, opaque+i);
8119 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008120 }
8121 }
8122 }
cristy4c08aed2011-07-01 19:47:50 +00008123 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008124 {
8125 if (number_transparent < 259)
8126 {
8127 if (number_transparent == 0)
8128 {
cristy4c08aed2011-07-01 19:47:50 +00008129 GetPixelPacket(image, q, transparent);
8130 ping_trans_color.red=(unsigned short)
8131 GetPixelRed(image,q);
8132 ping_trans_color.green=(unsigned short)
8133 GetPixelGreen(image,q);
8134 ping_trans_color.blue=(unsigned short)
8135 GetPixelBlue(image,q);
8136 ping_trans_color.gray=(unsigned short)
8137 GetPixelRed(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008138 number_transparent = 1;
8139 }
8140
8141 for (i=0; i< (ssize_t) number_transparent; i++)
8142 {
cristy4c08aed2011-07-01 19:47:50 +00008143 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008144 break;
8145 }
8146
8147 if (i == (ssize_t) number_transparent &&
8148 number_transparent < 259)
8149 {
8150 number_transparent++;
cristy4c08aed2011-07-01 19:47:50 +00008151 GetPixelPacket(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008152 }
8153 }
8154 }
8155 else
8156 {
8157 if (number_semitransparent < 259)
8158 {
8159 if (number_semitransparent == 0)
8160 {
cristy4c08aed2011-07-01 19:47:50 +00008161 GetPixelPacket(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008162 number_semitransparent = 1;
8163 }
8164
8165 for (i=0; i< (ssize_t) number_semitransparent; i++)
8166 {
cristy4c08aed2011-07-01 19:47:50 +00008167 if (IsPixelEquivalent(image,q, semitransparent+i)
8168 && GetPixelAlpha(image,q) ==
8169 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008170 break;
8171 }
8172
8173 if (i == (ssize_t) number_semitransparent &&
8174 number_semitransparent < 259)
8175 {
8176 number_semitransparent++;
cristy4c08aed2011-07-01 19:47:50 +00008177 GetPixelPacket(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008178 }
8179 }
8180 }
cristyed231572011-07-14 02:18:59 +00008181 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008182 }
8183 }
8184
cristy4054bfb2011-08-29 23:41:39 +00008185 if (mng_info->write_png8 == MagickFalse &&
8186 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008187 {
8188 /* Add the background color to the palette, if it
8189 * isn't already there.
8190 */
glennrpc6c391a2011-04-27 02:23:56 +00008191 if (logging != MagickFalse)
8192 {
8193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8194 " Check colormap for background (%d,%d,%d)",
8195 (int) image->background_color.red,
8196 (int) image->background_color.green,
8197 (int) image->background_color.blue);
8198 }
glennrpd71e86a2011-02-24 01:28:37 +00008199 for (i=0; i<number_opaque; i++)
8200 {
glennrpca7ad3a2011-04-26 04:44:54 +00008201 if (opaque[i].red == image->background_color.red &&
8202 opaque[i].green == image->background_color.green &&
8203 opaque[i].blue == image->background_color.blue)
8204 break;
glennrpd71e86a2011-02-24 01:28:37 +00008205 }
glennrpd71e86a2011-02-24 01:28:37 +00008206 if (number_opaque < 259 && i == number_opaque)
8207 {
glennrp8e045c82011-04-27 16:40:27 +00008208 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008209 ping_background.index = i;
8210 if (logging != MagickFalse)
8211 {
8212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8213 " background_color index is %d",(int) i);
8214 }
8215
glennrpd71e86a2011-02-24 01:28:37 +00008216 }
glennrpa080bc32011-03-11 18:03:44 +00008217 else if (logging != MagickFalse)
8218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8219 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008220 }
8221
8222 image_colors=number_opaque+number_transparent+number_semitransparent;
8223
glennrpa080bc32011-03-11 18:03:44 +00008224 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8225 {
8226 /* No room for the background color; remove it. */
8227 number_opaque--;
8228 image_colors--;
8229 }
8230
glennrpd71e86a2011-02-24 01:28:37 +00008231 if (logging != MagickFalse)
8232 {
8233 if (image_colors > 256)
8234 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8235 " image has more than 256 colors");
8236
8237 else
8238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8239 " image has %d colors",image_colors);
8240 }
8241
glennrp8d3d6e52011-04-19 04:39:51 +00008242 if (ping_preserve_colormap != MagickFalse)
8243 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008244
glennrpfd05d622011-02-25 04:10:33 +00008245 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008246 {
8247 ping_have_color=MagickFalse;
8248 ping_have_non_bw=MagickFalse;
8249
8250 if(image_colors > 256)
8251 {
8252 for (y=0; y < (ssize_t) image->rows; y++)
8253 {
8254 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8255
cristyacd2ed22011-08-30 01:44:23 +00008256 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008257 break;
8258
glennrpe5e6b802011-07-20 14:44:40 +00008259 s=q;
8260 for (x=0; x < (ssize_t) image->columns; x++)
8261 {
8262 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8263 GetPixelRed(image,s) != GetPixelBlue(image,s))
8264 {
8265 ping_have_color=MagickTrue;
8266 ping_have_non_bw=MagickTrue;
8267 break;
8268 }
8269 s+=GetPixelChannels(image);
8270 }
8271
8272 if (ping_have_color != MagickFalse)
8273 break;
8274
glennrpd71e86a2011-02-24 01:28:37 +00008275 /* Worst case is black-and-white; we are looking at every
8276 * pixel twice.
8277 */
8278
glennrpd71e86a2011-02-24 01:28:37 +00008279 if (ping_have_non_bw == MagickFalse)
8280 {
8281 s=q;
8282 for (x=0; x < (ssize_t) image->columns; x++)
8283 {
cristy4c08aed2011-07-01 19:47:50 +00008284 if (GetPixelRed(image,s) != 0 &&
8285 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008286 {
8287 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008288 break;
glennrpd71e86a2011-02-24 01:28:37 +00008289 }
cristyed231572011-07-14 02:18:59 +00008290 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008291 }
glennrpe5e6b802011-07-20 14:44:40 +00008292 }
glennrpd71e86a2011-02-24 01:28:37 +00008293 }
glennrpbb4f99d2011-05-22 11:13:17 +00008294 }
8295 }
glennrpd71e86a2011-02-24 01:28:37 +00008296
8297 if (image_colors < 257)
8298 {
8299 PixelPacket
8300 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008301
glennrpd71e86a2011-02-24 01:28:37 +00008302 /*
8303 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008304 */
8305
glennrpd71e86a2011-02-24 01:28:37 +00008306 if (logging != MagickFalse)
8307 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8308 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008309
glennrpd71e86a2011-02-24 01:28:37 +00008310 /* Sort palette, transparent first */;
8311
8312 n = 0;
8313
8314 for (i=0; i<number_transparent; i++)
8315 colormap[n++] = transparent[i];
8316
8317 for (i=0; i<number_semitransparent; i++)
8318 colormap[n++] = semitransparent[i];
8319
8320 for (i=0; i<number_opaque; i++)
8321 colormap[n++] = opaque[i];
8322
glennrpc6c391a2011-04-27 02:23:56 +00008323 ping_background.index +=
8324 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008325
glennrpd71e86a2011-02-24 01:28:37 +00008326 /* image_colors < 257; search the colormap instead of the pixels
8327 * to get ping_have_color and ping_have_non_bw
8328 */
8329 for (i=0; i<n; i++)
8330 {
8331 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008332 {
glennrpd71e86a2011-02-24 01:28:37 +00008333 if (colormap[i].red != colormap[i].green ||
8334 colormap[i].red != colormap[i].blue)
8335 {
8336 ping_have_color=MagickTrue;
8337 ping_have_non_bw=MagickTrue;
8338 break;
8339 }
8340 }
8341
8342 if (ping_have_non_bw == MagickFalse)
8343 {
8344 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008345 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008346 }
glennrp8bb3a022010-12-13 20:40:04 +00008347 }
8348
glennrpd71e86a2011-02-24 01:28:37 +00008349 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8350 (number_transparent == 0 && number_semitransparent == 0)) &&
8351 (((mng_info->write_png_colortype-1) ==
8352 PNG_COLOR_TYPE_PALETTE) ||
8353 (mng_info->write_png_colortype == 0)))
8354 {
glennrp6185c532011-01-14 17:58:40 +00008355 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008356 {
glennrpd71e86a2011-02-24 01:28:37 +00008357 if (n != (ssize_t) image_colors)
8358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8359 " image_colors (%d) and n (%d) don't match",
8360 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008361
glennrpd71e86a2011-02-24 01:28:37 +00008362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8363 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008364 }
glennrp03812ae2010-12-24 01:31:34 +00008365
glennrpd71e86a2011-02-24 01:28:37 +00008366 image->colors = image_colors;
8367
cristy018f07f2011-09-04 21:15:19 +00008368 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008369 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008370 ThrowWriterException(ResourceLimitError,
8371 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008372
8373 for (i=0; i< (ssize_t) image_colors; i++)
8374 image->colormap[i] = colormap[i];
8375
8376 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008377 {
glennrpd71e86a2011-02-24 01:28:37 +00008378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8379 " image->colors=%d (%d)",
8380 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008381
glennrpd71e86a2011-02-24 01:28:37 +00008382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8383 " Update the pixel indexes");
8384 }
glennrp6185c532011-01-14 17:58:40 +00008385
glennrpfd05d622011-02-25 04:10:33 +00008386 /* Sync the pixel indices with the new colormap */
8387
glennrpd71e86a2011-02-24 01:28:37 +00008388 for (y=0; y < (ssize_t) image->rows; y++)
8389 {
8390 q=GetAuthenticPixels(image,0,y,image->columns,1,
8391 exception);
glennrp6185c532011-01-14 17:58:40 +00008392
cristyacd2ed22011-08-30 01:44:23 +00008393 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008394 break;
glennrp6185c532011-01-14 17:58:40 +00008395
glennrpbb4f99d2011-05-22 11:13:17 +00008396
glennrpd71e86a2011-02-24 01:28:37 +00008397 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008398 {
glennrpd71e86a2011-02-24 01:28:37 +00008399 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008400 {
glennrpd71e86a2011-02-24 01:28:37 +00008401 if ((image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008402 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8403 image->colormap[i].red == GetPixelRed(image,q) &&
8404 image->colormap[i].green == GetPixelGreen(image,q) &&
8405 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008406 {
cristy4c08aed2011-07-01 19:47:50 +00008407 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008408 break;
glennrp6185c532011-01-14 17:58:40 +00008409 }
glennrp6185c532011-01-14 17:58:40 +00008410 }
cristyed231572011-07-14 02:18:59 +00008411 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008412 }
glennrp6185c532011-01-14 17:58:40 +00008413
glennrpd71e86a2011-02-24 01:28:37 +00008414 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8415 break;
8416 }
8417 }
8418 }
8419
8420 if (logging != MagickFalse)
8421 {
8422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8423 " image->colors=%d", (int) image->colors);
8424
8425 if (image->colormap != NULL)
8426 {
8427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008428 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008429
8430 for (i=0; i < (ssize_t) image->colors; i++)
8431 {
cristy72988482011-03-29 16:34:38 +00008432 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008433 {
8434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8435 " %d (%d,%d,%d,%d)",
8436 (int) i,
8437 (int) image->colormap[i].red,
8438 (int) image->colormap[i].green,
8439 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008440 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008441 }
glennrp6185c532011-01-14 17:58:40 +00008442 }
8443 }
glennrp03812ae2010-12-24 01:31:34 +00008444
glennrpd71e86a2011-02-24 01:28:37 +00008445 if (number_transparent < 257)
8446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8447 " number_transparent = %d",
8448 number_transparent);
8449 else
glennrp03812ae2010-12-24 01:31:34 +00008450
glennrpd71e86a2011-02-24 01:28:37 +00008451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8452 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008453
glennrpd71e86a2011-02-24 01:28:37 +00008454 if (number_opaque < 257)
8455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8456 " number_opaque = %d",
8457 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008458
glennrpd71e86a2011-02-24 01:28:37 +00008459 else
8460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8461 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008462
glennrpd71e86a2011-02-24 01:28:37 +00008463 if (number_semitransparent < 257)
8464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8465 " number_semitransparent = %d",
8466 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008467
glennrpd71e86a2011-02-24 01:28:37 +00008468 else
8469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8470 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008471
glennrpd71e86a2011-02-24 01:28:37 +00008472 if (ping_have_non_bw == MagickFalse)
8473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8474 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008475
glennrpd71e86a2011-02-24 01:28:37 +00008476 else if (ping_have_color == MagickFalse)
8477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8478 " All pixels and the background are gray");
8479
8480 else
8481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8482 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008483
glennrp03812ae2010-12-24 01:31:34 +00008484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8485 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008486 }
glennrpfd05d622011-02-25 04:10:33 +00008487
glennrpc8c2f062011-02-25 19:00:33 +00008488 if (mng_info->write_png8 == MagickFalse)
8489 break;
glennrpfd05d622011-02-25 04:10:33 +00008490
glennrpc8c2f062011-02-25 19:00:33 +00008491 /* Make any reductions necessary for the PNG8 format */
8492 if (image_colors <= 256 &&
8493 image_colors != 0 && image->colormap != NULL &&
8494 number_semitransparent == 0 &&
8495 number_transparent <= 1)
8496 break;
8497
8498 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008499 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8500 * transparent color so if more than one is transparent we merge
8501 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008502 */
glennrp130fc452011-08-20 03:43:18 +00008503 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008504 {
8505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8506 " Thresholding the alpha channel to binary");
8507
8508 for (y=0; y < (ssize_t) image->rows; y++)
8509 {
8510 r=GetAuthenticPixels(image,0,y,image->columns,1,
8511 exception);
8512
cristy4c08aed2011-07-01 19:47:50 +00008513 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008514 break;
8515
8516 for (x=0; x < (ssize_t) image->columns; x++)
8517 {
glennrpf73547f2011-08-20 04:40:26 +00008518 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008519 {
cristy4c08aed2011-07-01 19:47:50 +00008520 SetPixelPacket(image,&image->background_color,r);
8521 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008522 }
8523 else
cristy4c08aed2011-07-01 19:47:50 +00008524 SetPixelAlpha(image,OpaqueAlpha,r);
cristyed231572011-07-14 02:18:59 +00008525 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008526 }
glennrpbb4f99d2011-05-22 11:13:17 +00008527
glennrpc8c2f062011-02-25 19:00:33 +00008528 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8529 break;
8530
8531 if (image_colors != 0 && image_colors <= 256 &&
8532 image->colormap != NULL)
8533 for (i=0; i<image_colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00008534 image->colormap[i].alpha =
8535 (image->colormap[i].alpha > TransparentAlpha/2 ?
8536 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008537 }
8538 continue;
8539 }
8540
8541 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008542 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8543 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8544 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008545 */
glennrpd3371642011-03-22 19:42:23 +00008546 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8547 {
8548 if (logging != MagickFalse)
8549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8550 " Quantizing the background color to 4-4-4");
8551
8552 tried_444 = MagickTrue;
8553
glennrp91d99252011-06-25 14:30:13 +00008554 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008555
8556 if (logging != MagickFalse)
8557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8558 " Quantizing the pixel colors to 4-4-4");
8559
8560 if (image->colormap == NULL)
8561 {
8562 for (y=0; y < (ssize_t) image->rows; y++)
8563 {
8564 r=GetAuthenticPixels(image,0,y,image->columns,1,
8565 exception);
8566
cristy4c08aed2011-07-01 19:47:50 +00008567 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008568 break;
8569
8570 for (x=0; x < (ssize_t) image->columns; x++)
8571 {
cristy4c08aed2011-07-01 19:47:50 +00008572 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008573 LBR04PixelRGB(r);
glennrpd3371642011-03-22 19:42:23 +00008574 r++;
8575 }
glennrpbb4f99d2011-05-22 11:13:17 +00008576
glennrpd3371642011-03-22 19:42:23 +00008577 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8578 break;
8579 }
8580 }
8581
8582 else /* Should not reach this; colormap already exists and
8583 must be <= 256 */
8584 {
8585 if (logging != MagickFalse)
8586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8587 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008588
glennrpd3371642011-03-22 19:42:23 +00008589 for (i=0; i<image_colors; i++)
8590 {
glennrp91d99252011-06-25 14:30:13 +00008591 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008592 }
8593 }
8594 continue;
8595 }
8596
glennrp82b3c532011-03-22 19:20:54 +00008597 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8598 {
8599 if (logging != MagickFalse)
8600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8601 " Quantizing the background color to 3-3-3");
8602
8603 tried_333 = MagickTrue;
8604
glennrp91d99252011-06-25 14:30:13 +00008605 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008606
8607 if (logging != MagickFalse)
8608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008609 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008610
8611 if (image->colormap == NULL)
8612 {
8613 for (y=0; y < (ssize_t) image->rows; y++)
8614 {
8615 r=GetAuthenticPixels(image,0,y,image->columns,1,
8616 exception);
8617
cristy4c08aed2011-07-01 19:47:50 +00008618 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008619 break;
8620
8621 for (x=0; x < (ssize_t) image->columns; x++)
8622 {
cristy4c08aed2011-07-01 19:47:50 +00008623 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8624 LBR03RGB(r);
glennrp82b3c532011-03-22 19:20:54 +00008625 r++;
8626 }
glennrpbb4f99d2011-05-22 11:13:17 +00008627
glennrp82b3c532011-03-22 19:20:54 +00008628 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8629 break;
8630 }
8631 }
8632
8633 else /* Should not reach this; colormap already exists and
8634 must be <= 256 */
8635 {
8636 if (logging != MagickFalse)
8637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008638 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008639 for (i=0; i<image_colors; i++)
8640 {
glennrp91d99252011-06-25 14:30:13 +00008641 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008642 }
glennrpd3371642011-03-22 19:42:23 +00008643 }
8644 continue;
glennrp82b3c532011-03-22 19:20:54 +00008645 }
glennrpc8c2f062011-02-25 19:00:33 +00008646
glennrp8ca51ad2011-05-12 21:22:32 +00008647 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008648 {
8649 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008651 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008652
glennrp8ca51ad2011-05-12 21:22:32 +00008653 tried_332 = MagickTrue;
8654
glennrp3faa9a32011-04-23 14:00:25 +00008655 /* Red and green were already done so we only quantize the blue
8656 * channel
8657 */
8658
glennrp91d99252011-06-25 14:30:13 +00008659 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008660
glennrpc8c2f062011-02-25 19:00:33 +00008661 if (logging != MagickFalse)
8662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008663 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008664
glennrpc8c2f062011-02-25 19:00:33 +00008665 if (image->colormap == NULL)
8666 {
8667 for (y=0; y < (ssize_t) image->rows; y++)
8668 {
8669 r=GetAuthenticPixels(image,0,y,image->columns,1,
8670 exception);
8671
cristy4c08aed2011-07-01 19:47:50 +00008672 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008673 break;
8674
8675 for (x=0; x < (ssize_t) image->columns; x++)
8676 {
cristy4c08aed2011-07-01 19:47:50 +00008677 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008678 LBR02PixelBlue(r);
glennrp52a479c2011-02-26 21:14:38 +00008679 r++;
glennrpc8c2f062011-02-25 19:00:33 +00008680 }
glennrpbb4f99d2011-05-22 11:13:17 +00008681
glennrpc8c2f062011-02-25 19:00:33 +00008682 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8683 break;
8684 }
8685 }
glennrpfd05d622011-02-25 04:10:33 +00008686
glennrpc8c2f062011-02-25 19:00:33 +00008687 else /* Should not reach this; colormap already exists and
8688 must be <= 256 */
8689 {
8690 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008692 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008693 for (i=0; i<image_colors; i++)
8694 {
glennrp91d99252011-06-25 14:30:13 +00008695 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008696 }
8697 }
8698 continue;
8699 }
8700 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008701
8702 if (image_colors == 0 || image_colors > 256)
8703 {
8704 /* Take care of special case with 256 colors + 1 transparent
8705 * color. We don't need to quantize to 2-3-2-1; we only need to
8706 * eliminate one color, so we'll merge the two darkest red
8707 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8708 */
8709 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8710 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8711 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8712 {
8713 image->background_color.red=ScaleCharToQuantum(0x24);
8714 }
glennrpbb4f99d2011-05-22 11:13:17 +00008715
glennrp8ca51ad2011-05-12 21:22:32 +00008716 if (image->colormap == NULL)
8717 {
8718 for (y=0; y < (ssize_t) image->rows; y++)
8719 {
8720 r=GetAuthenticPixels(image,0,y,image->columns,1,
8721 exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008722
cristy4c08aed2011-07-01 19:47:50 +00008723 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008724 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008725
glennrp8ca51ad2011-05-12 21:22:32 +00008726 for (x=0; x < (ssize_t) image->columns; x++)
8727 {
cristy4c08aed2011-07-01 19:47:50 +00008728 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8729 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8730 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8731 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008732 {
cristy4c08aed2011-07-01 19:47:50 +00008733 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008734 }
cristyed231572011-07-14 02:18:59 +00008735 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008736 }
glennrpbb4f99d2011-05-22 11:13:17 +00008737
glennrp8ca51ad2011-05-12 21:22:32 +00008738 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8739 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008740
glennrp8ca51ad2011-05-12 21:22:32 +00008741 }
8742 }
8743
8744 else
8745 {
8746 for (i=0; i<image_colors; i++)
8747 {
8748 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8749 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8750 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8751 {
8752 image->colormap[i].red=ScaleCharToQuantum(0x24);
8753 }
8754 }
8755 }
8756 }
glennrpd71e86a2011-02-24 01:28:37 +00008757 }
glennrpfd05d622011-02-25 04:10:33 +00008758 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008759
glennrpfd05d622011-02-25 04:10:33 +00008760 /* If we are excluding the tRNS chunk and there is transparency,
8761 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8762 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008763 */
glennrp0e8ea192010-12-24 18:00:33 +00008764 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8765 (number_transparent != 0 || number_semitransparent != 0))
8766 {
glennrpd17915c2011-04-29 14:24:22 +00008767 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008768
8769 if (ping_have_color == MagickFalse)
8770 mng_info->write_png_colortype = 5;
8771
8772 else
8773 mng_info->write_png_colortype = 7;
8774
glennrp8d579662011-02-23 02:05:02 +00008775 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008776 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008777 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008778
glennrp0e8ea192010-12-24 18:00:33 +00008779 }
8780
glennrpfd05d622011-02-25 04:10:33 +00008781 /* See if cheap transparency is possible. It is only possible
8782 * when there is a single transparent color, no semitransparent
8783 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008784 * as the transparent color. We only need this information if
8785 * we are writing a PNG with colortype 0 or 2, and we have not
8786 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008787 */
glennrp5a39f372011-02-25 04:52:16 +00008788 if (number_transparent == 1 &&
8789 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008790 {
8791 ping_have_cheap_transparency = MagickTrue;
8792
8793 if (number_semitransparent != 0)
8794 ping_have_cheap_transparency = MagickFalse;
8795
8796 else if (image_colors == 0 || image_colors > 256 ||
8797 image->colormap == NULL)
8798 {
8799 ExceptionInfo
8800 *exception;
8801
cristy4c08aed2011-07-01 19:47:50 +00008802 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008803 *q;
8804
8805 exception=(&image->exception);
8806
8807 for (y=0; y < (ssize_t) image->rows; y++)
8808 {
8809 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8810
cristyacd2ed22011-08-30 01:44:23 +00008811 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008812 break;
8813
8814 for (x=0; x < (ssize_t) image->columns; x++)
8815 {
cristy4c08aed2011-07-01 19:47:50 +00008816 if (GetPixelAlpha(image,q) != TransparentAlpha &&
glennrp847370c2011-07-05 17:37:15 +00008817 (unsigned short) GetPixelRed(image,q) ==
8818 ping_trans_color.red &&
8819 (unsigned short) GetPixelGreen(image,q) ==
8820 ping_trans_color.green &&
8821 (unsigned short) GetPixelBlue(image,q) ==
8822 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008823 {
8824 ping_have_cheap_transparency = MagickFalse;
8825 break;
8826 }
8827
cristyed231572011-07-14 02:18:59 +00008828 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008829 }
glennrpbb4f99d2011-05-22 11:13:17 +00008830
glennrpfd05d622011-02-25 04:10:33 +00008831 if (ping_have_cheap_transparency == MagickFalse)
8832 break;
8833 }
8834 }
8835 else
8836 {
glennrp67b9c1a2011-04-22 18:47:36 +00008837 /* Assuming that image->colormap[0] is the one transparent color
8838 * and that all others are opaque.
8839 */
glennrpfd05d622011-02-25 04:10:33 +00008840 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008841 for (i=1; i<image_colors; i++)
8842 if (image->colormap[i].red == image->colormap[0].red &&
8843 image->colormap[i].green == image->colormap[0].green &&
8844 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008845 {
glennrp67b9c1a2011-04-22 18:47:36 +00008846 ping_have_cheap_transparency = MagickFalse;
8847 break;
glennrpfd05d622011-02-25 04:10:33 +00008848 }
8849 }
glennrpbb4f99d2011-05-22 11:13:17 +00008850
glennrpfd05d622011-02-25 04:10:33 +00008851 if (logging != MagickFalse)
8852 {
8853 if (ping_have_cheap_transparency == MagickFalse)
8854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8855 " Cheap transparency is not possible.");
8856
8857 else
8858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8859 " Cheap transparency is possible.");
8860 }
8861 }
8862 else
8863 ping_have_cheap_transparency = MagickFalse;
8864
glennrp8640fb52010-11-23 15:48:26 +00008865 image_depth=image->depth;
8866
glennrp26c990a2010-11-23 02:23:20 +00008867 quantum_info = (QuantumInfo *) NULL;
8868 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008869 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008870 image_matte=image->matte;
8871
glennrp0fe50b42010-11-16 03:52:51 +00008872 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008873 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008874
glennrp52a479c2011-02-26 21:14:38 +00008875 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8876 (image->colors == 0 || image->colormap == NULL))
8877 {
glennrp52a479c2011-02-26 21:14:38 +00008878 image_info=DestroyImageInfo(image_info);
8879 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008880 (void) ThrowMagickException(&IMimage->exception,
8881 GetMagickModule(),CoderError,
8882 "Cannot write PNG8 or color-type 3; colormap is NULL",
8883 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008884#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8885 UnlockSemaphoreInfo(ping_semaphore);
8886#endif
8887 return(MagickFalse);
8888 }
8889
cristy3ed852e2009-09-05 21:47:34 +00008890 /*
8891 Allocate the PNG structures
8892 */
8893#ifdef PNG_USER_MEM_SUPPORTED
8894 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008895 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8896 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008897
cristy3ed852e2009-09-05 21:47:34 +00008898#else
8899 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008900 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008901
cristy3ed852e2009-09-05 21:47:34 +00008902#endif
8903 if (ping == (png_struct *) NULL)
8904 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008905
cristy3ed852e2009-09-05 21:47:34 +00008906 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008907
cristy3ed852e2009-09-05 21:47:34 +00008908 if (ping_info == (png_info *) NULL)
8909 {
8910 png_destroy_write_struct(&ping,(png_info **) NULL);
8911 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8912 }
glennrp0fe50b42010-11-16 03:52:51 +00008913
cristy3ed852e2009-09-05 21:47:34 +00008914 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008915 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008916
glennrp5af765f2010-03-30 11:12:18 +00008917 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008918 {
8919 /*
8920 PNG write failed.
8921 */
8922#ifdef PNG_DEBUG
8923 if (image_info->verbose)
8924 (void) printf("PNG write has failed.\n");
8925#endif
8926 png_destroy_write_struct(&ping,&ping_info);
8927#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008928 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008929#endif
glennrpda8f3a72011-02-27 23:54:12 +00008930 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008931 (void) CloseBlob(image);
8932 image_info=DestroyImageInfo(image_info);
8933 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008934 return(MagickFalse);
8935 }
8936 /*
8937 Prepare PNG for writing.
8938 */
8939#if defined(PNG_MNG_FEATURES_SUPPORTED)
8940 if (mng_info->write_mng)
8941 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008942
cristy3ed852e2009-09-05 21:47:34 +00008943#else
8944# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8945 if (mng_info->write_mng)
8946 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008947
cristy3ed852e2009-09-05 21:47:34 +00008948# endif
8949#endif
glennrp2b013e42010-11-24 16:55:50 +00008950
cristy3ed852e2009-09-05 21:47:34 +00008951 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008952
cristy4e5bc842010-06-09 13:56:01 +00008953 ping_width=(png_uint_32) image->columns;
8954 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008955
cristy3ed852e2009-09-05 21:47:34 +00008956 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8957 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008958
cristy3ed852e2009-09-05 21:47:34 +00008959 if (mng_info->write_png_depth != 0)
8960 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008961
cristy3ed852e2009-09-05 21:47:34 +00008962 /* Adjust requested depth to next higher valid depth if necessary */
8963 if (image_depth > 8)
8964 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008965
cristy3ed852e2009-09-05 21:47:34 +00008966 if ((image_depth > 4) && (image_depth < 8))
8967 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008968
cristy3ed852e2009-09-05 21:47:34 +00008969 if (image_depth == 3)
8970 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008971
cristy3ed852e2009-09-05 21:47:34 +00008972 if (logging != MagickFalse)
8973 {
8974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008975 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008977 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008979 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008981 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008983 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008984 }
glennrp8640fb52010-11-23 15:48:26 +00008985
cristy3ed852e2009-09-05 21:47:34 +00008986 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008987 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008988
glennrp26f37912010-12-23 16:22:42 +00008989
cristy3ed852e2009-09-05 21:47:34 +00008990#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008991 if (ping_exclude_pHYs == MagickFalse)
8992 {
cristy3ed852e2009-09-05 21:47:34 +00008993 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8994 (!mng_info->write_mng || !mng_info->equal_physs))
8995 {
glennrp0fe50b42010-11-16 03:52:51 +00008996 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8998 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008999
9000 if (image->units == PixelsPerInchResolution)
9001 {
glennrpdfd70802010-11-14 01:23:35 +00009002 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00009003 ping_pHYs_x_resolution=
9004 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
9005 ping_pHYs_y_resolution=
9006 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00009007 }
glennrpdfd70802010-11-14 01:23:35 +00009008
cristy3ed852e2009-09-05 21:47:34 +00009009 else if (image->units == PixelsPerCentimeterResolution)
9010 {
glennrpdfd70802010-11-14 01:23:35 +00009011 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00009012 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
9013 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00009014 }
glennrp991d11d2010-11-12 21:55:28 +00009015
cristy3ed852e2009-09-05 21:47:34 +00009016 else
9017 {
glennrpdfd70802010-11-14 01:23:35 +00009018 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9019 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
9020 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00009021 }
glennrp991d11d2010-11-12 21:55:28 +00009022
glennrp823b55c2011-03-14 18:46:46 +00009023 if (logging != MagickFalse)
9024 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9025 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9026 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9027 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009028 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009029 }
glennrp26f37912010-12-23 16:22:42 +00009030 }
cristy3ed852e2009-09-05 21:47:34 +00009031#endif
glennrpa521b2f2010-10-29 04:11:03 +00009032
glennrp26f37912010-12-23 16:22:42 +00009033 if (ping_exclude_bKGD == MagickFalse)
9034 {
glennrpa521b2f2010-10-29 04:11:03 +00009035 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009036 {
glennrpa521b2f2010-10-29 04:11:03 +00009037 unsigned int
9038 mask;
cristy3ed852e2009-09-05 21:47:34 +00009039
glennrpa521b2f2010-10-29 04:11:03 +00009040 mask=0xffff;
9041 if (ping_bit_depth == 8)
9042 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009043
glennrpa521b2f2010-10-29 04:11:03 +00009044 if (ping_bit_depth == 4)
9045 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009046
glennrpa521b2f2010-10-29 04:11:03 +00009047 if (ping_bit_depth == 2)
9048 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009049
glennrpa521b2f2010-10-29 04:11:03 +00009050 if (ping_bit_depth == 1)
9051 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009052
glennrpa521b2f2010-10-29 04:11:03 +00009053 ping_background.red=(png_uint_16)
9054 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009055
glennrpa521b2f2010-10-29 04:11:03 +00009056 ping_background.green=(png_uint_16)
9057 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009058
glennrpa521b2f2010-10-29 04:11:03 +00009059 ping_background.blue=(png_uint_16)
9060 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009061
9062 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009063 }
cristy3ed852e2009-09-05 21:47:34 +00009064
glennrp0fe50b42010-11-16 03:52:51 +00009065 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009066 {
9067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9068 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9070 " background_color index is %d",
9071 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009072
9073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9074 " ping_bit_depth=%d",ping_bit_depth);
9075 }
glennrp0fe50b42010-11-16 03:52:51 +00009076
9077 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009078 }
glennrp0fe50b42010-11-16 03:52:51 +00009079
cristy3ed852e2009-09-05 21:47:34 +00009080 /*
9081 Select the color type.
9082 */
9083 matte=image_matte;
9084 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009085
glennrp1273f7b2011-02-24 03:20:30 +00009086 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009087 {
glennrp0fe50b42010-11-16 03:52:51 +00009088
glennrpfd05d622011-02-25 04:10:33 +00009089 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009090 for reducing the sample depth from 8. */
9091
glennrp0fe50b42010-11-16 03:52:51 +00009092 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009093
glennrp8bb3a022010-12-13 20:40:04 +00009094 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009095
9096 /*
9097 Set image palette.
9098 */
9099 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9100
glennrp0fe50b42010-11-16 03:52:51 +00009101 if (logging != MagickFalse)
9102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9103 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009104 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009105
9106 for (i=0; i < (ssize_t) number_colors; i++)
9107 {
9108 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9109 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9110 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9111 if (logging != MagickFalse)
9112 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009113#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009114 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009115#else
9116 " %5ld (%5d,%5d,%5d)",
9117#endif
glennrp0fe50b42010-11-16 03:52:51 +00009118 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9119
9120 }
glennrp2b013e42010-11-24 16:55:50 +00009121
glennrp8bb3a022010-12-13 20:40:04 +00009122 ping_have_PLTE=MagickTrue;
9123 image_depth=ping_bit_depth;
9124 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009125
glennrp58e01762011-01-07 15:28:54 +00009126 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009127 {
glennrp0fe50b42010-11-16 03:52:51 +00009128 /*
9129 Identify which colormap entry is transparent.
9130 */
9131 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009132 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009133
glennrp8bb3a022010-12-13 20:40:04 +00009134 for (i=0; i < (ssize_t) number_transparent; i++)
9135 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009136
glennrp0fe50b42010-11-16 03:52:51 +00009137
glennrp2cc891a2010-12-24 13:44:32 +00009138 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009139 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009140
9141 if (ping_num_trans == 0)
9142 ping_have_tRNS=MagickFalse;
9143
glennrp8bb3a022010-12-13 20:40:04 +00009144 else
9145 ping_have_tRNS=MagickTrue;
9146 }
glennrp0fe50b42010-11-16 03:52:51 +00009147
glennrp1273f7b2011-02-24 03:20:30 +00009148 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009149 {
glennrp1273f7b2011-02-24 03:20:30 +00009150 /*
9151 * Identify which colormap entry is the background color.
9152 */
9153
glennrp4f25bd02011-01-01 18:51:28 +00009154 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9155 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9156 break;
glennrp0fe50b42010-11-16 03:52:51 +00009157
glennrp4f25bd02011-01-01 18:51:28 +00009158 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009159
9160 if (logging != MagickFalse)
9161 {
9162 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9163 " background_color index is %d",
9164 (int) ping_background.index);
9165 }
glennrp4f25bd02011-01-01 18:51:28 +00009166 }
cristy3ed852e2009-09-05 21:47:34 +00009167 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009168
glennrp7e65e932011-08-19 02:31:16 +00009169 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009170 {
9171 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009172 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009173 }
glennrp0fe50b42010-11-16 03:52:51 +00009174
glennrp7e65e932011-08-19 02:31:16 +00009175 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009176 {
9177 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009178 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009179 }
glennrp0fe50b42010-11-16 03:52:51 +00009180
glennrp8bb3a022010-12-13 20:40:04 +00009181 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009182 {
glennrp5af765f2010-03-30 11:12:18 +00009183 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009184
glennrp8bb3a022010-12-13 20:40:04 +00009185 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009186 {
glennrp5af765f2010-03-30 11:12:18 +00009187 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009188
glennrp5af765f2010-03-30 11:12:18 +00009189 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9190 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009191 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009192
glennrp8bb3a022010-12-13 20:40:04 +00009193 else
9194 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009195
9196 if (logging != MagickFalse)
9197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9198 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009199 }
glennrp0fe50b42010-11-16 03:52:51 +00009200
glennrp7c4c9e62011-03-21 20:23:32 +00009201 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009202 {
9203 if (logging != MagickFalse)
9204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009205 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009206
glennrpd6bf1612010-12-17 17:28:54 +00009207 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009208 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009209
glennrpd6bf1612010-12-17 17:28:54 +00009210 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009211 {
glennrp5af765f2010-03-30 11:12:18 +00009212 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009213 image_matte=MagickFalse;
9214 }
glennrp0fe50b42010-11-16 03:52:51 +00009215
glennrpd6bf1612010-12-17 17:28:54 +00009216 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009217 {
glennrp5af765f2010-03-30 11:12:18 +00009218 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009219 image_matte=MagickTrue;
9220 }
glennrp0fe50b42010-11-16 03:52:51 +00009221
glennrp5aa37f62011-01-02 03:07:57 +00009222 if (image_info->type == PaletteType ||
9223 image_info->type == PaletteMatteType)
9224 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9225
glennrp7c4c9e62011-03-21 20:23:32 +00009226 if (mng_info->write_png_colortype == 0 &&
9227 (image_info->type == UndefinedType ||
9228 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009229 {
glennrp5aa37f62011-01-02 03:07:57 +00009230 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009231 {
glennrp5aa37f62011-01-02 03:07:57 +00009232 if (image_matte == MagickFalse)
9233 {
9234 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9235 image_matte=MagickFalse;
9236 }
glennrp0fe50b42010-11-16 03:52:51 +00009237
glennrp0b206f52011-01-07 04:55:32 +00009238 else
glennrp5aa37f62011-01-02 03:07:57 +00009239 {
9240 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9241 image_matte=MagickTrue;
9242 }
9243 }
9244 else
glennrp8bb3a022010-12-13 20:40:04 +00009245 {
glennrp5aa37f62011-01-02 03:07:57 +00009246 if (image_matte == MagickFalse)
9247 {
9248 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9249 image_matte=MagickFalse;
9250 }
glennrp8bb3a022010-12-13 20:40:04 +00009251
glennrp0b206f52011-01-07 04:55:32 +00009252 else
glennrp5aa37f62011-01-02 03:07:57 +00009253 {
9254 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9255 image_matte=MagickTrue;
9256 }
9257 }
glennrp0fe50b42010-11-16 03:52:51 +00009258 }
glennrp5aa37f62011-01-02 03:07:57 +00009259
cristy3ed852e2009-09-05 21:47:34 +00009260 }
glennrp0fe50b42010-11-16 03:52:51 +00009261
cristy3ed852e2009-09-05 21:47:34 +00009262 if (logging != MagickFalse)
9263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009264 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009265
glennrp5af765f2010-03-30 11:12:18 +00009266 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009267 {
9268 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9269 ping_color_type == PNG_COLOR_TYPE_RGB ||
9270 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9271 ping_bit_depth=8;
9272 }
cristy3ed852e2009-09-05 21:47:34 +00009273
glennrpd6bf1612010-12-17 17:28:54 +00009274 old_bit_depth=ping_bit_depth;
9275
glennrp5af765f2010-03-30 11:12:18 +00009276 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009277 {
glennrp8d579662011-02-23 02:05:02 +00009278 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9279 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009280 }
glennrp8640fb52010-11-23 15:48:26 +00009281
glennrp5af765f2010-03-30 11:12:18 +00009282 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009283 {
cristy35ef8242010-06-03 16:24:13 +00009284 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009285 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009286
9287 if (image->colors == 0)
9288 {
glennrp0fe50b42010-11-16 03:52:51 +00009289 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00009290 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00009291 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009292 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009293 }
9294
cristy35ef8242010-06-03 16:24:13 +00009295 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009296 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009297 }
glennrp2b013e42010-11-24 16:55:50 +00009298
glennrpd6bf1612010-12-17 17:28:54 +00009299 if (logging != MagickFalse)
9300 {
9301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9302 " Number of colors: %.20g",(double) image_colors);
9303
9304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9305 " Tentative PNG bit depth: %d",ping_bit_depth);
9306 }
9307
9308 if (ping_bit_depth < (int) mng_info->write_png_depth)
9309 ping_bit_depth = mng_info->write_png_depth;
9310 }
glennrp2cc891a2010-12-24 13:44:32 +00009311
glennrp5af765f2010-03-30 11:12:18 +00009312 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009313
cristy3ed852e2009-09-05 21:47:34 +00009314 if (logging != MagickFalse)
9315 {
9316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009317 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009318
cristy3ed852e2009-09-05 21:47:34 +00009319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009320 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009321
cristy3ed852e2009-09-05 21:47:34 +00009322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009323 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009324
cristy3ed852e2009-09-05 21:47:34 +00009325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009326
glennrp8640fb52010-11-23 15:48:26 +00009327 " image->depth: %.20g",(double) image->depth);
9328
9329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009330 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009331 }
9332
glennrp58e01762011-01-07 15:28:54 +00009333 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009334 {
glennrp4f25bd02011-01-01 18:51:28 +00009335 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009336 {
glennrp7c4c9e62011-03-21 20:23:32 +00009337 if (mng_info->write_png_colortype == 0)
9338 {
9339 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009340
glennrp7c4c9e62011-03-21 20:23:32 +00009341 if (ping_have_color != MagickFalse)
9342 ping_color_type=PNG_COLOR_TYPE_RGBA;
9343 }
glennrp4f25bd02011-01-01 18:51:28 +00009344
9345 /*
9346 * Determine if there is any transparent color.
9347 */
9348 if (number_transparent + number_semitransparent == 0)
9349 {
9350 /*
9351 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9352 */
glennrpa6a06632011-01-19 15:15:34 +00009353
glennrp4f25bd02011-01-01 18:51:28 +00009354 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009355
9356 if (mng_info->write_png_colortype == 0)
9357 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009358 }
9359
9360 else
9361 {
9362 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009363 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009364
9365 mask=0xffff;
9366
9367 if (ping_bit_depth == 8)
9368 mask=0x00ff;
9369
9370 if (ping_bit_depth == 4)
9371 mask=0x000f;
9372
9373 if (ping_bit_depth == 2)
9374 mask=0x0003;
9375
9376 if (ping_bit_depth == 1)
9377 mask=0x0001;
9378
9379 ping_trans_color.red=(png_uint_16)
9380 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9381
9382 ping_trans_color.green=(png_uint_16)
9383 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9384
9385 ping_trans_color.blue=(png_uint_16)
9386 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9387
9388 ping_trans_color.gray=(png_uint_16)
cristy4c08aed2011-07-01 19:47:50 +00009389 (ScaleQuantumToShort(GetPixelPacketIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009390 image->colormap)) & mask);
9391
9392 ping_trans_color.index=(png_byte) 0;
9393
9394 ping_have_tRNS=MagickTrue;
9395 }
9396
9397 if (ping_have_tRNS != MagickFalse)
9398 {
9399 /*
glennrpfd05d622011-02-25 04:10:33 +00009400 * Determine if there is one and only one transparent color
9401 * and if so if it is fully transparent.
9402 */
9403 if (ping_have_cheap_transparency == MagickFalse)
9404 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009405 }
9406
9407 if (ping_have_tRNS != MagickFalse)
9408 {
glennrp7c4c9e62011-03-21 20:23:32 +00009409 if (mng_info->write_png_colortype == 0)
9410 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009411
9412 if (image_depth == 8)
9413 {
9414 ping_trans_color.red&=0xff;
9415 ping_trans_color.green&=0xff;
9416 ping_trans_color.blue&=0xff;
9417 ping_trans_color.gray&=0xff;
9418 }
9419 }
9420 }
cristy3ed852e2009-09-05 21:47:34 +00009421 else
9422 {
cristy3ed852e2009-09-05 21:47:34 +00009423 if (image_depth == 8)
9424 {
glennrp5af765f2010-03-30 11:12:18 +00009425 ping_trans_color.red&=0xff;
9426 ping_trans_color.green&=0xff;
9427 ping_trans_color.blue&=0xff;
9428 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009429 }
9430 }
9431 }
glennrp8640fb52010-11-23 15:48:26 +00009432
cristy3ed852e2009-09-05 21:47:34 +00009433 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009434
glennrp2e09f552010-11-14 00:38:48 +00009435 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009436 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009437
glennrp39992b42010-11-14 00:03:43 +00009438 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009439 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009440 ping_have_color == MagickFalse &&
9441 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009442 {
cristy35ef8242010-06-03 16:24:13 +00009443 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009444
cristy3ed852e2009-09-05 21:47:34 +00009445 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009446 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009447
glennrp7c4c9e62011-03-21 20:23:32 +00009448 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009449 {
glennrp5af765f2010-03-30 11:12:18 +00009450 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009451
cristy3ed852e2009-09-05 21:47:34 +00009452 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009453 {
9454 if (logging != MagickFalse)
9455 {
9456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9457 " Scaling ping_trans_color (0)");
9458 }
9459 ping_trans_color.gray*=0x0101;
9460 }
cristy3ed852e2009-09-05 21:47:34 +00009461 }
glennrp0fe50b42010-11-16 03:52:51 +00009462
cristy3ed852e2009-09-05 21:47:34 +00009463 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9464 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009465
glennrp136ee3a2011-04-27 15:47:45 +00009466 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009467 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009468 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009469
cristy3ed852e2009-09-05 21:47:34 +00009470 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009471 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009472
cristy3ed852e2009-09-05 21:47:34 +00009473 else
9474 {
glennrp5af765f2010-03-30 11:12:18 +00009475 ping_bit_depth=8;
9476 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009477 {
9478 if(!mng_info->write_png_depth)
9479 {
glennrp5af765f2010-03-30 11:12:18 +00009480 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009481
cristy35ef8242010-06-03 16:24:13 +00009482 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009483 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009484 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009485 }
9486 }
glennrp2b013e42010-11-24 16:55:50 +00009487
glennrp0fe50b42010-11-16 03:52:51 +00009488 else if (ping_color_type ==
9489 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009490 mng_info->IsPalette)
9491 {
cristy3ed852e2009-09-05 21:47:34 +00009492 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009493
cristy3ed852e2009-09-05 21:47:34 +00009494 int
9495 depth_4_ok=MagickTrue,
9496 depth_2_ok=MagickTrue,
9497 depth_1_ok=MagickTrue;
9498
cristybb503372010-05-27 20:51:26 +00009499 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009500 {
9501 unsigned char
9502 intensity;
9503
9504 intensity=ScaleQuantumToChar(image->colormap[i].red);
9505
9506 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9507 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9508 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9509 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009510 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009511 depth_1_ok=MagickFalse;
9512 }
glennrp2b013e42010-11-24 16:55:50 +00009513
cristy3ed852e2009-09-05 21:47:34 +00009514 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009515 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009516
cristy3ed852e2009-09-05 21:47:34 +00009517 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009518 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009519
cristy3ed852e2009-09-05 21:47:34 +00009520 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009521 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009522 }
9523 }
glennrp2b013e42010-11-24 16:55:50 +00009524
glennrp5af765f2010-03-30 11:12:18 +00009525 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009526 }
glennrp0fe50b42010-11-16 03:52:51 +00009527
cristy3ed852e2009-09-05 21:47:34 +00009528 else
glennrp0fe50b42010-11-16 03:52:51 +00009529
cristy3ed852e2009-09-05 21:47:34 +00009530 if (mng_info->IsPalette)
9531 {
glennrp17a14852010-05-10 03:01:59 +00009532 number_colors=image_colors;
9533
cristy3ed852e2009-09-05 21:47:34 +00009534 if (image_depth <= 8)
9535 {
cristy3ed852e2009-09-05 21:47:34 +00009536 /*
9537 Set image palette.
9538 */
glennrp5af765f2010-03-30 11:12:18 +00009539 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009540
glennrp58e01762011-01-07 15:28:54 +00009541 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009542 {
glennrp9c1eb072010-06-06 22:19:15 +00009543 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009544
glennrp3b51f0e2010-11-27 18:14:08 +00009545 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9547 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009548 }
glennrp0fe50b42010-11-16 03:52:51 +00009549
cristy3ed852e2009-09-05 21:47:34 +00009550 else
9551 {
cristybb503372010-05-27 20:51:26 +00009552 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009553 {
9554 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9555 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9556 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9557 }
glennrp0fe50b42010-11-16 03:52:51 +00009558
glennrp3b51f0e2010-11-27 18:14:08 +00009559 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009561 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009562 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009563
glennrp39992b42010-11-14 00:03:43 +00009564 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009565 }
glennrp0fe50b42010-11-16 03:52:51 +00009566
cristy3ed852e2009-09-05 21:47:34 +00009567 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009568 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009569 {
cristybefe4d22010-06-07 01:18:58 +00009570 size_t
9571 one;
9572
glennrp5af765f2010-03-30 11:12:18 +00009573 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009574 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009575
cristy94b11832011-09-08 19:46:03 +00009576 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009577 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009578 }
glennrp0fe50b42010-11-16 03:52:51 +00009579
glennrp5af765f2010-03-30 11:12:18 +00009580 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009581
glennrp58e01762011-01-07 15:28:54 +00009582 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009583 {
glennrp0fe50b42010-11-16 03:52:51 +00009584 /*
glennrpd6bf1612010-12-17 17:28:54 +00009585 * Set up trans_colors array.
9586 */
glennrp0fe50b42010-11-16 03:52:51 +00009587 assert(number_colors <= 256);
9588
glennrpd6bf1612010-12-17 17:28:54 +00009589 ping_num_trans=(unsigned short) (number_transparent +
9590 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009591
9592 if (ping_num_trans == 0)
9593 ping_have_tRNS=MagickFalse;
9594
glennrpd6bf1612010-12-17 17:28:54 +00009595 else
glennrp0fe50b42010-11-16 03:52:51 +00009596 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009597 if (logging != MagickFalse)
9598 {
9599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9600 " Scaling ping_trans_color (1)");
9601 }
glennrpd6bf1612010-12-17 17:28:54 +00009602 ping_have_tRNS=MagickTrue;
9603
9604 for (i=0; i < ping_num_trans; i++)
9605 {
cristy4c08aed2011-07-01 19:47:50 +00009606 ping_trans_alpha[i]= (png_byte)
9607 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009608 }
glennrp0fe50b42010-11-16 03:52:51 +00009609 }
9610 }
cristy3ed852e2009-09-05 21:47:34 +00009611 }
9612 }
glennrp0fe50b42010-11-16 03:52:51 +00009613
cristy3ed852e2009-09-05 21:47:34 +00009614 else
9615 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009616
cristy3ed852e2009-09-05 21:47:34 +00009617 if (image_depth < 8)
9618 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009619
cristy3ed852e2009-09-05 21:47:34 +00009620 if ((save_image_depth == 16) && (image_depth == 8))
9621 {
glennrp4f25bd02011-01-01 18:51:28 +00009622 if (logging != MagickFalse)
9623 {
9624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9625 " Scaling ping_trans_color from (%d,%d,%d)",
9626 (int) ping_trans_color.red,
9627 (int) ping_trans_color.green,
9628 (int) ping_trans_color.blue);
9629 }
9630
glennrp5af765f2010-03-30 11:12:18 +00009631 ping_trans_color.red*=0x0101;
9632 ping_trans_color.green*=0x0101;
9633 ping_trans_color.blue*=0x0101;
9634 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009635
9636 if (logging != MagickFalse)
9637 {
9638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9639 " to (%d,%d,%d)",
9640 (int) ping_trans_color.red,
9641 (int) ping_trans_color.green,
9642 (int) ping_trans_color.blue);
9643 }
cristy3ed852e2009-09-05 21:47:34 +00009644 }
9645 }
9646
cristy4383ec82011-01-05 15:42:32 +00009647 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9648 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009649
cristy3ed852e2009-09-05 21:47:34 +00009650 /*
9651 Adjust background and transparency samples in sub-8-bit grayscale files.
9652 */
glennrp5af765f2010-03-30 11:12:18 +00009653 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009654 PNG_COLOR_TYPE_GRAY)
9655 {
9656 png_uint_16
9657 maxval;
9658
cristy35ef8242010-06-03 16:24:13 +00009659 size_t
9660 one=1;
9661
cristy22ffd972010-06-03 16:51:47 +00009662 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009663
glennrp4f25bd02011-01-01 18:51:28 +00009664 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009665 {
cristy3ed852e2009-09-05 21:47:34 +00009666
glennrpa521b2f2010-10-29 04:11:03 +00009667 ping_background.gray=(png_uint_16)
glennrp847370c2011-07-05 17:37:15 +00009668 ((maxval/255.)*((GetPixelPacketIntensity(&image->background_color)))
9669 +.5);
cristy3ed852e2009-09-05 21:47:34 +00009670
9671 if (logging != MagickFalse)
9672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009673 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9675 " background_color index is %d",
9676 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009677
glennrp991d11d2010-11-12 21:55:28 +00009678 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009679 }
cristy3ed852e2009-09-05 21:47:34 +00009680
glennrp3e3e20f2011-06-09 04:21:43 +00009681 if (logging != MagickFalse)
9682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9683 " Scaling ping_trans_color.gray from %d",
9684 (int)ping_trans_color.gray);
9685
glennrp9be9b1c2011-06-09 12:21:45 +00009686 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009687 ping_trans_color.gray)+.5);
9688
9689 if (logging != MagickFalse)
9690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9691 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009692 }
glennrp17a14852010-05-10 03:01:59 +00009693
glennrp26f37912010-12-23 16:22:42 +00009694 if (ping_exclude_bKGD == MagickFalse)
9695 {
glennrp1273f7b2011-02-24 03:20:30 +00009696 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009697 {
9698 /*
9699 Identify which colormap entry is the background color.
9700 */
9701
glennrp17a14852010-05-10 03:01:59 +00009702 number_colors=image_colors;
9703
glennrpa521b2f2010-10-29 04:11:03 +00009704 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9705 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009706 break;
9707
9708 ping_background.index=(png_byte) i;
9709
glennrp3b51f0e2010-11-27 18:14:08 +00009710 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009711 {
9712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009713 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009714 }
glennrp0fe50b42010-11-16 03:52:51 +00009715
cristy13d07042010-11-21 20:56:18 +00009716 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009717 {
9718 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009719
9720 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009721 {
9722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9723 " background =(%d,%d,%d)",
9724 (int) ping_background.red,
9725 (int) ping_background.green,
9726 (int) ping_background.blue);
9727 }
9728 }
glennrpa521b2f2010-10-29 04:11:03 +00009729
glennrpd6bf1612010-12-17 17:28:54 +00009730 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009731 {
glennrp3b51f0e2010-11-27 18:14:08 +00009732 if (logging != MagickFalse)
9733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9734 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009735 ping_have_bKGD = MagickFalse;
9736 }
glennrp17a14852010-05-10 03:01:59 +00009737 }
glennrp26f37912010-12-23 16:22:42 +00009738 }
glennrp17a14852010-05-10 03:01:59 +00009739
cristy3ed852e2009-09-05 21:47:34 +00009740 if (logging != MagickFalse)
9741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009742 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009743 /*
9744 Initialize compression level and filtering.
9745 */
9746 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009747 {
9748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9749 " Setting up deflate compression");
9750
9751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9752 " Compression buffer size: 32768");
9753 }
9754
cristy3ed852e2009-09-05 21:47:34 +00009755 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009756
cristy3ed852e2009-09-05 21:47:34 +00009757 if (logging != MagickFalse)
9758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9759 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009760
cristy4054bfb2011-08-29 23:41:39 +00009761 png_set_compression_mem_level(ping, 9);
9762
glennrp10d739e2011-06-29 18:00:52 +00009763 /* Untangle the "-quality" setting:
9764
9765 Undefined is 0; the default is used.
9766 Default is 75
9767
9768 10's digit:
9769
9770 0: Use Z_HUFFMAN_ONLY strategy with the
9771 zlib default compression level
9772
9773 1-9: the zlib compression level
9774
9775 1's digit:
9776
9777 0-4: the PNG filter method
9778
9779 5: libpng adaptive filtering if compression level > 5
9780 libpng filter type "none" if compression level <= 5
9781 or if image is grayscale or palette
9782
9783 6: libpng adaptive filtering
9784
9785 7: "LOCO" filtering (intrapixel differing) if writing
9786 a MNG, othewise "none". Did not work in IM-6.7.0-9
9787 and earlier because of a missing "else".
9788
9789 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009790 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009791
9792 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009793 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009794
9795 Note that using the -quality option, not all combinations of
9796 PNG filter type, zlib compression level, and zlib compression
cristy5e6be1e2011-07-16 01:23:39 +00009797 strategy are possible. This will be addressed soon in a
cristy4054bfb2011-08-29 23:41:39 +00009798 release that accomodates "-define PNG:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +00009799
9800 */
9801
cristy3ed852e2009-09-05 21:47:34 +00009802 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9803 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009804
glennrp18682582011-06-30 18:11:47 +00009805 if (quality <= 9)
9806 {
9807 if (mng_info->write_png_compression_strategy == 0)
9808 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9809 }
9810
9811 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009812 {
9813 int
9814 level;
9815
cristybb503372010-05-27 20:51:26 +00009816 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009817
glennrp18682582011-06-30 18:11:47 +00009818 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009819 }
glennrp0fe50b42010-11-16 03:52:51 +00009820
glennrp18682582011-06-30 18:11:47 +00009821 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009822 {
glennrp18682582011-06-30 18:11:47 +00009823 if ((quality %10) == 8 || (quality %10) == 9)
9824 mng_info->write_png_compression_strategy=Z_RLE;
cristy3ed852e2009-09-05 21:47:34 +00009825 }
glennrp0fe50b42010-11-16 03:52:51 +00009826
glennrp18682582011-06-30 18:11:47 +00009827 if (mng_info->write_png_compression_filter == 0)
9828 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9829
cristy3ed852e2009-09-05 21:47:34 +00009830 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009831 {
glennrp18682582011-06-30 18:11:47 +00009832 if (mng_info->write_png_compression_level)
9833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9834 " Compression level: %d",
9835 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009836
glennrp18682582011-06-30 18:11:47 +00009837 if (mng_info->write_png_compression_strategy)
9838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9839 " Compression strategy: %d",
9840 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009841
glennrp18682582011-06-30 18:11:47 +00009842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9843 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009844
cristy4054bfb2011-08-29 23:41:39 +00009845 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +00009846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9847 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +00009848 else if (mng_info->write_png_compression_filter == 0 ||
9849 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +00009850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9851 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009852 else
9853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9854 " Base filter method: %d",
9855 (int) mng_info->write_png_compression_filter-1);
9856 }
glennrp2b013e42010-11-24 16:55:50 +00009857
glennrp18682582011-06-30 18:11:47 +00009858 if (mng_info->write_png_compression_level != 0)
9859 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9860
9861 if (mng_info->write_png_compression_filter == 6)
9862 {
9863 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9864 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9865 (quality < 50))
9866 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9867 else
9868 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9869 }
cristy4054bfb2011-08-29 23:41:39 +00009870 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +00009871 mng_info->write_png_compression_filter == 10)
9872 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9873
9874 else if (mng_info->write_png_compression_filter == 8)
9875 {
9876#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9877 if (mng_info->write_mng)
9878 {
9879 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9880 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9881 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9882 }
9883#endif
cristy4054bfb2011-08-29 23:41:39 +00009884 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +00009885 }
9886
9887 else if (mng_info->write_png_compression_filter == 9)
9888 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9889
9890 else if (mng_info->write_png_compression_filter != 0)
9891 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9892 mng_info->write_png_compression_filter-1);
9893
9894 if (mng_info->write_png_compression_strategy != 0)
9895 png_set_compression_strategy(ping,
9896 mng_info->write_png_compression_strategy-1);
9897
cristy0d57eec2011-09-04 22:13:56 +00009898 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9899 if (ping_exclude_sRGB != MagickFalse ||
9900 (image->rendering_intent == UndefinedIntent))
9901 {
9902 if ((ping_exclude_tEXt == MagickFalse ||
9903 ping_exclude_zTXt == MagickFalse) &&
9904 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009905 {
9906 ResetImageProfileIterator(image);
9907 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009908 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009909 profile=GetImageProfile(image,name);
9910
9911 if (profile != (StringInfo *) NULL)
9912 {
glennrp5af765f2010-03-30 11:12:18 +00009913#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009914 if ((LocaleCompare(name,"ICC") == 0) ||
9915 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009916 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009917
9918 if (ping_exclude_iCCP == MagickFalse)
9919 {
cristy9f027d12011-09-21 01:17:17 +00009920 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009921#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009922 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009923#else
9924 (png_const_bytep) GetStringInfoDatum(profile),
9925#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009926 (png_uint_32) GetStringInfoLength(profile));
9927 }
glennrp26f37912010-12-23 16:22:42 +00009928 }
glennrp0fe50b42010-11-16 03:52:51 +00009929
glennrpc8cbc5d2011-01-01 00:12:34 +00009930 else
cristy3ed852e2009-09-05 21:47:34 +00009931#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009932 if (ping_exclude_zCCP == MagickFalse)
9933 {
glennrpcf002022011-01-30 02:38:15 +00009934 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009935 (unsigned char *) name,(unsigned char *) name,
9936 GetStringInfoDatum(profile),
9937 (png_uint_32) GetStringInfoLength(profile));
9938 }
9939 }
glennrp0b206f52011-01-07 04:55:32 +00009940
glennrpc8cbc5d2011-01-01 00:12:34 +00009941 if (logging != MagickFalse)
9942 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9943 " Setting up text chunk with %s profile",name);
9944
9945 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009946 }
cristy0d57eec2011-09-04 22:13:56 +00009947 }
cristy3ed852e2009-09-05 21:47:34 +00009948 }
9949
9950#if defined(PNG_WRITE_sRGB_SUPPORTED)
9951 if ((mng_info->have_write_global_srgb == 0) &&
9952 ((image->rendering_intent != UndefinedIntent) ||
9953 (image->colorspace == sRGBColorspace)))
9954 {
glennrp26f37912010-12-23 16:22:42 +00009955 if (ping_exclude_sRGB == MagickFalse)
9956 {
9957 /*
9958 Note image rendering intent.
9959 */
9960 if (logging != MagickFalse)
9961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9962 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009963
glennrp26f37912010-12-23 16:22:42 +00009964 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009965 Magick_RenderingIntent_to_PNG_RenderingIntent(
9966 image->rendering_intent)));
glennrp26f37912010-12-23 16:22:42 +00009967 }
cristy3ed852e2009-09-05 21:47:34 +00009968 }
glennrp26f37912010-12-23 16:22:42 +00009969
glennrp5af765f2010-03-30 11:12:18 +00009970 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009971#endif
9972 {
glennrp2cc891a2010-12-24 13:44:32 +00009973 if (ping_exclude_gAMA == MagickFalse &&
9974 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009975 (image->gamma < .45 || image->gamma > .46)))
9976 {
cristy3ed852e2009-09-05 21:47:34 +00009977 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9978 {
9979 /*
9980 Note image gamma.
9981 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9982 */
9983 if (logging != MagickFalse)
9984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9985 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009986
cristy3ed852e2009-09-05 21:47:34 +00009987 png_set_gAMA(ping,ping_info,image->gamma);
9988 }
glennrp26f37912010-12-23 16:22:42 +00009989 }
glennrp2b013e42010-11-24 16:55:50 +00009990
glennrp26f37912010-12-23 16:22:42 +00009991 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009992 {
glennrp26f37912010-12-23 16:22:42 +00009993 if ((mng_info->have_write_global_chrm == 0) &&
9994 (image->chromaticity.red_primary.x != 0.0))
9995 {
9996 /*
9997 Note image chromaticity.
9998 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9999 */
10000 PrimaryInfo
10001 bp,
10002 gp,
10003 rp,
10004 wp;
cristy3ed852e2009-09-05 21:47:34 +000010005
glennrp26f37912010-12-23 16:22:42 +000010006 wp=image->chromaticity.white_point;
10007 rp=image->chromaticity.red_primary;
10008 gp=image->chromaticity.green_primary;
10009 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +000010010
glennrp26f37912010-12-23 16:22:42 +000010011 if (logging != MagickFalse)
10012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10013 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010014
glennrp26f37912010-12-23 16:22:42 +000010015 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10016 bp.x,bp.y);
10017 }
10018 }
cristy3ed852e2009-09-05 21:47:34 +000010019 }
glennrpdfd70802010-11-14 01:23:35 +000010020
glennrp5af765f2010-03-30 11:12:18 +000010021 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +000010022
10023 if (mng_info->write_mng)
10024 png_set_sig_bytes(ping,8);
10025
10026 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
10027
glennrpd6bf1612010-12-17 17:28:54 +000010028 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +000010029 {
10030 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +000010031 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010032 {
glennrp5af765f2010-03-30 11:12:18 +000010033 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +000010034
glennrp5af765f2010-03-30 11:12:18 +000010035 if (ping_bit_depth < 8)
10036 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +000010037 }
glennrp0fe50b42010-11-16 03:52:51 +000010038
cristy3ed852e2009-09-05 21:47:34 +000010039 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +000010040 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +000010041 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +000010042 }
10043
glennrp0e8ea192010-12-24 18:00:33 +000010044 if (ping_need_colortype_warning != MagickFalse ||
10045 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +000010046 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +000010047 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +000010048 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +000010049 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +000010050 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +000010051 {
10052 if (logging != MagickFalse)
10053 {
glennrp0e8ea192010-12-24 18:00:33 +000010054 if (ping_need_colortype_warning != MagickFalse)
10055 {
10056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10057 " Image has transparency but tRNS chunk was excluded");
10058 }
10059
cristy3ed852e2009-09-05 21:47:34 +000010060 if (mng_info->write_png_depth)
10061 {
10062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10063 " Defined PNG:bit-depth=%u, Computed depth=%u",
10064 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +000010065 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +000010066 }
glennrp0e8ea192010-12-24 18:00:33 +000010067
cristy3ed852e2009-09-05 21:47:34 +000010068 if (mng_info->write_png_colortype)
10069 {
10070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10071 " Defined PNG:color-type=%u, Computed color type=%u",
10072 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +000010073 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010074 }
10075 }
glennrp0e8ea192010-12-24 18:00:33 +000010076
glennrp3bd2e412010-08-10 13:34:52 +000010077 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +000010078 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
10079 }
10080
glennrp58e01762011-01-07 15:28:54 +000010081 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010082 {
10083 /* Add an opaque matte channel */
10084 image->matte = MagickTrue;
10085 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +000010086
glennrpb4a13412010-05-05 12:47:19 +000010087 if (logging != MagickFalse)
10088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10089 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +000010090 }
10091
glennrp0e319732011-01-25 21:53:13 +000010092 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +000010093 {
glennrp991d11d2010-11-12 21:55:28 +000010094 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +000010095 {
glennrp991d11d2010-11-12 21:55:28 +000010096 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010097 if (logging != MagickFalse)
10098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10099 " Setting ping_have_tRNS=MagickTrue.");
10100 }
glennrpe9c26dc2010-05-30 01:56:35 +000010101 }
10102
cristy3ed852e2009-09-05 21:47:34 +000010103 if (logging != MagickFalse)
10104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10105 " Writing PNG header chunks");
10106
glennrp5af765f2010-03-30 11:12:18 +000010107 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10108 ping_bit_depth,ping_color_type,
10109 ping_interlace_method,ping_compression_method,
10110 ping_filter_method);
10111
glennrp39992b42010-11-14 00:03:43 +000010112 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10113 {
glennrpf09bded2011-01-08 01:15:59 +000010114 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010115
glennrp3b51f0e2010-11-27 18:14:08 +000010116 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +000010117 {
glennrp8640fb52010-11-23 15:48:26 +000010118 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +000010119 {
glennrpd6bf1612010-12-17 17:28:54 +000010120 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +000010121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010122 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10123 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010124 (int) palette[i].red,
10125 (int) palette[i].green,
10126 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +000010127 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010128 (int) ping_trans_alpha[i]);
10129 else
10130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010131 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010132 (int) i,
10133 (int) palette[i].red,
10134 (int) palette[i].green,
10135 (int) palette[i].blue);
10136 }
glennrp39992b42010-11-14 00:03:43 +000010137 }
glennrp39992b42010-11-14 00:03:43 +000010138 }
10139
glennrp26f37912010-12-23 16:22:42 +000010140 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010141 {
glennrp26f37912010-12-23 16:22:42 +000010142 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010143 {
glennrp26f37912010-12-23 16:22:42 +000010144 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010145 if (logging)
10146 {
10147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10148 " Setting up bKGD chunk");
10149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10150 " background color = (%d,%d,%d)",
10151 (int) ping_background.red,
10152 (int) ping_background.green,
10153 (int) ping_background.blue);
10154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10155 " index = %d, gray=%d",
10156 (int) ping_background.index,
10157 (int) ping_background.gray);
10158 }
10159 }
glennrp26f37912010-12-23 16:22:42 +000010160 }
10161
10162 if (ping_exclude_pHYs == MagickFalse)
10163 {
10164 if (ping_have_pHYs != MagickFalse)
10165 {
10166 png_set_pHYs(ping,ping_info,
10167 ping_pHYs_x_resolution,
10168 ping_pHYs_y_resolution,
10169 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010170
10171 if (logging)
10172 {
10173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10174 " Setting up pHYs chunk");
10175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10176 " x_resolution=%lu",
10177 (unsigned long) ping_pHYs_x_resolution);
10178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10179 " y_resolution=%lu",
10180 (unsigned long) ping_pHYs_y_resolution);
10181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10182 " unit_type=%lu",
10183 (unsigned long) ping_pHYs_unit_type);
10184 }
glennrp26f37912010-12-23 16:22:42 +000010185 }
glennrpdfd70802010-11-14 01:23:35 +000010186 }
10187
10188#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010189 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010190 {
glennrp26f37912010-12-23 16:22:42 +000010191 if (image->page.x || image->page.y)
10192 {
10193 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10194 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010195
glennrp26f37912010-12-23 16:22:42 +000010196 if (logging != MagickFalse)
10197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10198 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10199 (int) image->page.x, (int) image->page.y);
10200 }
glennrpdfd70802010-11-14 01:23:35 +000010201 }
10202#endif
10203
glennrpda8f3a72011-02-27 23:54:12 +000010204 if (mng_info->need_blob != MagickFalse)
10205 {
10206 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
10207 MagickFalse)
10208 png_error(ping,"WriteBlob Failed");
10209
10210 ping_have_blob=MagickTrue;
10211 }
10212
cristy3ed852e2009-09-05 21:47:34 +000010213 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010214
glennrp39992b42010-11-14 00:03:43 +000010215 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010216 {
glennrp3b51f0e2010-11-27 18:14:08 +000010217 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010218 {
10219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10220 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10221 }
10222
10223 if (ping_color_type == 3)
10224 (void) png_set_tRNS(ping, ping_info,
10225 ping_trans_alpha,
10226 ping_num_trans,
10227 NULL);
10228
10229 else
10230 {
10231 (void) png_set_tRNS(ping, ping_info,
10232 NULL,
10233 0,
10234 &ping_trans_color);
10235
glennrp3b51f0e2010-11-27 18:14:08 +000010236 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010237 {
10238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010239 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010240 (int) ping_trans_color.red,
10241 (int) ping_trans_color.green,
10242 (int) ping_trans_color.blue);
10243 }
10244 }
glennrp991d11d2010-11-12 21:55:28 +000010245 }
10246
cristy3ed852e2009-09-05 21:47:34 +000010247 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010248 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010249
cristy3ed852e2009-09-05 21:47:34 +000010250 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010251
cristy3ed852e2009-09-05 21:47:34 +000010252 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010253 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010254
glennrp26f37912010-12-23 16:22:42 +000010255 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010256 {
glennrp4f25bd02011-01-01 18:51:28 +000010257 if ((image->page.width != 0 && image->page.width != image->columns) ||
10258 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010259 {
10260 unsigned char
10261 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010262
glennrp26f37912010-12-23 16:22:42 +000010263 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10264 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010265 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010266 PNGLong(chunk+4,(png_uint_32) image->page.width);
10267 PNGLong(chunk+8,(png_uint_32) image->page.height);
10268 chunk[12]=0; /* unit = pixels */
10269 (void) WriteBlob(image,13,chunk);
10270 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10271 }
cristy3ed852e2009-09-05 21:47:34 +000010272 }
10273
10274#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010275 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010276#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010277 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010278#undef PNG_HAVE_IDAT
10279#endif
10280
10281 png_set_packing(ping);
10282 /*
10283 Allocate memory.
10284 */
10285 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010286 if (image_depth > 8)
10287 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010288 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010289 {
glennrpb4a13412010-05-05 12:47:19 +000010290 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010291 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010292 break;
glennrp0fe50b42010-11-16 03:52:51 +000010293
glennrpb4a13412010-05-05 12:47:19 +000010294 case PNG_COLOR_TYPE_GRAY_ALPHA:
10295 rowbytes*=2;
10296 break;
glennrp0fe50b42010-11-16 03:52:51 +000010297
glennrpb4a13412010-05-05 12:47:19 +000010298 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010299 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010300 break;
glennrp0fe50b42010-11-16 03:52:51 +000010301
glennrpb4a13412010-05-05 12:47:19 +000010302 default:
10303 break;
cristy3ed852e2009-09-05 21:47:34 +000010304 }
glennrp3b51f0e2010-11-27 18:14:08 +000010305
10306 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010307 {
10308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10309 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010310
glennrpb4a13412010-05-05 12:47:19 +000010311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010312 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010313 }
glennrpcf002022011-01-30 02:38:15 +000010314 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10315 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010316
glennrpcf002022011-01-30 02:38:15 +000010317 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010318 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010319
cristy3ed852e2009-09-05 21:47:34 +000010320 /*
10321 Initialize image scanlines.
10322 */
glennrp5af765f2010-03-30 11:12:18 +000010323 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010324 {
10325 /*
10326 PNG write failed.
10327 */
10328#ifdef PNG_DEBUG
10329 if (image_info->verbose)
10330 (void) printf("PNG write has failed.\n");
10331#endif
10332 png_destroy_write_struct(&ping,&ping_info);
10333 if (quantum_info != (QuantumInfo *) NULL)
10334 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010335 if (ping_pixels != (unsigned char *) NULL)
10336 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010337#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010338 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010339#endif
glennrpda8f3a72011-02-27 23:54:12 +000010340 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010341 (void) CloseBlob(image);
10342 image_info=DestroyImageInfo(image_info);
10343 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010344 return(MagickFalse);
10345 }
cristyed552522009-10-16 14:04:35 +000010346 quantum_info=AcquireQuantumInfo(image_info,image);
10347 if (quantum_info == (QuantumInfo *) NULL)
10348 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010349 quantum_info->format=UndefinedQuantumFormat;
10350 quantum_info->depth=image_depth;
10351 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010352
cristy3ed852e2009-09-05 21:47:34 +000010353 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010354 !mng_info->write_png32) &&
10355 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010356 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010357 image_matte == MagickFalse &&
10358 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010359 {
glennrp8bb3a022010-12-13 20:40:04 +000010360 /* Palette, Bilevel, or Opaque Monochrome */
cristy4c08aed2011-07-01 19:47:50 +000010361 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010362 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010363
cristy3ed852e2009-09-05 21:47:34 +000010364 quantum_info->depth=8;
10365 for (pass=0; pass < num_passes; pass++)
10366 {
10367 /*
10368 Convert PseudoClass image to a PNG monochrome image.
10369 */
cristybb503372010-05-27 20:51:26 +000010370 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010371 {
glennrpd71e86a2011-02-24 01:28:37 +000010372 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10374 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010375
cristy3ed852e2009-09-05 21:47:34 +000010376 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010377
cristy4c08aed2011-07-01 19:47:50 +000010378 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010379 break;
glennrp0fe50b42010-11-16 03:52:51 +000010380
cristy3ed852e2009-09-05 21:47:34 +000010381 if (mng_info->IsPalette)
10382 {
cristy4c08aed2011-07-01 19:47:50 +000010383 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010384 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010385 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10386 mng_info->write_png_depth &&
10387 mng_info->write_png_depth != old_bit_depth)
10388 {
10389 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010390 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010391 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010392 >> (8-old_bit_depth));
10393 }
10394 }
glennrp0fe50b42010-11-16 03:52:51 +000010395
cristy3ed852e2009-09-05 21:47:34 +000010396 else
10397 {
cristy4c08aed2011-07-01 19:47:50 +000010398 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010399 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010400 }
glennrp0fe50b42010-11-16 03:52:51 +000010401
cristy3ed852e2009-09-05 21:47:34 +000010402 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010403 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010404 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010405 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010406
glennrp3b51f0e2010-11-27 18:14:08 +000010407 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10409 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010410
glennrpcf002022011-01-30 02:38:15 +000010411 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010412 }
10413 if (image->previous == (Image *) NULL)
10414 {
10415 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10416 if (status == MagickFalse)
10417 break;
10418 }
10419 }
10420 }
glennrp0fe50b42010-11-16 03:52:51 +000010421
glennrp8bb3a022010-12-13 20:40:04 +000010422 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010423 {
glennrp0fe50b42010-11-16 03:52:51 +000010424 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010425 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010426 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010427 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010428 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010429 {
cristy4c08aed2011-07-01 19:47:50 +000010430 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010431 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010432
glennrp8bb3a022010-12-13 20:40:04 +000010433 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010434 {
glennrp8bb3a022010-12-13 20:40:04 +000010435
cristybb503372010-05-27 20:51:26 +000010436 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010437 {
10438 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010439
cristy4c08aed2011-07-01 19:47:50 +000010440 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010441 break;
glennrp2cc891a2010-12-24 13:44:32 +000010442
glennrp5af765f2010-03-30 11:12:18 +000010443 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010444 {
glennrp8bb3a022010-12-13 20:40:04 +000010445 if (mng_info->IsPalette)
cristy4c08aed2011-07-01 19:47:50 +000010446 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010447 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010448
glennrp8bb3a022010-12-13 20:40:04 +000010449 else
cristy4c08aed2011-07-01 19:47:50 +000010450 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010451 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010452
glennrp3b51f0e2010-11-27 18:14:08 +000010453 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010455 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010456 }
glennrp2cc891a2010-12-24 13:44:32 +000010457
glennrp8bb3a022010-12-13 20:40:04 +000010458 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10459 {
10460 if (logging != MagickFalse && y == 0)
10461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10462 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010463
cristy4c08aed2011-07-01 19:47:50 +000010464 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010465 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010466 }
glennrp2cc891a2010-12-24 13:44:32 +000010467
glennrp3b51f0e2010-11-27 18:14:08 +000010468 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010470 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010471
glennrpcf002022011-01-30 02:38:15 +000010472 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010473 }
glennrp2cc891a2010-12-24 13:44:32 +000010474
glennrp8bb3a022010-12-13 20:40:04 +000010475 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010476 {
glennrp8bb3a022010-12-13 20:40:04 +000010477 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10478 if (status == MagickFalse)
10479 break;
cristy3ed852e2009-09-05 21:47:34 +000010480 }
cristy3ed852e2009-09-05 21:47:34 +000010481 }
10482 }
glennrp8bb3a022010-12-13 20:40:04 +000010483
10484 else
10485 {
cristy4c08aed2011-07-01 19:47:50 +000010486 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010487 *p;
10488
10489 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010490 {
glennrp8bb3a022010-12-13 20:40:04 +000010491 if ((image_depth > 8) || (mng_info->write_png24 ||
10492 mng_info->write_png32 ||
10493 (!mng_info->write_png8 && !mng_info->IsPalette)))
10494 {
10495 for (y=0; y < (ssize_t) image->rows; y++)
10496 {
10497 p=GetVirtualPixels(image,0,y,image->columns,1,
10498 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010499
cristy4c08aed2011-07-01 19:47:50 +000010500 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010501 break;
glennrp2cc891a2010-12-24 13:44:32 +000010502
glennrp8bb3a022010-12-13 20:40:04 +000010503 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10504 {
10505 if (image->storage_class == DirectClass)
cristy4c08aed2011-07-01 19:47:50 +000010506 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010507 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010508
glennrp8bb3a022010-12-13 20:40:04 +000010509 else
cristy4c08aed2011-07-01 19:47:50 +000010510 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010511 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010512 }
glennrp2cc891a2010-12-24 13:44:32 +000010513
glennrp8bb3a022010-12-13 20:40:04 +000010514 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10515 {
cristy4c08aed2011-07-01 19:47:50 +000010516 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010517 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +000010518 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010519
glennrp8bb3a022010-12-13 20:40:04 +000010520 if (logging != MagickFalse && y == 0)
10521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10522 " Writing GRAY_ALPHA PNG pixels (3)");
10523 }
glennrp2cc891a2010-12-24 13:44:32 +000010524
glennrp8bb3a022010-12-13 20:40:04 +000010525 else if (image_matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +000010526 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010527 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010528
glennrp8bb3a022010-12-13 20:40:04 +000010529 else
cristy4c08aed2011-07-01 19:47:50 +000010530 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010531 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010532
glennrp8bb3a022010-12-13 20:40:04 +000010533 if (logging != MagickFalse && y == 0)
10534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10535 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010536
glennrpcf002022011-01-30 02:38:15 +000010537 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010538 }
10539 }
glennrp2cc891a2010-12-24 13:44:32 +000010540
glennrp8bb3a022010-12-13 20:40:04 +000010541 else
10542 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10543 mng_info->write_png32 ||
10544 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10545 {
10546 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10547 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10548 {
10549 if (logging != MagickFalse)
10550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10551 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010552
glennrp8bb3a022010-12-13 20:40:04 +000010553 quantum_info->depth=8;
10554 image_depth=8;
10555 }
glennrp2cc891a2010-12-24 13:44:32 +000010556
glennrp8bb3a022010-12-13 20:40:04 +000010557 for (y=0; y < (ssize_t) image->rows; y++)
10558 {
10559 if (logging != MagickFalse && y == 0)
10560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10561 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010562
glennrp770d1932011-03-06 22:11:17 +000010563 p=GetVirtualPixels(image,0,y,image->columns,1,
10564 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010565
cristy4c08aed2011-07-01 19:47:50 +000010566 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010567 break;
glennrp2cc891a2010-12-24 13:44:32 +000010568
glennrp8bb3a022010-12-13 20:40:04 +000010569 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010570 {
glennrp4bf89732011-03-21 13:48:28 +000010571 quantum_info->depth=image->depth;
10572
cristy4c08aed2011-07-01 19:47:50 +000010573 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010574 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +000010575 }
glennrp2cc891a2010-12-24 13:44:32 +000010576
glennrp8bb3a022010-12-13 20:40:04 +000010577 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10578 {
10579 if (logging != MagickFalse && y == 0)
10580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10581 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010582
cristy4c08aed2011-07-01 19:47:50 +000010583 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010584 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +000010585 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010586 }
glennrp2cc891a2010-12-24 13:44:32 +000010587
glennrp8bb3a022010-12-13 20:40:04 +000010588 else
glennrp8bb3a022010-12-13 20:40:04 +000010589 {
cristy4c08aed2011-07-01 19:47:50 +000010590 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +000010591 quantum_info,IndexQuantum,ping_pixels,&image->exception);
10592
10593 if (logging != MagickFalse && y <= 2)
10594 {
10595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010596 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010597
10598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10599 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10600 (int)ping_pixels[0],(int)ping_pixels[1]);
10601 }
glennrp8bb3a022010-12-13 20:40:04 +000010602 }
glennrpcf002022011-01-30 02:38:15 +000010603 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010604 }
10605 }
glennrp2cc891a2010-12-24 13:44:32 +000010606
glennrp8bb3a022010-12-13 20:40:04 +000010607 if (image->previous == (Image *) NULL)
10608 {
10609 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10610 if (status == MagickFalse)
10611 break;
10612 }
cristy3ed852e2009-09-05 21:47:34 +000010613 }
glennrp8bb3a022010-12-13 20:40:04 +000010614 }
10615 }
10616
cristyb32b90a2009-09-07 21:45:48 +000010617 if (quantum_info != (QuantumInfo *) NULL)
10618 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010619
10620 if (logging != MagickFalse)
10621 {
10622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010623 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010624
cristy3ed852e2009-09-05 21:47:34 +000010625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010626 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010627
cristy3ed852e2009-09-05 21:47:34 +000010628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010629 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010630
cristy3ed852e2009-09-05 21:47:34 +000010631 if (mng_info->write_png_depth)
10632 {
10633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10634 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
10635 }
glennrp0fe50b42010-11-16 03:52:51 +000010636
cristy3ed852e2009-09-05 21:47:34 +000010637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010638 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010639
cristy3ed852e2009-09-05 21:47:34 +000010640 if (mng_info->write_png_colortype)
10641 {
10642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10643 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
10644 }
glennrp0fe50b42010-11-16 03:52:51 +000010645
cristy3ed852e2009-09-05 21:47:34 +000010646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010647 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010648
cristy3ed852e2009-09-05 21:47:34 +000010649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010650 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010651 }
10652 /*
glennrpa0ed0092011-04-18 16:36:29 +000010653 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010654 */
glennrp823b55c2011-03-14 18:46:46 +000010655 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010656 {
glennrp26f37912010-12-23 16:22:42 +000010657 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010658 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010659 while (property != (const char *) NULL)
10660 {
10661 png_textp
10662 text;
glennrp2cc891a2010-12-24 13:44:32 +000010663
glennrp26f37912010-12-23 16:22:42 +000010664 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +000010665
10666 /* Don't write any "png:" properties; those are just for "identify" */
10667 if (LocaleNCompare(property,"png:",4) != 0 &&
10668
10669 /* Suppress density and units if we wrote a pHYs chunk */
10670 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010671 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010672 LocaleCompare(property,"units") != 0) &&
10673
10674 /* Suppress the IM-generated Date:create and Date:modify */
10675 (ping_exclude_date == MagickFalse ||
10676 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010677 {
glennrpc70af4a2011-03-07 00:08:23 +000010678 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010679 {
glennrpc70af4a2011-03-07 00:08:23 +000010680 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10681 text[0].key=(char *) property;
10682 text[0].text=(char *) value;
10683 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010684
glennrpc70af4a2011-03-07 00:08:23 +000010685 if (ping_exclude_tEXt != MagickFalse)
10686 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10687
10688 else if (ping_exclude_zTXt != MagickFalse)
10689 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10690
10691 else
glennrp26f37912010-12-23 16:22:42 +000010692 {
glennrpc70af4a2011-03-07 00:08:23 +000010693 text[0].compression=image_info->compression == NoCompression ||
10694 (image_info->compression == UndefinedCompression &&
10695 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10696 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010697 }
glennrp2cc891a2010-12-24 13:44:32 +000010698
glennrpc70af4a2011-03-07 00:08:23 +000010699 if (logging != MagickFalse)
10700 {
10701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10702 " Setting up text chunk");
10703
10704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10705 " keyword: %s",text[0].key);
10706 }
10707
10708 png_set_text(ping,ping_info,text,1);
10709 png_free(ping,text);
10710 }
glennrp26f37912010-12-23 16:22:42 +000010711 }
10712 property=GetNextImageProperty(image);
10713 }
cristy3ed852e2009-09-05 21:47:34 +000010714 }
10715
10716 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010717 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010718
10719 if (logging != MagickFalse)
10720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10721 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010722
cristy3ed852e2009-09-05 21:47:34 +000010723 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010724
cristy3ed852e2009-09-05 21:47:34 +000010725 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10726 {
10727 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010728 (ping_width != mng_info->page.width) ||
10729 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010730 {
10731 unsigned char
10732 chunk[32];
10733
10734 /*
10735 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10736 */
10737 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10738 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010739 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010740 chunk[4]=4;
10741 chunk[5]=0; /* frame name separator (no name) */
10742 chunk[6]=1; /* flag for changing delay, for next frame only */
10743 chunk[7]=0; /* flag for changing frame timeout */
10744 chunk[8]=1; /* flag for changing frame clipping for next frame */
10745 chunk[9]=0; /* flag for changing frame sync_id */
10746 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10747 chunk[14]=0; /* clipping boundaries delta type */
10748 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10749 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010750 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010751 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10752 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010753 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010754 (void) WriteBlob(image,31,chunk);
10755 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10756 mng_info->old_framing_mode=4;
10757 mng_info->framing_mode=1;
10758 }
glennrp0fe50b42010-11-16 03:52:51 +000010759
cristy3ed852e2009-09-05 21:47:34 +000010760 else
10761 mng_info->framing_mode=3;
10762 }
10763 if (mng_info->write_mng && !mng_info->need_fram &&
10764 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +000010765 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010766 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010767 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010768
cristy3ed852e2009-09-05 21:47:34 +000010769 /*
10770 Free PNG resources.
10771 */
glennrp5af765f2010-03-30 11:12:18 +000010772
cristy3ed852e2009-09-05 21:47:34 +000010773 png_destroy_write_struct(&ping,&ping_info);
10774
glennrpcf002022011-01-30 02:38:15 +000010775 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010776
10777#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010778 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010779#endif
10780
glennrpda8f3a72011-02-27 23:54:12 +000010781 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010782 (void) CloseBlob(image);
10783
10784 image_info=DestroyImageInfo(image_info);
10785 image=DestroyImage(image);
10786
10787 /* Store bit depth actually written */
10788 s[0]=(char) ping_bit_depth;
10789 s[1]='\0';
10790
10791 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
10792
cristy3ed852e2009-09-05 21:47:34 +000010793 if (logging != MagickFalse)
10794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10795 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010796
cristy3ed852e2009-09-05 21:47:34 +000010797 return(MagickTrue);
10798/* End write one PNG image */
10799}
10800
10801/*
10802%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10803% %
10804% %
10805% %
10806% W r i t e P N G I m a g e %
10807% %
10808% %
10809% %
10810%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10811%
10812% WritePNGImage() writes a Portable Network Graphics (PNG) or
10813% Multiple-image Network Graphics (MNG) image file.
10814%
10815% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10816%
10817% The format of the WritePNGImage method is:
10818%
cristy1e178e72011-08-28 19:44:34 +000010819% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10820% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010821%
10822% A description of each parameter follows:
10823%
10824% o image_info: the image info.
10825%
10826% o image: The image.
10827%
cristy1e178e72011-08-28 19:44:34 +000010828% o exception: return any errors or warnings in this structure.
10829%
cristy3ed852e2009-09-05 21:47:34 +000010830% Returns MagickTrue on success, MagickFalse on failure.
10831%
10832% Communicating with the PNG encoder:
10833%
10834% While the datastream written is always in PNG format and normally would
10835% be given the "png" file extension, this method also writes the following
10836% pseudo-formats which are subsets of PNG:
10837%
glennrp5a39f372011-02-25 04:52:16 +000010838% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10839% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010840% is present, the tRNS chunk must only have values 0 and 255
10841% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010842% transparent). If other values are present they will be
10843% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010844% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000010845% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10846% of any resulting fully-transparent pixels is changed to
10847% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000010848%
10849% If you want better quantization or dithering of the colors
10850% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010851% PNG encoder. The pixels contain 8-bit indices even if
10852% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010853% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010854% PNG grayscale type might be slightly more efficient. Please
10855% note that writing to the PNG8 format may result in loss
10856% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010857%
10858% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10859% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010860% one of the colors as transparent. The only loss incurred
10861% is reduction of sample depth to 8. If the image has more
10862% than one transparent color, has semitransparent pixels, or
10863% has an opaque pixel with the same RGB components as the
10864% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010865%
10866% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10867% transparency is permitted, i.e., the alpha sample for
10868% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010869% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010870% The only loss in data is the reduction of the sample depth
10871% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010872%
10873% o -define: For more precise control of the PNG output, you can use the
10874% Image options "png:bit-depth" and "png:color-type". These
10875% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010876% from the application programming interfaces. The options
10877% are case-independent and are converted to lowercase before
10878% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010879%
10880% png:color-type can be 0, 2, 3, 4, or 6.
10881%
10882% When png:color-type is 0 (Grayscale), png:bit-depth can
10883% be 1, 2, 4, 8, or 16.
10884%
10885% When png:color-type is 2 (RGB), png:bit-depth can
10886% be 8 or 16.
10887%
10888% When png:color-type is 3 (Indexed), png:bit-depth can
10889% be 1, 2, 4, or 8. This refers to the number of bits
10890% used to store the index. The color samples always have
10891% bit-depth 8 in indexed PNG files.
10892%
10893% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10894% png:bit-depth can be 8 or 16.
10895%
glennrp5a39f372011-02-25 04:52:16 +000010896% If the image cannot be written without loss with the requested bit-depth
10897% and color-type, a PNG file will not be written, and the encoder will
10898% return MagickFalse.
10899%
cristy3ed852e2009-09-05 21:47:34 +000010900% Since image encoders should not be responsible for the "heavy lifting",
10901% the user should make sure that ImageMagick has already reduced the
10902% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010903% transparency prior to attempting to write the image with depth, color,
glennrp54dc0692011-07-01 19:02:13 +000010904% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010905%
cristy3ed852e2009-09-05 21:47:34 +000010906% Note that another definition, "png:bit-depth-written" exists, but it
10907% is not intended for external use. It is only used internally by the
10908% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10909%
10910% It is possible to request that the PNG encoder write previously-formatted
10911% ancillary chunks in the output PNG file, using the "-profile" commandline
10912% option as shown below or by setting the profile via a programming
10913% interface:
10914%
10915% -profile PNG-chunk-x:<file>
10916%
10917% where x is a location flag and <file> is a file containing the chunk
10918% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010919% This encoder will compute the chunk length and CRC, so those must not
10920% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010921%
10922% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10923% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10924% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010925% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010926%
glennrpbb8a7332010-11-13 15:17:35 +000010927% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010928%
glennrp3241bd02010-12-12 04:36:28 +000010929% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010930%
glennrpd6afd542010-11-19 01:53:05 +000010931% o 32-bit depth is reduced to 16.
10932% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10933% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010934% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010935% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010936% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010937% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10938% this can be done without loss and a larger bit depth N was not
10939% requested via the "-define PNG:bit-depth=N" option.
10940% o If matte channel is present but only one transparent color is
10941% present, RGB+tRNS is written instead of RGBA
10942% o Opaque matte channel is removed (or added, if color-type 4 or 6
10943% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010944%
cristy3ed852e2009-09-05 21:47:34 +000010945%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10946*/
10947static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +000010948 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010949{
10950 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010951 excluding,
10952 logging,
10953 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010954 status;
10955
10956 MngInfo
10957 *mng_info;
10958
10959 const char
10960 *value;
10961
10962 int
glennrp21f0e622011-01-07 16:20:57 +000010963 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010964 source;
10965
cristy3ed852e2009-09-05 21:47:34 +000010966 /*
10967 Open image file.
10968 */
10969 assert(image_info != (const ImageInfo *) NULL);
10970 assert(image_info->signature == MagickSignature);
10971 assert(image != (Image *) NULL);
10972 assert(image->signature == MagickSignature);
10973 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010974 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010975 /*
10976 Allocate a MngInfo structure.
10977 */
10978 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010979 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010980
cristy3ed852e2009-09-05 21:47:34 +000010981 if (mng_info == (MngInfo *) NULL)
10982 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010983
cristy3ed852e2009-09-05 21:47:34 +000010984 /*
10985 Initialize members of the MngInfo structure.
10986 */
10987 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10988 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010989 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010990 have_mng_structure=MagickTrue;
10991
10992 /* See if user has requested a specific PNG subformat */
10993
10994 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10995 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10996 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10997
10998 if (mng_info->write_png8)
10999 {
glennrp9c1eb072010-06-06 22:19:15 +000011000 mng_info->write_png_colortype = /* 3 */ 4;
11001 mng_info->write_png_depth = 8;
11002 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000011003 }
11004
11005 if (mng_info->write_png24)
11006 {
glennrp9c1eb072010-06-06 22:19:15 +000011007 mng_info->write_png_colortype = /* 2 */ 3;
11008 mng_info->write_png_depth = 8;
11009 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011010
glennrp9c1eb072010-06-06 22:19:15 +000011011 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011012 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011013
glennrp9c1eb072010-06-06 22:19:15 +000011014 else
cristy018f07f2011-09-04 21:15:19 +000011015 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011016
glennrp9c1eb072010-06-06 22:19:15 +000011017 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000011018 }
11019
11020 if (mng_info->write_png32)
11021 {
glennrp9c1eb072010-06-06 22:19:15 +000011022 mng_info->write_png_colortype = /* 6 */ 7;
11023 mng_info->write_png_depth = 8;
11024 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011025
glennrp9c1eb072010-06-06 22:19:15 +000011026 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011027 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011028
glennrp9c1eb072010-06-06 22:19:15 +000011029 else
cristy018f07f2011-09-04 21:15:19 +000011030 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011031
glennrp9c1eb072010-06-06 22:19:15 +000011032 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000011033 }
11034
11035 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011036
cristy3ed852e2009-09-05 21:47:34 +000011037 if (value != (char *) NULL)
11038 {
11039 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011040 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011041
cristy3ed852e2009-09-05 21:47:34 +000011042 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011043 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011044
cristy3ed852e2009-09-05 21:47:34 +000011045 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011046 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011047
cristy3ed852e2009-09-05 21:47:34 +000011048 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011049 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011050
cristy3ed852e2009-09-05 21:47:34 +000011051 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011052 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011053
glennrpbb8a7332010-11-13 15:17:35 +000011054 else
11055 (void) ThrowMagickException(&image->exception,
11056 GetMagickModule(),CoderWarning,
11057 "ignoring invalid defined png:bit-depth",
11058 "=%s",value);
11059
cristy3ed852e2009-09-05 21:47:34 +000011060 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011062 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011063 }
glennrp0fe50b42010-11-16 03:52:51 +000011064
cristy3ed852e2009-09-05 21:47:34 +000011065 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011066
cristy3ed852e2009-09-05 21:47:34 +000011067 if (value != (char *) NULL)
11068 {
11069 /* We must store colortype+1 because 0 is a valid colortype */
11070 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011071 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011072
cristy3ed852e2009-09-05 21:47:34 +000011073 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011074 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011075
cristy3ed852e2009-09-05 21:47:34 +000011076 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011077 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011078
cristy3ed852e2009-09-05 21:47:34 +000011079 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011080 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011081
cristy3ed852e2009-09-05 21:47:34 +000011082 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011083 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011084
glennrpbb8a7332010-11-13 15:17:35 +000011085 else
11086 (void) ThrowMagickException(&image->exception,
11087 GetMagickModule(),CoderWarning,
11088 "ignoring invalid defined png:color-type",
11089 "=%s",value);
11090
cristy3ed852e2009-09-05 21:47:34 +000011091 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011093 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011094 }
11095
glennrp0e8ea192010-12-24 18:00:33 +000011096 /* Check for chunks to be excluded:
11097 *
glennrp0dff56c2011-01-29 19:10:02 +000011098 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011099 * listed in the "unused_chunks" array, above.
11100 *
11101 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
11102 * define (in the image properties or in the image artifacts)
11103 * or via a mng_info member. For convenience, in addition
11104 * to or instead of a comma-separated list of chunks, the
11105 * "exclude-chunk" string can be simply "all" or "none".
11106 *
11107 * The exclude-chunk define takes priority over the mng_info.
11108 *
11109 * A "PNG:include-chunk" define takes priority over both the
11110 * mng_info and the "PNG:exclude-chunk" define. Like the
11111 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011112 * well as a comma-separated list. Chunks that are unknown to
11113 * ImageMagick are always excluded, regardless of their "copy-safe"
11114 * status according to the PNG specification, and even if they
11115 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000011116 *
11117 * Finally, all chunks listed in the "unused_chunks" array are
11118 * automatically excluded, regardless of the other instructions
11119 * or lack thereof.
11120 *
11121 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11122 * will not be written and the gAMA chunk will only be written if it
11123 * is not between .45 and .46, or approximately (1.0/2.2).
11124 *
11125 * If you exclude tRNS and the image has transparency, the colortype
11126 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11127 *
11128 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011129 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011130 */
11131
glennrp26f37912010-12-23 16:22:42 +000011132 mng_info->ping_exclude_bKGD=MagickFalse;
11133 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011134 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011135 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11136 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011137 mng_info->ping_exclude_iCCP=MagickFalse;
11138 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11139 mng_info->ping_exclude_oFFs=MagickFalse;
11140 mng_info->ping_exclude_pHYs=MagickFalse;
11141 mng_info->ping_exclude_sRGB=MagickFalse;
11142 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011143 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011144 mng_info->ping_exclude_vpAg=MagickFalse;
11145 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11146 mng_info->ping_exclude_zTXt=MagickFalse;
11147
glennrp8d3d6e52011-04-19 04:39:51 +000011148 mng_info->ping_preserve_colormap=MagickFalse;
11149
11150 value=GetImageArtifact(image,"png:preserve-colormap");
11151 if (value == NULL)
11152 value=GetImageOption(image_info,"png:preserve-colormap");
11153 if (value != NULL)
11154 mng_info->ping_preserve_colormap=MagickTrue;
11155
glennrp18682582011-06-30 18:11:47 +000011156 /* Thes compression-level, compression-strategy, and compression-filter
11157 * defines take precedence over values from the -quality option.
11158 */
11159 value=GetImageArtifact(image,"png:compression-level");
11160 if (value == NULL)
11161 value=GetImageOption(image_info,"png:compression-level");
11162 if (value != NULL)
11163 {
glennrp18682582011-06-30 18:11:47 +000011164 /* We have to add 1 to everything because 0 is a valid input,
11165 * and we want to use 0 (the default) to mean undefined.
11166 */
11167 if (LocaleCompare(value,"0") == 0)
11168 mng_info->write_png_compression_level = 1;
11169
11170 if (LocaleCompare(value,"1") == 0)
11171 mng_info->write_png_compression_level = 2;
11172
11173 else if (LocaleCompare(value,"2") == 0)
11174 mng_info->write_png_compression_level = 3;
11175
11176 else if (LocaleCompare(value,"3") == 0)
11177 mng_info->write_png_compression_level = 4;
11178
11179 else if (LocaleCompare(value,"4") == 0)
11180 mng_info->write_png_compression_level = 5;
11181
11182 else if (LocaleCompare(value,"5") == 0)
11183 mng_info->write_png_compression_level = 6;
11184
11185 else if (LocaleCompare(value,"6") == 0)
11186 mng_info->write_png_compression_level = 7;
11187
11188 else if (LocaleCompare(value,"7") == 0)
11189 mng_info->write_png_compression_level = 8;
11190
11191 else if (LocaleCompare(value,"8") == 0)
11192 mng_info->write_png_compression_level = 9;
11193
11194 else if (LocaleCompare(value,"9") == 0)
11195 mng_info->write_png_compression_level = 10;
11196
11197 else
11198 (void) ThrowMagickException(&image->exception,
11199 GetMagickModule(),CoderWarning,
11200 "ignoring invalid defined png:compression-level",
11201 "=%s",value);
11202 }
11203
11204 value=GetImageArtifact(image,"png:compression-strategy");
11205 if (value == NULL)
11206 value=GetImageOption(image_info,"png:compression-strategy");
11207 if (value != NULL)
11208 {
11209
11210 if (LocaleCompare(value,"0") == 0)
11211 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11212
11213 else if (LocaleCompare(value,"1") == 0)
11214 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11215
11216 else if (LocaleCompare(value,"2") == 0)
11217 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11218
11219 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011220#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011221 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011222#else
11223 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11224#endif
glennrp18682582011-06-30 18:11:47 +000011225
11226 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011227#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011228 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011229#else
11230 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11231#endif
glennrp18682582011-06-30 18:11:47 +000011232
11233 else
11234 (void) ThrowMagickException(&image->exception,
11235 GetMagickModule(),CoderWarning,
11236 "ignoring invalid defined png:compression-strategy",
11237 "=%s",value);
11238 }
11239
11240 value=GetImageArtifact(image,"png:compression-filter");
11241 if (value == NULL)
11242 value=GetImageOption(image_info,"png:compression-filter");
11243 if (value != NULL)
11244 {
11245
11246 /* To do: combinations of filters allowed by libpng
11247 * masks 0x08 through 0xf8
11248 *
11249 * Implement this as a comma-separated list of 0,1,2,3,4,5
11250 * where 5 is a special case meaning PNG_ALL_FILTERS.
11251 */
11252
11253 if (LocaleCompare(value,"0") == 0)
11254 mng_info->write_png_compression_filter = 1;
11255
11256 if (LocaleCompare(value,"1") == 0)
11257 mng_info->write_png_compression_filter = 2;
11258
11259 else if (LocaleCompare(value,"2") == 0)
11260 mng_info->write_png_compression_filter = 3;
11261
11262 else if (LocaleCompare(value,"3") == 0)
11263 mng_info->write_png_compression_filter = 4;
11264
11265 else if (LocaleCompare(value,"4") == 0)
11266 mng_info->write_png_compression_filter = 5;
11267
11268 else if (LocaleCompare(value,"5") == 0)
11269 mng_info->write_png_compression_filter = 6;
11270
glennrp18682582011-06-30 18:11:47 +000011271 else
11272 (void) ThrowMagickException(&image->exception,
11273 GetMagickModule(),CoderWarning,
11274 "ignoring invalid defined png:compression-filter",
11275 "=%s",value);
11276 }
11277
glennrp03812ae2010-12-24 01:31:34 +000011278 excluding=MagickFalse;
11279
glennrp5c7cf4e2010-12-24 00:30:00 +000011280 for (source=0; source<1; source++)
11281 {
11282 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011283 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011284 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011285
11286 if (value == NULL)
11287 value=GetImageArtifact(image,"png:exclude-chunks");
11288 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011289 else
glennrpacba0042010-12-24 14:27:26 +000011290 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011291 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011292
glennrpacba0042010-12-24 14:27:26 +000011293 if (value == NULL)
11294 value=GetImageOption(image_info,"png:exclude-chunks");
11295 }
11296
glennrp03812ae2010-12-24 01:31:34 +000011297 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011298 {
glennrp03812ae2010-12-24 01:31:34 +000011299
11300 size_t
11301 last;
11302
11303 excluding=MagickTrue;
11304
11305 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011306 {
11307 if (source == 0)
11308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11309 " png:exclude-chunk=%s found in image artifacts.\n", value);
11310 else
11311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11312 " png:exclude-chunk=%s found in image properties.\n", value);
11313 }
glennrp03812ae2010-12-24 01:31:34 +000011314
11315 last=strlen(value);
11316
11317 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011318 {
glennrp03812ae2010-12-24 01:31:34 +000011319
11320 if (LocaleNCompare(value+i,"all",3) == 0)
11321 {
11322 mng_info->ping_exclude_bKGD=MagickTrue;
11323 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011324 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011325 mng_info->ping_exclude_EXIF=MagickTrue;
11326 mng_info->ping_exclude_gAMA=MagickTrue;
11327 mng_info->ping_exclude_iCCP=MagickTrue;
11328 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11329 mng_info->ping_exclude_oFFs=MagickTrue;
11330 mng_info->ping_exclude_pHYs=MagickTrue;
11331 mng_info->ping_exclude_sRGB=MagickTrue;
11332 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011333 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011334 mng_info->ping_exclude_vpAg=MagickTrue;
11335 mng_info->ping_exclude_zCCP=MagickTrue;
11336 mng_info->ping_exclude_zTXt=MagickTrue;
11337 i--;
11338 }
glennrp2cc891a2010-12-24 13:44:32 +000011339
glennrp03812ae2010-12-24 01:31:34 +000011340 if (LocaleNCompare(value+i,"none",4) == 0)
11341 {
11342 mng_info->ping_exclude_bKGD=MagickFalse;
11343 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011344 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011345 mng_info->ping_exclude_EXIF=MagickFalse;
11346 mng_info->ping_exclude_gAMA=MagickFalse;
11347 mng_info->ping_exclude_iCCP=MagickFalse;
11348 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11349 mng_info->ping_exclude_oFFs=MagickFalse;
11350 mng_info->ping_exclude_pHYs=MagickFalse;
11351 mng_info->ping_exclude_sRGB=MagickFalse;
11352 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011353 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011354 mng_info->ping_exclude_vpAg=MagickFalse;
11355 mng_info->ping_exclude_zCCP=MagickFalse;
11356 mng_info->ping_exclude_zTXt=MagickFalse;
11357 }
glennrp2cc891a2010-12-24 13:44:32 +000011358
glennrp03812ae2010-12-24 01:31:34 +000011359 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11360 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011361
glennrp03812ae2010-12-24 01:31:34 +000011362 if (LocaleNCompare(value+i,"chrm",4) == 0)
11363 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011364
glennrpa0ed0092011-04-18 16:36:29 +000011365 if (LocaleNCompare(value+i,"date",4) == 0)
11366 mng_info->ping_exclude_date=MagickTrue;
11367
glennrp03812ae2010-12-24 01:31:34 +000011368 if (LocaleNCompare(value+i,"exif",4) == 0)
11369 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011370
glennrp03812ae2010-12-24 01:31:34 +000011371 if (LocaleNCompare(value+i,"gama",4) == 0)
11372 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011373
glennrp03812ae2010-12-24 01:31:34 +000011374 if (LocaleNCompare(value+i,"iccp",4) == 0)
11375 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011376
glennrp03812ae2010-12-24 01:31:34 +000011377 /*
11378 if (LocaleNCompare(value+i,"itxt",4) == 0)
11379 mng_info->ping_exclude_iTXt=MagickTrue;
11380 */
glennrp2cc891a2010-12-24 13:44:32 +000011381
glennrp03812ae2010-12-24 01:31:34 +000011382 if (LocaleNCompare(value+i,"gama",4) == 0)
11383 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011384
glennrp03812ae2010-12-24 01:31:34 +000011385 if (LocaleNCompare(value+i,"offs",4) == 0)
11386 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011387
glennrp03812ae2010-12-24 01:31:34 +000011388 if (LocaleNCompare(value+i,"phys",4) == 0)
11389 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011390
glennrpa1e3b7b2010-12-24 16:37:33 +000011391 if (LocaleNCompare(value+i,"srgb",4) == 0)
11392 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011393
glennrp03812ae2010-12-24 01:31:34 +000011394 if (LocaleNCompare(value+i,"text",4) == 0)
11395 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011396
glennrpa1e3b7b2010-12-24 16:37:33 +000011397 if (LocaleNCompare(value+i,"trns",4) == 0)
11398 mng_info->ping_exclude_tRNS=MagickTrue;
11399
glennrp03812ae2010-12-24 01:31:34 +000011400 if (LocaleNCompare(value+i,"vpag",4) == 0)
11401 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011402
glennrp03812ae2010-12-24 01:31:34 +000011403 if (LocaleNCompare(value+i,"zccp",4) == 0)
11404 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011405
glennrp03812ae2010-12-24 01:31:34 +000011406 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11407 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011408
glennrp03812ae2010-12-24 01:31:34 +000011409 }
glennrpce91ed52010-12-23 22:37:49 +000011410 }
glennrp26f37912010-12-23 16:22:42 +000011411 }
11412
glennrp5c7cf4e2010-12-24 00:30:00 +000011413 for (source=0; source<1; source++)
11414 {
11415 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011416 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011417 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011418
11419 if (value == NULL)
11420 value=GetImageArtifact(image,"png:include-chunks");
11421 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011422 else
glennrpacba0042010-12-24 14:27:26 +000011423 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011424 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011425
glennrpacba0042010-12-24 14:27:26 +000011426 if (value == NULL)
11427 value=GetImageOption(image_info,"png:include-chunks");
11428 }
11429
glennrp03812ae2010-12-24 01:31:34 +000011430 if (value != NULL)
11431 {
11432 size_t
11433 last;
glennrp26f37912010-12-23 16:22:42 +000011434
glennrp03812ae2010-12-24 01:31:34 +000011435 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011436
glennrp03812ae2010-12-24 01:31:34 +000011437 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011438 {
11439 if (source == 0)
11440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11441 " png:include-chunk=%s found in image artifacts.\n", value);
11442 else
11443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11444 " png:include-chunk=%s found in image properties.\n", value);
11445 }
glennrp03812ae2010-12-24 01:31:34 +000011446
11447 last=strlen(value);
11448
11449 for (i=0; i<(int) last; i+=5)
11450 {
11451 if (LocaleNCompare(value+i,"all",3) == 0)
11452 {
11453 mng_info->ping_exclude_bKGD=MagickFalse;
11454 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011455 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011456 mng_info->ping_exclude_EXIF=MagickFalse;
11457 mng_info->ping_exclude_gAMA=MagickFalse;
11458 mng_info->ping_exclude_iCCP=MagickFalse;
11459 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11460 mng_info->ping_exclude_oFFs=MagickFalse;
11461 mng_info->ping_exclude_pHYs=MagickFalse;
11462 mng_info->ping_exclude_sRGB=MagickFalse;
11463 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011464 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011465 mng_info->ping_exclude_vpAg=MagickFalse;
11466 mng_info->ping_exclude_zCCP=MagickFalse;
11467 mng_info->ping_exclude_zTXt=MagickFalse;
11468 i--;
11469 }
glennrp2cc891a2010-12-24 13:44:32 +000011470
glennrp03812ae2010-12-24 01:31:34 +000011471 if (LocaleNCompare(value+i,"none",4) == 0)
11472 {
11473 mng_info->ping_exclude_bKGD=MagickTrue;
11474 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011475 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011476 mng_info->ping_exclude_EXIF=MagickTrue;
11477 mng_info->ping_exclude_gAMA=MagickTrue;
11478 mng_info->ping_exclude_iCCP=MagickTrue;
11479 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11480 mng_info->ping_exclude_oFFs=MagickTrue;
11481 mng_info->ping_exclude_pHYs=MagickTrue;
11482 mng_info->ping_exclude_sRGB=MagickTrue;
11483 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011484 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011485 mng_info->ping_exclude_vpAg=MagickTrue;
11486 mng_info->ping_exclude_zCCP=MagickTrue;
11487 mng_info->ping_exclude_zTXt=MagickTrue;
11488 }
glennrp2cc891a2010-12-24 13:44:32 +000011489
glennrp03812ae2010-12-24 01:31:34 +000011490 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11491 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011492
glennrp03812ae2010-12-24 01:31:34 +000011493 if (LocaleNCompare(value+i,"chrm",4) == 0)
11494 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011495
glennrpa0ed0092011-04-18 16:36:29 +000011496 if (LocaleNCompare(value+i,"date",4) == 0)
11497 mng_info->ping_exclude_date=MagickFalse;
11498
glennrp03812ae2010-12-24 01:31:34 +000011499 if (LocaleNCompare(value+i,"exif",4) == 0)
11500 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011501
glennrp03812ae2010-12-24 01:31:34 +000011502 if (LocaleNCompare(value+i,"gama",4) == 0)
11503 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011504
glennrp03812ae2010-12-24 01:31:34 +000011505 if (LocaleNCompare(value+i,"iccp",4) == 0)
11506 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011507
glennrp03812ae2010-12-24 01:31:34 +000011508 /*
11509 if (LocaleNCompare(value+i,"itxt",4) == 0)
11510 mng_info->ping_exclude_iTXt=MagickFalse;
11511 */
glennrp2cc891a2010-12-24 13:44:32 +000011512
glennrp03812ae2010-12-24 01:31:34 +000011513 if (LocaleNCompare(value+i,"gama",4) == 0)
11514 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011515
glennrp03812ae2010-12-24 01:31:34 +000011516 if (LocaleNCompare(value+i,"offs",4) == 0)
11517 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011518
glennrp03812ae2010-12-24 01:31:34 +000011519 if (LocaleNCompare(value+i,"phys",4) == 0)
11520 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011521
glennrpa1e3b7b2010-12-24 16:37:33 +000011522 if (LocaleNCompare(value+i,"srgb",4) == 0)
11523 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011524
glennrp03812ae2010-12-24 01:31:34 +000011525 if (LocaleNCompare(value+i,"text",4) == 0)
11526 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011527
glennrpa1e3b7b2010-12-24 16:37:33 +000011528 if (LocaleNCompare(value+i,"trns",4) == 0)
11529 mng_info->ping_exclude_tRNS=MagickFalse;
11530
glennrp03812ae2010-12-24 01:31:34 +000011531 if (LocaleNCompare(value+i,"vpag",4) == 0)
11532 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011533
glennrp03812ae2010-12-24 01:31:34 +000011534 if (LocaleNCompare(value+i,"zccp",4) == 0)
11535 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011536
glennrp03812ae2010-12-24 01:31:34 +000011537 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11538 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011539
glennrp03812ae2010-12-24 01:31:34 +000011540 }
glennrpce91ed52010-12-23 22:37:49 +000011541 }
glennrp26f37912010-12-23 16:22:42 +000011542 }
11543
glennrp03812ae2010-12-24 01:31:34 +000011544 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011545 {
11546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11547 " Chunks to be excluded from the output PNG:");
11548 if (mng_info->ping_exclude_bKGD != MagickFalse)
11549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11550 " bKGD");
11551 if (mng_info->ping_exclude_cHRM != MagickFalse)
11552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11553 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011554 if (mng_info->ping_exclude_date != MagickFalse)
11555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11556 " date");
glennrp26f37912010-12-23 16:22:42 +000011557 if (mng_info->ping_exclude_EXIF != MagickFalse)
11558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11559 " EXIF");
11560 if (mng_info->ping_exclude_gAMA != MagickFalse)
11561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11562 " gAMA");
11563 if (mng_info->ping_exclude_iCCP != MagickFalse)
11564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11565 " iCCP");
11566/*
11567 if (mng_info->ping_exclude_iTXt != MagickFalse)
11568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11569 " iTXt");
11570*/
11571 if (mng_info->ping_exclude_oFFs != MagickFalse)
11572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11573 " oFFs");
11574 if (mng_info->ping_exclude_pHYs != MagickFalse)
11575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11576 " pHYs");
11577 if (mng_info->ping_exclude_sRGB != MagickFalse)
11578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11579 " sRGB");
11580 if (mng_info->ping_exclude_tEXt != MagickFalse)
11581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11582 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011583 if (mng_info->ping_exclude_tRNS != MagickFalse)
11584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11585 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011586 if (mng_info->ping_exclude_vpAg != MagickFalse)
11587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11588 " vpAg");
11589 if (mng_info->ping_exclude_zCCP != MagickFalse)
11590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11591 " zCCP");
11592 if (mng_info->ping_exclude_zTXt != MagickFalse)
11593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11594 " zTXt");
11595 }
11596
glennrpb9cfe272010-12-21 15:08:06 +000011597 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011598
cristy018f07f2011-09-04 21:15:19 +000011599 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011600
11601 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011602
cristy3ed852e2009-09-05 21:47:34 +000011603 if (logging != MagickFalse)
11604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011605
cristy3ed852e2009-09-05 21:47:34 +000011606 return(status);
11607}
11608
11609#if defined(JNG_SUPPORTED)
11610
11611/* Write one JNG image */
11612static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +000011613 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011614{
11615 Image
11616 *jpeg_image;
11617
11618 ImageInfo
11619 *jpeg_image_info;
11620
11621 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011622 logging,
cristy3ed852e2009-09-05 21:47:34 +000011623 status;
11624
11625 size_t
11626 length;
11627
11628 unsigned char
11629 *blob,
11630 chunk[80],
11631 *p;
11632
11633 unsigned int
11634 jng_alpha_compression_method,
11635 jng_alpha_sample_depth,
11636 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011637 transparent;
11638
cristybb503372010-05-27 20:51:26 +000011639 size_t
cristy3ed852e2009-09-05 21:47:34 +000011640 jng_quality;
11641
11642 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011643 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011644
11645 blob=(unsigned char *) NULL;
11646 jpeg_image=(Image *) NULL;
11647 jpeg_image_info=(ImageInfo *) NULL;
11648
11649 status=MagickTrue;
11650 transparent=image_info->type==GrayscaleMatteType ||
11651 image_info->type==TrueColorMatteType;
11652 jng_color_type=10;
11653 jng_alpha_sample_depth=0;
11654 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11655 jng_alpha_compression_method=0;
11656
11657 if (image->matte != MagickFalse)
11658 {
11659 /* if any pixels are transparent */
11660 transparent=MagickTrue;
11661 if (image_info->compression==JPEGCompression)
11662 jng_alpha_compression_method=8;
11663 }
11664
11665 if (transparent)
11666 {
cristybd5a96c2011-08-21 00:04:26 +000011667 ChannelType
11668 channel_mask;
11669
cristy3ed852e2009-09-05 21:47:34 +000011670 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011671
cristy3ed852e2009-09-05 21:47:34 +000011672 /* Create JPEG blob, image, and image_info */
11673 if (logging != MagickFalse)
11674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +000011675 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011676
cristy3ed852e2009-09-05 21:47:34 +000011677 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011678
cristy3ed852e2009-09-05 21:47:34 +000011679 if (jpeg_image_info == (ImageInfo *) NULL)
11680 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011681
cristy3ed852e2009-09-05 21:47:34 +000011682 if (logging != MagickFalse)
11683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11684 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011685
cristy3ed852e2009-09-05 21:47:34 +000011686 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011687
cristy3ed852e2009-09-05 21:47:34 +000011688 if (jpeg_image == (Image *) NULL)
11689 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011690
cristy3ed852e2009-09-05 21:47:34 +000011691 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristybd5a96c2011-08-21 00:04:26 +000011692 channel_mask=SetPixelChannelMask(jpeg_image,AlphaChannel);
cristy3139dc22011-07-08 00:11:42 +000011693 status=SeparateImage(jpeg_image);
cristybd5a96c2011-08-21 00:04:26 +000011694 (void) SetPixelChannelMap(jpeg_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +000011695 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011696
cristy3ed852e2009-09-05 21:47:34 +000011697 if (jng_quality >= 1000)
11698 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000011699
cristy3ed852e2009-09-05 21:47:34 +000011700 else
11701 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000011702
cristy3ed852e2009-09-05 21:47:34 +000011703 jpeg_image_info->type=GrayscaleType;
cristy018f07f2011-09-04 21:15:19 +000011704 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000011705 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011706 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011707 "%s",jpeg_image->filename);
11708 }
11709
11710 /* To do: check bit depth of PNG alpha channel */
11711
11712 /* Check if image is grayscale. */
11713 if (image_info->type != TrueColorMatteType && image_info->type !=
11714 TrueColorType && ImageIsGray(image))
11715 jng_color_type-=2;
11716
11717 if (transparent)
11718 {
11719 if (jng_alpha_compression_method==0)
11720 {
11721 const char
11722 *value;
11723
cristy4c08aed2011-07-01 19:47:50 +000011724 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011725 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11726 &image->exception);
11727 if (logging != MagickFalse)
11728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11729 " Creating PNG blob.");
11730 length=0;
11731
11732 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11733 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11734 jpeg_image_info->interlace=NoInterlace;
11735
11736 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11737 &image->exception);
11738
11739 /* Retrieve sample depth used */
11740 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
11741 if (value != (char *) NULL)
11742 jng_alpha_sample_depth= (unsigned int) value[0];
11743 }
11744 else
11745 {
cristy4c08aed2011-07-01 19:47:50 +000011746 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011747
11748 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11749 &image->exception);
11750
11751 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11752 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11753 jpeg_image_info->interlace=NoInterlace;
11754 if (logging != MagickFalse)
11755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11756 " Creating blob.");
11757 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000011758 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000011759 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011760
cristy3ed852e2009-09-05 21:47:34 +000011761 if (logging != MagickFalse)
11762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011763 " Successfully read jpeg_image into a blob, length=%.20g.",
11764 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011765
11766 }
11767 /* Destroy JPEG image and image_info */
11768 jpeg_image=DestroyImage(jpeg_image);
11769 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11770 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11771 }
11772
11773 /* Write JHDR chunk */
11774 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11775 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011776 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011777 PNGLong(chunk+4,(png_uint_32) image->columns);
11778 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011779 chunk[12]=jng_color_type;
11780 chunk[13]=8; /* sample depth */
11781 chunk[14]=8; /*jng_image_compression_method */
11782 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11783 chunk[16]=jng_alpha_sample_depth;
11784 chunk[17]=jng_alpha_compression_method;
11785 chunk[18]=0; /*jng_alpha_filter_method */
11786 chunk[19]=0; /*jng_alpha_interlace_method */
11787 (void) WriteBlob(image,20,chunk);
11788 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11789 if (logging != MagickFalse)
11790 {
11791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011792 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011793
cristy3ed852e2009-09-05 21:47:34 +000011794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011795 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011796
cristy3ed852e2009-09-05 21:47:34 +000011797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11798 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011799
cristy3ed852e2009-09-05 21:47:34 +000011800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11801 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011802
cristy3ed852e2009-09-05 21:47:34 +000011803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11804 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011805
cristy3ed852e2009-09-05 21:47:34 +000011806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11807 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011808
cristy3ed852e2009-09-05 21:47:34 +000011809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11810 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011811
cristy3ed852e2009-09-05 21:47:34 +000011812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11813 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011814
cristy3ed852e2009-09-05 21:47:34 +000011815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11816 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011817
cristy3ed852e2009-09-05 21:47:34 +000011818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11819 " JNG alpha interlace:%5d",0);
11820 }
11821
glennrp0fe50b42010-11-16 03:52:51 +000011822 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011823 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011824
11825 /*
11826 Write leading ancillary chunks
11827 */
11828
11829 if (transparent)
11830 {
11831 /*
11832 Write JNG bKGD chunk
11833 */
11834
11835 unsigned char
11836 blue,
11837 green,
11838 red;
11839
cristybb503372010-05-27 20:51:26 +000011840 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011841 num_bytes;
11842
11843 if (jng_color_type == 8 || jng_color_type == 12)
11844 num_bytes=6L;
11845 else
11846 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011847 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011848 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011849 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011850 red=ScaleQuantumToChar(image->background_color.red);
11851 green=ScaleQuantumToChar(image->background_color.green);
11852 blue=ScaleQuantumToChar(image->background_color.blue);
11853 *(chunk+4)=0;
11854 *(chunk+5)=red;
11855 *(chunk+6)=0;
11856 *(chunk+7)=green;
11857 *(chunk+8)=0;
11858 *(chunk+9)=blue;
11859 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11860 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11861 }
11862
11863 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11864 {
11865 /*
11866 Write JNG sRGB chunk
11867 */
11868 (void) WriteBlobMSBULong(image,1L);
11869 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011870 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011871
cristy3ed852e2009-09-05 21:47:34 +000011872 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011873 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011874 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011875 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011876
cristy3ed852e2009-09-05 21:47:34 +000011877 else
glennrpe610a072010-08-05 17:08:46 +000011878 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011879 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011880 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011881
cristy3ed852e2009-09-05 21:47:34 +000011882 (void) WriteBlob(image,5,chunk);
11883 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11884 }
11885 else
11886 {
11887 if (image->gamma != 0.0)
11888 {
11889 /*
11890 Write JNG gAMA chunk
11891 */
11892 (void) WriteBlobMSBULong(image,4L);
11893 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011894 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011895 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011896 (void) WriteBlob(image,8,chunk);
11897 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11898 }
glennrp0fe50b42010-11-16 03:52:51 +000011899
cristy3ed852e2009-09-05 21:47:34 +000011900 if ((mng_info->equal_chrms == MagickFalse) &&
11901 (image->chromaticity.red_primary.x != 0.0))
11902 {
11903 PrimaryInfo
11904 primary;
11905
11906 /*
11907 Write JNG cHRM chunk
11908 */
11909 (void) WriteBlobMSBULong(image,32L);
11910 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011911 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011912 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011913 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11914 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011915 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011916 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11917 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011918 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011919 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11920 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011921 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011922 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11923 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011924 (void) WriteBlob(image,36,chunk);
11925 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11926 }
11927 }
glennrp0fe50b42010-11-16 03:52:51 +000011928
cristy3ed852e2009-09-05 21:47:34 +000011929 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11930 {
11931 /*
11932 Write JNG pHYs chunk
11933 */
11934 (void) WriteBlobMSBULong(image,9L);
11935 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011936 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011937 if (image->units == PixelsPerInchResolution)
11938 {
cristy35ef8242010-06-03 16:24:13 +000011939 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011940 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011941
cristy35ef8242010-06-03 16:24:13 +000011942 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011943 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011944
cristy3ed852e2009-09-05 21:47:34 +000011945 chunk[12]=1;
11946 }
glennrp0fe50b42010-11-16 03:52:51 +000011947
cristy3ed852e2009-09-05 21:47:34 +000011948 else
11949 {
11950 if (image->units == PixelsPerCentimeterResolution)
11951 {
cristy35ef8242010-06-03 16:24:13 +000011952 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011953 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011954
cristy35ef8242010-06-03 16:24:13 +000011955 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011956 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011957
cristy3ed852e2009-09-05 21:47:34 +000011958 chunk[12]=1;
11959 }
glennrp0fe50b42010-11-16 03:52:51 +000011960
cristy3ed852e2009-09-05 21:47:34 +000011961 else
11962 {
cristy35ef8242010-06-03 16:24:13 +000011963 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11964 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011965 chunk[12]=0;
11966 }
11967 }
11968 (void) WriteBlob(image,13,chunk);
11969 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11970 }
11971
11972 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11973 {
11974 /*
11975 Write JNG oFFs chunk
11976 */
11977 (void) WriteBlobMSBULong(image,9L);
11978 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011979 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011980 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11981 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011982 chunk[12]=0;
11983 (void) WriteBlob(image,13,chunk);
11984 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11985 }
11986 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11987 {
11988 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11989 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011990 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011991 PNGLong(chunk+4,(png_uint_32) image->page.width);
11992 PNGLong(chunk+8,(png_uint_32) image->page.height);
11993 chunk[12]=0; /* unit = pixels */
11994 (void) WriteBlob(image,13,chunk);
11995 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11996 }
11997
11998
11999 if (transparent)
12000 {
12001 if (jng_alpha_compression_method==0)
12002 {
cristybb503372010-05-27 20:51:26 +000012003 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012004 i;
12005
cristybb503372010-05-27 20:51:26 +000012006 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012007 len;
12008
12009 /* Write IDAT chunk header */
12010 if (logging != MagickFalse)
12011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012012 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000012013 length);
cristy3ed852e2009-09-05 21:47:34 +000012014
12015 /* Copy IDAT chunks */
12016 len=0;
12017 p=blob+8;
cristybb503372010-05-27 20:51:26 +000012018 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000012019 {
12020 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12021 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012022
cristy3ed852e2009-09-05 21:47:34 +000012023 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12024 {
12025 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000012026 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000012027 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000012028 (void) WriteBlob(image,(size_t) len+4,p);
12029 (void) WriteBlobMSBULong(image,
12030 crc32(0,p,(uInt) len+4));
12031 }
glennrp0fe50b42010-11-16 03:52:51 +000012032
cristy3ed852e2009-09-05 21:47:34 +000012033 else
12034 {
12035 if (logging != MagickFalse)
12036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012037 " Skipping %c%c%c%c chunk, length=%.20g.",
12038 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012039 }
12040 p+=(8+len);
12041 }
12042 }
12043 else
12044 {
12045 /* Write JDAA chunk header */
12046 if (logging != MagickFalse)
12047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012048 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012049 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012050 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012051 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012052 /* Write JDAT chunk(s) data */
12053 (void) WriteBlob(image,4,chunk);
12054 (void) WriteBlob(image,length,blob);
12055 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12056 (uInt) length));
12057 }
12058 blob=(unsigned char *) RelinquishMagickMemory(blob);
12059 }
12060
12061 /* Encode image as a JPEG blob */
12062 if (logging != MagickFalse)
12063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12064 " Creating jpeg_image_info.");
12065 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12066 if (jpeg_image_info == (ImageInfo *) NULL)
12067 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12068
12069 if (logging != MagickFalse)
12070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12071 " Creating jpeg_image.");
12072
12073 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12074 if (jpeg_image == (Image *) NULL)
12075 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12076 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12077
12078 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012079 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012080 jpeg_image->filename);
12081
12082 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12083 &image->exception);
12084
12085 if (logging != MagickFalse)
12086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012087 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12088 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012089
12090 if (jng_color_type == 8 || jng_color_type == 12)
12091 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012092
cristy3ed852e2009-09-05 21:47:34 +000012093 jpeg_image_info->quality=jng_quality % 1000;
12094 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12095 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012096
cristy3ed852e2009-09-05 21:47:34 +000012097 if (logging != MagickFalse)
12098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12099 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012100
cristy3ed852e2009-09-05 21:47:34 +000012101 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000012102
cristy3ed852e2009-09-05 21:47:34 +000012103 if (logging != MagickFalse)
12104 {
12105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012106 " Successfully read jpeg_image into a blob, length=%.20g.",
12107 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012108
12109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012110 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012111 }
glennrp0fe50b42010-11-16 03:52:51 +000012112
cristy3ed852e2009-09-05 21:47:34 +000012113 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012114 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012115 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012116 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012117 (void) WriteBlob(image,4,chunk);
12118 (void) WriteBlob(image,length,blob);
12119 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12120
12121 jpeg_image=DestroyImage(jpeg_image);
12122 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12123 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12124 blob=(unsigned char *) RelinquishMagickMemory(blob);
12125
12126 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012127 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012128
12129 /* Write IEND chunk */
12130 (void) WriteBlobMSBULong(image,0L);
12131 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012132 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012133 (void) WriteBlob(image,4,chunk);
12134 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12135
12136 if (logging != MagickFalse)
12137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12138 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012139
cristy3ed852e2009-09-05 21:47:34 +000012140 return(status);
12141}
12142
12143
12144/*
12145%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12146% %
12147% %
12148% %
12149% W r i t e J N G I m a g e %
12150% %
12151% %
12152% %
12153%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12154%
12155% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12156%
12157% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12158%
12159% The format of the WriteJNGImage method is:
12160%
cristy1e178e72011-08-28 19:44:34 +000012161% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12162% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012163%
12164% A description of each parameter follows:
12165%
12166% o image_info: the image info.
12167%
12168% o image: The image.
12169%
cristy1e178e72011-08-28 19:44:34 +000012170% o exception: return any errors or warnings in this structure.
12171%
cristy3ed852e2009-09-05 21:47:34 +000012172%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12173*/
cristy1e178e72011-08-28 19:44:34 +000012174static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12175 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012176{
12177 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012178 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012179 logging,
cristy3ed852e2009-09-05 21:47:34 +000012180 status;
12181
12182 MngInfo
12183 *mng_info;
12184
cristy3ed852e2009-09-05 21:47:34 +000012185 /*
12186 Open image file.
12187 */
12188 assert(image_info != (const ImageInfo *) NULL);
12189 assert(image_info->signature == MagickSignature);
12190 assert(image != (Image *) NULL);
12191 assert(image->signature == MagickSignature);
12192 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012193 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012194 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12195 if (status == MagickFalse)
12196 return(status);
12197
12198 /*
12199 Allocate a MngInfo structure.
12200 */
12201 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012202 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012203 if (mng_info == (MngInfo *) NULL)
12204 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12205 /*
12206 Initialize members of the MngInfo structure.
12207 */
12208 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12209 mng_info->image=image;
12210 have_mng_structure=MagickTrue;
12211
12212 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12213
cristy018f07f2011-09-04 21:15:19 +000012214 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012215 (void) CloseBlob(image);
12216
12217 (void) CatchImageException(image);
12218 MngInfoFreeStruct(mng_info,&have_mng_structure);
12219 if (logging != MagickFalse)
12220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12221 return(status);
12222}
12223#endif
12224
cristy1e178e72011-08-28 19:44:34 +000012225static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12226 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012227{
12228 const char
12229 *option;
12230
12231 Image
12232 *next_image;
12233
12234 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012235 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012236 status;
12237
glennrp03812ae2010-12-24 01:31:34 +000012238 volatile MagickBooleanType
12239 logging;
12240
cristy3ed852e2009-09-05 21:47:34 +000012241 MngInfo
12242 *mng_info;
12243
12244 int
cristy3ed852e2009-09-05 21:47:34 +000012245 image_count,
12246 need_iterations,
12247 need_matte;
12248
12249 volatile int
12250#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12251 defined(PNG_MNG_FEATURES_SUPPORTED)
12252 need_local_plte,
12253#endif
12254 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012255 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012256 use_global_plte;
12257
cristybb503372010-05-27 20:51:26 +000012258 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012259 i;
12260
12261 unsigned char
12262 chunk[800];
12263
12264 volatile unsigned int
12265 write_jng,
12266 write_mng;
12267
cristybb503372010-05-27 20:51:26 +000012268 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012269 scene;
12270
cristybb503372010-05-27 20:51:26 +000012271 size_t
cristy3ed852e2009-09-05 21:47:34 +000012272 final_delay=0,
12273 initial_delay;
12274
glennrpd5045b42010-03-24 12:40:35 +000012275#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012276 if (image_info->verbose)
12277 printf("Your PNG library (libpng-%s) is rather old.\n",
12278 PNG_LIBPNG_VER_STRING);
12279#endif
12280
12281 /*
12282 Open image file.
12283 */
12284 assert(image_info != (const ImageInfo *) NULL);
12285 assert(image_info->signature == MagickSignature);
12286 assert(image != (Image *) NULL);
12287 assert(image->signature == MagickSignature);
12288 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012289 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012290 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12291 if (status == MagickFalse)
12292 return(status);
12293
12294 /*
12295 Allocate a MngInfo structure.
12296 */
12297 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012298 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012299 if (mng_info == (MngInfo *) NULL)
12300 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12301 /*
12302 Initialize members of the MngInfo structure.
12303 */
12304 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12305 mng_info->image=image;
12306 have_mng_structure=MagickTrue;
12307 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12308
12309 /*
12310 * See if user has requested a specific PNG subformat to be used
12311 * for all of the PNGs in the MNG being written, e.g.,
12312 *
12313 * convert *.png png8:animation.mng
12314 *
12315 * To do: check -define png:bit_depth and png:color_type as well,
12316 * or perhaps use mng:bit_depth and mng:color_type instead for
12317 * global settings.
12318 */
12319
12320 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12321 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12322 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12323
12324 write_jng=MagickFalse;
12325 if (image_info->compression == JPEGCompression)
12326 write_jng=MagickTrue;
12327
12328 mng_info->adjoin=image_info->adjoin &&
12329 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12330
cristy3ed852e2009-09-05 21:47:34 +000012331 if (logging != MagickFalse)
12332 {
12333 /* Log some info about the input */
12334 Image
12335 *p;
12336
12337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12338 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012339
cristy3ed852e2009-09-05 21:47:34 +000012340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012341 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012342
cristy3ed852e2009-09-05 21:47:34 +000012343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12344 " Type: %d",image_info->type);
12345
12346 scene=0;
12347 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12348 {
12349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012350 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012351
cristy3ed852e2009-09-05 21:47:34 +000012352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012353 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012354
cristy3ed852e2009-09-05 21:47:34 +000012355 if (p->matte)
12356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12357 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012358
cristy3ed852e2009-09-05 21:47:34 +000012359 else
12360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12361 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012362
cristy3ed852e2009-09-05 21:47:34 +000012363 if (p->storage_class == PseudoClass)
12364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12365 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012366
cristy3ed852e2009-09-05 21:47:34 +000012367 else
12368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12369 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012370
cristy3ed852e2009-09-05 21:47:34 +000012371 if (p->colors)
12372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012373 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012374
cristy3ed852e2009-09-05 21:47:34 +000012375 else
12376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12377 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012378
cristy3ed852e2009-09-05 21:47:34 +000012379 if (mng_info->adjoin == MagickFalse)
12380 break;
12381 }
12382 }
12383
cristy3ed852e2009-09-05 21:47:34 +000012384 use_global_plte=MagickFalse;
12385 all_images_are_gray=MagickFalse;
12386#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12387 need_local_plte=MagickTrue;
12388#endif
12389 need_defi=MagickFalse;
12390 need_matte=MagickFalse;
12391 mng_info->framing_mode=1;
12392 mng_info->old_framing_mode=1;
12393
12394 if (write_mng)
12395 if (image_info->page != (char *) NULL)
12396 {
12397 /*
12398 Determine image bounding box.
12399 */
12400 SetGeometry(image,&mng_info->page);
12401 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12402 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12403 }
12404 if (write_mng)
12405 {
12406 unsigned int
12407 need_geom;
12408
12409 unsigned short
12410 red,
12411 green,
12412 blue;
12413
12414 mng_info->page=image->page;
12415 need_geom=MagickTrue;
12416 if (mng_info->page.width || mng_info->page.height)
12417 need_geom=MagickFalse;
12418 /*
12419 Check all the scenes.
12420 */
12421 initial_delay=image->delay;
12422 need_iterations=MagickFalse;
12423 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12424 mng_info->equal_physs=MagickTrue,
12425 mng_info->equal_gammas=MagickTrue;
12426 mng_info->equal_srgbs=MagickTrue;
12427 mng_info->equal_backgrounds=MagickTrue;
12428 image_count=0;
12429#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12430 defined(PNG_MNG_FEATURES_SUPPORTED)
12431 all_images_are_gray=MagickTrue;
12432 mng_info->equal_palettes=MagickFalse;
12433 need_local_plte=MagickFalse;
12434#endif
12435 for (next_image=image; next_image != (Image *) NULL; )
12436 {
12437 if (need_geom)
12438 {
12439 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12440 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012441
cristy3ed852e2009-09-05 21:47:34 +000012442 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12443 mng_info->page.height=next_image->rows+next_image->page.y;
12444 }
glennrp0fe50b42010-11-16 03:52:51 +000012445
cristy3ed852e2009-09-05 21:47:34 +000012446 if (next_image->page.x || next_image->page.y)
12447 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012448
cristy3ed852e2009-09-05 21:47:34 +000012449 if (next_image->matte)
12450 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012451
cristy3ed852e2009-09-05 21:47:34 +000012452 if ((int) next_image->dispose >= BackgroundDispose)
12453 if (next_image->matte || next_image->page.x || next_image->page.y ||
12454 ((next_image->columns < mng_info->page.width) &&
12455 (next_image->rows < mng_info->page.height)))
12456 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012457
cristy3ed852e2009-09-05 21:47:34 +000012458 if (next_image->iterations)
12459 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012460
cristy3ed852e2009-09-05 21:47:34 +000012461 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012462
cristy3ed852e2009-09-05 21:47:34 +000012463 if (final_delay != initial_delay || final_delay > 1UL*
12464 next_image->ticks_per_second)
12465 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012466
cristy3ed852e2009-09-05 21:47:34 +000012467#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12468 defined(PNG_MNG_FEATURES_SUPPORTED)
12469 /*
12470 check for global palette possibility.
12471 */
12472 if (image->matte != MagickFalse)
12473 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012474
cristy3ed852e2009-09-05 21:47:34 +000012475 if (need_local_plte == 0)
12476 {
12477 if (ImageIsGray(image) == MagickFalse)
12478 all_images_are_gray=MagickFalse;
12479 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12480 if (use_global_plte == 0)
12481 use_global_plte=mng_info->equal_palettes;
12482 need_local_plte=!mng_info->equal_palettes;
12483 }
12484#endif
12485 if (GetNextImageInList(next_image) != (Image *) NULL)
12486 {
12487 if (next_image->background_color.red !=
12488 next_image->next->background_color.red ||
12489 next_image->background_color.green !=
12490 next_image->next->background_color.green ||
12491 next_image->background_color.blue !=
12492 next_image->next->background_color.blue)
12493 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012494
cristy3ed852e2009-09-05 21:47:34 +000012495 if (next_image->gamma != next_image->next->gamma)
12496 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012497
cristy3ed852e2009-09-05 21:47:34 +000012498 if (next_image->rendering_intent !=
12499 next_image->next->rendering_intent)
12500 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012501
cristy3ed852e2009-09-05 21:47:34 +000012502 if ((next_image->units != next_image->next->units) ||
12503 (next_image->x_resolution != next_image->next->x_resolution) ||
12504 (next_image->y_resolution != next_image->next->y_resolution))
12505 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012506
cristy3ed852e2009-09-05 21:47:34 +000012507 if (mng_info->equal_chrms)
12508 {
12509 if (next_image->chromaticity.red_primary.x !=
12510 next_image->next->chromaticity.red_primary.x ||
12511 next_image->chromaticity.red_primary.y !=
12512 next_image->next->chromaticity.red_primary.y ||
12513 next_image->chromaticity.green_primary.x !=
12514 next_image->next->chromaticity.green_primary.x ||
12515 next_image->chromaticity.green_primary.y !=
12516 next_image->next->chromaticity.green_primary.y ||
12517 next_image->chromaticity.blue_primary.x !=
12518 next_image->next->chromaticity.blue_primary.x ||
12519 next_image->chromaticity.blue_primary.y !=
12520 next_image->next->chromaticity.blue_primary.y ||
12521 next_image->chromaticity.white_point.x !=
12522 next_image->next->chromaticity.white_point.x ||
12523 next_image->chromaticity.white_point.y !=
12524 next_image->next->chromaticity.white_point.y)
12525 mng_info->equal_chrms=MagickFalse;
12526 }
12527 }
12528 image_count++;
12529 next_image=GetNextImageInList(next_image);
12530 }
12531 if (image_count < 2)
12532 {
12533 mng_info->equal_backgrounds=MagickFalse;
12534 mng_info->equal_chrms=MagickFalse;
12535 mng_info->equal_gammas=MagickFalse;
12536 mng_info->equal_srgbs=MagickFalse;
12537 mng_info->equal_physs=MagickFalse;
12538 use_global_plte=MagickFalse;
12539#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12540 need_local_plte=MagickTrue;
12541#endif
12542 need_iterations=MagickFalse;
12543 }
glennrp0fe50b42010-11-16 03:52:51 +000012544
cristy3ed852e2009-09-05 21:47:34 +000012545 if (mng_info->need_fram == MagickFalse)
12546 {
12547 /*
12548 Only certain framing rates 100/n are exactly representable without
12549 the FRAM chunk but we'll allow some slop in VLC files
12550 */
12551 if (final_delay == 0)
12552 {
12553 if (need_iterations != MagickFalse)
12554 {
12555 /*
12556 It's probably a GIF with loop; don't run it *too* fast.
12557 */
glennrp02617122010-07-28 13:07:35 +000012558 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012559 {
12560 final_delay=10;
12561 (void) ThrowMagickException(&image->exception,
12562 GetMagickModule(),CoderWarning,
12563 "input has zero delay between all frames; assuming",
12564 " 10 cs `%s'","");
12565 }
cristy3ed852e2009-09-05 21:47:34 +000012566 }
12567 else
12568 mng_info->ticks_per_second=0;
12569 }
12570 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012571 mng_info->ticks_per_second=(png_uint_32)
12572 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012573 if (final_delay > 50)
12574 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012575
cristy3ed852e2009-09-05 21:47:34 +000012576 if (final_delay > 75)
12577 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012578
cristy3ed852e2009-09-05 21:47:34 +000012579 if (final_delay > 125)
12580 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012581
cristy3ed852e2009-09-05 21:47:34 +000012582 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12583 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12584 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12585 1UL*image->ticks_per_second))
12586 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12587 }
glennrp0fe50b42010-11-16 03:52:51 +000012588
cristy3ed852e2009-09-05 21:47:34 +000012589 if (mng_info->need_fram != MagickFalse)
12590 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12591 /*
12592 If pseudocolor, we should also check to see if all the
12593 palettes are identical and write a global PLTE if they are.
12594 ../glennrp Feb 99.
12595 */
12596 /*
12597 Write the MNG version 1.0 signature and MHDR chunk.
12598 */
12599 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12600 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12601 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012602 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012603 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12604 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012605 PNGLong(chunk+12,mng_info->ticks_per_second);
12606 PNGLong(chunk+16,0L); /* layer count=unknown */
12607 PNGLong(chunk+20,0L); /* frame count=unknown */
12608 PNGLong(chunk+24,0L); /* play time=unknown */
12609 if (write_jng)
12610 {
12611 if (need_matte)
12612 {
12613 if (need_defi || mng_info->need_fram || use_global_plte)
12614 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012615
cristy3ed852e2009-09-05 21:47:34 +000012616 else
12617 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12618 }
glennrp0fe50b42010-11-16 03:52:51 +000012619
cristy3ed852e2009-09-05 21:47:34 +000012620 else
12621 {
12622 if (need_defi || mng_info->need_fram || use_global_plte)
12623 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012624
cristy3ed852e2009-09-05 21:47:34 +000012625 else
12626 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12627 }
12628 }
glennrp0fe50b42010-11-16 03:52:51 +000012629
cristy3ed852e2009-09-05 21:47:34 +000012630 else
12631 {
12632 if (need_matte)
12633 {
12634 if (need_defi || mng_info->need_fram || use_global_plte)
12635 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012636
cristy3ed852e2009-09-05 21:47:34 +000012637 else
12638 PNGLong(chunk+28,9L); /* simplicity=VLC */
12639 }
glennrp0fe50b42010-11-16 03:52:51 +000012640
cristy3ed852e2009-09-05 21:47:34 +000012641 else
12642 {
12643 if (need_defi || mng_info->need_fram || use_global_plte)
12644 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012645
cristy3ed852e2009-09-05 21:47:34 +000012646 else
12647 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12648 }
12649 }
12650 (void) WriteBlob(image,32,chunk);
12651 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12652 option=GetImageOption(image_info,"mng:need-cacheoff");
12653 if (option != (const char *) NULL)
12654 {
12655 size_t
12656 length;
12657
12658 /*
12659 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12660 */
12661 PNGType(chunk,mng_nEED);
12662 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012663 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012664 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012665 length+=4;
12666 (void) WriteBlob(image,length,chunk);
12667 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12668 }
12669 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12670 (GetNextImageInList(image) != (Image *) NULL) &&
12671 (image->iterations != 1))
12672 {
12673 /*
12674 Write MNG TERM chunk
12675 */
12676 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12677 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012678 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012679 chunk[4]=3; /* repeat animation */
12680 chunk[5]=0; /* show last frame when done */
12681 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12682 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012683
cristy3ed852e2009-09-05 21:47:34 +000012684 if (image->iterations == 0)
12685 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012686
cristy3ed852e2009-09-05 21:47:34 +000012687 else
12688 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012689
cristy3ed852e2009-09-05 21:47:34 +000012690 if (logging != MagickFalse)
12691 {
12692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012693 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12694 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012695
cristy3ed852e2009-09-05 21:47:34 +000012696 if (image->iterations == 0)
12697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012698 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012699
cristy3ed852e2009-09-05 21:47:34 +000012700 else
12701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012702 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012703 }
12704 (void) WriteBlob(image,14,chunk);
12705 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12706 }
12707 /*
12708 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12709 */
12710 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12711 mng_info->equal_srgbs)
12712 {
12713 /*
12714 Write MNG sRGB chunk
12715 */
12716 (void) WriteBlobMSBULong(image,1L);
12717 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012718 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012719
cristy3ed852e2009-09-05 21:47:34 +000012720 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012721 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012722 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012723 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012724
cristy3ed852e2009-09-05 21:47:34 +000012725 else
glennrpe610a072010-08-05 17:08:46 +000012726 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012727 Magick_RenderingIntent_to_PNG_RenderingIntent(
12728 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012729
cristy3ed852e2009-09-05 21:47:34 +000012730 (void) WriteBlob(image,5,chunk);
12731 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12732 mng_info->have_write_global_srgb=MagickTrue;
12733 }
glennrp0fe50b42010-11-16 03:52:51 +000012734
cristy3ed852e2009-09-05 21:47:34 +000012735 else
12736 {
12737 if (image->gamma && mng_info->equal_gammas)
12738 {
12739 /*
12740 Write MNG gAMA chunk
12741 */
12742 (void) WriteBlobMSBULong(image,4L);
12743 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012744 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012745 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012746 (void) WriteBlob(image,8,chunk);
12747 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12748 mng_info->have_write_global_gama=MagickTrue;
12749 }
12750 if (mng_info->equal_chrms)
12751 {
12752 PrimaryInfo
12753 primary;
12754
12755 /*
12756 Write MNG cHRM chunk
12757 */
12758 (void) WriteBlobMSBULong(image,32L);
12759 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012760 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012761 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012762 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12763 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012764 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012765 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12766 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012767 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012768 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12769 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012770 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012771 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12772 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012773 (void) WriteBlob(image,36,chunk);
12774 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12775 mng_info->have_write_global_chrm=MagickTrue;
12776 }
12777 }
12778 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
12779 {
12780 /*
12781 Write MNG pHYs chunk
12782 */
12783 (void) WriteBlobMSBULong(image,9L);
12784 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012785 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012786
cristy3ed852e2009-09-05 21:47:34 +000012787 if (image->units == PixelsPerInchResolution)
12788 {
cristy35ef8242010-06-03 16:24:13 +000012789 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012790 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012791
cristy35ef8242010-06-03 16:24:13 +000012792 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012793 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012794
cristy3ed852e2009-09-05 21:47:34 +000012795 chunk[12]=1;
12796 }
glennrp0fe50b42010-11-16 03:52:51 +000012797
cristy3ed852e2009-09-05 21:47:34 +000012798 else
12799 {
12800 if (image->units == PixelsPerCentimeterResolution)
12801 {
cristy35ef8242010-06-03 16:24:13 +000012802 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012803 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012804
cristy35ef8242010-06-03 16:24:13 +000012805 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012806 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012807
cristy3ed852e2009-09-05 21:47:34 +000012808 chunk[12]=1;
12809 }
glennrp0fe50b42010-11-16 03:52:51 +000012810
cristy3ed852e2009-09-05 21:47:34 +000012811 else
12812 {
cristy35ef8242010-06-03 16:24:13 +000012813 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
12814 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012815 chunk[12]=0;
12816 }
12817 }
12818 (void) WriteBlob(image,13,chunk);
12819 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12820 }
12821 /*
12822 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12823 or does not cover the entire frame.
12824 */
12825 if (write_mng && (image->matte || image->page.x > 0 ||
12826 image->page.y > 0 || (image->page.width &&
12827 (image->page.width+image->page.x < mng_info->page.width))
12828 || (image->page.height && (image->page.height+image->page.y
12829 < mng_info->page.height))))
12830 {
12831 (void) WriteBlobMSBULong(image,6L);
12832 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012833 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012834 red=ScaleQuantumToShort(image->background_color.red);
12835 green=ScaleQuantumToShort(image->background_color.green);
12836 blue=ScaleQuantumToShort(image->background_color.blue);
12837 PNGShort(chunk+4,red);
12838 PNGShort(chunk+6,green);
12839 PNGShort(chunk+8,blue);
12840 (void) WriteBlob(image,10,chunk);
12841 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12842 if (mng_info->equal_backgrounds)
12843 {
12844 (void) WriteBlobMSBULong(image,6L);
12845 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012846 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012847 (void) WriteBlob(image,10,chunk);
12848 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12849 }
12850 }
12851
12852#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12853 if ((need_local_plte == MagickFalse) &&
12854 (image->storage_class == PseudoClass) &&
12855 (all_images_are_gray == MagickFalse))
12856 {
cristybb503372010-05-27 20:51:26 +000012857 size_t
cristy3ed852e2009-09-05 21:47:34 +000012858 data_length;
12859
12860 /*
12861 Write MNG PLTE chunk
12862 */
12863 data_length=3*image->colors;
12864 (void) WriteBlobMSBULong(image,data_length);
12865 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012866 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012867
cristybb503372010-05-27 20:51:26 +000012868 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012869 {
cristy5f07f702011-09-26 17:29:10 +000012870 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12871 image->colormap[i].red) & 0xff);
12872 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12873 image->colormap[i].green) & 0xff);
12874 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12875 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000012876 }
glennrp0fe50b42010-11-16 03:52:51 +000012877
cristy3ed852e2009-09-05 21:47:34 +000012878 (void) WriteBlob(image,data_length+4,chunk);
12879 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12880 mng_info->have_write_global_plte=MagickTrue;
12881 }
12882#endif
12883 }
12884 scene=0;
12885 mng_info->delay=0;
12886#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12887 defined(PNG_MNG_FEATURES_SUPPORTED)
12888 mng_info->equal_palettes=MagickFalse;
12889#endif
12890 do
12891 {
12892 if (mng_info->adjoin)
12893 {
12894#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12895 defined(PNG_MNG_FEATURES_SUPPORTED)
12896 /*
12897 If we aren't using a global palette for the entire MNG, check to
12898 see if we can use one for two or more consecutive images.
12899 */
12900 if (need_local_plte && use_global_plte && !all_images_are_gray)
12901 {
12902 if (mng_info->IsPalette)
12903 {
12904 /*
12905 When equal_palettes is true, this image has the same palette
12906 as the previous PseudoClass image
12907 */
12908 mng_info->have_write_global_plte=mng_info->equal_palettes;
12909 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12910 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12911 {
12912 /*
12913 Write MNG PLTE chunk
12914 */
cristybb503372010-05-27 20:51:26 +000012915 size_t
cristy3ed852e2009-09-05 21:47:34 +000012916 data_length;
12917
12918 data_length=3*image->colors;
12919 (void) WriteBlobMSBULong(image,data_length);
12920 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012921 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012922
cristybb503372010-05-27 20:51:26 +000012923 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012924 {
12925 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12926 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12927 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12928 }
glennrp0fe50b42010-11-16 03:52:51 +000012929
cristy3ed852e2009-09-05 21:47:34 +000012930 (void) WriteBlob(image,data_length+4,chunk);
12931 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12932 (uInt) (data_length+4)));
12933 mng_info->have_write_global_plte=MagickTrue;
12934 }
12935 }
12936 else
12937 mng_info->have_write_global_plte=MagickFalse;
12938 }
12939#endif
12940 if (need_defi)
12941 {
cristybb503372010-05-27 20:51:26 +000012942 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012943 previous_x,
12944 previous_y;
12945
12946 if (scene)
12947 {
12948 previous_x=mng_info->page.x;
12949 previous_y=mng_info->page.y;
12950 }
12951 else
12952 {
12953 previous_x=0;
12954 previous_y=0;
12955 }
12956 mng_info->page=image->page;
12957 if ((mng_info->page.x != previous_x) ||
12958 (mng_info->page.y != previous_y))
12959 {
12960 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12961 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012962 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012963 chunk[4]=0; /* object 0 MSB */
12964 chunk[5]=0; /* object 0 LSB */
12965 chunk[6]=0; /* visible */
12966 chunk[7]=0; /* abstract */
12967 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12968 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12969 (void) WriteBlob(image,16,chunk);
12970 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12971 }
12972 }
12973 }
12974
12975 mng_info->write_mng=write_mng;
12976
12977 if ((int) image->dispose >= 3)
12978 mng_info->framing_mode=3;
12979
12980 if (mng_info->need_fram && mng_info->adjoin &&
12981 ((image->delay != mng_info->delay) ||
12982 (mng_info->framing_mode != mng_info->old_framing_mode)))
12983 {
12984 if (image->delay == mng_info->delay)
12985 {
12986 /*
12987 Write a MNG FRAM chunk with the new framing mode.
12988 */
12989 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12990 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012991 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012992 chunk[4]=(unsigned char) mng_info->framing_mode;
12993 (void) WriteBlob(image,5,chunk);
12994 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12995 }
12996 else
12997 {
12998 /*
12999 Write a MNG FRAM chunk with the delay.
13000 */
13001 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13002 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013003 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013004 chunk[4]=(unsigned char) mng_info->framing_mode;
13005 chunk[5]=0; /* frame name separator (no name) */
13006 chunk[6]=2; /* flag for changing default delay */
13007 chunk[7]=0; /* flag for changing frame timeout */
13008 chunk[8]=0; /* flag for changing frame clipping */
13009 chunk[9]=0; /* flag for changing frame sync_id */
13010 PNGLong(chunk+10,(png_uint_32)
13011 ((mng_info->ticks_per_second*
13012 image->delay)/MagickMax(image->ticks_per_second,1)));
13013 (void) WriteBlob(image,14,chunk);
13014 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000013015 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000013016 }
13017 mng_info->old_framing_mode=mng_info->framing_mode;
13018 }
13019
13020#if defined(JNG_SUPPORTED)
13021 if (image_info->compression == JPEGCompression)
13022 {
13023 ImageInfo
13024 *write_info;
13025
13026 if (logging != MagickFalse)
13027 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13028 " Writing JNG object.");
13029 /* To do: specify the desired alpha compression method. */
13030 write_info=CloneImageInfo(image_info);
13031 write_info->compression=UndefinedCompression;
cristy018f07f2011-09-04 21:15:19 +000013032 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013033 write_info=DestroyImageInfo(write_info);
13034 }
13035 else
13036#endif
13037 {
13038 if (logging != MagickFalse)
13039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13040 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013041
glennrpb9cfe272010-12-21 15:08:06 +000013042 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013043 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013044
13045 /* We don't want any ancillary chunks written */
13046 mng_info->ping_exclude_bKGD=MagickTrue;
13047 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013048 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013049 mng_info->ping_exclude_EXIF=MagickTrue;
13050 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013051 mng_info->ping_exclude_iCCP=MagickTrue;
13052 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13053 mng_info->ping_exclude_oFFs=MagickTrue;
13054 mng_info->ping_exclude_pHYs=MagickTrue;
13055 mng_info->ping_exclude_sRGB=MagickTrue;
13056 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013057 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013058 mng_info->ping_exclude_vpAg=MagickTrue;
13059 mng_info->ping_exclude_zCCP=MagickTrue;
13060 mng_info->ping_exclude_zTXt=MagickTrue;
13061
cristy018f07f2011-09-04 21:15:19 +000013062 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013063 }
13064
13065 if (status == MagickFalse)
13066 {
13067 MngInfoFreeStruct(mng_info,&have_mng_structure);
13068 (void) CloseBlob(image);
13069 return(MagickFalse);
13070 }
13071 (void) CatchImageException(image);
13072 if (GetNextImageInList(image) == (Image *) NULL)
13073 break;
13074 image=SyncNextImageInList(image);
13075 status=SetImageProgress(image,SaveImagesTag,scene++,
13076 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013077
cristy3ed852e2009-09-05 21:47:34 +000013078 if (status == MagickFalse)
13079 break;
glennrp0fe50b42010-11-16 03:52:51 +000013080
cristy3ed852e2009-09-05 21:47:34 +000013081 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013082
cristy3ed852e2009-09-05 21:47:34 +000013083 if (write_mng)
13084 {
13085 while (GetPreviousImageInList(image) != (Image *) NULL)
13086 image=GetPreviousImageInList(image);
13087 /*
13088 Write the MEND chunk.
13089 */
13090 (void) WriteBlobMSBULong(image,0x00000000L);
13091 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013092 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013093 (void) WriteBlob(image,4,chunk);
13094 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13095 }
13096 /*
13097 Relinquish resources.
13098 */
13099 (void) CloseBlob(image);
13100 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013101
cristy3ed852e2009-09-05 21:47:34 +000013102 if (logging != MagickFalse)
13103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013104
cristy3ed852e2009-09-05 21:47:34 +000013105 return(MagickTrue);
13106}
glennrpd5045b42010-03-24 12:40:35 +000013107#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013108
cristy3ed852e2009-09-05 21:47:34 +000013109static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13110{
13111 image=image;
13112 printf("Your PNG library is too old: You have libpng-%s\n",
13113 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013114
cristy3ed852e2009-09-05 21:47:34 +000013115 ThrowBinaryException(CoderError,"PNG library is too old",
13116 image_info->filename);
13117}
glennrp39992b42010-11-14 00:03:43 +000013118
cristy3ed852e2009-09-05 21:47:34 +000013119static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13120{
13121 return(WritePNGImage(image_info,image));
13122}
glennrpd5045b42010-03-24 12:40:35 +000013123#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013124#endif