blob: 289d60aa97385426777ea40661e1672a663166d4 [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
cristy101ab702011-10-13 13:06:32 +0000132 * and PixelInfos all have the image->depth, and for use
glennrp8e58efd2011-05-20 12:16:29 +0000133 * 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
cristy101ab702011-10-13 13:06:32 +0000906 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000907 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++)
cristy101ab702011-10-13 13:06:32 +00001148 if (IsPixelInfoGray(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,
cristyd15e6592011-10-15 00:13:06 +00001801 png_textp text,int ii,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001802{
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 */
cristyd15e6592011-10-15 00:13:06 +00001889 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001890 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
cristy101ab702011-10-13 13:06:32 +00002003 PixelInfo
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);
cristyd15e6592011-10-15 00:13:06 +00002308 (void) SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00002309 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(),
cristy101ab702011-10-13 13:06:32 +00002630 " scaled graylevel is %.20g.",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);
cristyd15e6592011-10-15 00:13:06 +00002769 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002770
cristy3b6fd2e2011-05-20 12:53:50 +00002771 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
cristyd15e6592011-10-15 00:13:06 +00002772 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002773
cristy3b6fd2e2011-05-20 12:53:50 +00002774 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
cristyd15e6592011-10-15 00:13:06 +00002775 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002776
cristy3b6fd2e2011-05-20 12:53:50 +00002777 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002778 (int) ping_interlace_method);
cristyd15e6592011-10-15 00:13:06 +00002779 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg,exception);
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 {
cristyd15e6592011-10-15 00:13:06 +00003350 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i,
3351 exception);
glennrp4eb39312011-03-30 21:34:55 +00003352 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003353 }
glennrp0fe50b42010-11-16 03:52:51 +00003354
glennrp4eb39312011-03-30 21:34:55 +00003355 else
3356 {
3357 char
3358 *value;
3359
3360 length=text[i].text_length;
3361 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3362 sizeof(*value));
3363 if (value == (char *) NULL)
3364 {
3365 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3366 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3367 image->filename);
3368 break;
3369 }
3370 *value='\0';
3371 (void) ConcatenateMagickString(value,text[i].text,length+2);
3372
3373 /* Don't save "density" or "units" property if we have a pHYs
3374 * chunk
3375 */
3376 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3377 (LocaleCompare(text[i].key,"density") != 0 &&
3378 LocaleCompare(text[i].key,"units") != 0))
cristyd15e6592011-10-15 00:13:06 +00003379 (void) SetImageProperty(image,text[i].key,value,exception);
glennrp4eb39312011-03-30 21:34:55 +00003380
3381 if (logging != MagickFalse)
3382 {
3383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3384 " length: %lu",(unsigned long) length);
3385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3386 " Keyword: %s",text[i].key);
3387 }
3388
3389 value=DestroyString(value);
3390 }
3391 }
3392 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003393 }
glennrp3c218112010-11-27 15:31:26 +00003394
cristy3ed852e2009-09-05 21:47:34 +00003395#ifdef MNG_OBJECT_BUFFERS
3396 /*
3397 Store the object if necessary.
3398 */
3399 if (object_id && !mng_info->frozen[object_id])
3400 {
3401 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3402 {
3403 /*
3404 create a new object buffer.
3405 */
3406 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003407 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003408
cristy3ed852e2009-09-05 21:47:34 +00003409 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3410 {
3411 mng_info->ob[object_id]->image=(Image *) NULL;
3412 mng_info->ob[object_id]->reference_count=1;
3413 }
3414 }
glennrp47b9dd52010-11-24 18:12:06 +00003415
cristy3ed852e2009-09-05 21:47:34 +00003416 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3417 mng_info->ob[object_id]->frozen)
3418 {
3419 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3420 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3421 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3422 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003423
cristy3ed852e2009-09-05 21:47:34 +00003424 if (mng_info->ob[object_id]->frozen)
3425 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3426 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3427 "`%s'",image->filename);
3428 }
glennrp0fe50b42010-11-16 03:52:51 +00003429
cristy3ed852e2009-09-05 21:47:34 +00003430 else
3431 {
cristy3ed852e2009-09-05 21:47:34 +00003432
3433 if (mng_info->ob[object_id]->image != (Image *) NULL)
3434 mng_info->ob[object_id]->image=DestroyImage
3435 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003436
cristy3ed852e2009-09-05 21:47:34 +00003437 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3438 &image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00003439
cristy3ed852e2009-09-05 21:47:34 +00003440 if (mng_info->ob[object_id]->image != (Image *) NULL)
3441 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003442
cristy3ed852e2009-09-05 21:47:34 +00003443 else
3444 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3445 ResourceLimitError,"Cloning image for object buffer failed",
3446 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003447
glennrpfaa852b2010-03-30 12:17:00 +00003448 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003449 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003450
glennrpfaa852b2010-03-30 12:17:00 +00003451 mng_info->ob[object_id]->width=ping_width;
3452 mng_info->ob[object_id]->height=ping_height;
3453 mng_info->ob[object_id]->color_type=ping_color_type;
3454 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3455 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3456 mng_info->ob[object_id]->compression_method=
3457 ping_compression_method;
3458 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003459
glennrpfaa852b2010-03-30 12:17:00 +00003460 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003461 {
3462 int
3463 number_colors;
3464
3465 png_colorp
3466 plte;
3467
3468 /*
3469 Copy the PLTE to the object buffer.
3470 */
3471 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3472 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003473
cristy3ed852e2009-09-05 21:47:34 +00003474 for (i=0; i < number_colors; i++)
3475 {
3476 mng_info->ob[object_id]->plte[i]=plte[i];
3477 }
3478 }
glennrp47b9dd52010-11-24 18:12:06 +00003479
cristy3ed852e2009-09-05 21:47:34 +00003480 else
3481 mng_info->ob[object_id]->plte_length=0;
3482 }
3483 }
3484#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003485
3486 /* Set image->matte to MagickTrue if the input colortype supports
3487 * alpha or if a valid tRNS chunk is present, no matter whether there
3488 * is actual transparency present.
3489 */
3490 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3491 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3492 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3493 MagickTrue : MagickFalse;
3494
glennrpcb395ac2011-03-30 19:50:23 +00003495 /* Set more properties for identify to retrieve */
3496 {
3497 char
3498 msg[MaxTextExtent];
3499
glennrp4eb39312011-03-30 21:34:55 +00003500 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003501 {
3502 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003503 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003504 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
cristyd15e6592011-10-15 00:13:06 +00003505 (void) SetImageProperty(image,"PNG:text ",msg,exception);
glennrpcb395ac2011-03-30 19:50:23 +00003506 }
3507
3508 if (num_raw_profiles != 0)
3509 {
cristy3b6fd2e2011-05-20 12:53:50 +00003510 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003511 "%d were found", num_raw_profiles);
cristyd15e6592011-10-15 00:13:06 +00003512 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg,exception);
glennrpcb395ac2011-03-30 19:50:23 +00003513 }
3514
glennrpcb395ac2011-03-30 19:50:23 +00003515 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003516 {
cristy3b6fd2e2011-05-20 12:53:50 +00003517 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003518 "chunk was found (see Chromaticity, above)");
cristyd15e6592011-10-15 00:13:06 +00003519 (void) SetImageProperty(image,"PNG:cHRM ",msg,exception);
glennrp59612252011-03-30 21:45:21 +00003520 }
glennrpcb395ac2011-03-30 19:50:23 +00003521
3522 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003523 {
cristy3b6fd2e2011-05-20 12:53:50 +00003524 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003525 "chunk was found (see Background color, above)");
cristyd15e6592011-10-15 00:13:06 +00003526 (void) SetImageProperty(image,"PNG:bKGD ",msg,exception);
glennrp59612252011-03-30 21:45:21 +00003527 }
3528
cristy3b6fd2e2011-05-20 12:53:50 +00003529 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003530 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003531
3532 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristyd15e6592011-10-15 00:13:06 +00003533 (void) SetImageProperty(image,"PNG:iCCP ",msg,exception);
glennrpcb395ac2011-03-30 19:50:23 +00003534
glennrpcb395ac2011-03-30 19:50:23 +00003535 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristyd15e6592011-10-15 00:13:06 +00003536 (void) SetImageProperty(image,"PNG:tRNS ",msg,exception);
glennrp4eb39312011-03-30 21:34:55 +00003537
3538#if defined(PNG_sRGB_SUPPORTED)
3539 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3540 {
cristy3b6fd2e2011-05-20 12:53:50 +00003541 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003542 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003543 (int) intent);
cristyd15e6592011-10-15 00:13:06 +00003544 (void) SetImageProperty(image,"PNG:sRGB ",msg,exception);
glennrp4eb39312011-03-30 21:34:55 +00003545 }
3546#endif
3547
3548 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3549 {
cristy3b6fd2e2011-05-20 12:53:50 +00003550 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003551 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003552 file_gamma);
cristyd15e6592011-10-15 00:13:06 +00003553 (void) SetImageProperty(image,"PNG:gAMA ",msg,exception);
glennrp4eb39312011-03-30 21:34:55 +00003554 }
3555
3556#if defined(PNG_pHYs_SUPPORTED)
3557 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3558 {
cristy3b6fd2e2011-05-20 12:53:50 +00003559 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003560 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003561 (double) x_resolution,(double) y_resolution, unit_type);
cristyd15e6592011-10-15 00:13:06 +00003562 (void) SetImageProperty(image,"PNG:pHYs ",msg,exception);
glennrp4eb39312011-03-30 21:34:55 +00003563 }
3564#endif
3565
3566#if defined(PNG_oFFs_SUPPORTED)
3567 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3568 {
cristy3b6fd2e2011-05-20 12:53:50 +00003569 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003570 (double) image->page.x,(double) image->page.y);
cristyd15e6592011-10-15 00:13:06 +00003571 (void) SetImageProperty(image,"PNG:oFFs ",msg,exception);
glennrp4eb39312011-03-30 21:34:55 +00003572 }
3573#endif
3574
glennrp07523c72011-03-31 18:12:10 +00003575 if ((image->page.width != 0 && image->page.width != image->columns) ||
3576 (image->page.height != 0 && image->page.height != image->rows))
3577 {
cristy3b6fd2e2011-05-20 12:53:50 +00003578 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003579 "width=%.20g, height=%.20g",
3580 (double) image->page.width,(double) image->page.height);
cristyd15e6592011-10-15 00:13:06 +00003581 (void) SetImageProperty(image,"PNG:vpAg ",msg,exception);
glennrp07523c72011-03-31 18:12:10 +00003582 }
glennrpcb395ac2011-03-30 19:50:23 +00003583 }
3584
cristy3ed852e2009-09-05 21:47:34 +00003585 /*
3586 Relinquish resources.
3587 */
3588 png_destroy_read_struct(&ping,&ping_info,&end_info);
3589
glennrpcf002022011-01-30 02:38:15 +00003590 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003591#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003592 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003593#endif
3594
3595 if (logging != MagickFalse)
3596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3597 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003598
cristy3ed852e2009-09-05 21:47:34 +00003599 return(image);
3600
3601/* end of reading one PNG image */
3602}
3603
3604static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3605{
3606 Image
3607 *image,
3608 *previous;
3609
3610 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003611 have_mng_structure,
3612 logging,
cristy3ed852e2009-09-05 21:47:34 +00003613 status;
3614
3615 MngInfo
3616 *mng_info;
3617
3618 char
3619 magic_number[MaxTextExtent];
3620
cristy3ed852e2009-09-05 21:47:34 +00003621 ssize_t
3622 count;
3623
3624 /*
3625 Open image file.
3626 */
3627 assert(image_info != (const ImageInfo *) NULL);
3628 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003629
cristy3ed852e2009-09-05 21:47:34 +00003630 if (image_info->debug != MagickFalse)
3631 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3632 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003633
cristy3ed852e2009-09-05 21:47:34 +00003634 assert(exception != (ExceptionInfo *) NULL);
3635 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003636 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy9950d572011-10-01 18:22:35 +00003637 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003638 mng_info=(MngInfo *) NULL;
3639 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003640
cristy3ed852e2009-09-05 21:47:34 +00003641 if (status == MagickFalse)
3642 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003643
cristy3ed852e2009-09-05 21:47:34 +00003644 /*
3645 Verify PNG signature.
3646 */
3647 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003648
glennrpdde35db2011-02-21 12:06:32 +00003649 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003650 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003651
cristy3ed852e2009-09-05 21:47:34 +00003652 /*
3653 Allocate a MngInfo structure.
3654 */
3655 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003656 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003657
cristy3ed852e2009-09-05 21:47:34 +00003658 if (mng_info == (MngInfo *) NULL)
3659 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003660
cristy3ed852e2009-09-05 21:47:34 +00003661 /*
3662 Initialize members of the MngInfo structure.
3663 */
3664 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3665 mng_info->image=image;
3666 have_mng_structure=MagickTrue;
3667
3668 previous=image;
3669 image=ReadOnePNGImage(mng_info,image_info,exception);
3670 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003671
cristy3ed852e2009-09-05 21:47:34 +00003672 if (image == (Image *) NULL)
3673 {
3674 if (previous != (Image *) NULL)
3675 {
3676 if (previous->signature != MagickSignature)
3677 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003678
cristy3ed852e2009-09-05 21:47:34 +00003679 (void) CloseBlob(previous);
3680 (void) DestroyImageList(previous);
3681 }
glennrp0fe50b42010-11-16 03:52:51 +00003682
cristy3ed852e2009-09-05 21:47:34 +00003683 if (logging != MagickFalse)
3684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3685 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003686
cristy3ed852e2009-09-05 21:47:34 +00003687 return((Image *) NULL);
3688 }
glennrp47b9dd52010-11-24 18:12:06 +00003689
cristy3ed852e2009-09-05 21:47:34 +00003690 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003691
cristy3ed852e2009-09-05 21:47:34 +00003692 if ((image->columns == 0) || (image->rows == 0))
3693 {
3694 if (logging != MagickFalse)
3695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3696 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003697
cristy3ed852e2009-09-05 21:47:34 +00003698 ThrowReaderException(CorruptImageError,"CorruptImage");
3699 }
glennrp47b9dd52010-11-24 18:12:06 +00003700
cristy3ed852e2009-09-05 21:47:34 +00003701 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3702 {
cristy018f07f2011-09-04 21:15:19 +00003703 (void) SetImageType(image,TrueColorType,exception);
cristy3ed852e2009-09-05 21:47:34 +00003704 image->matte=MagickFalse;
3705 }
glennrp0fe50b42010-11-16 03:52:51 +00003706
cristy3ed852e2009-09-05 21:47:34 +00003707 if (LocaleCompare(image_info->magick,"PNG32") == 0)
cristy018f07f2011-09-04 21:15:19 +00003708 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003709
cristy3ed852e2009-09-05 21:47:34 +00003710 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3712 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3713 (double) image->page.width,(double) image->page.height,
3714 (double) image->page.x,(double) image->page.y);
3715
3716 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003718
cristy3ed852e2009-09-05 21:47:34 +00003719 return(image);
3720}
3721
3722
3723
3724#if defined(JNG_SUPPORTED)
3725/*
3726%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3727% %
3728% %
3729% %
3730% R e a d O n e J N G I m a g e %
3731% %
3732% %
3733% %
3734%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3735%
3736% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3737% (minus the 8-byte signature) and returns it. It allocates the memory
3738% necessary for the new Image structure and returns a pointer to the new
3739% image.
3740%
3741% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3742%
3743% The format of the ReadOneJNGImage method is:
3744%
3745% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3746% ExceptionInfo *exception)
3747%
3748% A description of each parameter follows:
3749%
3750% o mng_info: Specifies a pointer to a MngInfo structure.
3751%
3752% o image_info: the image info.
3753%
3754% o exception: return any errors or warnings in this structure.
3755%
3756*/
3757static Image *ReadOneJNGImage(MngInfo *mng_info,
3758 const ImageInfo *image_info, ExceptionInfo *exception)
3759{
3760 Image
3761 *alpha_image,
3762 *color_image,
3763 *image,
3764 *jng_image;
3765
3766 ImageInfo
3767 *alpha_image_info,
3768 *color_image_info;
3769
cristy4383ec82011-01-05 15:42:32 +00003770 MagickBooleanType
3771 logging;
3772
cristybb503372010-05-27 20:51:26 +00003773 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003774 y;
3775
3776 MagickBooleanType
3777 status;
3778
3779 png_uint_32
3780 jng_height,
3781 jng_width;
3782
3783 png_byte
3784 jng_color_type,
3785 jng_image_sample_depth,
3786 jng_image_compression_method,
3787 jng_image_interlace_method,
3788 jng_alpha_sample_depth,
3789 jng_alpha_compression_method,
3790 jng_alpha_filter_method,
3791 jng_alpha_interlace_method;
3792
cristy4c08aed2011-07-01 19:47:50 +00003793 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003794 *s;
3795
cristybb503372010-05-27 20:51:26 +00003796 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003797 i,
3798 x;
3799
cristy4c08aed2011-07-01 19:47:50 +00003800 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003801 *q;
3802
3803 register unsigned char
3804 *p;
3805
3806 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003807 read_JSEP,
3808 reading_idat,
3809 skip_to_iend;
3810
cristybb503372010-05-27 20:51:26 +00003811 size_t
cristy3ed852e2009-09-05 21:47:34 +00003812 length;
3813
3814 jng_alpha_compression_method=0;
3815 jng_alpha_sample_depth=8;
3816 jng_color_type=0;
3817 jng_height=0;
3818 jng_width=0;
3819 alpha_image=(Image *) NULL;
3820 color_image=(Image *) NULL;
3821 alpha_image_info=(ImageInfo *) NULL;
3822 color_image_info=(ImageInfo *) NULL;
3823
3824 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003825 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003826
3827 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003828
cristy4c08aed2011-07-01 19:47:50 +00003829 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003830 {
3831 /*
3832 Allocate next image structure.
3833 */
3834 if (logging != MagickFalse)
3835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3836 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003837
cristy9950d572011-10-01 18:22:35 +00003838 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003839
cristy3ed852e2009-09-05 21:47:34 +00003840 if (GetNextImageInList(image) == (Image *) NULL)
3841 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003842
cristy3ed852e2009-09-05 21:47:34 +00003843 image=SyncNextImageInList(image);
3844 }
3845 mng_info->image=image;
3846
3847 /*
3848 Signature bytes have already been read.
3849 */
3850
3851 read_JSEP=MagickFalse;
3852 reading_idat=MagickFalse;
3853 skip_to_iend=MagickFalse;
3854 for (;;)
3855 {
3856 char
3857 type[MaxTextExtent];
3858
3859 unsigned char
3860 *chunk;
3861
3862 unsigned int
3863 count;
3864
3865 /*
3866 Read a new JNG chunk.
3867 */
3868 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3869 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003870
cristy3ed852e2009-09-05 21:47:34 +00003871 if (status == MagickFalse)
3872 break;
glennrp0fe50b42010-11-16 03:52:51 +00003873
cristy3ed852e2009-09-05 21:47:34 +00003874 type[0]='\0';
3875 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3876 length=ReadBlobMSBLong(image);
3877 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3878
3879 if (logging != MagickFalse)
3880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003881 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3882 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003883
3884 if (length > PNG_UINT_31_MAX || count == 0)
3885 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003886
cristy3ed852e2009-09-05 21:47:34 +00003887 p=NULL;
3888 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003889
cristy3ed852e2009-09-05 21:47:34 +00003890 if (length)
3891 {
3892 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003893
cristy3ed852e2009-09-05 21:47:34 +00003894 if (chunk == (unsigned char *) NULL)
3895 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003896
cristybb503372010-05-27 20:51:26 +00003897 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003898 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003899
cristy3ed852e2009-09-05 21:47:34 +00003900 p=chunk;
3901 }
glennrp47b9dd52010-11-24 18:12:06 +00003902
cristy3ed852e2009-09-05 21:47:34 +00003903 (void) ReadBlobMSBLong(image); /* read crc word */
3904
3905 if (skip_to_iend)
3906 {
3907 if (length)
3908 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003909
cristy3ed852e2009-09-05 21:47:34 +00003910 continue;
3911 }
3912
3913 if (memcmp(type,mng_JHDR,4) == 0)
3914 {
3915 if (length == 16)
3916 {
cristybb503372010-05-27 20:51:26 +00003917 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003918 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003919 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003920 (p[6] << 8) | p[7]);
3921 jng_color_type=p[8];
3922 jng_image_sample_depth=p[9];
3923 jng_image_compression_method=p[10];
3924 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003925
cristy3ed852e2009-09-05 21:47:34 +00003926 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3927 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003928
cristy3ed852e2009-09-05 21:47:34 +00003929 jng_alpha_sample_depth=p[12];
3930 jng_alpha_compression_method=p[13];
3931 jng_alpha_filter_method=p[14];
3932 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003933
cristy3ed852e2009-09-05 21:47:34 +00003934 if (logging != MagickFalse)
3935 {
3936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003937 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003938
cristy3ed852e2009-09-05 21:47:34 +00003939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003940 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003941
cristy3ed852e2009-09-05 21:47:34 +00003942 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3943 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003944
cristy3ed852e2009-09-05 21:47:34 +00003945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3946 " jng_image_sample_depth: %3d",
3947 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003948
cristy3ed852e2009-09-05 21:47:34 +00003949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3950 " jng_image_compression_method:%3d",
3951 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003952
cristy3ed852e2009-09-05 21:47:34 +00003953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3954 " jng_image_interlace_method: %3d",
3955 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003956
cristy3ed852e2009-09-05 21:47:34 +00003957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3958 " jng_alpha_sample_depth: %3d",
3959 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003960
cristy3ed852e2009-09-05 21:47:34 +00003961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3962 " jng_alpha_compression_method:%3d",
3963 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003964
cristy3ed852e2009-09-05 21:47:34 +00003965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3966 " jng_alpha_filter_method: %3d",
3967 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00003968
cristy3ed852e2009-09-05 21:47:34 +00003969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3970 " jng_alpha_interlace_method: %3d",
3971 jng_alpha_interlace_method);
3972 }
3973 }
glennrp47b9dd52010-11-24 18:12:06 +00003974
cristy3ed852e2009-09-05 21:47:34 +00003975 if (length)
3976 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003977
cristy3ed852e2009-09-05 21:47:34 +00003978 continue;
3979 }
3980
3981
3982 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3983 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3984 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3985 {
3986 /*
3987 o create color_image
3988 o open color_blob, attached to color_image
3989 o if (color type has alpha)
3990 open alpha_blob, attached to alpha_image
3991 */
3992
cristy73bd4a52010-10-05 11:24:23 +00003993 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003994
cristy3ed852e2009-09-05 21:47:34 +00003995 if (color_image_info == (ImageInfo *) NULL)
3996 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003997
cristy3ed852e2009-09-05 21:47:34 +00003998 GetImageInfo(color_image_info);
cristy9950d572011-10-01 18:22:35 +00003999 color_image=AcquireImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004000
cristy3ed852e2009-09-05 21:47:34 +00004001 if (color_image == (Image *) NULL)
4002 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4003
4004 if (logging != MagickFalse)
4005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4006 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004007
cristy3ed852e2009-09-05 21:47:34 +00004008 (void) AcquireUniqueFilename(color_image->filename);
4009 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4010 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004011
cristy3ed852e2009-09-05 21:47:34 +00004012 if (status == MagickFalse)
4013 return((Image *) NULL);
4014
4015 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4016 {
4017 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004018 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004019
cristy3ed852e2009-09-05 21:47:34 +00004020 if (alpha_image_info == (ImageInfo *) NULL)
4021 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004022
cristy3ed852e2009-09-05 21:47:34 +00004023 GetImageInfo(alpha_image_info);
cristy9950d572011-10-01 18:22:35 +00004024 alpha_image=AcquireImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004025
cristy3ed852e2009-09-05 21:47:34 +00004026 if (alpha_image == (Image *) NULL)
4027 {
4028 alpha_image=DestroyImage(alpha_image);
4029 ThrowReaderException(ResourceLimitError,
4030 "MemoryAllocationFailed");
4031 }
glennrp0fe50b42010-11-16 03:52:51 +00004032
cristy3ed852e2009-09-05 21:47:34 +00004033 if (logging != MagickFalse)
4034 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4035 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004036
cristy3ed852e2009-09-05 21:47:34 +00004037 (void) AcquireUniqueFilename(alpha_image->filename);
4038 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4039 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004040
cristy3ed852e2009-09-05 21:47:34 +00004041 if (status == MagickFalse)
4042 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004043
cristy3ed852e2009-09-05 21:47:34 +00004044 if (jng_alpha_compression_method == 0)
4045 {
4046 unsigned char
4047 data[18];
4048
4049 if (logging != MagickFalse)
4050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4051 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004052
cristy3ed852e2009-09-05 21:47:34 +00004053 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4054 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004055
cristy3ed852e2009-09-05 21:47:34 +00004056 (void) WriteBlobMSBULong(alpha_image,13L);
4057 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004058 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004059 PNGLong(data+4,jng_width);
4060 PNGLong(data+8,jng_height);
4061 data[12]=jng_alpha_sample_depth;
4062 data[13]=0; /* color_type gray */
4063 data[14]=0; /* compression method 0 */
4064 data[15]=0; /* filter_method 0 */
4065 data[16]=0; /* interlace_method 0 */
4066 (void) WriteBlob(alpha_image,17,data);
4067 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4068 }
4069 }
4070 reading_idat=MagickTrue;
4071 }
4072
4073 if (memcmp(type,mng_JDAT,4) == 0)
4074 {
glennrp47b9dd52010-11-24 18:12:06 +00004075 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004076
4077 if (logging != MagickFalse)
4078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4079 " Copying JDAT chunk data to color_blob.");
4080
4081 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004082
cristy3ed852e2009-09-05 21:47:34 +00004083 if (length)
4084 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004085
cristy3ed852e2009-09-05 21:47:34 +00004086 continue;
4087 }
4088
4089 if (memcmp(type,mng_IDAT,4) == 0)
4090 {
4091 png_byte
4092 data[5];
4093
glennrp47b9dd52010-11-24 18:12:06 +00004094 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004095
4096 if (image_info->ping == MagickFalse)
4097 {
4098 if (logging != MagickFalse)
4099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4100 " Copying IDAT chunk data to alpha_blob.");
4101
cristybb503372010-05-27 20:51:26 +00004102 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004103 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004104 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004105 (void) WriteBlob(alpha_image,4,data);
4106 (void) WriteBlob(alpha_image,length,chunk);
4107 (void) WriteBlobMSBULong(alpha_image,
4108 crc32(crc32(0,data,4),chunk,(uInt) length));
4109 }
glennrp0fe50b42010-11-16 03:52:51 +00004110
cristy3ed852e2009-09-05 21:47:34 +00004111 if (length)
4112 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004113
cristy3ed852e2009-09-05 21:47:34 +00004114 continue;
4115 }
4116
4117 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4118 {
glennrp47b9dd52010-11-24 18:12:06 +00004119 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004120
4121 if (image_info->ping == MagickFalse)
4122 {
4123 if (logging != MagickFalse)
4124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4125 " Copying JDAA chunk data to alpha_blob.");
4126
4127 (void) WriteBlob(alpha_image,length,chunk);
4128 }
glennrp0fe50b42010-11-16 03:52:51 +00004129
cristy3ed852e2009-09-05 21:47:34 +00004130 if (length)
4131 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004132
cristy3ed852e2009-09-05 21:47:34 +00004133 continue;
4134 }
4135
4136 if (memcmp(type,mng_JSEP,4) == 0)
4137 {
4138 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004139
cristy3ed852e2009-09-05 21:47:34 +00004140 if (length)
4141 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004142
cristy3ed852e2009-09-05 21:47:34 +00004143 continue;
4144 }
4145
4146 if (memcmp(type,mng_bKGD,4) == 0)
4147 {
4148 if (length == 2)
4149 {
4150 image->background_color.red=ScaleCharToQuantum(p[1]);
4151 image->background_color.green=image->background_color.red;
4152 image->background_color.blue=image->background_color.red;
4153 }
glennrp0fe50b42010-11-16 03:52:51 +00004154
cristy3ed852e2009-09-05 21:47:34 +00004155 if (length == 6)
4156 {
4157 image->background_color.red=ScaleCharToQuantum(p[1]);
4158 image->background_color.green=ScaleCharToQuantum(p[3]);
4159 image->background_color.blue=ScaleCharToQuantum(p[5]);
4160 }
glennrp0fe50b42010-11-16 03:52:51 +00004161
cristy3ed852e2009-09-05 21:47:34 +00004162 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4163 continue;
4164 }
4165
4166 if (memcmp(type,mng_gAMA,4) == 0)
4167 {
4168 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004169 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004170
cristy3ed852e2009-09-05 21:47:34 +00004171 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4172 continue;
4173 }
4174
4175 if (memcmp(type,mng_cHRM,4) == 0)
4176 {
4177 if (length == 32)
4178 {
cristy8182b072010-05-30 20:10:53 +00004179 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4180 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4181 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4182 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4183 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4184 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4185 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4186 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004187 }
glennrp47b9dd52010-11-24 18:12:06 +00004188
cristy3ed852e2009-09-05 21:47:34 +00004189 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4190 continue;
4191 }
4192
4193 if (memcmp(type,mng_sRGB,4) == 0)
4194 {
4195 if (length == 1)
4196 {
glennrpe610a072010-08-05 17:08:46 +00004197 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004198 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004199 image->gamma=0.45455f;
4200 image->chromaticity.red_primary.x=0.6400f;
4201 image->chromaticity.red_primary.y=0.3300f;
4202 image->chromaticity.green_primary.x=0.3000f;
4203 image->chromaticity.green_primary.y=0.6000f;
4204 image->chromaticity.blue_primary.x=0.1500f;
4205 image->chromaticity.blue_primary.y=0.0600f;
4206 image->chromaticity.white_point.x=0.3127f;
4207 image->chromaticity.white_point.y=0.3290f;
4208 }
glennrp47b9dd52010-11-24 18:12:06 +00004209
cristy3ed852e2009-09-05 21:47:34 +00004210 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4211 continue;
4212 }
4213
4214 if (memcmp(type,mng_oFFs,4) == 0)
4215 {
4216 if (length > 8)
4217 {
glennrp5eae7602011-02-22 15:21:32 +00004218 image->page.x=(ssize_t) mng_get_long(p);
4219 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004220
cristy3ed852e2009-09-05 21:47:34 +00004221 if ((int) p[8] != 0)
4222 {
4223 image->page.x/=10000;
4224 image->page.y/=10000;
4225 }
4226 }
glennrp47b9dd52010-11-24 18:12:06 +00004227
cristy3ed852e2009-09-05 21:47:34 +00004228 if (length)
4229 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004230
cristy3ed852e2009-09-05 21:47:34 +00004231 continue;
4232 }
4233
4234 if (memcmp(type,mng_pHYs,4) == 0)
4235 {
4236 if (length > 8)
4237 {
cristy8182b072010-05-30 20:10:53 +00004238 image->x_resolution=(double) mng_get_long(p);
4239 image->y_resolution=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004240 if ((int) p[8] == PNG_RESOLUTION_METER)
4241 {
4242 image->units=PixelsPerCentimeterResolution;
4243 image->x_resolution=image->x_resolution/100.0f;
4244 image->y_resolution=image->y_resolution/100.0f;
4245 }
4246 }
glennrp0fe50b42010-11-16 03:52:51 +00004247
cristy3ed852e2009-09-05 21:47:34 +00004248 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4249 continue;
4250 }
4251
4252#if 0
4253 if (memcmp(type,mng_iCCP,4) == 0)
4254 {
glennrpfd05d622011-02-25 04:10:33 +00004255 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004256 if (length)
4257 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004258
cristy3ed852e2009-09-05 21:47:34 +00004259 continue;
4260 }
4261#endif
4262
4263 if (length)
4264 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4265
4266 if (memcmp(type,mng_IEND,4))
4267 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004268
cristy3ed852e2009-09-05 21:47:34 +00004269 break;
4270 }
4271
4272
4273 /* IEND found */
4274
4275 /*
4276 Finish up reading image data:
4277
4278 o read main image from color_blob.
4279
4280 o close color_blob.
4281
4282 o if (color_type has alpha)
4283 if alpha_encoding is PNG
4284 read secondary image from alpha_blob via ReadPNG
4285 if alpha_encoding is JPEG
4286 read secondary image from alpha_blob via ReadJPEG
4287
4288 o close alpha_blob.
4289
4290 o copy intensity of secondary image into
cristy4c08aed2011-07-01 19:47:50 +00004291 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004292
4293 o destroy the secondary image.
4294 */
4295
4296 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004297
cristy3ed852e2009-09-05 21:47:34 +00004298 if (logging != MagickFalse)
4299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4300 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004301
cristy3b6fd2e2011-05-20 12:53:50 +00004302 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004303 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004304
cristy3ed852e2009-09-05 21:47:34 +00004305 color_image_info->ping=MagickFalse; /* To do: avoid this */
4306 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004307
cristy3ed852e2009-09-05 21:47:34 +00004308 if (jng_image == (Image *) NULL)
4309 return((Image *) NULL);
4310
4311 (void) RelinquishUniqueFileResource(color_image->filename);
4312 color_image=DestroyImage(color_image);
4313 color_image_info=DestroyImageInfo(color_image_info);
4314
4315 if (jng_image == (Image *) NULL)
4316 return((Image *) NULL);
4317
4318 if (logging != MagickFalse)
4319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4320 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004321
cristy3ed852e2009-09-05 21:47:34 +00004322 image->rows=jng_height;
4323 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004324
cristybb503372010-05-27 20:51:26 +00004325 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004326 {
4327 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
4328 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004329 for (x=(ssize_t) image->columns; x != 0; x--)
4330 {
4331 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4332 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4333 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004334 q+=GetPixelChannels(image);
4335 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004336 }
glennrp47b9dd52010-11-24 18:12:06 +00004337
cristy3ed852e2009-09-05 21:47:34 +00004338 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4339 break;
4340 }
glennrp0fe50b42010-11-16 03:52:51 +00004341
cristy3ed852e2009-09-05 21:47:34 +00004342 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004343
cristy3ed852e2009-09-05 21:47:34 +00004344 if (image_info->ping == MagickFalse)
4345 {
4346 if (jng_color_type >= 12)
4347 {
4348 if (jng_alpha_compression_method == 0)
4349 {
4350 png_byte
4351 data[5];
4352 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4353 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004354 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004355 (void) WriteBlob(alpha_image,4,data);
4356 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4357 }
glennrp0fe50b42010-11-16 03:52:51 +00004358
cristy3ed852e2009-09-05 21:47:34 +00004359 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004360
cristy3ed852e2009-09-05 21:47:34 +00004361 if (logging != MagickFalse)
4362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00004363 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004364
cristy3b6fd2e2011-05-20 12:53:50 +00004365 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004366 "%s",alpha_image->filename);
4367
4368 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004369
cristy3ed852e2009-09-05 21:47:34 +00004370 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004371 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004372 {
4373 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristy4c08aed2011-07-01 19:47:50 +00004374 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +00004375 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004376
cristy3ed852e2009-09-05 21:47:34 +00004377 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00004378 for (x=(ssize_t) image->columns; x != 0; x--)
4379 {
4380 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004381 q+=GetPixelChannels(image);
4382 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004383 }
glennrp0fe50b42010-11-16 03:52:51 +00004384
cristy3ed852e2009-09-05 21:47:34 +00004385 else
cristy4c08aed2011-07-01 19:47:50 +00004386 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004387 {
cristy4c08aed2011-07-01 19:47:50 +00004388 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4389 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004390 image->matte=MagickTrue;
cristyed231572011-07-14 02:18:59 +00004391 q+=GetPixelChannels(image);
4392 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004393 }
glennrp0fe50b42010-11-16 03:52:51 +00004394
cristy3ed852e2009-09-05 21:47:34 +00004395 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4396 break;
4397 }
4398 (void) RelinquishUniqueFileResource(alpha_image->filename);
4399 alpha_image=DestroyImage(alpha_image);
4400 alpha_image_info=DestroyImageInfo(alpha_image_info);
4401 if (jng_image != (Image *) NULL)
4402 jng_image=DestroyImage(jng_image);
4403 }
4404 }
4405
glennrp47b9dd52010-11-24 18:12:06 +00004406 /* Read the JNG image. */
4407
cristy3ed852e2009-09-05 21:47:34 +00004408 if (mng_info->mng_type == 0)
4409 {
4410 mng_info->mng_width=jng_width;
4411 mng_info->mng_height=jng_height;
4412 }
glennrp0fe50b42010-11-16 03:52:51 +00004413
cristy3ed852e2009-09-05 21:47:34 +00004414 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004415 {
4416 image->page.width=jng_width;
4417 image->page.height=jng_height;
4418 }
4419
cristy3ed852e2009-09-05 21:47:34 +00004420 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004421 {
4422 image->page.x=mng_info->x_off[mng_info->object_id];
4423 image->page.y=mng_info->y_off[mng_info->object_id];
4424 }
4425
cristy3ed852e2009-09-05 21:47:34 +00004426 else
glennrp0fe50b42010-11-16 03:52:51 +00004427 {
4428 image->page.y=mng_info->y_off[mng_info->object_id];
4429 }
4430
cristy3ed852e2009-09-05 21:47:34 +00004431 mng_info->image_found++;
4432 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4433 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004434
cristy3ed852e2009-09-05 21:47:34 +00004435 if (logging != MagickFalse)
4436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4437 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004438
cristy3ed852e2009-09-05 21:47:34 +00004439 return(image);
4440}
4441
4442/*
4443%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4444% %
4445% %
4446% %
4447% R e a d J N G I m a g e %
4448% %
4449% %
4450% %
4451%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4452%
4453% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4454% (including the 8-byte signature) and returns it. It allocates the memory
4455% necessary for the new Image structure and returns a pointer to the new
4456% image.
4457%
4458% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4459%
4460% The format of the ReadJNGImage method is:
4461%
4462% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4463% *exception)
4464%
4465% A description of each parameter follows:
4466%
4467% o image_info: the image info.
4468%
4469% o exception: return any errors or warnings in this structure.
4470%
4471*/
4472
4473static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4474{
4475 Image
4476 *image,
4477 *previous;
4478
4479 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004480 have_mng_structure,
4481 logging,
cristy3ed852e2009-09-05 21:47:34 +00004482 status;
4483
4484 MngInfo
4485 *mng_info;
4486
4487 char
4488 magic_number[MaxTextExtent];
4489
cristy3ed852e2009-09-05 21:47:34 +00004490 size_t
4491 count;
4492
4493 /*
4494 Open image file.
4495 */
4496 assert(image_info != (const ImageInfo *) NULL);
4497 assert(image_info->signature == MagickSignature);
4498 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4499 assert(exception != (ExceptionInfo *) NULL);
4500 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004501 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy9950d572011-10-01 18:22:35 +00004502 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004503 mng_info=(MngInfo *) NULL;
4504 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004505
cristy3ed852e2009-09-05 21:47:34 +00004506 if (status == MagickFalse)
4507 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004508
cristy3ed852e2009-09-05 21:47:34 +00004509 if (LocaleCompare(image_info->magick,"JNG") != 0)
4510 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004511
glennrp47b9dd52010-11-24 18:12:06 +00004512 /* Verify JNG signature. */
4513
cristy3ed852e2009-09-05 21:47:34 +00004514 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004515
glennrp3b8763e2011-02-21 12:08:18 +00004516 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004517 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004518
glennrp47b9dd52010-11-24 18:12:06 +00004519 /* Allocate a MngInfo structure. */
4520
cristy3ed852e2009-09-05 21:47:34 +00004521 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004522 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004523
cristy3ed852e2009-09-05 21:47:34 +00004524 if (mng_info == (MngInfo *) NULL)
4525 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004526
glennrp47b9dd52010-11-24 18:12:06 +00004527 /* Initialize members of the MngInfo structure. */
4528
cristy3ed852e2009-09-05 21:47:34 +00004529 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4530 have_mng_structure=MagickTrue;
4531
4532 mng_info->image=image;
4533 previous=image;
4534 image=ReadOneJNGImage(mng_info,image_info,exception);
4535 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004536
cristy3ed852e2009-09-05 21:47:34 +00004537 if (image == (Image *) NULL)
4538 {
4539 if (IsImageObject(previous) != MagickFalse)
4540 {
4541 (void) CloseBlob(previous);
4542 (void) DestroyImageList(previous);
4543 }
glennrp0fe50b42010-11-16 03:52:51 +00004544
cristy3ed852e2009-09-05 21:47:34 +00004545 if (logging != MagickFalse)
4546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4547 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004548
cristy3ed852e2009-09-05 21:47:34 +00004549 return((Image *) NULL);
4550 }
4551 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004552
cristy3ed852e2009-09-05 21:47:34 +00004553 if (image->columns == 0 || image->rows == 0)
4554 {
4555 if (logging != MagickFalse)
4556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4557 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004558
cristy3ed852e2009-09-05 21:47:34 +00004559 ThrowReaderException(CorruptImageError,"CorruptImage");
4560 }
glennrp0fe50b42010-11-16 03:52:51 +00004561
cristy3ed852e2009-09-05 21:47:34 +00004562 if (logging != MagickFalse)
4563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004564
cristy3ed852e2009-09-05 21:47:34 +00004565 return(image);
4566}
4567#endif
4568
4569static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4570{
4571 char
4572 page_geometry[MaxTextExtent];
4573
4574 Image
4575 *image,
4576 *previous;
4577
cristy4383ec82011-01-05 15:42:32 +00004578 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004579 logging,
4580 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004581
cristy3ed852e2009-09-05 21:47:34 +00004582 volatile int
4583 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004584 object_id,
4585 term_chunk_found,
4586 skip_to_iend;
4587
cristybb503372010-05-27 20:51:26 +00004588 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004589 image_count=0;
4590
4591 MagickBooleanType
4592 status;
4593
4594 MagickOffsetType
4595 offset;
4596
4597 MngInfo
4598 *mng_info;
4599
4600 MngBox
4601 default_fb,
4602 fb,
4603 previous_fb;
4604
4605#if defined(MNG_INSERT_LAYERS)
cristy101ab702011-10-13 13:06:32 +00004606 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004607 mng_background_color;
4608#endif
4609
4610 register unsigned char
4611 *p;
4612
cristybb503372010-05-27 20:51:26 +00004613 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004614 i;
4615
4616 size_t
4617 count;
4618
cristybb503372010-05-27 20:51:26 +00004619 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004620 loop_level;
4621
4622 volatile short
4623 skipping_loop;
4624
4625#if defined(MNG_INSERT_LAYERS)
4626 unsigned int
4627 mandatory_back=0;
4628#endif
4629
4630 volatile unsigned int
4631#ifdef MNG_OBJECT_BUFFERS
4632 mng_background_object=0,
4633#endif
4634 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4635
cristybb503372010-05-27 20:51:26 +00004636 size_t
cristy3ed852e2009-09-05 21:47:34 +00004637 default_frame_timeout,
4638 frame_timeout,
4639#if defined(MNG_INSERT_LAYERS)
4640 image_height,
4641 image_width,
4642#endif
4643 length;
4644
glennrp38ea0832010-06-02 18:50:28 +00004645 /* These delays are all measured in image ticks_per_second,
4646 * not in MNG ticks_per_second
4647 */
cristybb503372010-05-27 20:51:26 +00004648 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004649 default_frame_delay,
4650 final_delay,
4651 final_image_delay,
4652 frame_delay,
4653#if defined(MNG_INSERT_LAYERS)
4654 insert_layers,
4655#endif
4656 mng_iterations=1,
4657 simplicity=0,
4658 subframe_height=0,
4659 subframe_width=0;
4660
4661 previous_fb.top=0;
4662 previous_fb.bottom=0;
4663 previous_fb.left=0;
4664 previous_fb.right=0;
4665 default_fb.top=0;
4666 default_fb.bottom=0;
4667 default_fb.left=0;
4668 default_fb.right=0;
4669
glennrp47b9dd52010-11-24 18:12:06 +00004670 /* Open image file. */
4671
cristy3ed852e2009-09-05 21:47:34 +00004672 assert(image_info != (const ImageInfo *) NULL);
4673 assert(image_info->signature == MagickSignature);
4674 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4675 assert(exception != (ExceptionInfo *) NULL);
4676 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004677 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy9950d572011-10-01 18:22:35 +00004678 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004679 mng_info=(MngInfo *) NULL;
4680 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004681
cristy3ed852e2009-09-05 21:47:34 +00004682 if (status == MagickFalse)
4683 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004684
cristy3ed852e2009-09-05 21:47:34 +00004685 first_mng_object=MagickFalse;
4686 skipping_loop=(-1);
4687 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004688
4689 /* Allocate a MngInfo structure. */
4690
cristy73bd4a52010-10-05 11:24:23 +00004691 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004692
cristy3ed852e2009-09-05 21:47:34 +00004693 if (mng_info == (MngInfo *) NULL)
4694 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004695
glennrp47b9dd52010-11-24 18:12:06 +00004696 /* Initialize members of the MngInfo structure. */
4697
cristy3ed852e2009-09-05 21:47:34 +00004698 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4699 mng_info->image=image;
4700 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004701
4702 if (LocaleCompare(image_info->magick,"MNG") == 0)
4703 {
4704 char
4705 magic_number[MaxTextExtent];
4706
glennrp47b9dd52010-11-24 18:12:06 +00004707 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004708 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4709 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4710 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004711
4712 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004713 for (i=0; i < MNG_MAX_OBJECTS; i++)
4714 {
cristybb503372010-05-27 20:51:26 +00004715 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4716 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004717 }
4718 mng_info->exists[0]=MagickTrue;
4719 }
glennrp47b9dd52010-11-24 18:12:06 +00004720
cristy3ed852e2009-09-05 21:47:34 +00004721 first_mng_object=MagickTrue;
4722 mng_type=0;
4723#if defined(MNG_INSERT_LAYERS)
4724 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4725#endif
4726 default_frame_delay=0;
4727 default_frame_timeout=0;
4728 frame_delay=0;
4729 final_delay=1;
4730 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4731 object_id=0;
4732 skip_to_iend=MagickFalse;
4733 term_chunk_found=MagickFalse;
4734 mng_info->framing_mode=1;
4735#if defined(MNG_INSERT_LAYERS)
4736 mandatory_back=MagickFalse;
4737#endif
4738#if defined(MNG_INSERT_LAYERS)
4739 mng_background_color=image->background_color;
4740#endif
4741 default_fb=mng_info->frame;
4742 previous_fb=mng_info->frame;
4743 do
4744 {
4745 char
4746 type[MaxTextExtent];
4747
4748 if (LocaleCompare(image_info->magick,"MNG") == 0)
4749 {
4750 unsigned char
4751 *chunk;
4752
4753 /*
4754 Read a new chunk.
4755 */
4756 type[0]='\0';
4757 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4758 length=ReadBlobMSBLong(image);
4759 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4760
4761 if (logging != MagickFalse)
4762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004763 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4764 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004765
4766 if (length > PNG_UINT_31_MAX)
4767 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004768
cristy3ed852e2009-09-05 21:47:34 +00004769 if (count == 0)
4770 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004771
cristy3ed852e2009-09-05 21:47:34 +00004772 p=NULL;
4773 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004774
cristy3ed852e2009-09-05 21:47:34 +00004775 if (length)
4776 {
4777 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004778
cristy3ed852e2009-09-05 21:47:34 +00004779 if (chunk == (unsigned char *) NULL)
4780 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004781
cristybb503372010-05-27 20:51:26 +00004782 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004783 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004784
cristy3ed852e2009-09-05 21:47:34 +00004785 p=chunk;
4786 }
glennrp0fe50b42010-11-16 03:52:51 +00004787
cristy3ed852e2009-09-05 21:47:34 +00004788 (void) ReadBlobMSBLong(image); /* read crc word */
4789
4790#if !defined(JNG_SUPPORTED)
4791 if (memcmp(type,mng_JHDR,4) == 0)
4792 {
4793 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004794
cristy3ed852e2009-09-05 21:47:34 +00004795 if (mng_info->jhdr_warning == 0)
4796 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4797 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004798
cristy3ed852e2009-09-05 21:47:34 +00004799 mng_info->jhdr_warning++;
4800 }
4801#endif
4802 if (memcmp(type,mng_DHDR,4) == 0)
4803 {
4804 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004805
cristy3ed852e2009-09-05 21:47:34 +00004806 if (mng_info->dhdr_warning == 0)
4807 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4808 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004809
cristy3ed852e2009-09-05 21:47:34 +00004810 mng_info->dhdr_warning++;
4811 }
4812 if (memcmp(type,mng_MEND,4) == 0)
4813 break;
glennrp47b9dd52010-11-24 18:12:06 +00004814
cristy3ed852e2009-09-05 21:47:34 +00004815 if (skip_to_iend)
4816 {
4817 if (memcmp(type,mng_IEND,4) == 0)
4818 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004819
cristy3ed852e2009-09-05 21:47:34 +00004820 if (length)
4821 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004822
cristy3ed852e2009-09-05 21:47:34 +00004823 if (logging != MagickFalse)
4824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4825 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004826
cristy3ed852e2009-09-05 21:47:34 +00004827 continue;
4828 }
glennrp0fe50b42010-11-16 03:52:51 +00004829
cristy3ed852e2009-09-05 21:47:34 +00004830 if (memcmp(type,mng_MHDR,4) == 0)
4831 {
cristybb503372010-05-27 20:51:26 +00004832 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004833 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004834
cristybb503372010-05-27 20:51:26 +00004835 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004836 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004837
cristy3ed852e2009-09-05 21:47:34 +00004838 if (logging != MagickFalse)
4839 {
4840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004841 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004843 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004844 }
glennrp0fe50b42010-11-16 03:52:51 +00004845
cristy3ed852e2009-09-05 21:47:34 +00004846 p+=8;
cristy8182b072010-05-30 20:10:53 +00004847 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004848
cristy3ed852e2009-09-05 21:47:34 +00004849 if (mng_info->ticks_per_second == 0)
4850 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004851
cristy3ed852e2009-09-05 21:47:34 +00004852 else
4853 default_frame_delay=1UL*image->ticks_per_second/
4854 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004855
cristy3ed852e2009-09-05 21:47:34 +00004856 frame_delay=default_frame_delay;
4857 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004858
cristy3ed852e2009-09-05 21:47:34 +00004859 if (length > 16)
4860 {
4861 p+=16;
cristy8182b072010-05-30 20:10:53 +00004862 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004863 }
glennrp0fe50b42010-11-16 03:52:51 +00004864
cristy3ed852e2009-09-05 21:47:34 +00004865 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004866
cristy3ed852e2009-09-05 21:47:34 +00004867 if ((simplicity != 0) && ((simplicity | 11) == 11))
4868 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004869
cristy3ed852e2009-09-05 21:47:34 +00004870 if ((simplicity != 0) && ((simplicity | 9) == 9))
4871 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004872
cristy3ed852e2009-09-05 21:47:34 +00004873#if defined(MNG_INSERT_LAYERS)
4874 if (mng_type != 3)
4875 insert_layers=MagickTrue;
4876#endif
cristy4c08aed2011-07-01 19:47:50 +00004877 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004878 {
glennrp47b9dd52010-11-24 18:12:06 +00004879 /* Allocate next image structure. */
cristy9950d572011-10-01 18:22:35 +00004880 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004881
cristy3ed852e2009-09-05 21:47:34 +00004882 if (GetNextImageInList(image) == (Image *) NULL)
4883 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004884
cristy3ed852e2009-09-05 21:47:34 +00004885 image=SyncNextImageInList(image);
4886 mng_info->image=image;
4887 }
4888
4889 if ((mng_info->mng_width > 65535L) ||
4890 (mng_info->mng_height > 65535L))
4891 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004892
cristy3b6fd2e2011-05-20 12:53:50 +00004893 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004894 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004895 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004896
cristy3ed852e2009-09-05 21:47:34 +00004897 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004898 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004899 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004900 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004901 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004902
cristy3ed852e2009-09-05 21:47:34 +00004903 for (i=0; i < MNG_MAX_OBJECTS; i++)
4904 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004905
cristy3ed852e2009-09-05 21:47:34 +00004906 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4907 continue;
4908 }
4909
4910 if (memcmp(type,mng_TERM,4) == 0)
4911 {
4912 int
4913 repeat=0;
4914
4915
4916 if (length)
4917 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004918
cristy3ed852e2009-09-05 21:47:34 +00004919 if (repeat == 3)
4920 {
cristy8182b072010-05-30 20:10:53 +00004921 final_delay=(png_uint_32) mng_get_long(&p[2]);
4922 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004923
cristy3ed852e2009-09-05 21:47:34 +00004924 if (mng_iterations == PNG_UINT_31_MAX)
4925 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004926
cristy3ed852e2009-09-05 21:47:34 +00004927 image->iterations=mng_iterations;
4928 term_chunk_found=MagickTrue;
4929 }
glennrp0fe50b42010-11-16 03:52:51 +00004930
cristy3ed852e2009-09-05 21:47:34 +00004931 if (logging != MagickFalse)
4932 {
4933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4934 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004935
cristy3ed852e2009-09-05 21:47:34 +00004936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004937 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004938
cristy3ed852e2009-09-05 21:47:34 +00004939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004940 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004941 }
glennrp0fe50b42010-11-16 03:52:51 +00004942
cristy3ed852e2009-09-05 21:47:34 +00004943 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4944 continue;
4945 }
4946 if (memcmp(type,mng_DEFI,4) == 0)
4947 {
4948 if (mng_type == 3)
4949 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4950 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4951 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004952
cristy3ed852e2009-09-05 21:47:34 +00004953 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004954
cristy3ed852e2009-09-05 21:47:34 +00004955 if (mng_type == 2 && object_id != 0)
4956 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4957 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4958 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004959
cristy3ed852e2009-09-05 21:47:34 +00004960 if (object_id > MNG_MAX_OBJECTS)
4961 {
4962 /*
4963 Instead ofsuing a warning we should allocate a larger
4964 MngInfo structure and continue.
4965 */
4966 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4967 CoderError,"object id too large","`%s'",image->filename);
4968 object_id=MNG_MAX_OBJECTS;
4969 }
glennrp0fe50b42010-11-16 03:52:51 +00004970
cristy3ed852e2009-09-05 21:47:34 +00004971 if (mng_info->exists[object_id])
4972 if (mng_info->frozen[object_id])
4973 {
4974 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4975 (void) ThrowMagickException(&image->exception,
4976 GetMagickModule(),CoderError,
4977 "DEFI cannot redefine a frozen MNG object","`%s'",
4978 image->filename);
4979 continue;
4980 }
glennrp0fe50b42010-11-16 03:52:51 +00004981
cristy3ed852e2009-09-05 21:47:34 +00004982 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004983
cristy3ed852e2009-09-05 21:47:34 +00004984 if (length > 2)
4985 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00004986
cristy3ed852e2009-09-05 21:47:34 +00004987 /*
4988 Extract object offset info.
4989 */
4990 if (length > 11)
4991 {
glennrp0fe50b42010-11-16 03:52:51 +00004992 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4993 (p[5] << 16) | (p[6] << 8) | p[7]);
4994
4995 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4996 (p[9] << 16) | (p[10] << 8) | p[11]);
4997
cristy3ed852e2009-09-05 21:47:34 +00004998 if (logging != MagickFalse)
4999 {
5000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005001 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005002 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005003
cristy3ed852e2009-09-05 21:47:34 +00005004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005005 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005006 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005007 }
5008 }
glennrp0fe50b42010-11-16 03:52:51 +00005009
cristy3ed852e2009-09-05 21:47:34 +00005010 /*
5011 Extract object clipping info.
5012 */
5013 if (length > 27)
5014 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5015 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005016
cristy3ed852e2009-09-05 21:47:34 +00005017 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5018 continue;
5019 }
5020 if (memcmp(type,mng_bKGD,4) == 0)
5021 {
5022 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005023
cristy3ed852e2009-09-05 21:47:34 +00005024 if (length > 5)
5025 {
5026 mng_info->mng_global_bkgd.red=
5027 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005028
cristy3ed852e2009-09-05 21:47:34 +00005029 mng_info->mng_global_bkgd.green=
5030 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005031
cristy3ed852e2009-09-05 21:47:34 +00005032 mng_info->mng_global_bkgd.blue=
5033 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005034
cristy3ed852e2009-09-05 21:47:34 +00005035 mng_info->have_global_bkgd=MagickTrue;
5036 }
glennrp0fe50b42010-11-16 03:52:51 +00005037
cristy3ed852e2009-09-05 21:47:34 +00005038 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5039 continue;
5040 }
5041 if (memcmp(type,mng_BACK,4) == 0)
5042 {
5043#if defined(MNG_INSERT_LAYERS)
5044 if (length > 6)
5045 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005046
cristy3ed852e2009-09-05 21:47:34 +00005047 else
5048 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005049
cristy3ed852e2009-09-05 21:47:34 +00005050 if (mandatory_back && length > 5)
5051 {
5052 mng_background_color.red=
5053 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005054
cristy3ed852e2009-09-05 21:47:34 +00005055 mng_background_color.green=
5056 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005057
cristy3ed852e2009-09-05 21:47:34 +00005058 mng_background_color.blue=
5059 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005060
cristy4c08aed2011-07-01 19:47:50 +00005061 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005062 }
glennrp0fe50b42010-11-16 03:52:51 +00005063
cristy3ed852e2009-09-05 21:47:34 +00005064#ifdef MNG_OBJECT_BUFFERS
5065 if (length > 8)
5066 mng_background_object=(p[7] << 8) | p[8];
5067#endif
5068#endif
5069 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5070 continue;
5071 }
glennrp47b9dd52010-11-24 18:12:06 +00005072
cristy3ed852e2009-09-05 21:47:34 +00005073 if (memcmp(type,mng_PLTE,4) == 0)
5074 {
glennrp47b9dd52010-11-24 18:12:06 +00005075 /* Read global PLTE. */
5076
cristy3ed852e2009-09-05 21:47:34 +00005077 if (length && (length < 769))
5078 {
5079 if (mng_info->global_plte == (png_colorp) NULL)
5080 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5081 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005082
cristybb503372010-05-27 20:51:26 +00005083 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005084 {
5085 mng_info->global_plte[i].red=p[3*i];
5086 mng_info->global_plte[i].green=p[3*i+1];
5087 mng_info->global_plte[i].blue=p[3*i+2];
5088 }
glennrp0fe50b42010-11-16 03:52:51 +00005089
cristy35ef8242010-06-03 16:24:13 +00005090 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005091 }
5092#ifdef MNG_LOOSE
5093 for ( ; i < 256; i++)
5094 {
5095 mng_info->global_plte[i].red=i;
5096 mng_info->global_plte[i].green=i;
5097 mng_info->global_plte[i].blue=i;
5098 }
glennrp0fe50b42010-11-16 03:52:51 +00005099
cristy3ed852e2009-09-05 21:47:34 +00005100 if (length)
5101 mng_info->global_plte_length=256;
5102#endif
5103 else
5104 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005105
cristy3ed852e2009-09-05 21:47:34 +00005106 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5107 continue;
5108 }
glennrp47b9dd52010-11-24 18:12:06 +00005109
cristy3ed852e2009-09-05 21:47:34 +00005110 if (memcmp(type,mng_tRNS,4) == 0)
5111 {
5112 /* read global tRNS */
5113
5114 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005115 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005116 mng_info->global_trns[i]=p[i];
5117
5118#ifdef MNG_LOOSE
5119 for ( ; i < 256; i++)
5120 mng_info->global_trns[i]=255;
5121#endif
cristy12560f32010-06-03 16:51:08 +00005122 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005123 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5124 continue;
5125 }
5126 if (memcmp(type,mng_gAMA,4) == 0)
5127 {
5128 if (length == 4)
5129 {
cristybb503372010-05-27 20:51:26 +00005130 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005131 igamma;
5132
cristy8182b072010-05-30 20:10:53 +00005133 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005134 mng_info->global_gamma=((float) igamma)*0.00001;
5135 mng_info->have_global_gama=MagickTrue;
5136 }
glennrp0fe50b42010-11-16 03:52:51 +00005137
cristy3ed852e2009-09-05 21:47:34 +00005138 else
5139 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005140
cristy3ed852e2009-09-05 21:47:34 +00005141 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5142 continue;
5143 }
5144
5145 if (memcmp(type,mng_cHRM,4) == 0)
5146 {
glennrp47b9dd52010-11-24 18:12:06 +00005147 /* Read global cHRM */
5148
cristy3ed852e2009-09-05 21:47:34 +00005149 if (length == 32)
5150 {
cristy8182b072010-05-30 20:10:53 +00005151 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5152 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5153 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005154 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005155 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005156 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005157 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005158 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005159 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005160 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005161 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005162 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005163 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005164 mng_info->have_global_chrm=MagickTrue;
5165 }
5166 else
5167 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005168
cristy3ed852e2009-09-05 21:47:34 +00005169 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5170 continue;
5171 }
glennrp47b9dd52010-11-24 18:12:06 +00005172
cristy3ed852e2009-09-05 21:47:34 +00005173 if (memcmp(type,mng_sRGB,4) == 0)
5174 {
5175 /*
5176 Read global sRGB.
5177 */
5178 if (length)
5179 {
glennrpe610a072010-08-05 17:08:46 +00005180 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005181 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005182 mng_info->have_global_srgb=MagickTrue;
5183 }
5184 else
5185 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005186
cristy3ed852e2009-09-05 21:47:34 +00005187 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5188 continue;
5189 }
glennrp47b9dd52010-11-24 18:12:06 +00005190
cristy3ed852e2009-09-05 21:47:34 +00005191 if (memcmp(type,mng_iCCP,4) == 0)
5192 {
glennrpfd05d622011-02-25 04:10:33 +00005193 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005194
5195 /*
5196 Read global iCCP.
5197 */
5198 if (length)
5199 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005200
cristy3ed852e2009-09-05 21:47:34 +00005201 continue;
5202 }
glennrp47b9dd52010-11-24 18:12:06 +00005203
cristy3ed852e2009-09-05 21:47:34 +00005204 if (memcmp(type,mng_FRAM,4) == 0)
5205 {
5206 if (mng_type == 3)
5207 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5208 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5209 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005210
cristy3ed852e2009-09-05 21:47:34 +00005211 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5212 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005213
cristy3ed852e2009-09-05 21:47:34 +00005214 frame_delay=default_frame_delay;
5215 frame_timeout=default_frame_timeout;
5216 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005217
cristy3ed852e2009-09-05 21:47:34 +00005218 if (length)
5219 if (p[0])
5220 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005221
cristy3ed852e2009-09-05 21:47:34 +00005222 if (logging != MagickFalse)
5223 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5224 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005225
cristy3ed852e2009-09-05 21:47:34 +00005226 if (length > 6)
5227 {
glennrp47b9dd52010-11-24 18:12:06 +00005228 /* Note the delay and frame clipping boundaries. */
5229
cristy3ed852e2009-09-05 21:47:34 +00005230 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005231
cristybb503372010-05-27 20:51:26 +00005232 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005233 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005234
cristy3ed852e2009-09-05 21:47:34 +00005235 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005236
cristybb503372010-05-27 20:51:26 +00005237 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005238 {
5239 int
5240 change_delay,
5241 change_timeout,
5242 change_clipping;
5243
5244 change_delay=(*p++);
5245 change_timeout=(*p++);
5246 change_clipping=(*p++);
5247 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005248
cristy3ed852e2009-09-05 21:47:34 +00005249 if (change_delay)
5250 {
cristy8182b072010-05-30 20:10:53 +00005251 frame_delay=1UL*image->ticks_per_second*
5252 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005253
cristy8182b072010-05-30 20:10:53 +00005254 if (mng_info->ticks_per_second != 0)
5255 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005256
glennrpbb010dd2010-06-01 13:07:15 +00005257 else
5258 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005259
cristy3ed852e2009-09-05 21:47:34 +00005260 if (change_delay == 2)
5261 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005262
cristy3ed852e2009-09-05 21:47:34 +00005263 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005264
cristy3ed852e2009-09-05 21:47:34 +00005265 if (logging != MagickFalse)
5266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005267 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005268 }
glennrp47b9dd52010-11-24 18:12:06 +00005269
cristy3ed852e2009-09-05 21:47:34 +00005270 if (change_timeout)
5271 {
glennrpbb010dd2010-06-01 13:07:15 +00005272 frame_timeout=1UL*image->ticks_per_second*
5273 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005274
glennrpbb010dd2010-06-01 13:07:15 +00005275 if (mng_info->ticks_per_second != 0)
5276 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005277
glennrpbb010dd2010-06-01 13:07:15 +00005278 else
5279 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005280
cristy3ed852e2009-09-05 21:47:34 +00005281 if (change_delay == 2)
5282 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005283
cristy3ed852e2009-09-05 21:47:34 +00005284 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005285
cristy3ed852e2009-09-05 21:47:34 +00005286 if (logging != MagickFalse)
5287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005288 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005289 }
glennrp47b9dd52010-11-24 18:12:06 +00005290
cristy3ed852e2009-09-05 21:47:34 +00005291 if (change_clipping)
5292 {
5293 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5294 p+=17;
5295 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005296
cristy3ed852e2009-09-05 21:47:34 +00005297 if (logging != MagickFalse)
5298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005299 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005300 (double) fb.left,(double) fb.right,(double) fb.top,
5301 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005302
cristy3ed852e2009-09-05 21:47:34 +00005303 if (change_clipping == 2)
5304 default_fb=fb;
5305 }
5306 }
5307 }
5308 mng_info->clip=fb;
5309 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005310
cristybb503372010-05-27 20:51:26 +00005311 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005312 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005313
cristybb503372010-05-27 20:51:26 +00005314 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005315 -mng_info->clip.top);
5316 /*
5317 Insert a background layer behind the frame if framing_mode is 4.
5318 */
5319#if defined(MNG_INSERT_LAYERS)
5320 if (logging != MagickFalse)
5321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005322 " subframe_width=%.20g, subframe_height=%.20g",(double)
5323 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005324
cristy3ed852e2009-09-05 21:47:34 +00005325 if (insert_layers && (mng_info->framing_mode == 4) &&
5326 (subframe_width) && (subframe_height))
5327 {
glennrp47b9dd52010-11-24 18:12:06 +00005328 /* Allocate next image structure. */
cristy4c08aed2011-07-01 19:47:50 +00005329 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005330 {
cristy9950d572011-10-01 18:22:35 +00005331 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005332
cristy3ed852e2009-09-05 21:47:34 +00005333 if (GetNextImageInList(image) == (Image *) NULL)
5334 {
5335 image=DestroyImageList(image);
5336 MngInfoFreeStruct(mng_info,&have_mng_structure);
5337 return((Image *) NULL);
5338 }
glennrp47b9dd52010-11-24 18:12:06 +00005339
cristy3ed852e2009-09-05 21:47:34 +00005340 image=SyncNextImageInList(image);
5341 }
glennrp0fe50b42010-11-16 03:52:51 +00005342
cristy3ed852e2009-09-05 21:47:34 +00005343 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005344
cristy3ed852e2009-09-05 21:47:34 +00005345 if (term_chunk_found)
5346 {
5347 image->start_loop=MagickTrue;
5348 image->iterations=mng_iterations;
5349 term_chunk_found=MagickFalse;
5350 }
glennrp0fe50b42010-11-16 03:52:51 +00005351
cristy3ed852e2009-09-05 21:47:34 +00005352 else
5353 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005354
cristy3ed852e2009-09-05 21:47:34 +00005355 image->columns=subframe_width;
5356 image->rows=subframe_height;
5357 image->page.width=subframe_width;
5358 image->page.height=subframe_height;
5359 image->page.x=mng_info->clip.left;
5360 image->page.y=mng_info->clip.top;
5361 image->background_color=mng_background_color;
5362 image->matte=MagickFalse;
5363 image->delay=0;
5364 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005365
cristy3ed852e2009-09-05 21:47:34 +00005366 if (logging != MagickFalse)
5367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005368 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005369 (double) mng_info->clip.left,(double) mng_info->clip.right,
5370 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005371 }
5372#endif
5373 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5374 continue;
5375 }
5376 if (memcmp(type,mng_CLIP,4) == 0)
5377 {
5378 unsigned int
5379 first_object,
5380 last_object;
5381
5382 /*
5383 Read CLIP.
5384 */
5385 first_object=(p[0] << 8) | p[1];
5386 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005387
cristy3ed852e2009-09-05 21:47:34 +00005388 for (i=(int) first_object; i <= (int) last_object; i++)
5389 {
5390 if (mng_info->exists[i] && !mng_info->frozen[i])
5391 {
5392 MngBox
5393 box;
5394
5395 box=mng_info->object_clip[i];
5396 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5397 }
5398 }
glennrp47b9dd52010-11-24 18:12:06 +00005399
cristy3ed852e2009-09-05 21:47:34 +00005400 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5401 continue;
5402 }
5403 if (memcmp(type,mng_SAVE,4) == 0)
5404 {
5405 for (i=1; i < MNG_MAX_OBJECTS; i++)
5406 if (mng_info->exists[i])
5407 {
5408 mng_info->frozen[i]=MagickTrue;
5409#ifdef MNG_OBJECT_BUFFERS
5410 if (mng_info->ob[i] != (MngBuffer *) NULL)
5411 mng_info->ob[i]->frozen=MagickTrue;
5412#endif
5413 }
glennrp0fe50b42010-11-16 03:52:51 +00005414
cristy3ed852e2009-09-05 21:47:34 +00005415 if (length)
5416 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005417
cristy3ed852e2009-09-05 21:47:34 +00005418 continue;
5419 }
5420
5421 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5422 {
glennrp47b9dd52010-11-24 18:12:06 +00005423 /* Read DISC or SEEK. */
5424
cristy3ed852e2009-09-05 21:47:34 +00005425 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5426 {
5427 for (i=1; i < MNG_MAX_OBJECTS; i++)
5428 MngInfoDiscardObject(mng_info,i);
5429 }
glennrp0fe50b42010-11-16 03:52:51 +00005430
cristy3ed852e2009-09-05 21:47:34 +00005431 else
5432 {
cristybb503372010-05-27 20:51:26 +00005433 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005434 j;
5435
cristybb503372010-05-27 20:51:26 +00005436 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005437 {
5438 i=p[j] << 8 | p[j+1];
5439 MngInfoDiscardObject(mng_info,i);
5440 }
5441 }
glennrp0fe50b42010-11-16 03:52:51 +00005442
cristy3ed852e2009-09-05 21:47:34 +00005443 if (length)
5444 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005445
cristy3ed852e2009-09-05 21:47:34 +00005446 continue;
5447 }
glennrp47b9dd52010-11-24 18:12:06 +00005448
cristy3ed852e2009-09-05 21:47:34 +00005449 if (memcmp(type,mng_MOVE,4) == 0)
5450 {
cristybb503372010-05-27 20:51:26 +00005451 size_t
cristy3ed852e2009-09-05 21:47:34 +00005452 first_object,
5453 last_object;
5454
glennrp47b9dd52010-11-24 18:12:06 +00005455 /* read MOVE */
5456
cristy3ed852e2009-09-05 21:47:34 +00005457 first_object=(p[0] << 8) | p[1];
5458 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005459 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005460 {
5461 if (mng_info->exists[i] && !mng_info->frozen[i])
5462 {
5463 MngPair
5464 new_pair;
5465
5466 MngPair
5467 old_pair;
5468
5469 old_pair.a=mng_info->x_off[i];
5470 old_pair.b=mng_info->y_off[i];
5471 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5472 mng_info->x_off[i]=new_pair.a;
5473 mng_info->y_off[i]=new_pair.b;
5474 }
5475 }
glennrp47b9dd52010-11-24 18:12:06 +00005476
cristy3ed852e2009-09-05 21:47:34 +00005477 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5478 continue;
5479 }
5480
5481 if (memcmp(type,mng_LOOP,4) == 0)
5482 {
cristybb503372010-05-27 20:51:26 +00005483 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005484 loop_level=chunk[0];
5485 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005486
5487 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005488 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005489
cristy3ed852e2009-09-05 21:47:34 +00005490 if (logging != MagickFalse)
5491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005492 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5493 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005494
cristy3ed852e2009-09-05 21:47:34 +00005495 if (loop_iters == 0)
5496 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005497
cristy3ed852e2009-09-05 21:47:34 +00005498 else
5499 {
5500 mng_info->loop_jump[loop_level]=TellBlob(image);
5501 mng_info->loop_count[loop_level]=loop_iters;
5502 }
glennrp0fe50b42010-11-16 03:52:51 +00005503
cristy3ed852e2009-09-05 21:47:34 +00005504 mng_info->loop_iteration[loop_level]=0;
5505 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5506 continue;
5507 }
glennrp47b9dd52010-11-24 18:12:06 +00005508
cristy3ed852e2009-09-05 21:47:34 +00005509 if (memcmp(type,mng_ENDL,4) == 0)
5510 {
5511 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005512
cristy3ed852e2009-09-05 21:47:34 +00005513 if (skipping_loop > 0)
5514 {
5515 if (skipping_loop == loop_level)
5516 {
5517 /*
5518 Found end of zero-iteration loop.
5519 */
5520 skipping_loop=(-1);
5521 mng_info->loop_active[loop_level]=0;
5522 }
5523 }
glennrp47b9dd52010-11-24 18:12:06 +00005524
cristy3ed852e2009-09-05 21:47:34 +00005525 else
5526 {
5527 if (mng_info->loop_active[loop_level] == 1)
5528 {
5529 mng_info->loop_count[loop_level]--;
5530 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005531
cristy3ed852e2009-09-05 21:47:34 +00005532 if (logging != MagickFalse)
5533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005534 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005535 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005536 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005537
cristy3ed852e2009-09-05 21:47:34 +00005538 if (mng_info->loop_count[loop_level] != 0)
5539 {
5540 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5541 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005542
cristy3ed852e2009-09-05 21:47:34 +00005543 if (offset < 0)
5544 ThrowReaderException(CorruptImageError,
5545 "ImproperImageHeader");
5546 }
glennrp47b9dd52010-11-24 18:12:06 +00005547
cristy3ed852e2009-09-05 21:47:34 +00005548 else
5549 {
5550 short
5551 last_level;
5552
5553 /*
5554 Finished loop.
5555 */
5556 mng_info->loop_active[loop_level]=0;
5557 last_level=(-1);
5558 for (i=0; i < loop_level; i++)
5559 if (mng_info->loop_active[i] == 1)
5560 last_level=(short) i;
5561 loop_level=last_level;
5562 }
5563 }
5564 }
glennrp47b9dd52010-11-24 18:12:06 +00005565
cristy3ed852e2009-09-05 21:47:34 +00005566 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5567 continue;
5568 }
glennrp47b9dd52010-11-24 18:12:06 +00005569
cristy3ed852e2009-09-05 21:47:34 +00005570 if (memcmp(type,mng_CLON,4) == 0)
5571 {
5572 if (mng_info->clon_warning == 0)
5573 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5574 CoderError,"CLON is not implemented yet","`%s'",
5575 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005576
cristy3ed852e2009-09-05 21:47:34 +00005577 mng_info->clon_warning++;
5578 }
glennrp47b9dd52010-11-24 18:12:06 +00005579
cristy3ed852e2009-09-05 21:47:34 +00005580 if (memcmp(type,mng_MAGN,4) == 0)
5581 {
5582 png_uint_16
5583 magn_first,
5584 magn_last,
5585 magn_mb,
5586 magn_ml,
5587 magn_mr,
5588 magn_mt,
5589 magn_mx,
5590 magn_my,
5591 magn_methx,
5592 magn_methy;
5593
5594 if (length > 1)
5595 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005596
cristy3ed852e2009-09-05 21:47:34 +00005597 else
5598 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005599
cristy3ed852e2009-09-05 21:47:34 +00005600 if (length > 3)
5601 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005602
cristy3ed852e2009-09-05 21:47:34 +00005603 else
5604 magn_last=magn_first;
5605#ifndef MNG_OBJECT_BUFFERS
5606 if (magn_first || magn_last)
5607 if (mng_info->magn_warning == 0)
5608 {
5609 (void) ThrowMagickException(&image->exception,
5610 GetMagickModule(),CoderError,
5611 "MAGN is not implemented yet for nonzero objects",
5612 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005613
cristy3ed852e2009-09-05 21:47:34 +00005614 mng_info->magn_warning++;
5615 }
5616#endif
5617 if (length > 4)
5618 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005619
cristy3ed852e2009-09-05 21:47:34 +00005620 else
5621 magn_methx=0;
5622
5623 if (length > 6)
5624 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005625
cristy3ed852e2009-09-05 21:47:34 +00005626 else
5627 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005628
cristy3ed852e2009-09-05 21:47:34 +00005629 if (magn_mx == 0)
5630 magn_mx=1;
5631
5632 if (length > 8)
5633 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005634
cristy3ed852e2009-09-05 21:47:34 +00005635 else
5636 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005637
cristy3ed852e2009-09-05 21:47:34 +00005638 if (magn_my == 0)
5639 magn_my=1;
5640
5641 if (length > 10)
5642 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005643
cristy3ed852e2009-09-05 21:47:34 +00005644 else
5645 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005646
cristy3ed852e2009-09-05 21:47:34 +00005647 if (magn_ml == 0)
5648 magn_ml=1;
5649
5650 if (length > 12)
5651 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005652
cristy3ed852e2009-09-05 21:47:34 +00005653 else
5654 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005655
cristy3ed852e2009-09-05 21:47:34 +00005656 if (magn_mr == 0)
5657 magn_mr=1;
5658
5659 if (length > 14)
5660 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005661
cristy3ed852e2009-09-05 21:47:34 +00005662 else
5663 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005664
cristy3ed852e2009-09-05 21:47:34 +00005665 if (magn_mt == 0)
5666 magn_mt=1;
5667
5668 if (length > 16)
5669 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005670
cristy3ed852e2009-09-05 21:47:34 +00005671 else
5672 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005673
cristy3ed852e2009-09-05 21:47:34 +00005674 if (magn_mb == 0)
5675 magn_mb=1;
5676
5677 if (length > 17)
5678 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005679
cristy3ed852e2009-09-05 21:47:34 +00005680 else
5681 magn_methy=magn_methx;
5682
glennrp47b9dd52010-11-24 18:12:06 +00005683
cristy3ed852e2009-09-05 21:47:34 +00005684 if (magn_methx > 5 || magn_methy > 5)
5685 if (mng_info->magn_warning == 0)
5686 {
5687 (void) ThrowMagickException(&image->exception,
5688 GetMagickModule(),CoderError,
5689 "Unknown MAGN method in MNG datastream","`%s'",
5690 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005691
cristy3ed852e2009-09-05 21:47:34 +00005692 mng_info->magn_warning++;
5693 }
5694#ifdef MNG_OBJECT_BUFFERS
5695 /* Magnify existing objects in the range magn_first to magn_last */
5696#endif
5697 if (magn_first == 0 || magn_last == 0)
5698 {
5699 /* Save the magnification factors for object 0 */
5700 mng_info->magn_mb=magn_mb;
5701 mng_info->magn_ml=magn_ml;
5702 mng_info->magn_mr=magn_mr;
5703 mng_info->magn_mt=magn_mt;
5704 mng_info->magn_mx=magn_mx;
5705 mng_info->magn_my=magn_my;
5706 mng_info->magn_methx=magn_methx;
5707 mng_info->magn_methy=magn_methy;
5708 }
5709 }
glennrp47b9dd52010-11-24 18:12:06 +00005710
cristy3ed852e2009-09-05 21:47:34 +00005711 if (memcmp(type,mng_PAST,4) == 0)
5712 {
5713 if (mng_info->past_warning == 0)
5714 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5715 CoderError,"PAST is not implemented yet","`%s'",
5716 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005717
cristy3ed852e2009-09-05 21:47:34 +00005718 mng_info->past_warning++;
5719 }
glennrp47b9dd52010-11-24 18:12:06 +00005720
cristy3ed852e2009-09-05 21:47:34 +00005721 if (memcmp(type,mng_SHOW,4) == 0)
5722 {
5723 if (mng_info->show_warning == 0)
5724 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5725 CoderError,"SHOW is not implemented yet","`%s'",
5726 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005727
cristy3ed852e2009-09-05 21:47:34 +00005728 mng_info->show_warning++;
5729 }
glennrp47b9dd52010-11-24 18:12:06 +00005730
cristy3ed852e2009-09-05 21:47:34 +00005731 if (memcmp(type,mng_sBIT,4) == 0)
5732 {
5733 if (length < 4)
5734 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005735
cristy3ed852e2009-09-05 21:47:34 +00005736 else
5737 {
5738 mng_info->global_sbit.gray=p[0];
5739 mng_info->global_sbit.red=p[0];
5740 mng_info->global_sbit.green=p[1];
5741 mng_info->global_sbit.blue=p[2];
5742 mng_info->global_sbit.alpha=p[3];
5743 mng_info->have_global_sbit=MagickTrue;
5744 }
5745 }
5746 if (memcmp(type,mng_pHYs,4) == 0)
5747 {
5748 if (length > 8)
5749 {
5750 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005751 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005752 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005753 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005754 mng_info->global_phys_unit_type=p[8];
5755 mng_info->have_global_phys=MagickTrue;
5756 }
glennrp47b9dd52010-11-24 18:12:06 +00005757
cristy3ed852e2009-09-05 21:47:34 +00005758 else
5759 mng_info->have_global_phys=MagickFalse;
5760 }
5761 if (memcmp(type,mng_pHYg,4) == 0)
5762 {
5763 if (mng_info->phyg_warning == 0)
5764 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5765 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005766
cristy3ed852e2009-09-05 21:47:34 +00005767 mng_info->phyg_warning++;
5768 }
5769 if (memcmp(type,mng_BASI,4) == 0)
5770 {
5771 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005772
cristy3ed852e2009-09-05 21:47:34 +00005773 if (mng_info->basi_warning == 0)
5774 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5775 CoderError,"BASI is not implemented yet","`%s'",
5776 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005777
cristy3ed852e2009-09-05 21:47:34 +00005778 mng_info->basi_warning++;
5779#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005780 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005781 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005782 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005783 (p[6] << 8) | p[7]);
5784 basi_color_type=p[8];
5785 basi_compression_method=p[9];
5786 basi_filter_type=p[10];
5787 basi_interlace_method=p[11];
5788 if (length > 11)
5789 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005790
cristy3ed852e2009-09-05 21:47:34 +00005791 else
5792 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005793
cristy3ed852e2009-09-05 21:47:34 +00005794 if (length > 13)
5795 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005796
cristy3ed852e2009-09-05 21:47:34 +00005797 else
5798 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005799
cristy3ed852e2009-09-05 21:47:34 +00005800 if (length > 15)
5801 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005802
cristy3ed852e2009-09-05 21:47:34 +00005803 else
5804 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005805
cristy3ed852e2009-09-05 21:47:34 +00005806 if (length > 17)
5807 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005808
cristy3ed852e2009-09-05 21:47:34 +00005809 else
5810 {
5811 if (basi_sample_depth == 16)
5812 basi_alpha=65535L;
5813 else
5814 basi_alpha=255;
5815 }
glennrp47b9dd52010-11-24 18:12:06 +00005816
cristy3ed852e2009-09-05 21:47:34 +00005817 if (length > 19)
5818 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005819
cristy3ed852e2009-09-05 21:47:34 +00005820 else
5821 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005822
cristy3ed852e2009-09-05 21:47:34 +00005823#endif
5824 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5825 continue;
5826 }
glennrp47b9dd52010-11-24 18:12:06 +00005827
cristy3ed852e2009-09-05 21:47:34 +00005828 if (memcmp(type,mng_IHDR,4)
5829#if defined(JNG_SUPPORTED)
5830 && memcmp(type,mng_JHDR,4)
5831#endif
5832 )
5833 {
5834 /* Not an IHDR or JHDR chunk */
5835 if (length)
5836 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005837
cristy3ed852e2009-09-05 21:47:34 +00005838 continue;
5839 }
5840/* Process IHDR */
5841 if (logging != MagickFalse)
5842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5843 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005844
cristy3ed852e2009-09-05 21:47:34 +00005845 mng_info->exists[object_id]=MagickTrue;
5846 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005847
cristy3ed852e2009-09-05 21:47:34 +00005848 if (mng_info->invisible[object_id])
5849 {
5850 if (logging != MagickFalse)
5851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5852 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005853
cristy3ed852e2009-09-05 21:47:34 +00005854 skip_to_iend=MagickTrue;
5855 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5856 continue;
5857 }
5858#if defined(MNG_INSERT_LAYERS)
5859 if (length < 8)
5860 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005861
cristy8182b072010-05-30 20:10:53 +00005862 image_width=(size_t) mng_get_long(p);
5863 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005864#endif
5865 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5866
5867 /*
5868 Insert a transparent background layer behind the entire animation
5869 if it is not full screen.
5870 */
5871#if defined(MNG_INSERT_LAYERS)
5872 if (insert_layers && mng_type && first_mng_object)
5873 {
5874 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5875 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005876 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005877 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005878 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005879 {
cristy4c08aed2011-07-01 19:47:50 +00005880 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005881 {
5882 /*
5883 Allocate next image structure.
5884 */
cristy9950d572011-10-01 18:22:35 +00005885 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005886
cristy3ed852e2009-09-05 21:47:34 +00005887 if (GetNextImageInList(image) == (Image *) NULL)
5888 {
5889 image=DestroyImageList(image);
5890 MngInfoFreeStruct(mng_info,&have_mng_structure);
5891 return((Image *) NULL);
5892 }
glennrp47b9dd52010-11-24 18:12:06 +00005893
cristy3ed852e2009-09-05 21:47:34 +00005894 image=SyncNextImageInList(image);
5895 }
5896 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005897
cristy3ed852e2009-09-05 21:47:34 +00005898 if (term_chunk_found)
5899 {
5900 image->start_loop=MagickTrue;
5901 image->iterations=mng_iterations;
5902 term_chunk_found=MagickFalse;
5903 }
glennrp47b9dd52010-11-24 18:12:06 +00005904
cristy3ed852e2009-09-05 21:47:34 +00005905 else
5906 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005907
5908 /* Make a background rectangle. */
5909
cristy3ed852e2009-09-05 21:47:34 +00005910 image->delay=0;
5911 image->columns=mng_info->mng_width;
5912 image->rows=mng_info->mng_height;
5913 image->page.width=mng_info->mng_width;
5914 image->page.height=mng_info->mng_height;
5915 image->page.x=0;
5916 image->page.y=0;
5917 image->background_color=mng_background_color;
5918 (void) SetImageBackgroundColor(image);
5919 if (logging != MagickFalse)
5920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005921 " Inserted transparent background layer, W=%.20g, H=%.20g",
5922 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005923 }
5924 }
5925 /*
5926 Insert a background layer behind the upcoming image if
5927 framing_mode is 3, and we haven't already inserted one.
5928 */
5929 if (insert_layers && (mng_info->framing_mode == 3) &&
5930 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5931 (simplicity & 0x08)))
5932 {
cristy4c08aed2011-07-01 19:47:50 +00005933 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005934 {
5935 /*
5936 Allocate next image structure.
5937 */
cristy9950d572011-10-01 18:22:35 +00005938 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005939
cristy3ed852e2009-09-05 21:47:34 +00005940 if (GetNextImageInList(image) == (Image *) NULL)
5941 {
5942 image=DestroyImageList(image);
5943 MngInfoFreeStruct(mng_info,&have_mng_structure);
5944 return((Image *) NULL);
5945 }
glennrp47b9dd52010-11-24 18:12:06 +00005946
cristy3ed852e2009-09-05 21:47:34 +00005947 image=SyncNextImageInList(image);
5948 }
glennrp0fe50b42010-11-16 03:52:51 +00005949
cristy3ed852e2009-09-05 21:47:34 +00005950 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005951
cristy3ed852e2009-09-05 21:47:34 +00005952 if (term_chunk_found)
5953 {
5954 image->start_loop=MagickTrue;
5955 image->iterations=mng_iterations;
5956 term_chunk_found=MagickFalse;
5957 }
glennrp0fe50b42010-11-16 03:52:51 +00005958
cristy3ed852e2009-09-05 21:47:34 +00005959 else
5960 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005961
cristy3ed852e2009-09-05 21:47:34 +00005962 image->delay=0;
5963 image->columns=subframe_width;
5964 image->rows=subframe_height;
5965 image->page.width=subframe_width;
5966 image->page.height=subframe_height;
5967 image->page.x=mng_info->clip.left;
5968 image->page.y=mng_info->clip.top;
5969 image->background_color=mng_background_color;
5970 image->matte=MagickFalse;
5971 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005972
cristy3ed852e2009-09-05 21:47:34 +00005973 if (logging != MagickFalse)
5974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005975 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005976 (double) mng_info->clip.left,(double) mng_info->clip.right,
5977 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005978 }
5979#endif /* MNG_INSERT_LAYERS */
5980 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005981
cristy4c08aed2011-07-01 19:47:50 +00005982 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005983 {
5984 /*
5985 Allocate next image structure.
5986 */
cristy9950d572011-10-01 18:22:35 +00005987 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005988
cristy3ed852e2009-09-05 21:47:34 +00005989 if (GetNextImageInList(image) == (Image *) NULL)
5990 {
5991 image=DestroyImageList(image);
5992 MngInfoFreeStruct(mng_info,&have_mng_structure);
5993 return((Image *) NULL);
5994 }
glennrp47b9dd52010-11-24 18:12:06 +00005995
cristy3ed852e2009-09-05 21:47:34 +00005996 image=SyncNextImageInList(image);
5997 }
5998 mng_info->image=image;
5999 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6000 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006001
cristy3ed852e2009-09-05 21:47:34 +00006002 if (status == MagickFalse)
6003 break;
glennrp0fe50b42010-11-16 03:52:51 +00006004
cristy3ed852e2009-09-05 21:47:34 +00006005 if (term_chunk_found)
6006 {
6007 image->start_loop=MagickTrue;
6008 term_chunk_found=MagickFalse;
6009 }
glennrp0fe50b42010-11-16 03:52:51 +00006010
cristy3ed852e2009-09-05 21:47:34 +00006011 else
6012 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006013
cristy3ed852e2009-09-05 21:47:34 +00006014 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6015 {
6016 image->delay=frame_delay;
6017 frame_delay=default_frame_delay;
6018 }
glennrp0fe50b42010-11-16 03:52:51 +00006019
cristy3ed852e2009-09-05 21:47:34 +00006020 else
6021 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006022
cristy3ed852e2009-09-05 21:47:34 +00006023 image->page.width=mng_info->mng_width;
6024 image->page.height=mng_info->mng_height;
6025 image->page.x=mng_info->x_off[object_id];
6026 image->page.y=mng_info->y_off[object_id];
6027 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006028
cristy3ed852e2009-09-05 21:47:34 +00006029 /*
6030 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6031 */
glennrp47b9dd52010-11-24 18:12:06 +00006032
cristy3ed852e2009-09-05 21:47:34 +00006033 if (logging != MagickFalse)
6034 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6035 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6036 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006037
cristybb503372010-05-27 20:51:26 +00006038 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006039
cristy3ed852e2009-09-05 21:47:34 +00006040 if (offset < 0)
6041 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6042 }
6043
6044 previous=image;
6045 mng_info->image=image;
6046 mng_info->mng_type=mng_type;
6047 mng_info->object_id=object_id;
6048
6049 if (memcmp(type,mng_IHDR,4) == 0)
6050 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006051
cristy3ed852e2009-09-05 21:47:34 +00006052#if defined(JNG_SUPPORTED)
6053 else
6054 image=ReadOneJNGImage(mng_info,image_info,exception);
6055#endif
6056
6057 if (image == (Image *) NULL)
6058 {
6059 if (IsImageObject(previous) != MagickFalse)
6060 {
6061 (void) DestroyImageList(previous);
6062 (void) CloseBlob(previous);
6063 }
glennrp47b9dd52010-11-24 18:12:06 +00006064
cristy3ed852e2009-09-05 21:47:34 +00006065 MngInfoFreeStruct(mng_info,&have_mng_structure);
6066 return((Image *) NULL);
6067 }
glennrp0fe50b42010-11-16 03:52:51 +00006068
cristy3ed852e2009-09-05 21:47:34 +00006069 if (image->columns == 0 || image->rows == 0)
6070 {
6071 (void) CloseBlob(image);
6072 image=DestroyImageList(image);
6073 MngInfoFreeStruct(mng_info,&have_mng_structure);
6074 return((Image *) NULL);
6075 }
glennrp0fe50b42010-11-16 03:52:51 +00006076
cristy3ed852e2009-09-05 21:47:34 +00006077 mng_info->image=image;
6078
6079 if (mng_type)
6080 {
6081 MngBox
6082 crop_box;
6083
6084 if (mng_info->magn_methx || mng_info->magn_methy)
6085 {
6086 png_uint_32
6087 magnified_height,
6088 magnified_width;
6089
6090 if (logging != MagickFalse)
6091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6092 " Processing MNG MAGN chunk");
6093
6094 if (mng_info->magn_methx == 1)
6095 {
6096 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006097
cristy3ed852e2009-09-05 21:47:34 +00006098 if (image->columns > 1)
6099 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006100
cristy3ed852e2009-09-05 21:47:34 +00006101 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006102 magnified_width += (png_uint_32)
6103 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006104 }
glennrp47b9dd52010-11-24 18:12:06 +00006105
cristy3ed852e2009-09-05 21:47:34 +00006106 else
6107 {
cristy4e5bc842010-06-09 13:56:01 +00006108 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006109
cristy3ed852e2009-09-05 21:47:34 +00006110 if (image->columns > 1)
6111 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006112
cristy3ed852e2009-09-05 21:47:34 +00006113 if (image->columns > 2)
6114 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006115
cristy3ed852e2009-09-05 21:47:34 +00006116 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006117 magnified_width += (png_uint_32)
6118 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006119 }
glennrp47b9dd52010-11-24 18:12:06 +00006120
cristy3ed852e2009-09-05 21:47:34 +00006121 if (mng_info->magn_methy == 1)
6122 {
6123 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006124
cristy3ed852e2009-09-05 21:47:34 +00006125 if (image->rows > 1)
6126 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006127
cristy3ed852e2009-09-05 21:47:34 +00006128 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006129 magnified_height += (png_uint_32)
6130 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006131 }
glennrp47b9dd52010-11-24 18:12:06 +00006132
cristy3ed852e2009-09-05 21:47:34 +00006133 else
6134 {
cristy4e5bc842010-06-09 13:56:01 +00006135 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006136
cristy3ed852e2009-09-05 21:47:34 +00006137 if (image->rows > 1)
6138 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006139
cristy3ed852e2009-09-05 21:47:34 +00006140 if (image->rows > 2)
6141 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006142
cristy3ed852e2009-09-05 21:47:34 +00006143 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006144 magnified_height += (png_uint_32)
6145 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006146 }
glennrp47b9dd52010-11-24 18:12:06 +00006147
cristy3ed852e2009-09-05 21:47:34 +00006148 if (magnified_height > image->rows ||
6149 magnified_width > image->columns)
6150 {
6151 Image
6152 *large_image;
6153
6154 int
6155 yy;
6156
cristy4c08aed2011-07-01 19:47:50 +00006157 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006158 *next,
6159 *prev;
6160
6161 png_uint_16
6162 magn_methx,
6163 magn_methy;
6164
cristy4c08aed2011-07-01 19:47:50 +00006165 ssize_t
6166 m,
6167 y;
6168
6169 register Quantum
6170 *n,
6171 *q;
6172
6173 register ssize_t
6174 x;
6175
glennrp47b9dd52010-11-24 18:12:06 +00006176 /* Allocate next image structure. */
6177
cristy3ed852e2009-09-05 21:47:34 +00006178 if (logging != MagickFalse)
6179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6180 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006181
cristy9950d572011-10-01 18:22:35 +00006182 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006183
cristy3ed852e2009-09-05 21:47:34 +00006184 if (GetNextImageInList(image) == (Image *) NULL)
6185 {
6186 image=DestroyImageList(image);
6187 MngInfoFreeStruct(mng_info,&have_mng_structure);
6188 return((Image *) NULL);
6189 }
6190
6191 large_image=SyncNextImageInList(image);
6192
6193 large_image->columns=magnified_width;
6194 large_image->rows=magnified_height;
6195
6196 magn_methx=mng_info->magn_methx;
6197 magn_methy=mng_info->magn_methy;
6198
glennrp3faa9a32011-04-23 14:00:25 +00006199#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006200#define QM unsigned short
6201 if (magn_methx != 1 || magn_methy != 1)
6202 {
6203 /*
6204 Scale pixels to unsigned shorts to prevent
6205 overflow of intermediate values of interpolations
6206 */
cristybb503372010-05-27 20:51:26 +00006207 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006208 {
6209 q=GetAuthenticPixels(image,0,y,image->columns,1,
6210 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006211
cristybb503372010-05-27 20:51:26 +00006212 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006213 {
cristy4c08aed2011-07-01 19:47:50 +00006214 SetPixelRed(image,ScaleQuantumToShort(
6215 GetPixelRed(image,q)),q);
6216 SetPixelGreen(image,ScaleQuantumToShort(
6217 GetPixelGreen(image,q)),q);
6218 SetPixelBlue(image,ScaleQuantumToShort(
6219 GetPixelBlue(image,q)),q);
6220 SetPixelAlpha(image,ScaleQuantumToShort(
6221 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006222 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006223 }
glennrp47b9dd52010-11-24 18:12:06 +00006224
cristy3ed852e2009-09-05 21:47:34 +00006225 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6226 break;
6227 }
6228 }
6229#else
6230#define QM Quantum
6231#endif
6232
6233 if (image->matte != MagickFalse)
6234 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006235
cristy3ed852e2009-09-05 21:47:34 +00006236 else
6237 {
cristy4c08aed2011-07-01 19:47:50 +00006238 large_image->background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00006239 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006240
cristy3ed852e2009-09-05 21:47:34 +00006241 if (magn_methx == 4)
6242 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006243
cristy3ed852e2009-09-05 21:47:34 +00006244 if (magn_methx == 5)
6245 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006246
cristy3ed852e2009-09-05 21:47:34 +00006247 if (magn_methy == 4)
6248 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006249
cristy3ed852e2009-09-05 21:47:34 +00006250 if (magn_methy == 5)
6251 magn_methy=3;
6252 }
6253
6254 /* magnify the rows into the right side of the large image */
6255
6256 if (logging != MagickFalse)
6257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006258 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006259 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006260 yy=0;
6261 length=(size_t) image->columns;
cristy4c08aed2011-07-01 19:47:50 +00006262 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6263 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006264
cristy4c08aed2011-07-01 19:47:50 +00006265 if ((prev == (Quantum *) NULL) ||
6266 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006267 {
6268 image=DestroyImageList(image);
6269 MngInfoFreeStruct(mng_info,&have_mng_structure);
6270 ThrowReaderException(ResourceLimitError,
6271 "MemoryAllocationFailed");
6272 }
glennrp47b9dd52010-11-24 18:12:06 +00006273
cristy3ed852e2009-09-05 21:47:34 +00006274 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6275 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006276
cristybb503372010-05-27 20:51:26 +00006277 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006278 {
6279 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006280 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006281
cristybb503372010-05-27 20:51:26 +00006282 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6283 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006284
cristybb503372010-05-27 20:51:26 +00006285 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6286 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006287
cristybb503372010-05-27 20:51:26 +00006288 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006289 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006290
cristy3ed852e2009-09-05 21:47:34 +00006291 else
cristybb503372010-05-27 20:51:26 +00006292 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006293
cristy3ed852e2009-09-05 21:47:34 +00006294 n=prev;
6295 prev=next;
6296 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006297
cristybb503372010-05-27 20:51:26 +00006298 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006299 {
6300 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6301 exception);
6302 (void) CopyMagickMemory(next,n,length);
6303 }
glennrp47b9dd52010-11-24 18:12:06 +00006304
cristy3ed852e2009-09-05 21:47:34 +00006305 for (i=0; i < m; i++, yy++)
6306 {
cristy4c08aed2011-07-01 19:47:50 +00006307 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006308 *pixels;
6309
cristybb503372010-05-27 20:51:26 +00006310 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006311 pixels=prev;
6312 n=next;
6313 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006314 1,exception);
cristy3ed852e2009-09-05 21:47:34 +00006315 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00006316
cristybb503372010-05-27 20:51:26 +00006317 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006318 {
glennrpfd05d622011-02-25 04:10:33 +00006319 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006320 /*
6321 if (image->storage_class == PseudoClass)
6322 {
6323 }
6324 */
6325
6326 if (magn_methy <= 1)
6327 {
glennrpbb4f99d2011-05-22 11:13:17 +00006328 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006329 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
glennrp847370c2011-07-05 17:37:15 +00006330 SetPixelGreen(large_image,GetPixelGreen(image,
6331 pixels),q);
6332 SetPixelBlue(large_image,GetPixelBlue(image,
6333 pixels),q);
6334 SetPixelAlpha(large_image,GetPixelAlpha(image,
6335 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006336 }
glennrp47b9dd52010-11-24 18:12:06 +00006337
cristy3ed852e2009-09-05 21:47:34 +00006338 else if (magn_methy == 2 || magn_methy == 4)
6339 {
6340 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006341 {
glennrp847370c2011-07-05 17:37:15 +00006342 SetPixelRed(large_image,GetPixelRed(image,
6343 pixels),q);
6344 SetPixelGreen(large_image,GetPixelGreen(image,
6345 pixels),q);
6346 SetPixelBlue(large_image,GetPixelBlue(image,
6347 pixels),q);
6348 SetPixelAlpha(large_image,GetPixelAlpha(image,
6349 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006350 }
glennrp47b9dd52010-11-24 18:12:06 +00006351
cristy3ed852e2009-09-05 21:47:34 +00006352 else
6353 {
6354 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006355 SetPixelRed(large_image,((QM) (((ssize_t)
6356 (2*i*(GetPixelRed(image,n)
6357 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006358 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006359 +GetPixelRed(image,pixels)))),q);
6360 SetPixelGreen(large_image,((QM) (((ssize_t)
6361 (2*i*(GetPixelGreen(image,n)
6362 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006363 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006364 +GetPixelGreen(image,pixels)))),q);
6365 SetPixelBlue(large_image,((QM) (((ssize_t)
6366 (2*i*(GetPixelBlue(image,n)
6367 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006368 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006369 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006370
cristy3ed852e2009-09-05 21:47:34 +00006371 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006372 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6373 (2*i*(GetPixelAlpha(image,n)
6374 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006375 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006376 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006377 }
glennrp47b9dd52010-11-24 18:12:06 +00006378
cristy3ed852e2009-09-05 21:47:34 +00006379 if (magn_methy == 4)
6380 {
6381 /* Replicate nearest */
6382 if (i <= ((m+1) << 1))
glennrp847370c2011-07-05 17:37:15 +00006383 SetPixelAlpha(large_image,GetPixelAlpha(image,
6384 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006385 else
glennrp847370c2011-07-05 17:37:15 +00006386 SetPixelAlpha(large_image,GetPixelAlpha(image,
6387 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006388 }
6389 }
glennrp47b9dd52010-11-24 18:12:06 +00006390
cristy3ed852e2009-09-05 21:47:34 +00006391 else /* if (magn_methy == 3 || magn_methy == 5) */
6392 {
6393 /* Replicate nearest */
6394 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006395 {
glennrp847370c2011-07-05 17:37:15 +00006396 SetPixelRed(large_image,GetPixelRed(image,
6397 pixels),q);
6398 SetPixelGreen(large_image,GetPixelGreen(image,
6399 pixels),q);
6400 SetPixelBlue(large_image,GetPixelBlue(image,
6401 pixels),q);
6402 SetPixelAlpha(large_image,GetPixelAlpha(image,
6403 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006404 }
glennrp47b9dd52010-11-24 18:12:06 +00006405
cristy3ed852e2009-09-05 21:47:34 +00006406 else
glennrpbb4f99d2011-05-22 11:13:17 +00006407 {
cristy4c08aed2011-07-01 19:47:50 +00006408 SetPixelRed(large_image,GetPixelRed(image,n),q);
glennrp847370c2011-07-05 17:37:15 +00006409 SetPixelGreen(large_image,GetPixelGreen(image,n),
6410 q);
6411 SetPixelBlue(large_image,GetPixelBlue(image,n),
6412 q);
6413 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6414 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006415 }
glennrp47b9dd52010-11-24 18:12:06 +00006416
cristy3ed852e2009-09-05 21:47:34 +00006417 if (magn_methy == 5)
6418 {
cristy4c08aed2011-07-01 19:47:50 +00006419 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6420 (GetPixelAlpha(image,n)
6421 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006422 +m))/((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006423 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006424 }
6425 }
cristyed231572011-07-14 02:18:59 +00006426 n+=GetPixelChannels(image);
6427 q+=GetPixelChannels(large_image);
6428 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006429 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006430
cristy3ed852e2009-09-05 21:47:34 +00006431 if (SyncAuthenticPixels(large_image,exception) == 0)
6432 break;
glennrp47b9dd52010-11-24 18:12:06 +00006433
cristy3ed852e2009-09-05 21:47:34 +00006434 } /* i */
6435 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006436
cristy4c08aed2011-07-01 19:47:50 +00006437 prev=(Quantum *) RelinquishMagickMemory(prev);
6438 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006439
6440 length=image->columns;
6441
6442 if (logging != MagickFalse)
6443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6444 " Delete original image");
6445
6446 DeleteImageFromList(&image);
6447
6448 image=large_image;
6449
6450 mng_info->image=image;
6451
6452 /* magnify the columns */
6453 if (logging != MagickFalse)
6454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006455 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006456
cristybb503372010-05-27 20:51:26 +00006457 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006458 {
cristy4c08aed2011-07-01 19:47:50 +00006459 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006460 *pixels;
6461
6462 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristyed231572011-07-14 02:18:59 +00006463 pixels=q+(image->columns-length)*GetPixelChannels(image);
6464 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006465
cristybb503372010-05-27 20:51:26 +00006466 for (x=(ssize_t) (image->columns-length);
6467 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006468 {
cristyed231572011-07-14 02:18:59 +00006469 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006470
cristybb503372010-05-27 20:51:26 +00006471 if (x == (ssize_t) (image->columns-length))
6472 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006473
cristybb503372010-05-27 20:51:26 +00006474 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6475 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006476
cristybb503372010-05-27 20:51:26 +00006477 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6478 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006479
cristybb503372010-05-27 20:51:26 +00006480 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006481 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006482
cristy3ed852e2009-09-05 21:47:34 +00006483 else
cristybb503372010-05-27 20:51:26 +00006484 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006485
cristy3ed852e2009-09-05 21:47:34 +00006486 for (i=0; i < m; i++)
6487 {
6488 if (magn_methx <= 1)
6489 {
6490 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006491 SetPixelRed(image,GetPixelRed(image,pixels),q);
6492 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6493 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6494 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006495 }
glennrp47b9dd52010-11-24 18:12:06 +00006496
cristy3ed852e2009-09-05 21:47:34 +00006497 else if (magn_methx == 2 || magn_methx == 4)
6498 {
6499 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006500 {
cristy4c08aed2011-07-01 19:47:50 +00006501 SetPixelRed(image,GetPixelRed(image,pixels),q);
6502 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6503 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6504 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006505 }
glennrp47b9dd52010-11-24 18:12:06 +00006506
cristyed231572011-07-14 02:18:59 +00006507 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006508 else
6509 {
6510 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006511 SetPixelRed(image,(QM) ((2*i*(
6512 GetPixelRed(image,n)
6513 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006514 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006515 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006516
cristy4c08aed2011-07-01 19:47:50 +00006517 SetPixelGreen(image,(QM) ((2*i*(
6518 GetPixelGreen(image,n)
6519 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006520 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006521 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006522
cristy4c08aed2011-07-01 19:47:50 +00006523 SetPixelBlue(image,(QM) ((2*i*(
6524 GetPixelBlue(image,n)
6525 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006526 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006527 GetPixelBlue(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006528 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006529 SetPixelAlpha(image,(QM) ((2*i*(
6530 GetPixelAlpha(image,n)
6531 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006532 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006533 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006534 }
glennrp47b9dd52010-11-24 18:12:06 +00006535
cristy3ed852e2009-09-05 21:47:34 +00006536 if (magn_methx == 4)
6537 {
6538 /* Replicate nearest */
6539 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006540 {
cristy4c08aed2011-07-01 19:47:50 +00006541 SetPixelAlpha(image,
6542 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006543 }
cristy3ed852e2009-09-05 21:47:34 +00006544 else
glennrpbb4f99d2011-05-22 11:13:17 +00006545 {
cristy4c08aed2011-07-01 19:47:50 +00006546 SetPixelAlpha(image,
6547 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006548 }
cristy3ed852e2009-09-05 21:47:34 +00006549 }
6550 }
glennrp47b9dd52010-11-24 18:12:06 +00006551
cristy3ed852e2009-09-05 21:47:34 +00006552 else /* if (magn_methx == 3 || magn_methx == 5) */
6553 {
6554 /* Replicate nearest */
6555 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006556 {
cristy4c08aed2011-07-01 19:47:50 +00006557 SetPixelRed(image,GetPixelRed(image,pixels),q);
6558 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6559 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6560 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006561 }
glennrp47b9dd52010-11-24 18:12:06 +00006562
cristy3ed852e2009-09-05 21:47:34 +00006563 else
glennrpbb4f99d2011-05-22 11:13:17 +00006564 {
cristy4c08aed2011-07-01 19:47:50 +00006565 SetPixelRed(image,GetPixelRed(image,n),q);
6566 SetPixelGreen(image,GetPixelGreen(image,n),q);
6567 SetPixelBlue(image,GetPixelBlue(image,n),q);
6568 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006569 }
glennrp47b9dd52010-11-24 18:12:06 +00006570
cristy3ed852e2009-09-05 21:47:34 +00006571 if (magn_methx == 5)
6572 {
6573 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006574 SetPixelAlpha(image,
6575 (QM) ((2*i*( GetPixelAlpha(image,n)
6576 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006577 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006578 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006579 }
6580 }
cristyed231572011-07-14 02:18:59 +00006581 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006582 }
cristyed231572011-07-14 02:18:59 +00006583 n+=GetPixelChannels(image);
6584 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006585 }
glennrp47b9dd52010-11-24 18:12:06 +00006586
cristy3ed852e2009-09-05 21:47:34 +00006587 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6588 break;
6589 }
glennrp3faa9a32011-04-23 14:00:25 +00006590#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006591 if (magn_methx != 1 || magn_methy != 1)
6592 {
6593 /*
6594 Rescale pixels to Quantum
6595 */
cristybb503372010-05-27 20:51:26 +00006596 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006597 {
6598 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006599
cristybb503372010-05-27 20:51:26 +00006600 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006601 {
cristy4c08aed2011-07-01 19:47:50 +00006602 SetPixelRed(image,ScaleShortToQuantum(
6603 GetPixelRed(image,q)),q);
6604 SetPixelGreen(image,ScaleShortToQuantum(
6605 GetPixelGreen(image,q)),q);
6606 SetPixelBlue(image,ScaleShortToQuantum(
6607 GetPixelBlue(image,q)),q);
6608 SetPixelAlpha(image,ScaleShortToQuantum(
6609 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006610 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006611 }
glennrp47b9dd52010-11-24 18:12:06 +00006612
cristy3ed852e2009-09-05 21:47:34 +00006613 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6614 break;
6615 }
6616 }
6617#endif
6618 if (logging != MagickFalse)
6619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6620 " Finished MAGN processing");
6621 }
6622 }
6623
6624 /*
6625 Crop_box is with respect to the upper left corner of the MNG.
6626 */
6627 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6628 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6629 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6630 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6631 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6632 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6633 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6634 if ((crop_box.left != (mng_info->image_box.left
6635 +mng_info->x_off[object_id])) ||
6636 (crop_box.right != (mng_info->image_box.right
6637 +mng_info->x_off[object_id])) ||
6638 (crop_box.top != (mng_info->image_box.top
6639 +mng_info->y_off[object_id])) ||
6640 (crop_box.bottom != (mng_info->image_box.bottom
6641 +mng_info->y_off[object_id])))
6642 {
6643 if (logging != MagickFalse)
6644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6645 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006646
cristy3ed852e2009-09-05 21:47:34 +00006647 if ((crop_box.left < crop_box.right) &&
6648 (crop_box.top < crop_box.bottom))
6649 {
6650 Image
6651 *im;
6652
6653 RectangleInfo
6654 crop_info;
6655
6656 /*
6657 Crop_info is with respect to the upper left corner of
6658 the image.
6659 */
6660 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6661 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006662 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6663 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006664 image->page.width=image->columns;
6665 image->page.height=image->rows;
6666 image->page.x=0;
6667 image->page.y=0;
6668 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006669
cristy3ed852e2009-09-05 21:47:34 +00006670 if (im != (Image *) NULL)
6671 {
6672 image->columns=im->columns;
6673 image->rows=im->rows;
6674 im=DestroyImage(im);
6675 image->page.width=image->columns;
6676 image->page.height=image->rows;
6677 image->page.x=crop_box.left;
6678 image->page.y=crop_box.top;
6679 }
6680 }
glennrp47b9dd52010-11-24 18:12:06 +00006681
cristy3ed852e2009-09-05 21:47:34 +00006682 else
6683 {
6684 /*
6685 No pixels in crop area. The MNG spec still requires
6686 a layer, though, so make a single transparent pixel in
6687 the top left corner.
6688 */
6689 image->columns=1;
6690 image->rows=1;
6691 image->colors=2;
6692 (void) SetImageBackgroundColor(image);
6693 image->page.width=1;
6694 image->page.height=1;
6695 image->page.x=0;
6696 image->page.y=0;
6697 }
6698 }
6699#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6700 image=mng_info->image;
6701#endif
6702 }
6703
glennrp2b013e42010-11-24 16:55:50 +00006704#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6705 /* PNG does not handle depths greater than 16 so reduce it even
6706 * if lossy
6707 */
6708 if (image->depth > 16)
6709 image->depth=16;
6710#endif
6711
glennrp3faa9a32011-04-23 14:00:25 +00006712#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006713 if (LosslessReduceDepthOK(image) != MagickFalse)
6714 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006715#endif
glennrpd6afd542010-11-19 01:53:05 +00006716
cristy3ed852e2009-09-05 21:47:34 +00006717 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006718
cristy3ed852e2009-09-05 21:47:34 +00006719 if (image_info->number_scenes != 0)
6720 {
6721 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006722 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006723 break;
6724 }
glennrpd6afd542010-11-19 01:53:05 +00006725
cristy3ed852e2009-09-05 21:47:34 +00006726 if (logging != MagickFalse)
6727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6728 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006729
cristy3ed852e2009-09-05 21:47:34 +00006730 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006731
cristy3ed852e2009-09-05 21:47:34 +00006732 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006733
cristy3ed852e2009-09-05 21:47:34 +00006734 if (logging != MagickFalse)
6735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6736 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006737
cristy3ed852e2009-09-05 21:47:34 +00006738#if defined(MNG_INSERT_LAYERS)
6739 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6740 (mng_info->mng_height))
6741 {
6742 /*
6743 Insert a background layer if nothing else was found.
6744 */
6745 if (logging != MagickFalse)
6746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6747 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006748
cristy4c08aed2011-07-01 19:47:50 +00006749 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006750 {
6751 /*
6752 Allocate next image structure.
6753 */
cristy9950d572011-10-01 18:22:35 +00006754 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006755 if (GetNextImageInList(image) == (Image *) NULL)
6756 {
6757 image=DestroyImageList(image);
6758 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006759
cristy3ed852e2009-09-05 21:47:34 +00006760 if (logging != MagickFalse)
6761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6762 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006763
cristy3ed852e2009-09-05 21:47:34 +00006764 return((Image *) NULL);
6765 }
6766 image=SyncNextImageInList(image);
6767 }
6768 image->columns=mng_info->mng_width;
6769 image->rows=mng_info->mng_height;
6770 image->page.width=mng_info->mng_width;
6771 image->page.height=mng_info->mng_height;
6772 image->page.x=0;
6773 image->page.y=0;
6774 image->background_color=mng_background_color;
6775 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006776
cristy3ed852e2009-09-05 21:47:34 +00006777 if (image_info->ping == MagickFalse)
6778 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006779
cristy3ed852e2009-09-05 21:47:34 +00006780 mng_info->image_found++;
6781 }
6782#endif
6783 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006784
cristy3ed852e2009-09-05 21:47:34 +00006785 if (mng_iterations == 1)
6786 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006787
cristy3ed852e2009-09-05 21:47:34 +00006788 while (GetPreviousImageInList(image) != (Image *) NULL)
6789 {
6790 image_count++;
6791 if (image_count > 10*mng_info->image_found)
6792 {
6793 if (logging != MagickFalse)
6794 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006795
cristy3ed852e2009-09-05 21:47:34 +00006796 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6797 CoderError,"Linked list is corrupted, beginning of list not found",
6798 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006799
cristy3ed852e2009-09-05 21:47:34 +00006800 return((Image *) NULL);
6801 }
glennrp0fe50b42010-11-16 03:52:51 +00006802
cristy3ed852e2009-09-05 21:47:34 +00006803 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006804
cristy3ed852e2009-09-05 21:47:34 +00006805 if (GetNextImageInList(image) == (Image *) NULL)
6806 {
6807 if (logging != MagickFalse)
6808 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006809
cristy3ed852e2009-09-05 21:47:34 +00006810 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6811 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6812 image_info->filename);
6813 }
6814 }
glennrp47b9dd52010-11-24 18:12:06 +00006815
cristy3ed852e2009-09-05 21:47:34 +00006816 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6817 GetNextImageInList(image) ==
6818 (Image *) NULL)
6819 {
6820 if (logging != MagickFalse)
6821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6822 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006823
cristy3ed852e2009-09-05 21:47:34 +00006824 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6825 CoderError,"image->next for first image is NULL but shouldn't be.",
6826 "`%s'",image_info->filename);
6827 }
glennrp47b9dd52010-11-24 18:12:06 +00006828
cristy3ed852e2009-09-05 21:47:34 +00006829 if (mng_info->image_found == 0)
6830 {
6831 if (logging != MagickFalse)
6832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6833 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006834
cristy3ed852e2009-09-05 21:47:34 +00006835 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6836 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006837
cristy3ed852e2009-09-05 21:47:34 +00006838 if (image != (Image *) NULL)
6839 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006840
cristy3ed852e2009-09-05 21:47:34 +00006841 MngInfoFreeStruct(mng_info,&have_mng_structure);
6842 return((Image *) NULL);
6843 }
6844
6845 if (mng_info->ticks_per_second)
6846 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6847 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006848
cristy3ed852e2009-09-05 21:47:34 +00006849 else
6850 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006851
cristy3ed852e2009-09-05 21:47:34 +00006852 /* Find final nonzero image delay */
6853 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006854
cristy3ed852e2009-09-05 21:47:34 +00006855 while (GetNextImageInList(image) != (Image *) NULL)
6856 {
6857 if (image->delay)
6858 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006859
cristy3ed852e2009-09-05 21:47:34 +00006860 image=GetNextImageInList(image);
6861 }
glennrp0fe50b42010-11-16 03:52:51 +00006862
cristy3ed852e2009-09-05 21:47:34 +00006863 if (final_delay < final_image_delay)
6864 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006865
cristy3ed852e2009-09-05 21:47:34 +00006866 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006867
cristy3ed852e2009-09-05 21:47:34 +00006868 if (logging != MagickFalse)
6869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006870 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6871 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006872
cristy3ed852e2009-09-05 21:47:34 +00006873 if (logging != MagickFalse)
6874 {
6875 int
6876 scene;
6877
6878 scene=0;
6879 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006880
cristy3ed852e2009-09-05 21:47:34 +00006881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6882 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006883
cristy3ed852e2009-09-05 21:47:34 +00006884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006885 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006886
cristy3ed852e2009-09-05 21:47:34 +00006887 while (GetNextImageInList(image) != (Image *) NULL)
6888 {
6889 image=GetNextImageInList(image);
6890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006891 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006892 }
6893 }
6894
6895 image=GetFirstImageInList(image);
6896#ifdef MNG_COALESCE_LAYERS
6897 if (insert_layers)
6898 {
6899 Image
6900 *next_image,
6901 *next;
6902
cristybb503372010-05-27 20:51:26 +00006903 size_t
cristy3ed852e2009-09-05 21:47:34 +00006904 scene;
6905
6906 if (logging != MagickFalse)
6907 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006908
cristy3ed852e2009-09-05 21:47:34 +00006909 scene=image->scene;
6910 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006911
cristy3ed852e2009-09-05 21:47:34 +00006912 if (next_image == (Image *) NULL)
6913 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006914
cristy3ed852e2009-09-05 21:47:34 +00006915 image=DestroyImageList(image);
6916 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006917
cristy3ed852e2009-09-05 21:47:34 +00006918 for (next=image; next != (Image *) NULL; next=next_image)
6919 {
6920 next->page.width=mng_info->mng_width;
6921 next->page.height=mng_info->mng_height;
6922 next->page.x=0;
6923 next->page.y=0;
6924 next->scene=scene++;
6925 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006926
cristy3ed852e2009-09-05 21:47:34 +00006927 if (next_image == (Image *) NULL)
6928 break;
glennrp47b9dd52010-11-24 18:12:06 +00006929
cristy3ed852e2009-09-05 21:47:34 +00006930 if (next->delay == 0)
6931 {
6932 scene--;
6933 next_image->previous=GetPreviousImageInList(next);
6934 if (GetPreviousImageInList(next) == (Image *) NULL)
6935 image=next_image;
6936 else
6937 next->previous->next=next_image;
6938 next=DestroyImage(next);
6939 }
6940 }
6941 }
6942#endif
6943
6944 while (GetNextImageInList(image) != (Image *) NULL)
6945 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006946
cristy3ed852e2009-09-05 21:47:34 +00006947 image->dispose=BackgroundDispose;
6948
6949 if (logging != MagickFalse)
6950 {
6951 int
6952 scene;
6953
6954 scene=0;
6955 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006956
cristy3ed852e2009-09-05 21:47:34 +00006957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6958 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006959
cristy3ed852e2009-09-05 21:47:34 +00006960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006961 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6962 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006963
cristy3ed852e2009-09-05 21:47:34 +00006964 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006965 {
6966 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006967
cristyf2faecf2010-05-28 19:19:36 +00006968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006969 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6970 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006971 }
6972 }
glennrp47b9dd52010-11-24 18:12:06 +00006973
cristy3ed852e2009-09-05 21:47:34 +00006974 image=GetFirstImageInList(image);
6975 MngInfoFreeStruct(mng_info,&have_mng_structure);
6976 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006977
cristy3ed852e2009-09-05 21:47:34 +00006978 if (logging != MagickFalse)
6979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006980
cristy3ed852e2009-09-05 21:47:34 +00006981 return(GetFirstImageInList(image));
6982}
glennrp25c1e2b2010-03-25 01:39:56 +00006983#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006984static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6985{
6986 printf("Your PNG library is too old: You have libpng-%s\n",
6987 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006988
cristy3ed852e2009-09-05 21:47:34 +00006989 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6990 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006991
cristy3ed852e2009-09-05 21:47:34 +00006992 return(Image *) NULL;
6993}
glennrp47b9dd52010-11-24 18:12:06 +00006994
cristy3ed852e2009-09-05 21:47:34 +00006995static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6996{
6997 return(ReadPNGImage(image_info,exception));
6998}
glennrp25c1e2b2010-03-25 01:39:56 +00006999#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007000#endif
7001
7002/*
7003%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7004% %
7005% %
7006% %
7007% R e g i s t e r P N G I m a g e %
7008% %
7009% %
7010% %
7011%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7012%
7013% RegisterPNGImage() adds properties for the PNG image format to
7014% the list of supported formats. The properties include the image format
7015% tag, a method to read and/or write the format, whether the format
7016% supports the saving of more than one frame to the same file or blob,
7017% whether the format supports native in-memory I/O, and a brief
7018% description of the format.
7019%
7020% The format of the RegisterPNGImage method is:
7021%
cristybb503372010-05-27 20:51:26 +00007022% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007023%
7024*/
cristybb503372010-05-27 20:51:26 +00007025ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007026{
7027 char
7028 version[MaxTextExtent];
7029
7030 MagickInfo
7031 *entry;
7032
7033 static const char
7034 *PNGNote=
7035 {
7036 "See http://www.libpng.org/ for details about the PNG format."
7037 },
glennrp47b9dd52010-11-24 18:12:06 +00007038
cristy3ed852e2009-09-05 21:47:34 +00007039 *JNGNote=
7040 {
7041 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7042 "format."
7043 },
glennrp47b9dd52010-11-24 18:12:06 +00007044
cristy3ed852e2009-09-05 21:47:34 +00007045 *MNGNote=
7046 {
7047 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7048 "format."
7049 };
7050
7051 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007052
cristy3ed852e2009-09-05 21:47:34 +00007053#if defined(PNG_LIBPNG_VER_STRING)
7054 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7055 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007056
cristy3ed852e2009-09-05 21:47:34 +00007057 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7058 {
7059 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7060 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7061 MaxTextExtent);
7062 }
7063#endif
glennrp47b9dd52010-11-24 18:12:06 +00007064
cristy3ed852e2009-09-05 21:47:34 +00007065 entry=SetMagickInfo("MNG");
7066 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007067
cristy3ed852e2009-09-05 21:47:34 +00007068#if defined(MAGICKCORE_PNG_DELEGATE)
7069 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7070 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7071#endif
glennrp47b9dd52010-11-24 18:12:06 +00007072
cristy3ed852e2009-09-05 21:47:34 +00007073 entry->magick=(IsImageFormatHandler *) IsMNG;
7074 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007075
cristy3ed852e2009-09-05 21:47:34 +00007076 if (*version != '\0')
7077 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007078
cristy3ed852e2009-09-05 21:47:34 +00007079 entry->module=ConstantString("PNG");
7080 entry->note=ConstantString(MNGNote);
7081 (void) RegisterMagickInfo(entry);
7082
7083 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007084
cristy3ed852e2009-09-05 21:47:34 +00007085#if defined(MAGICKCORE_PNG_DELEGATE)
7086 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7087 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7088#endif
glennrp47b9dd52010-11-24 18:12:06 +00007089
cristy3ed852e2009-09-05 21:47:34 +00007090 entry->magick=(IsImageFormatHandler *) IsPNG;
7091 entry->adjoin=MagickFalse;
7092 entry->description=ConstantString("Portable Network Graphics");
7093 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007094
cristy3ed852e2009-09-05 21:47:34 +00007095 if (*version != '\0')
7096 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007097
cristy3ed852e2009-09-05 21:47:34 +00007098 entry->note=ConstantString(PNGNote);
7099 (void) RegisterMagickInfo(entry);
7100
7101 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007102
cristy3ed852e2009-09-05 21:47:34 +00007103#if defined(MAGICKCORE_PNG_DELEGATE)
7104 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7105 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7106#endif
glennrp47b9dd52010-11-24 18:12:06 +00007107
cristy3ed852e2009-09-05 21:47:34 +00007108 entry->magick=(IsImageFormatHandler *) IsPNG;
7109 entry->adjoin=MagickFalse;
7110 entry->description=ConstantString(
7111 "8-bit indexed with optional binary transparency");
7112 entry->module=ConstantString("PNG");
7113 (void) RegisterMagickInfo(entry);
7114
7115 entry=SetMagickInfo("PNG24");
7116 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007117
cristy3ed852e2009-09-05 21:47:34 +00007118#if defined(ZLIB_VERSION)
7119 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7120 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007121
cristy3ed852e2009-09-05 21:47:34 +00007122 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7123 {
7124 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7125 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7126 }
7127#endif
glennrp47b9dd52010-11-24 18:12:06 +00007128
cristy3ed852e2009-09-05 21:47:34 +00007129 if (*version != '\0')
7130 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007131
cristy3ed852e2009-09-05 21:47:34 +00007132#if defined(MAGICKCORE_PNG_DELEGATE)
7133 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7134 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7135#endif
glennrp47b9dd52010-11-24 18:12:06 +00007136
cristy3ed852e2009-09-05 21:47:34 +00007137 entry->magick=(IsImageFormatHandler *) IsPNG;
7138 entry->adjoin=MagickFalse;
7139 entry->description=ConstantString("opaque 24-bit RGB");
7140 entry->module=ConstantString("PNG");
7141 (void) RegisterMagickInfo(entry);
7142
7143 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007144
cristy3ed852e2009-09-05 21:47:34 +00007145#if defined(MAGICKCORE_PNG_DELEGATE)
7146 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7147 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7148#endif
glennrp47b9dd52010-11-24 18:12:06 +00007149
cristy3ed852e2009-09-05 21:47:34 +00007150 entry->magick=(IsImageFormatHandler *) IsPNG;
7151 entry->adjoin=MagickFalse;
7152 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7153 entry->module=ConstantString("PNG");
7154 (void) RegisterMagickInfo(entry);
7155
7156 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007157
cristy3ed852e2009-09-05 21:47:34 +00007158#if defined(JNG_SUPPORTED)
7159#if defined(MAGICKCORE_PNG_DELEGATE)
7160 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7161 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7162#endif
7163#endif
glennrp47b9dd52010-11-24 18:12:06 +00007164
cristy3ed852e2009-09-05 21:47:34 +00007165 entry->magick=(IsImageFormatHandler *) IsJNG;
7166 entry->adjoin=MagickFalse;
7167 entry->description=ConstantString("JPEG Network Graphics");
7168 entry->module=ConstantString("PNG");
7169 entry->note=ConstantString(JNGNote);
7170 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007171
cristy18b17442009-10-25 18:36:48 +00007172#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007173 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007174#endif
glennrp47b9dd52010-11-24 18:12:06 +00007175
cristy3ed852e2009-09-05 21:47:34 +00007176 return(MagickImageCoderSignature);
7177}
7178
7179/*
7180%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7181% %
7182% %
7183% %
7184% U n r e g i s t e r P N G I m a g e %
7185% %
7186% %
7187% %
7188%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7189%
7190% UnregisterPNGImage() removes format registrations made by the
7191% PNG module from the list of supported formats.
7192%
7193% The format of the UnregisterPNGImage method is:
7194%
7195% UnregisterPNGImage(void)
7196%
7197*/
7198ModuleExport void UnregisterPNGImage(void)
7199{
7200 (void) UnregisterMagickInfo("MNG");
7201 (void) UnregisterMagickInfo("PNG");
7202 (void) UnregisterMagickInfo("PNG8");
7203 (void) UnregisterMagickInfo("PNG24");
7204 (void) UnregisterMagickInfo("PNG32");
7205 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007206
cristy3ed852e2009-09-05 21:47:34 +00007207#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007208 if (ping_semaphore != (SemaphoreInfo *) NULL)
7209 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007210#endif
7211}
7212
7213#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007214#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007215/*
7216%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7217% %
7218% %
7219% %
7220% W r i t e M N G I m a g e %
7221% %
7222% %
7223% %
7224%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7225%
7226% WriteMNGImage() writes an image in the Portable Network Graphics
7227% Group's "Multiple-image Network Graphics" encoded image format.
7228%
7229% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7230%
7231% The format of the WriteMNGImage method is:
7232%
cristy1e178e72011-08-28 19:44:34 +00007233% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7234% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007235%
7236% A description of each parameter follows.
7237%
7238% o image_info: the image info.
7239%
7240% o image: The image.
7241%
cristy1e178e72011-08-28 19:44:34 +00007242% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007243%
7244% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7245% "To do" under ReadPNGImage):
7246%
cristy3ed852e2009-09-05 21:47:34 +00007247% Preserve all unknown and not-yet-handled known chunks found in input
7248% PNG file and copy them into output PNG files according to the PNG
7249% copying rules.
7250%
7251% Write the iCCP chunk at MNG level when (icc profile length > 0)
7252%
7253% Improve selection of color type (use indexed-colour or indexed-colour
7254% with tRNS when 256 or fewer unique RGBA values are present).
7255%
7256% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7257% This will be complicated if we limit ourselves to generating MNG-LC
7258% files. For now we ignore disposal method 3 and simply overlay the next
7259% image on it.
7260%
7261% Check for identical PLTE's or PLTE/tRNS combinations and use a
7262% global MNG PLTE or PLTE/tRNS combination when appropriate.
7263% [mostly done 15 June 1999 but still need to take care of tRNS]
7264%
7265% Check for identical sRGB and replace with a global sRGB (and remove
7266% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7267% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7268% local gAMA/cHRM with local sRGB if appropriate).
7269%
7270% Check for identical sBIT chunks and write global ones.
7271%
7272% Provide option to skip writing the signature tEXt chunks.
7273%
7274% Use signatures to detect identical objects and reuse the first
7275% instance of such objects instead of writing duplicate objects.
7276%
7277% Use a smaller-than-32k value of compression window size when
7278% appropriate.
7279%
7280% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7281% ancillary text chunks and save profiles.
7282%
7283% Provide an option to force LC files (to ensure exact framing rate)
7284% instead of VLC.
7285%
7286% Provide an option to force VLC files instead of LC, even when offsets
7287% are present. This will involve expanding the embedded images with a
7288% transparent region at the top and/or left.
7289*/
7290
cristy3ed852e2009-09-05 21:47:34 +00007291static void
glennrpcf002022011-01-30 02:38:15 +00007292Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007293 png_info *ping_info, unsigned char *profile_type, unsigned char
7294 *profile_description, unsigned char *profile_data, png_uint_32 length)
7295{
cristy3ed852e2009-09-05 21:47:34 +00007296 png_textp
7297 text;
7298
cristybb503372010-05-27 20:51:26 +00007299 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007300 i;
7301
7302 unsigned char
7303 *sp;
7304
7305 png_charp
7306 dp;
7307
7308 png_uint_32
7309 allocated_length,
7310 description_length;
7311
7312 unsigned char
7313 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007314
cristy3ed852e2009-09-05 21:47:34 +00007315 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7316 return;
7317
7318 if (image_info->verbose)
7319 {
glennrp0fe50b42010-11-16 03:52:51 +00007320 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7321 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007322 }
glennrp0fe50b42010-11-16 03:52:51 +00007323
cristy3ed852e2009-09-05 21:47:34 +00007324 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7325 description_length=(png_uint_32) strlen((const char *) profile_description);
7326 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7327 + description_length);
7328 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7329 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7330 text[0].key[0]='\0';
7331 (void) ConcatenateMagickString(text[0].key,
7332 "Raw profile type ",MaxTextExtent);
7333 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7334 sp=profile_data;
7335 dp=text[0].text;
7336 *dp++='\n';
7337 (void) CopyMagickString(dp,(const char *) profile_description,
7338 allocated_length);
7339 dp+=description_length;
7340 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007341 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007342 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007343 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007344
cristybb503372010-05-27 20:51:26 +00007345 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007346 {
7347 if (i%36 == 0)
7348 *dp++='\n';
7349 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7350 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7351 }
glennrp47b9dd52010-11-24 18:12:06 +00007352
cristy3ed852e2009-09-05 21:47:34 +00007353 *dp++='\n';
7354 *dp='\0';
7355 text[0].text_length=(png_size_t) (dp-text[0].text);
7356 text[0].compression=image_info->compression == NoCompression ||
7357 (image_info->compression == UndefinedCompression &&
7358 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007359
cristy3ed852e2009-09-05 21:47:34 +00007360 if (text[0].text_length <= allocated_length)
7361 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007362
cristy3ed852e2009-09-05 21:47:34 +00007363 png_free(ping,text[0].text);
7364 png_free(ping,text[0].key);
7365 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007366}
7367
glennrpcf002022011-01-30 02:38:15 +00007368static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007369 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007370{
7371 char
7372 *name;
7373
7374 const StringInfo
7375 *profile;
7376
7377 unsigned char
7378 *data;
7379
7380 png_uint_32 length;
7381
7382 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007383
7384 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7385 {
cristy3ed852e2009-09-05 21:47:34 +00007386 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007387
cristy3ed852e2009-09-05 21:47:34 +00007388 if (profile != (const StringInfo *) NULL)
7389 {
7390 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007391 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007392
glennrp47b9dd52010-11-24 18:12:06 +00007393 if (LocaleNCompare(name,string,11) == 0)
7394 {
7395 if (logging != MagickFalse)
7396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7397 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007398
glennrpcf002022011-01-30 02:38:15 +00007399 ping_profile=CloneStringInfo(profile);
7400 data=GetStringInfoDatum(ping_profile),
7401 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007402 data[4]=data[3];
7403 data[3]=data[2];
7404 data[2]=data[1];
7405 data[1]=data[0];
7406 (void) WriteBlobMSBULong(image,length-5); /* data length */
7407 (void) WriteBlob(image,length-1,data+1);
7408 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007409 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007410 }
cristy3ed852e2009-09-05 21:47:34 +00007411 }
glennrp47b9dd52010-11-24 18:12:06 +00007412
cristy3ed852e2009-09-05 21:47:34 +00007413 name=GetNextImageProfile(image);
7414 }
glennrp47b9dd52010-11-24 18:12:06 +00007415
cristy3ed852e2009-09-05 21:47:34 +00007416 return(MagickTrue);
7417}
7418
glennrpb9cfe272010-12-21 15:08:06 +00007419
cristy3ed852e2009-09-05 21:47:34 +00007420/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007421static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +00007422 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007423{
7424 Image
7425 *image;
7426
7427 ImageInfo
7428 *image_info;
7429
cristy3ed852e2009-09-05 21:47:34 +00007430 char
7431 s[2];
7432
7433 const char
7434 *name,
7435 *property,
7436 *value;
7437
7438 const StringInfo
7439 *profile;
7440
cristy3ed852e2009-09-05 21:47:34 +00007441 int
cristy3ed852e2009-09-05 21:47:34 +00007442 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007443 pass;
7444
glennrpe9c26dc2010-05-30 01:56:35 +00007445 png_byte
7446 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007447
glennrp39992b42010-11-14 00:03:43 +00007448 png_color
7449 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007450
glennrp5af765f2010-03-30 11:12:18 +00007451 png_color_16
7452 ping_background,
7453 ping_trans_color;
7454
cristy3ed852e2009-09-05 21:47:34 +00007455 png_info
7456 *ping_info;
7457
7458 png_struct
7459 *ping;
7460
glennrp5af765f2010-03-30 11:12:18 +00007461 png_uint_32
7462 ping_height,
7463 ping_width;
7464
cristybb503372010-05-27 20:51:26 +00007465 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007466 y;
7467
7468 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007469 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007470 logging,
glennrp58e01762011-01-07 15:28:54 +00007471 matte,
7472
glennrpda8f3a72011-02-27 23:54:12 +00007473 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007474 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007475 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007476 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007477 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007478 ping_have_bKGD,
7479 ping_have_pHYs,
7480 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007481
7482 ping_exclude_bKGD,
7483 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007484 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007485 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007486 ping_exclude_gAMA,
7487 ping_exclude_iCCP,
7488 /* ping_exclude_iTXt, */
7489 ping_exclude_oFFs,
7490 ping_exclude_pHYs,
7491 ping_exclude_sRGB,
7492 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007493 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007494 ping_exclude_vpAg,
7495 ping_exclude_zCCP, /* hex-encoded iCCP */
7496 ping_exclude_zTXt,
7497
glennrp8d3d6e52011-04-19 04:39:51 +00007498 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007499 ping_need_colortype_warning,
7500
glennrp82b3c532011-03-22 19:20:54 +00007501 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007502 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007503 tried_333,
7504 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007505
7506 QuantumInfo
7507 *quantum_info;
7508
cristybb503372010-05-27 20:51:26 +00007509 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007510 i,
7511 x;
7512
7513 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007514 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007515
glennrp5af765f2010-03-30 11:12:18 +00007516 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007517 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007518 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007519 ping_color_type,
7520 ping_interlace_method,
7521 ping_compression_method,
7522 ping_filter_method,
7523 ping_num_trans;
7524
cristybb503372010-05-27 20:51:26 +00007525 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007526 image_depth,
7527 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007528
cristybb503372010-05-27 20:51:26 +00007529 size_t
cristy3ed852e2009-09-05 21:47:34 +00007530 quality,
7531 rowbytes,
7532 save_image_depth;
7533
glennrpdfd70802010-11-14 01:23:35 +00007534 int
glennrpfd05d622011-02-25 04:10:33 +00007535 j,
glennrpf09bded2011-01-08 01:15:59 +00007536 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007537 number_opaque,
7538 number_semitransparent,
7539 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007540 ping_pHYs_unit_type;
7541
7542 png_uint_32
7543 ping_pHYs_x_resolution,
7544 ping_pHYs_y_resolution;
7545
cristy3ed852e2009-09-05 21:47:34 +00007546 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007547 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007548
glennrpb9cfe272010-12-21 15:08:06 +00007549 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7550 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007551 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007552 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007553
cristy3ed852e2009-09-05 21:47:34 +00007554#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007555 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007556#endif
7557
glennrp5af765f2010-03-30 11:12:18 +00007558 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007559 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007560 ping_color_type=0,
7561 ping_interlace_method=0,
7562 ping_compression_method=0,
7563 ping_filter_method=0,
7564 ping_num_trans = 0;
7565
7566 ping_background.red = 0;
7567 ping_background.green = 0;
7568 ping_background.blue = 0;
7569 ping_background.gray = 0;
7570 ping_background.index = 0;
7571
7572 ping_trans_color.red=0;
7573 ping_trans_color.green=0;
7574 ping_trans_color.blue=0;
7575 ping_trans_color.gray=0;
7576
glennrpdfd70802010-11-14 01:23:35 +00007577 ping_pHYs_unit_type = 0;
7578 ping_pHYs_x_resolution = 0;
7579 ping_pHYs_y_resolution = 0;
7580
glennrpda8f3a72011-02-27 23:54:12 +00007581 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007582 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007583 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007584 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007585 ping_have_bKGD=MagickFalse;
7586 ping_have_pHYs=MagickFalse;
7587 ping_have_tRNS=MagickFalse;
7588
glennrp0e8ea192010-12-24 18:00:33 +00007589 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7590 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007591 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007592 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007593 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007594 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7595 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7596 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7597 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7598 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7599 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007600 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007601 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7602 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7603 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7604
glennrp8d3d6e52011-04-19 04:39:51 +00007605 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007606 ping_need_colortype_warning = MagickFalse;
7607
cristy0d57eec2011-09-04 22:13:56 +00007608 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7609 * i.e., eliminate the ICC profile and set image->rendering_intent.
7610 * Note that this will not involve any changes to the actual pixels
7611 * but merely passes information to applications that read the resulting
7612 * PNG image.
7613 */
7614 if (ping_exclude_sRGB == MagickFalse)
7615 {
7616 char
7617 *name;
7618
7619 const StringInfo
7620 *profile;
7621
7622 ResetImageProfileIterator(image);
7623 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7624 {
7625 profile=GetImageProfile(image,name);
7626
7627 if (profile != (StringInfo *) NULL)
7628 {
7629 if ((LocaleCompare(name,"ICC") == 0) ||
glennrp29a106e2011-09-06 17:11:42 +00007630 (LocaleCompare(name,"ICM") == 0))
7631 {
glennrpee7b4c02011-10-04 01:21:09 +00007632 int
7633 icheck;
7634
7635 /* 0: not a known sRGB profile
7636 * 1: HP-Microsoft sRGB v2
7637 * 2: ICC sRGB v4 perceptual
7638 * 3: ICC sRGB v2 perceptual no black-compensation
7639 */
7640 png_uint_32
7641 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7642 check_len[4] = {0, 3144, 60960, 3052};
7643
7644 png_uint_32
7645 length,
7646 profile_crc;
7647
cristy0d57eec2011-09-04 22:13:56 +00007648 unsigned char
7649 *data;
7650
glennrp29a106e2011-09-06 17:11:42 +00007651 length=(png_uint_32) GetStringInfoLength(profile);
7652
glennrpee7b4c02011-10-04 01:21:09 +00007653 for (icheck=3; icheck > 0; icheck--)
cristy0d57eec2011-09-04 22:13:56 +00007654 {
glennrpee7b4c02011-10-04 01:21:09 +00007655 if (length == check_len[icheck])
glennrp29a106e2011-09-06 17:11:42 +00007656 {
glennrpee7b4c02011-10-04 01:21:09 +00007657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7658 " Got a %lu-byte ICC profile (potentially sRGB)",
7659 (unsigned long) length);
glennrp29a106e2011-09-06 17:11:42 +00007660
glennrpee7b4c02011-10-04 01:21:09 +00007661 data=GetStringInfoDatum(profile);
7662 profile_crc=crc32(0,data,length);
glennrp29a106e2011-09-06 17:11:42 +00007663
glennrpee7b4c02011-10-04 01:21:09 +00007664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe54cd4b2011-10-04 01:31:35 +00007665 " with crc=%8x",(unsigned int) profile_crc);
glennrpee7b4c02011-10-04 01:21:09 +00007666
7667 if (profile_crc == check_crc[icheck])
7668 {
7669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7670 " It is sRGB.");
7671 if (image->rendering_intent==UndefinedIntent)
7672 image->rendering_intent=PerceptualIntent;
7673 break;
7674 }
glennrp29a106e2011-09-06 17:11:42 +00007675 }
glennrp29a106e2011-09-06 17:11:42 +00007676 }
glennrpee7b4c02011-10-04 01:21:09 +00007677 if (icheck == 0)
glennrp29a106e2011-09-06 17:11:42 +00007678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpee7b4c02011-10-04 01:21:09 +00007679 " Got a %lu-byte ICC profile",
glennrp29a106e2011-09-06 17:11:42 +00007680 (unsigned long) length);
7681 }
cristy0d57eec2011-09-04 22:13:56 +00007682 }
7683 name=GetNextImageProfile(image);
7684 }
7685 }
7686
glennrp8bb3a022010-12-13 20:40:04 +00007687 number_opaque = 0;
7688 number_semitransparent = 0;
7689 number_transparent = 0;
7690
glennrpfd05d622011-02-25 04:10:33 +00007691 if (logging != MagickFalse)
7692 {
7693 if (image->storage_class == UndefinedClass)
7694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7695 " storage_class=UndefinedClass");
7696 if (image->storage_class == DirectClass)
7697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7698 " storage_class=DirectClass");
7699 if (image->storage_class == PseudoClass)
7700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7701 " storage_class=PseudoClass");
7702 }
glennrp28af3712011-04-06 18:07:30 +00007703
glennrp7e65e932011-08-19 02:31:16 +00007704 if (image->storage_class == PseudoClass &&
7705 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7706 (mng_info->write_png_colortype != 0 &&
7707 mng_info->write_png_colortype != 4)))
7708 {
7709 (void) SyncImage(image);
7710 image->storage_class = DirectClass;
7711 }
7712
glennrpc6c391a2011-04-27 02:23:56 +00007713 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007714 {
glennrpc6c391a2011-04-27 02:23:56 +00007715 if (image->storage_class != PseudoClass && image->colormap != NULL)
7716 {
7717 /* Free the bogus colormap; it can cause trouble later */
7718 if (logging != MagickFalse)
7719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7720 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00007721 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00007722 image->colormap=NULL;
7723 }
glennrp28af3712011-04-06 18:07:30 +00007724 }
glennrpbb4f99d2011-05-22 11:13:17 +00007725
cristy510d06a2011-07-06 23:43:54 +00007726 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristye941a752011-10-15 01:52:48 +00007727 (void) TransformImageColorspace(image,RGBColorspace,exception);
glennrp0fe50b42010-11-16 03:52:51 +00007728
glennrp3241bd02010-12-12 04:36:28 +00007729 /*
7730 Sometimes we get PseudoClass images whose RGB values don't match
7731 the colors in the colormap. This code syncs the RGB values.
7732 */
7733 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7734 (void) SyncImage(image);
7735
glennrpa6a06632011-01-19 15:15:34 +00007736#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7737 if (image->depth > 8)
7738 {
7739 if (logging != MagickFalse)
7740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7741 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7742
7743 image->depth=8;
7744 }
7745#endif
7746
glennrp8e58efd2011-05-20 12:16:29 +00007747 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007748 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7749 {
cristy4c08aed2011-07-01 19:47:50 +00007750 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007751 *r;
7752
7753 ExceptionInfo
7754 *exception;
7755
7756 exception=(&image->exception);
7757
7758 if (image->depth > 8)
7759 {
7760#if MAGICKCORE_QUANTUM_DEPTH > 16
7761 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007762 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007763
7764 for (y=0; y < (ssize_t) image->rows; y++)
7765 {
7766 r=GetAuthenticPixels(image,0,y,image->columns,1,
7767 exception);
7768
cristy4c08aed2011-07-01 19:47:50 +00007769 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007770 break;
7771
7772 for (x=0; x < (ssize_t) image->columns; x++)
7773 {
glennrp54cf7972011-08-06 14:28:09 +00007774 LBR16PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007775 r++;
7776 }
glennrpbb4f99d2011-05-22 11:13:17 +00007777
glennrp8e58efd2011-05-20 12:16:29 +00007778 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7779 break;
7780 }
7781
7782 if (image->storage_class == PseudoClass && image->colormap != NULL)
7783 {
cristy3e08f112011-05-24 13:19:30 +00007784 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007785 {
glennrp91d99252011-06-25 14:30:13 +00007786 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007787 }
7788 }
7789#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7790 }
7791
7792 else if (image->depth > 4)
7793 {
7794#if MAGICKCORE_QUANTUM_DEPTH > 8
7795 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007796 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007797
7798 for (y=0; y < (ssize_t) image->rows; y++)
7799 {
7800 r=GetAuthenticPixels(image,0,y,image->columns,1,
7801 exception);
7802
cristy4c08aed2011-07-01 19:47:50 +00007803 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007804 break;
7805
7806 for (x=0; x < (ssize_t) image->columns; x++)
7807 {
glennrp54cf7972011-08-06 14:28:09 +00007808 LBR08PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007809 r++;
7810 }
glennrpbb4f99d2011-05-22 11:13:17 +00007811
glennrp8e58efd2011-05-20 12:16:29 +00007812 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7813 break;
7814 }
7815
7816 if (image->storage_class == PseudoClass && image->colormap != NULL)
7817 {
cristy3e08f112011-05-24 13:19:30 +00007818 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007819 {
glennrp91d99252011-06-25 14:30:13 +00007820 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007821 }
7822 }
7823#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7824 }
7825 else
7826 if (image->depth > 2)
7827 {
7828 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007829 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007830
7831 for (y=0; y < (ssize_t) image->rows; y++)
7832 {
7833 r=GetAuthenticPixels(image,0,y,image->columns,1,
7834 exception);
7835
cristy4c08aed2011-07-01 19:47:50 +00007836 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007837 break;
7838
7839 for (x=0; x < (ssize_t) image->columns; x++)
7840 {
glennrp54cf7972011-08-06 14:28:09 +00007841 LBR04PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007842 r++;
7843 }
glennrpbb4f99d2011-05-22 11:13:17 +00007844
glennrp8e58efd2011-05-20 12:16:29 +00007845 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7846 break;
7847 }
7848
7849 if (image->storage_class == PseudoClass && image->colormap != NULL)
7850 {
cristy3e08f112011-05-24 13:19:30 +00007851 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007852 {
glennrp91d99252011-06-25 14:30:13 +00007853 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007854 }
7855 }
7856 }
7857
7858 else if (image->depth > 1)
7859 {
7860 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007861 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007862
7863 for (y=0; y < (ssize_t) image->rows; y++)
7864 {
7865 r=GetAuthenticPixels(image,0,y,image->columns,1,
7866 exception);
7867
cristy4c08aed2011-07-01 19:47:50 +00007868 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007869 break;
7870
7871 for (x=0; x < (ssize_t) image->columns; x++)
7872 {
glennrp54cf7972011-08-06 14:28:09 +00007873 LBR02PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007874 r++;
7875 }
glennrpbb4f99d2011-05-22 11:13:17 +00007876
glennrp8e58efd2011-05-20 12:16:29 +00007877 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7878 break;
7879 }
7880
7881 if (image->storage_class == PseudoClass && image->colormap != NULL)
7882 {
cristy3e08f112011-05-24 13:19:30 +00007883 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007884 {
glennrp91d99252011-06-25 14:30:13 +00007885 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007886 }
7887 }
7888 }
7889 else
7890 {
7891 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007892 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007893
7894 for (y=0; y < (ssize_t) image->rows; y++)
7895 {
7896 r=GetAuthenticPixels(image,0,y,image->columns,1,
7897 exception);
7898
cristy4c08aed2011-07-01 19:47:50 +00007899 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007900 break;
7901
7902 for (x=0; x < (ssize_t) image->columns; x++)
7903 {
glennrp54cf7972011-08-06 14:28:09 +00007904 LBR01PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007905 r++;
7906 }
glennrpbb4f99d2011-05-22 11:13:17 +00007907
glennrp8e58efd2011-05-20 12:16:29 +00007908 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7909 break;
7910 }
7911
7912 if (image->storage_class == PseudoClass && image->colormap != NULL)
7913 {
cristy3e08f112011-05-24 13:19:30 +00007914 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007915 {
glennrp91d99252011-06-25 14:30:13 +00007916 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007917 }
7918 }
7919 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007920 }
7921
glennrp67b9c1a2011-04-22 18:47:36 +00007922 /* To do: set to next higher multiple of 8 */
7923 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007924 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007925
glennrp2b013e42010-11-24 16:55:50 +00007926#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7927 /* PNG does not handle depths greater than 16 so reduce it even
7928 * if lossy
7929 */
glennrp8e58efd2011-05-20 12:16:29 +00007930 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007931 image->depth=16;
7932#endif
7933
glennrp3faa9a32011-04-23 14:00:25 +00007934#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007935 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007936 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007937 image->depth = 8;
7938#endif
7939
glennrpc8c2f062011-02-25 19:00:33 +00007940 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007941 * we reduce the transparency to binary and run again, then if there
7942 * 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 +00007943 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7944 * palette. Then (To do) we take care of a final reduction that is only
7945 * needed if there are still 256 colors present and one of them has both
7946 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007947 */
glennrp82b3c532011-03-22 19:20:54 +00007948
glennrp8ca51ad2011-05-12 21:22:32 +00007949 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007950 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007951 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007952
glennrp8ca51ad2011-05-12 21:22:32 +00007953 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007954 {
7955 /* BUILD_PALETTE
7956 *
7957 * Sometimes we get DirectClass images that have 256 colors or fewer.
7958 * This code will build a colormap.
7959 *
7960 * Also, sometimes we get PseudoClass images with an out-of-date
7961 * colormap. This code will replace the colormap with a new one.
7962 * Sometimes we get PseudoClass images that have more than 256 colors.
7963 * This code will delete the colormap and change the image to
7964 * DirectClass.
7965 *
cristy4c08aed2011-07-01 19:47:50 +00007966 * If image->matte is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00007967 * even though it sometimes contains left-over non-opaque values.
7968 *
7969 * Also we gather some information (number of opaque, transparent,
7970 * and semitransparent pixels, and whether the image has any non-gray
7971 * pixels or only black-and-white pixels) that we might need later.
7972 *
7973 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7974 * we need to check for bogus non-opaque values, at least.
7975 */
glennrp3c218112010-11-27 15:31:26 +00007976
glennrpd71e86a2011-02-24 01:28:37 +00007977 ExceptionInfo
7978 *exception;
glennrp3c218112010-11-27 15:31:26 +00007979
glennrpd71e86a2011-02-24 01:28:37 +00007980 int
7981 n;
glennrp3c218112010-11-27 15:31:26 +00007982
cristy101ab702011-10-13 13:06:32 +00007983 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00007984 opaque[260],
7985 semitransparent[260],
7986 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007987
cristy4c08aed2011-07-01 19:47:50 +00007988 register const Quantum
7989 *s;
glennrp8bb3a022010-12-13 20:40:04 +00007990
cristy4c08aed2011-07-01 19:47:50 +00007991 register Quantum
7992 *q,
glennrpfd05d622011-02-25 04:10:33 +00007993 *r;
7994
glennrpd71e86a2011-02-24 01:28:37 +00007995 if (logging != MagickFalse)
7996 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7997 " Enter BUILD_PALETTE:");
7998
7999 if (logging != MagickFalse)
8000 {
glennrp03812ae2010-12-24 01:31:34 +00008001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008002 " image->columns=%.20g",(double) image->columns);
8003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8004 " image->rows=%.20g",(double) image->rows);
8005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8006 " image->matte=%.20g",(double) image->matte);
8007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8008 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008009
glennrpfd05d622011-02-25 04:10:33 +00008010 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008011 {
8012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008013 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008015 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008016
glennrpd71e86a2011-02-24 01:28:37 +00008017 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008018 {
glennrpd71e86a2011-02-24 01:28:37 +00008019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8020 " %d (%d,%d,%d,%d)",
8021 (int) i,
8022 (int) image->colormap[i].red,
8023 (int) image->colormap[i].green,
8024 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008025 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008026 }
glennrp2cc891a2010-12-24 13:44:32 +00008027
glennrpd71e86a2011-02-24 01:28:37 +00008028 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8029 {
8030 if (i > 255)
8031 {
8032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8033 " %d (%d,%d,%d,%d)",
8034 (int) i,
8035 (int) image->colormap[i].red,
8036 (int) image->colormap[i].green,
8037 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008038 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008039 }
8040 }
glennrp03812ae2010-12-24 01:31:34 +00008041 }
glennrp7ddcc222010-12-11 05:01:05 +00008042
glennrpd71e86a2011-02-24 01:28:37 +00008043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8044 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008045
glennrpd71e86a2011-02-24 01:28:37 +00008046 if (image->colors == 0)
8047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8048 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008049
glennrp8d3d6e52011-04-19 04:39:51 +00008050 if (ping_preserve_colormap == MagickFalse)
8051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8052 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008053 }
8054
8055 exception=(&image->exception);
8056
8057 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008058 number_opaque = 0;
8059 number_semitransparent = 0;
8060 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008061
8062 for (y=0; y < (ssize_t) image->rows; y++)
8063 {
8064 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8065
cristyacd2ed22011-08-30 01:44:23 +00008066 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008067 break;
8068
8069 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008070 {
glennrp4737d522011-04-29 03:33:42 +00008071 if (image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008072 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008073 {
8074 if (number_opaque < 259)
8075 {
8076 if (number_opaque == 0)
8077 {
cristy101ab702011-10-13 13:06:32 +00008078 GetPixelInfoPixel(image, q, opaque);
cristy4c08aed2011-07-01 19:47:50 +00008079 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008080 number_opaque=1;
8081 }
glennrp2cc891a2010-12-24 13:44:32 +00008082
glennrpd71e86a2011-02-24 01:28:37 +00008083 for (i=0; i< (ssize_t) number_opaque; i++)
8084 {
cristy4c08aed2011-07-01 19:47:50 +00008085 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008086 break;
8087 }
glennrp7ddcc222010-12-11 05:01:05 +00008088
glennrpd71e86a2011-02-24 01:28:37 +00008089 if (i == (ssize_t) number_opaque &&
8090 number_opaque < 259)
8091 {
8092 number_opaque++;
cristy101ab702011-10-13 13:06:32 +00008093 GetPixelInfoPixel(image, q, opaque+i);
cristy4c08aed2011-07-01 19:47:50 +00008094 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008095 }
8096 }
8097 }
cristy4c08aed2011-07-01 19:47:50 +00008098 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008099 {
8100 if (number_transparent < 259)
8101 {
8102 if (number_transparent == 0)
8103 {
cristy101ab702011-10-13 13:06:32 +00008104 GetPixelInfoPixel(image, q, transparent);
cristy4c08aed2011-07-01 19:47:50 +00008105 ping_trans_color.red=(unsigned short)
8106 GetPixelRed(image,q);
8107 ping_trans_color.green=(unsigned short)
8108 GetPixelGreen(image,q);
8109 ping_trans_color.blue=(unsigned short)
8110 GetPixelBlue(image,q);
8111 ping_trans_color.gray=(unsigned short)
8112 GetPixelRed(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008113 number_transparent = 1;
8114 }
8115
8116 for (i=0; i< (ssize_t) number_transparent; i++)
8117 {
cristy4c08aed2011-07-01 19:47:50 +00008118 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008119 break;
8120 }
8121
8122 if (i == (ssize_t) number_transparent &&
8123 number_transparent < 259)
8124 {
8125 number_transparent++;
cristy101ab702011-10-13 13:06:32 +00008126 GetPixelInfoPixel(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008127 }
8128 }
8129 }
8130 else
8131 {
8132 if (number_semitransparent < 259)
8133 {
8134 if (number_semitransparent == 0)
8135 {
cristy101ab702011-10-13 13:06:32 +00008136 GetPixelInfoPixel(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008137 number_semitransparent = 1;
8138 }
8139
8140 for (i=0; i< (ssize_t) number_semitransparent; i++)
8141 {
cristy4c08aed2011-07-01 19:47:50 +00008142 if (IsPixelEquivalent(image,q, semitransparent+i)
8143 && GetPixelAlpha(image,q) ==
8144 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008145 break;
8146 }
8147
8148 if (i == (ssize_t) number_semitransparent &&
8149 number_semitransparent < 259)
8150 {
8151 number_semitransparent++;
cristy101ab702011-10-13 13:06:32 +00008152 GetPixelInfoPixel(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008153 }
8154 }
8155 }
cristyed231572011-07-14 02:18:59 +00008156 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008157 }
8158 }
8159
cristy4054bfb2011-08-29 23:41:39 +00008160 if (mng_info->write_png8 == MagickFalse &&
8161 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008162 {
8163 /* Add the background color to the palette, if it
8164 * isn't already there.
8165 */
glennrpc6c391a2011-04-27 02:23:56 +00008166 if (logging != MagickFalse)
8167 {
8168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8169 " Check colormap for background (%d,%d,%d)",
8170 (int) image->background_color.red,
8171 (int) image->background_color.green,
8172 (int) image->background_color.blue);
8173 }
glennrpd71e86a2011-02-24 01:28:37 +00008174 for (i=0; i<number_opaque; i++)
8175 {
glennrpca7ad3a2011-04-26 04:44:54 +00008176 if (opaque[i].red == image->background_color.red &&
8177 opaque[i].green == image->background_color.green &&
8178 opaque[i].blue == image->background_color.blue)
8179 break;
glennrpd71e86a2011-02-24 01:28:37 +00008180 }
glennrpd71e86a2011-02-24 01:28:37 +00008181 if (number_opaque < 259 && i == number_opaque)
8182 {
glennrp8e045c82011-04-27 16:40:27 +00008183 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008184 ping_background.index = i;
8185 if (logging != MagickFalse)
8186 {
8187 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8188 " background_color index is %d",(int) i);
8189 }
8190
glennrpd71e86a2011-02-24 01:28:37 +00008191 }
glennrpa080bc32011-03-11 18:03:44 +00008192 else if (logging != MagickFalse)
8193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8194 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008195 }
8196
8197 image_colors=number_opaque+number_transparent+number_semitransparent;
8198
glennrpa080bc32011-03-11 18:03:44 +00008199 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8200 {
8201 /* No room for the background color; remove it. */
8202 number_opaque--;
8203 image_colors--;
8204 }
8205
glennrpd71e86a2011-02-24 01:28:37 +00008206 if (logging != MagickFalse)
8207 {
8208 if (image_colors > 256)
8209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8210 " image has more than 256 colors");
8211
8212 else
8213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8214 " image has %d colors",image_colors);
8215 }
8216
glennrp8d3d6e52011-04-19 04:39:51 +00008217 if (ping_preserve_colormap != MagickFalse)
8218 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008219
glennrpfd05d622011-02-25 04:10:33 +00008220 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008221 {
8222 ping_have_color=MagickFalse;
8223 ping_have_non_bw=MagickFalse;
8224
8225 if(image_colors > 256)
8226 {
8227 for (y=0; y < (ssize_t) image->rows; y++)
8228 {
8229 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8230
cristyacd2ed22011-08-30 01:44:23 +00008231 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008232 break;
8233
glennrpe5e6b802011-07-20 14:44:40 +00008234 s=q;
8235 for (x=0; x < (ssize_t) image->columns; x++)
8236 {
8237 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8238 GetPixelRed(image,s) != GetPixelBlue(image,s))
8239 {
8240 ping_have_color=MagickTrue;
8241 ping_have_non_bw=MagickTrue;
8242 break;
8243 }
8244 s+=GetPixelChannels(image);
8245 }
8246
8247 if (ping_have_color != MagickFalse)
8248 break;
8249
glennrpd71e86a2011-02-24 01:28:37 +00008250 /* Worst case is black-and-white; we are looking at every
8251 * pixel twice.
8252 */
8253
glennrpd71e86a2011-02-24 01:28:37 +00008254 if (ping_have_non_bw == MagickFalse)
8255 {
8256 s=q;
8257 for (x=0; x < (ssize_t) image->columns; x++)
8258 {
cristy4c08aed2011-07-01 19:47:50 +00008259 if (GetPixelRed(image,s) != 0 &&
8260 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008261 {
8262 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008263 break;
glennrpd71e86a2011-02-24 01:28:37 +00008264 }
cristyed231572011-07-14 02:18:59 +00008265 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008266 }
glennrpe5e6b802011-07-20 14:44:40 +00008267 }
glennrpd71e86a2011-02-24 01:28:37 +00008268 }
glennrpbb4f99d2011-05-22 11:13:17 +00008269 }
8270 }
glennrpd71e86a2011-02-24 01:28:37 +00008271
8272 if (image_colors < 257)
8273 {
cristy101ab702011-10-13 13:06:32 +00008274 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008275 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008276
glennrpd71e86a2011-02-24 01:28:37 +00008277 /*
8278 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008279 */
8280
glennrpd71e86a2011-02-24 01:28:37 +00008281 if (logging != MagickFalse)
8282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8283 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008284
glennrpd71e86a2011-02-24 01:28:37 +00008285 /* Sort palette, transparent first */;
8286
8287 n = 0;
8288
8289 for (i=0; i<number_transparent; i++)
8290 colormap[n++] = transparent[i];
8291
8292 for (i=0; i<number_semitransparent; i++)
8293 colormap[n++] = semitransparent[i];
8294
8295 for (i=0; i<number_opaque; i++)
8296 colormap[n++] = opaque[i];
8297
glennrpc6c391a2011-04-27 02:23:56 +00008298 ping_background.index +=
8299 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008300
glennrpd71e86a2011-02-24 01:28:37 +00008301 /* image_colors < 257; search the colormap instead of the pixels
8302 * to get ping_have_color and ping_have_non_bw
8303 */
8304 for (i=0; i<n; i++)
8305 {
8306 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008307 {
glennrpd71e86a2011-02-24 01:28:37 +00008308 if (colormap[i].red != colormap[i].green ||
8309 colormap[i].red != colormap[i].blue)
8310 {
8311 ping_have_color=MagickTrue;
8312 ping_have_non_bw=MagickTrue;
8313 break;
8314 }
8315 }
8316
8317 if (ping_have_non_bw == MagickFalse)
8318 {
8319 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008320 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008321 }
glennrp8bb3a022010-12-13 20:40:04 +00008322 }
8323
glennrpd71e86a2011-02-24 01:28:37 +00008324 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8325 (number_transparent == 0 && number_semitransparent == 0)) &&
8326 (((mng_info->write_png_colortype-1) ==
8327 PNG_COLOR_TYPE_PALETTE) ||
8328 (mng_info->write_png_colortype == 0)))
8329 {
glennrp6185c532011-01-14 17:58:40 +00008330 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008331 {
glennrpd71e86a2011-02-24 01:28:37 +00008332 if (n != (ssize_t) image_colors)
8333 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8334 " image_colors (%d) and n (%d) don't match",
8335 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008336
glennrpd71e86a2011-02-24 01:28:37 +00008337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8338 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008339 }
glennrp03812ae2010-12-24 01:31:34 +00008340
glennrpd71e86a2011-02-24 01:28:37 +00008341 image->colors = image_colors;
8342
cristy018f07f2011-09-04 21:15:19 +00008343 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008344 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008345 ThrowWriterException(ResourceLimitError,
8346 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008347
8348 for (i=0; i< (ssize_t) image_colors; i++)
8349 image->colormap[i] = colormap[i];
8350
8351 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008352 {
glennrpd71e86a2011-02-24 01:28:37 +00008353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8354 " image->colors=%d (%d)",
8355 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008356
glennrpd71e86a2011-02-24 01:28:37 +00008357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8358 " Update the pixel indexes");
8359 }
glennrp6185c532011-01-14 17:58:40 +00008360
glennrpfd05d622011-02-25 04:10:33 +00008361 /* Sync the pixel indices with the new colormap */
8362
glennrpd71e86a2011-02-24 01:28:37 +00008363 for (y=0; y < (ssize_t) image->rows; y++)
8364 {
8365 q=GetAuthenticPixels(image,0,y,image->columns,1,
8366 exception);
glennrp6185c532011-01-14 17:58:40 +00008367
cristyacd2ed22011-08-30 01:44:23 +00008368 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008369 break;
glennrp6185c532011-01-14 17:58:40 +00008370
glennrpbb4f99d2011-05-22 11:13:17 +00008371
glennrpd71e86a2011-02-24 01:28:37 +00008372 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008373 {
glennrpd71e86a2011-02-24 01:28:37 +00008374 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008375 {
glennrpd71e86a2011-02-24 01:28:37 +00008376 if ((image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008377 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8378 image->colormap[i].red == GetPixelRed(image,q) &&
8379 image->colormap[i].green == GetPixelGreen(image,q) &&
8380 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008381 {
cristy4c08aed2011-07-01 19:47:50 +00008382 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008383 break;
glennrp6185c532011-01-14 17:58:40 +00008384 }
glennrp6185c532011-01-14 17:58:40 +00008385 }
cristyed231572011-07-14 02:18:59 +00008386 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008387 }
glennrp6185c532011-01-14 17:58:40 +00008388
glennrpd71e86a2011-02-24 01:28:37 +00008389 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8390 break;
8391 }
8392 }
8393 }
8394
8395 if (logging != MagickFalse)
8396 {
8397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8398 " image->colors=%d", (int) image->colors);
8399
8400 if (image->colormap != NULL)
8401 {
8402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008403 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008404
8405 for (i=0; i < (ssize_t) image->colors; i++)
8406 {
cristy72988482011-03-29 16:34:38 +00008407 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008408 {
8409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8410 " %d (%d,%d,%d,%d)",
8411 (int) i,
8412 (int) image->colormap[i].red,
8413 (int) image->colormap[i].green,
8414 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008415 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008416 }
glennrp6185c532011-01-14 17:58:40 +00008417 }
8418 }
glennrp03812ae2010-12-24 01:31:34 +00008419
glennrpd71e86a2011-02-24 01:28:37 +00008420 if (number_transparent < 257)
8421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8422 " number_transparent = %d",
8423 number_transparent);
8424 else
glennrp03812ae2010-12-24 01:31:34 +00008425
glennrpd71e86a2011-02-24 01:28:37 +00008426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8427 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008428
glennrpd71e86a2011-02-24 01:28:37 +00008429 if (number_opaque < 257)
8430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8431 " number_opaque = %d",
8432 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008433
glennrpd71e86a2011-02-24 01:28:37 +00008434 else
8435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8436 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008437
glennrpd71e86a2011-02-24 01:28:37 +00008438 if (number_semitransparent < 257)
8439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8440 " number_semitransparent = %d",
8441 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008442
glennrpd71e86a2011-02-24 01:28:37 +00008443 else
8444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8445 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008446
glennrpd71e86a2011-02-24 01:28:37 +00008447 if (ping_have_non_bw == MagickFalse)
8448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8449 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008450
glennrpd71e86a2011-02-24 01:28:37 +00008451 else if (ping_have_color == MagickFalse)
8452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8453 " All pixels and the background are gray");
8454
8455 else
8456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8457 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008458
glennrp03812ae2010-12-24 01:31:34 +00008459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8460 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008461 }
glennrpfd05d622011-02-25 04:10:33 +00008462
glennrpc8c2f062011-02-25 19:00:33 +00008463 if (mng_info->write_png8 == MagickFalse)
8464 break;
glennrpfd05d622011-02-25 04:10:33 +00008465
glennrpc8c2f062011-02-25 19:00:33 +00008466 /* Make any reductions necessary for the PNG8 format */
8467 if (image_colors <= 256 &&
8468 image_colors != 0 && image->colormap != NULL &&
8469 number_semitransparent == 0 &&
8470 number_transparent <= 1)
8471 break;
8472
8473 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008474 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8475 * transparent color so if more than one is transparent we merge
8476 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008477 */
glennrp130fc452011-08-20 03:43:18 +00008478 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008479 {
8480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8481 " Thresholding the alpha channel to binary");
8482
8483 for (y=0; y < (ssize_t) image->rows; y++)
8484 {
8485 r=GetAuthenticPixels(image,0,y,image->columns,1,
8486 exception);
8487
cristy4c08aed2011-07-01 19:47:50 +00008488 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008489 break;
8490
8491 for (x=0; x < (ssize_t) image->columns; x++)
8492 {
glennrpf73547f2011-08-20 04:40:26 +00008493 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008494 {
cristy101ab702011-10-13 13:06:32 +00008495 SetPixelPixelInfo(image,&image->background_color,r);
cristy4c08aed2011-07-01 19:47:50 +00008496 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008497 }
8498 else
cristy4c08aed2011-07-01 19:47:50 +00008499 SetPixelAlpha(image,OpaqueAlpha,r);
cristyed231572011-07-14 02:18:59 +00008500 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008501 }
glennrpbb4f99d2011-05-22 11:13:17 +00008502
glennrpc8c2f062011-02-25 19:00:33 +00008503 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8504 break;
8505
8506 if (image_colors != 0 && image_colors <= 256 &&
8507 image->colormap != NULL)
8508 for (i=0; i<image_colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00008509 image->colormap[i].alpha =
8510 (image->colormap[i].alpha > TransparentAlpha/2 ?
8511 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008512 }
8513 continue;
8514 }
8515
8516 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008517 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8518 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8519 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008520 */
glennrpd3371642011-03-22 19:42:23 +00008521 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8522 {
8523 if (logging != MagickFalse)
8524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8525 " Quantizing the background color to 4-4-4");
8526
8527 tried_444 = MagickTrue;
8528
glennrp91d99252011-06-25 14:30:13 +00008529 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008530
8531 if (logging != MagickFalse)
8532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8533 " Quantizing the pixel colors to 4-4-4");
8534
8535 if (image->colormap == NULL)
8536 {
8537 for (y=0; y < (ssize_t) image->rows; y++)
8538 {
8539 r=GetAuthenticPixels(image,0,y,image->columns,1,
8540 exception);
8541
cristy4c08aed2011-07-01 19:47:50 +00008542 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008543 break;
8544
8545 for (x=0; x < (ssize_t) image->columns; x++)
8546 {
cristy4c08aed2011-07-01 19:47:50 +00008547 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008548 LBR04PixelRGB(r);
glennrpd3371642011-03-22 19:42:23 +00008549 r++;
8550 }
glennrpbb4f99d2011-05-22 11:13:17 +00008551
glennrpd3371642011-03-22 19:42:23 +00008552 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8553 break;
8554 }
8555 }
8556
8557 else /* Should not reach this; colormap already exists and
8558 must be <= 256 */
8559 {
8560 if (logging != MagickFalse)
8561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8562 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008563
glennrpd3371642011-03-22 19:42:23 +00008564 for (i=0; i<image_colors; i++)
8565 {
glennrp91d99252011-06-25 14:30:13 +00008566 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008567 }
8568 }
8569 continue;
8570 }
8571
glennrp82b3c532011-03-22 19:20:54 +00008572 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8573 {
8574 if (logging != MagickFalse)
8575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8576 " Quantizing the background color to 3-3-3");
8577
8578 tried_333 = MagickTrue;
8579
glennrp91d99252011-06-25 14:30:13 +00008580 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008581
8582 if (logging != MagickFalse)
8583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008584 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008585
8586 if (image->colormap == NULL)
8587 {
8588 for (y=0; y < (ssize_t) image->rows; y++)
8589 {
8590 r=GetAuthenticPixels(image,0,y,image->columns,1,
8591 exception);
8592
cristy4c08aed2011-07-01 19:47:50 +00008593 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008594 break;
8595
8596 for (x=0; x < (ssize_t) image->columns; x++)
8597 {
cristy4c08aed2011-07-01 19:47:50 +00008598 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8599 LBR03RGB(r);
glennrp82b3c532011-03-22 19:20:54 +00008600 r++;
8601 }
glennrpbb4f99d2011-05-22 11:13:17 +00008602
glennrp82b3c532011-03-22 19:20:54 +00008603 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8604 break;
8605 }
8606 }
8607
8608 else /* Should not reach this; colormap already exists and
8609 must be <= 256 */
8610 {
8611 if (logging != MagickFalse)
8612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008613 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008614 for (i=0; i<image_colors; i++)
8615 {
glennrp91d99252011-06-25 14:30:13 +00008616 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008617 }
glennrpd3371642011-03-22 19:42:23 +00008618 }
8619 continue;
glennrp82b3c532011-03-22 19:20:54 +00008620 }
glennrpc8c2f062011-02-25 19:00:33 +00008621
glennrp8ca51ad2011-05-12 21:22:32 +00008622 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008623 {
8624 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008626 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008627
glennrp8ca51ad2011-05-12 21:22:32 +00008628 tried_332 = MagickTrue;
8629
glennrp3faa9a32011-04-23 14:00:25 +00008630 /* Red and green were already done so we only quantize the blue
8631 * channel
8632 */
8633
glennrp91d99252011-06-25 14:30:13 +00008634 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008635
glennrpc8c2f062011-02-25 19:00:33 +00008636 if (logging != MagickFalse)
8637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008638 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008639
glennrpc8c2f062011-02-25 19:00:33 +00008640 if (image->colormap == NULL)
8641 {
8642 for (y=0; y < (ssize_t) image->rows; y++)
8643 {
8644 r=GetAuthenticPixels(image,0,y,image->columns,1,
8645 exception);
8646
cristy4c08aed2011-07-01 19:47:50 +00008647 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008648 break;
8649
8650 for (x=0; x < (ssize_t) image->columns; x++)
8651 {
cristy4c08aed2011-07-01 19:47:50 +00008652 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008653 LBR02PixelBlue(r);
glennrp52a479c2011-02-26 21:14:38 +00008654 r++;
glennrpc8c2f062011-02-25 19:00:33 +00008655 }
glennrpbb4f99d2011-05-22 11:13:17 +00008656
glennrpc8c2f062011-02-25 19:00:33 +00008657 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8658 break;
8659 }
8660 }
glennrpfd05d622011-02-25 04:10:33 +00008661
glennrpc8c2f062011-02-25 19:00:33 +00008662 else /* Should not reach this; colormap already exists and
8663 must be <= 256 */
8664 {
8665 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008667 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008668 for (i=0; i<image_colors; i++)
8669 {
glennrp91d99252011-06-25 14:30:13 +00008670 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008671 }
8672 }
8673 continue;
8674 }
8675 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008676
8677 if (image_colors == 0 || image_colors > 256)
8678 {
8679 /* Take care of special case with 256 colors + 1 transparent
8680 * color. We don't need to quantize to 2-3-2-1; we only need to
8681 * eliminate one color, so we'll merge the two darkest red
8682 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8683 */
8684 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8685 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8686 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8687 {
8688 image->background_color.red=ScaleCharToQuantum(0x24);
8689 }
glennrpbb4f99d2011-05-22 11:13:17 +00008690
glennrp8ca51ad2011-05-12 21:22:32 +00008691 if (image->colormap == NULL)
8692 {
8693 for (y=0; y < (ssize_t) image->rows; y++)
8694 {
8695 r=GetAuthenticPixels(image,0,y,image->columns,1,
8696 exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008697
cristy4c08aed2011-07-01 19:47:50 +00008698 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008699 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008700
glennrp8ca51ad2011-05-12 21:22:32 +00008701 for (x=0; x < (ssize_t) image->columns; x++)
8702 {
cristy4c08aed2011-07-01 19:47:50 +00008703 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8704 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8705 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8706 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008707 {
cristy4c08aed2011-07-01 19:47:50 +00008708 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008709 }
cristyed231572011-07-14 02:18:59 +00008710 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008711 }
glennrpbb4f99d2011-05-22 11:13:17 +00008712
glennrp8ca51ad2011-05-12 21:22:32 +00008713 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8714 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008715
glennrp8ca51ad2011-05-12 21:22:32 +00008716 }
8717 }
8718
8719 else
8720 {
8721 for (i=0; i<image_colors; i++)
8722 {
8723 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8724 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8725 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8726 {
8727 image->colormap[i].red=ScaleCharToQuantum(0x24);
8728 }
8729 }
8730 }
8731 }
glennrpd71e86a2011-02-24 01:28:37 +00008732 }
glennrpfd05d622011-02-25 04:10:33 +00008733 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008734
glennrpfd05d622011-02-25 04:10:33 +00008735 /* If we are excluding the tRNS chunk and there is transparency,
8736 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8737 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008738 */
glennrp0e8ea192010-12-24 18:00:33 +00008739 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8740 (number_transparent != 0 || number_semitransparent != 0))
8741 {
glennrpd17915c2011-04-29 14:24:22 +00008742 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008743
8744 if (ping_have_color == MagickFalse)
8745 mng_info->write_png_colortype = 5;
8746
8747 else
8748 mng_info->write_png_colortype = 7;
8749
glennrp8d579662011-02-23 02:05:02 +00008750 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008751 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008752 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008753
glennrp0e8ea192010-12-24 18:00:33 +00008754 }
8755
glennrpfd05d622011-02-25 04:10:33 +00008756 /* See if cheap transparency is possible. It is only possible
8757 * when there is a single transparent color, no semitransparent
8758 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008759 * as the transparent color. We only need this information if
8760 * we are writing a PNG with colortype 0 or 2, and we have not
8761 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008762 */
glennrp5a39f372011-02-25 04:52:16 +00008763 if (number_transparent == 1 &&
8764 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008765 {
8766 ping_have_cheap_transparency = MagickTrue;
8767
8768 if (number_semitransparent != 0)
8769 ping_have_cheap_transparency = MagickFalse;
8770
8771 else if (image_colors == 0 || image_colors > 256 ||
8772 image->colormap == NULL)
8773 {
8774 ExceptionInfo
8775 *exception;
8776
cristy4c08aed2011-07-01 19:47:50 +00008777 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008778 *q;
8779
8780 exception=(&image->exception);
8781
8782 for (y=0; y < (ssize_t) image->rows; y++)
8783 {
8784 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8785
cristyacd2ed22011-08-30 01:44:23 +00008786 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008787 break;
8788
8789 for (x=0; x < (ssize_t) image->columns; x++)
8790 {
cristy4c08aed2011-07-01 19:47:50 +00008791 if (GetPixelAlpha(image,q) != TransparentAlpha &&
glennrp847370c2011-07-05 17:37:15 +00008792 (unsigned short) GetPixelRed(image,q) ==
8793 ping_trans_color.red &&
8794 (unsigned short) GetPixelGreen(image,q) ==
8795 ping_trans_color.green &&
8796 (unsigned short) GetPixelBlue(image,q) ==
8797 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008798 {
8799 ping_have_cheap_transparency = MagickFalse;
8800 break;
8801 }
8802
cristyed231572011-07-14 02:18:59 +00008803 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008804 }
glennrpbb4f99d2011-05-22 11:13:17 +00008805
glennrpfd05d622011-02-25 04:10:33 +00008806 if (ping_have_cheap_transparency == MagickFalse)
8807 break;
8808 }
8809 }
8810 else
8811 {
glennrp67b9c1a2011-04-22 18:47:36 +00008812 /* Assuming that image->colormap[0] is the one transparent color
8813 * and that all others are opaque.
8814 */
glennrpfd05d622011-02-25 04:10:33 +00008815 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008816 for (i=1; i<image_colors; i++)
8817 if (image->colormap[i].red == image->colormap[0].red &&
8818 image->colormap[i].green == image->colormap[0].green &&
8819 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008820 {
glennrp67b9c1a2011-04-22 18:47:36 +00008821 ping_have_cheap_transparency = MagickFalse;
8822 break;
glennrpfd05d622011-02-25 04:10:33 +00008823 }
8824 }
glennrpbb4f99d2011-05-22 11:13:17 +00008825
glennrpfd05d622011-02-25 04:10:33 +00008826 if (logging != MagickFalse)
8827 {
8828 if (ping_have_cheap_transparency == MagickFalse)
8829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8830 " Cheap transparency is not possible.");
8831
8832 else
8833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8834 " Cheap transparency is possible.");
8835 }
8836 }
8837 else
8838 ping_have_cheap_transparency = MagickFalse;
8839
glennrp8640fb52010-11-23 15:48:26 +00008840 image_depth=image->depth;
8841
glennrp26c990a2010-11-23 02:23:20 +00008842 quantum_info = (QuantumInfo *) NULL;
8843 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008844 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008845 image_matte=image->matte;
8846
glennrp0fe50b42010-11-16 03:52:51 +00008847 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008848 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008849
glennrp52a479c2011-02-26 21:14:38 +00008850 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8851 (image->colors == 0 || image->colormap == NULL))
8852 {
glennrp52a479c2011-02-26 21:14:38 +00008853 image_info=DestroyImageInfo(image_info);
8854 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008855 (void) ThrowMagickException(&IMimage->exception,
8856 GetMagickModule(),CoderError,
8857 "Cannot write PNG8 or color-type 3; colormap is NULL",
8858 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008859#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8860 UnlockSemaphoreInfo(ping_semaphore);
8861#endif
8862 return(MagickFalse);
8863 }
8864
cristy3ed852e2009-09-05 21:47:34 +00008865 /*
8866 Allocate the PNG structures
8867 */
8868#ifdef PNG_USER_MEM_SUPPORTED
8869 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008870 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8871 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008872
cristy3ed852e2009-09-05 21:47:34 +00008873#else
8874 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008875 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008876
cristy3ed852e2009-09-05 21:47:34 +00008877#endif
8878 if (ping == (png_struct *) NULL)
8879 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008880
cristy3ed852e2009-09-05 21:47:34 +00008881 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008882
cristy3ed852e2009-09-05 21:47:34 +00008883 if (ping_info == (png_info *) NULL)
8884 {
8885 png_destroy_write_struct(&ping,(png_info **) NULL);
8886 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8887 }
glennrp0fe50b42010-11-16 03:52:51 +00008888
cristy3ed852e2009-09-05 21:47:34 +00008889 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008890 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008891
glennrp5af765f2010-03-30 11:12:18 +00008892 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008893 {
8894 /*
8895 PNG write failed.
8896 */
8897#ifdef PNG_DEBUG
8898 if (image_info->verbose)
8899 (void) printf("PNG write has failed.\n");
8900#endif
8901 png_destroy_write_struct(&ping,&ping_info);
8902#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008903 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008904#endif
glennrpda8f3a72011-02-27 23:54:12 +00008905 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008906 (void) CloseBlob(image);
8907 image_info=DestroyImageInfo(image_info);
8908 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008909 return(MagickFalse);
8910 }
8911 /*
8912 Prepare PNG for writing.
8913 */
8914#if defined(PNG_MNG_FEATURES_SUPPORTED)
8915 if (mng_info->write_mng)
8916 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008917
cristy3ed852e2009-09-05 21:47:34 +00008918#else
8919# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8920 if (mng_info->write_mng)
8921 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008922
cristy3ed852e2009-09-05 21:47:34 +00008923# endif
8924#endif
glennrp2b013e42010-11-24 16:55:50 +00008925
cristy3ed852e2009-09-05 21:47:34 +00008926 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008927
cristy4e5bc842010-06-09 13:56:01 +00008928 ping_width=(png_uint_32) image->columns;
8929 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008930
cristy3ed852e2009-09-05 21:47:34 +00008931 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8932 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008933
cristy3ed852e2009-09-05 21:47:34 +00008934 if (mng_info->write_png_depth != 0)
8935 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008936
cristy3ed852e2009-09-05 21:47:34 +00008937 /* Adjust requested depth to next higher valid depth if necessary */
8938 if (image_depth > 8)
8939 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008940
cristy3ed852e2009-09-05 21:47:34 +00008941 if ((image_depth > 4) && (image_depth < 8))
8942 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008943
cristy3ed852e2009-09-05 21:47:34 +00008944 if (image_depth == 3)
8945 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008946
cristy3ed852e2009-09-05 21:47:34 +00008947 if (logging != MagickFalse)
8948 {
8949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008950 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008952 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008954 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008956 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008958 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008959 }
glennrp8640fb52010-11-23 15:48:26 +00008960
cristy3ed852e2009-09-05 21:47:34 +00008961 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008962 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008963
glennrp26f37912010-12-23 16:22:42 +00008964
cristy3ed852e2009-09-05 21:47:34 +00008965#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008966 if (ping_exclude_pHYs == MagickFalse)
8967 {
cristy3ed852e2009-09-05 21:47:34 +00008968 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8969 (!mng_info->write_mng || !mng_info->equal_physs))
8970 {
glennrp0fe50b42010-11-16 03:52:51 +00008971 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8973 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008974
8975 if (image->units == PixelsPerInchResolution)
8976 {
glennrpdfd70802010-11-14 01:23:35 +00008977 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008978 ping_pHYs_x_resolution=
8979 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8980 ping_pHYs_y_resolution=
8981 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008982 }
glennrpdfd70802010-11-14 01:23:35 +00008983
cristy3ed852e2009-09-05 21:47:34 +00008984 else if (image->units == PixelsPerCentimeterResolution)
8985 {
glennrpdfd70802010-11-14 01:23:35 +00008986 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008987 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8988 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008989 }
glennrp991d11d2010-11-12 21:55:28 +00008990
cristy3ed852e2009-09-05 21:47:34 +00008991 else
8992 {
glennrpdfd70802010-11-14 01:23:35 +00008993 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8994 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8995 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00008996 }
glennrp991d11d2010-11-12 21:55:28 +00008997
glennrp823b55c2011-03-14 18:46:46 +00008998 if (logging != MagickFalse)
8999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9000 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9001 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9002 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009003 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009004 }
glennrp26f37912010-12-23 16:22:42 +00009005 }
cristy3ed852e2009-09-05 21:47:34 +00009006#endif
glennrpa521b2f2010-10-29 04:11:03 +00009007
glennrp26f37912010-12-23 16:22:42 +00009008 if (ping_exclude_bKGD == MagickFalse)
9009 {
glennrpa521b2f2010-10-29 04:11:03 +00009010 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009011 {
glennrpa521b2f2010-10-29 04:11:03 +00009012 unsigned int
9013 mask;
cristy3ed852e2009-09-05 21:47:34 +00009014
glennrpa521b2f2010-10-29 04:11:03 +00009015 mask=0xffff;
9016 if (ping_bit_depth == 8)
9017 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009018
glennrpa521b2f2010-10-29 04:11:03 +00009019 if (ping_bit_depth == 4)
9020 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009021
glennrpa521b2f2010-10-29 04:11:03 +00009022 if (ping_bit_depth == 2)
9023 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009024
glennrpa521b2f2010-10-29 04:11:03 +00009025 if (ping_bit_depth == 1)
9026 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009027
glennrpa521b2f2010-10-29 04:11:03 +00009028 ping_background.red=(png_uint_16)
9029 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009030
glennrpa521b2f2010-10-29 04:11:03 +00009031 ping_background.green=(png_uint_16)
9032 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009033
glennrpa521b2f2010-10-29 04:11:03 +00009034 ping_background.blue=(png_uint_16)
9035 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009036
9037 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009038 }
cristy3ed852e2009-09-05 21:47:34 +00009039
glennrp0fe50b42010-11-16 03:52:51 +00009040 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009041 {
9042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9043 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9045 " background_color index is %d",
9046 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009047
9048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9049 " ping_bit_depth=%d",ping_bit_depth);
9050 }
glennrp0fe50b42010-11-16 03:52:51 +00009051
9052 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009053 }
glennrp0fe50b42010-11-16 03:52:51 +00009054
cristy3ed852e2009-09-05 21:47:34 +00009055 /*
9056 Select the color type.
9057 */
9058 matte=image_matte;
9059 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009060
glennrp1273f7b2011-02-24 03:20:30 +00009061 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009062 {
glennrp0fe50b42010-11-16 03:52:51 +00009063
glennrpfd05d622011-02-25 04:10:33 +00009064 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009065 for reducing the sample depth from 8. */
9066
glennrp0fe50b42010-11-16 03:52:51 +00009067 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009068
glennrp8bb3a022010-12-13 20:40:04 +00009069 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009070
9071 /*
9072 Set image palette.
9073 */
9074 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9075
glennrp0fe50b42010-11-16 03:52:51 +00009076 if (logging != MagickFalse)
9077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9078 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009079 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009080
9081 for (i=0; i < (ssize_t) number_colors; i++)
9082 {
9083 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9084 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9085 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9086 if (logging != MagickFalse)
9087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009088#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009089 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009090#else
9091 " %5ld (%5d,%5d,%5d)",
9092#endif
glennrp0fe50b42010-11-16 03:52:51 +00009093 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9094
9095 }
glennrp2b013e42010-11-24 16:55:50 +00009096
glennrp8bb3a022010-12-13 20:40:04 +00009097 ping_have_PLTE=MagickTrue;
9098 image_depth=ping_bit_depth;
9099 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009100
glennrp58e01762011-01-07 15:28:54 +00009101 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009102 {
glennrp0fe50b42010-11-16 03:52:51 +00009103 /*
9104 Identify which colormap entry is transparent.
9105 */
9106 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009107 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009108
glennrp8bb3a022010-12-13 20:40:04 +00009109 for (i=0; i < (ssize_t) number_transparent; i++)
9110 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009111
glennrp0fe50b42010-11-16 03:52:51 +00009112
glennrp2cc891a2010-12-24 13:44:32 +00009113 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009114 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009115
9116 if (ping_num_trans == 0)
9117 ping_have_tRNS=MagickFalse;
9118
glennrp8bb3a022010-12-13 20:40:04 +00009119 else
9120 ping_have_tRNS=MagickTrue;
9121 }
glennrp0fe50b42010-11-16 03:52:51 +00009122
glennrp1273f7b2011-02-24 03:20:30 +00009123 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009124 {
glennrp1273f7b2011-02-24 03:20:30 +00009125 /*
9126 * Identify which colormap entry is the background color.
9127 */
9128
glennrp4f25bd02011-01-01 18:51:28 +00009129 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9130 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9131 break;
glennrp0fe50b42010-11-16 03:52:51 +00009132
glennrp4f25bd02011-01-01 18:51:28 +00009133 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009134
9135 if (logging != MagickFalse)
9136 {
9137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9138 " background_color index is %d",
9139 (int) ping_background.index);
9140 }
glennrp4f25bd02011-01-01 18:51:28 +00009141 }
cristy3ed852e2009-09-05 21:47:34 +00009142 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009143
glennrp7e65e932011-08-19 02:31:16 +00009144 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009145 {
9146 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009147 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009148 }
glennrp0fe50b42010-11-16 03:52:51 +00009149
glennrp7e65e932011-08-19 02:31:16 +00009150 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009151 {
9152 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009153 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009154 }
glennrp0fe50b42010-11-16 03:52:51 +00009155
glennrp8bb3a022010-12-13 20:40:04 +00009156 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009157 {
glennrp5af765f2010-03-30 11:12:18 +00009158 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009159
glennrp8bb3a022010-12-13 20:40:04 +00009160 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009161 {
glennrp5af765f2010-03-30 11:12:18 +00009162 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009163
glennrp5af765f2010-03-30 11:12:18 +00009164 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9165 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009166 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009167
glennrp8bb3a022010-12-13 20:40:04 +00009168 else
9169 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009170
9171 if (logging != MagickFalse)
9172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9173 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009174 }
glennrp0fe50b42010-11-16 03:52:51 +00009175
glennrp7c4c9e62011-03-21 20:23:32 +00009176 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009177 {
9178 if (logging != MagickFalse)
9179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009180 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009181
glennrpd6bf1612010-12-17 17:28:54 +00009182 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009183 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009184
glennrpd6bf1612010-12-17 17:28:54 +00009185 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009186 {
glennrp5af765f2010-03-30 11:12:18 +00009187 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009188 image_matte=MagickFalse;
9189 }
glennrp0fe50b42010-11-16 03:52:51 +00009190
glennrpd6bf1612010-12-17 17:28:54 +00009191 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009192 {
glennrp5af765f2010-03-30 11:12:18 +00009193 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009194 image_matte=MagickTrue;
9195 }
glennrp0fe50b42010-11-16 03:52:51 +00009196
glennrp5aa37f62011-01-02 03:07:57 +00009197 if (image_info->type == PaletteType ||
9198 image_info->type == PaletteMatteType)
9199 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9200
glennrp7c4c9e62011-03-21 20:23:32 +00009201 if (mng_info->write_png_colortype == 0 &&
9202 (image_info->type == UndefinedType ||
9203 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009204 {
glennrp5aa37f62011-01-02 03:07:57 +00009205 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009206 {
glennrp5aa37f62011-01-02 03:07:57 +00009207 if (image_matte == MagickFalse)
9208 {
9209 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9210 image_matte=MagickFalse;
9211 }
glennrp0fe50b42010-11-16 03:52:51 +00009212
glennrp0b206f52011-01-07 04:55:32 +00009213 else
glennrp5aa37f62011-01-02 03:07:57 +00009214 {
9215 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9216 image_matte=MagickTrue;
9217 }
9218 }
9219 else
glennrp8bb3a022010-12-13 20:40:04 +00009220 {
glennrp5aa37f62011-01-02 03:07:57 +00009221 if (image_matte == MagickFalse)
9222 {
9223 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9224 image_matte=MagickFalse;
9225 }
glennrp8bb3a022010-12-13 20:40:04 +00009226
glennrp0b206f52011-01-07 04:55:32 +00009227 else
glennrp5aa37f62011-01-02 03:07:57 +00009228 {
9229 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9230 image_matte=MagickTrue;
9231 }
9232 }
glennrp0fe50b42010-11-16 03:52:51 +00009233 }
glennrp5aa37f62011-01-02 03:07:57 +00009234
cristy3ed852e2009-09-05 21:47:34 +00009235 }
glennrp0fe50b42010-11-16 03:52:51 +00009236
cristy3ed852e2009-09-05 21:47:34 +00009237 if (logging != MagickFalse)
9238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009239 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009240
glennrp5af765f2010-03-30 11:12:18 +00009241 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009242 {
9243 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9244 ping_color_type == PNG_COLOR_TYPE_RGB ||
9245 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9246 ping_bit_depth=8;
9247 }
cristy3ed852e2009-09-05 21:47:34 +00009248
glennrpd6bf1612010-12-17 17:28:54 +00009249 old_bit_depth=ping_bit_depth;
9250
glennrp5af765f2010-03-30 11:12:18 +00009251 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009252 {
glennrp8d579662011-02-23 02:05:02 +00009253 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9254 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009255 }
glennrp8640fb52010-11-23 15:48:26 +00009256
glennrp5af765f2010-03-30 11:12:18 +00009257 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009258 {
cristy35ef8242010-06-03 16:24:13 +00009259 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009260 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009261
9262 if (image->colors == 0)
9263 {
glennrp0fe50b42010-11-16 03:52:51 +00009264 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00009265 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00009266 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009267 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009268 }
9269
cristy35ef8242010-06-03 16:24:13 +00009270 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009271 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009272 }
glennrp2b013e42010-11-24 16:55:50 +00009273
glennrpd6bf1612010-12-17 17:28:54 +00009274 if (logging != MagickFalse)
9275 {
9276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9277 " Number of colors: %.20g",(double) image_colors);
9278
9279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9280 " Tentative PNG bit depth: %d",ping_bit_depth);
9281 }
9282
9283 if (ping_bit_depth < (int) mng_info->write_png_depth)
9284 ping_bit_depth = mng_info->write_png_depth;
9285 }
glennrp2cc891a2010-12-24 13:44:32 +00009286
glennrp5af765f2010-03-30 11:12:18 +00009287 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009288
cristy3ed852e2009-09-05 21:47:34 +00009289 if (logging != MagickFalse)
9290 {
9291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009292 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009293
cristy3ed852e2009-09-05 21:47:34 +00009294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009295 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009296
cristy3ed852e2009-09-05 21:47:34 +00009297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009298 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009299
cristy3ed852e2009-09-05 21:47:34 +00009300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009301
glennrp8640fb52010-11-23 15:48:26 +00009302 " image->depth: %.20g",(double) image->depth);
9303
9304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009305 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009306 }
9307
glennrp58e01762011-01-07 15:28:54 +00009308 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009309 {
glennrp4f25bd02011-01-01 18:51:28 +00009310 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009311 {
glennrp7c4c9e62011-03-21 20:23:32 +00009312 if (mng_info->write_png_colortype == 0)
9313 {
9314 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009315
glennrp7c4c9e62011-03-21 20:23:32 +00009316 if (ping_have_color != MagickFalse)
9317 ping_color_type=PNG_COLOR_TYPE_RGBA;
9318 }
glennrp4f25bd02011-01-01 18:51:28 +00009319
9320 /*
9321 * Determine if there is any transparent color.
9322 */
9323 if (number_transparent + number_semitransparent == 0)
9324 {
9325 /*
9326 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9327 */
glennrpa6a06632011-01-19 15:15:34 +00009328
glennrp4f25bd02011-01-01 18:51:28 +00009329 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009330
9331 if (mng_info->write_png_colortype == 0)
9332 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009333 }
9334
9335 else
9336 {
9337 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009338 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009339
9340 mask=0xffff;
9341
9342 if (ping_bit_depth == 8)
9343 mask=0x00ff;
9344
9345 if (ping_bit_depth == 4)
9346 mask=0x000f;
9347
9348 if (ping_bit_depth == 2)
9349 mask=0x0003;
9350
9351 if (ping_bit_depth == 1)
9352 mask=0x0001;
9353
9354 ping_trans_color.red=(png_uint_16)
9355 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9356
9357 ping_trans_color.green=(png_uint_16)
9358 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9359
9360 ping_trans_color.blue=(png_uint_16)
9361 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9362
9363 ping_trans_color.gray=(png_uint_16)
cristy101ab702011-10-13 13:06:32 +00009364 (ScaleQuantumToShort(GetPixelInfoIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009365 image->colormap)) & mask);
9366
9367 ping_trans_color.index=(png_byte) 0;
9368
9369 ping_have_tRNS=MagickTrue;
9370 }
9371
9372 if (ping_have_tRNS != MagickFalse)
9373 {
9374 /*
glennrpfd05d622011-02-25 04:10:33 +00009375 * Determine if there is one and only one transparent color
9376 * and if so if it is fully transparent.
9377 */
9378 if (ping_have_cheap_transparency == MagickFalse)
9379 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009380 }
9381
9382 if (ping_have_tRNS != MagickFalse)
9383 {
glennrp7c4c9e62011-03-21 20:23:32 +00009384 if (mng_info->write_png_colortype == 0)
9385 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009386
9387 if (image_depth == 8)
9388 {
9389 ping_trans_color.red&=0xff;
9390 ping_trans_color.green&=0xff;
9391 ping_trans_color.blue&=0xff;
9392 ping_trans_color.gray&=0xff;
9393 }
9394 }
9395 }
cristy3ed852e2009-09-05 21:47:34 +00009396 else
9397 {
cristy3ed852e2009-09-05 21:47:34 +00009398 if (image_depth == 8)
9399 {
glennrp5af765f2010-03-30 11:12:18 +00009400 ping_trans_color.red&=0xff;
9401 ping_trans_color.green&=0xff;
9402 ping_trans_color.blue&=0xff;
9403 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009404 }
9405 }
9406 }
glennrp8640fb52010-11-23 15:48:26 +00009407
cristy3ed852e2009-09-05 21:47:34 +00009408 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009409
glennrp2e09f552010-11-14 00:38:48 +00009410 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009411 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009412
glennrp39992b42010-11-14 00:03:43 +00009413 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009414 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009415 ping_have_color == MagickFalse &&
9416 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009417 {
cristy35ef8242010-06-03 16:24:13 +00009418 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009419
cristy3ed852e2009-09-05 21:47:34 +00009420 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009421 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009422
glennrp7c4c9e62011-03-21 20:23:32 +00009423 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009424 {
glennrp5af765f2010-03-30 11:12:18 +00009425 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009426
cristy3ed852e2009-09-05 21:47:34 +00009427 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009428 {
9429 if (logging != MagickFalse)
9430 {
9431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9432 " Scaling ping_trans_color (0)");
9433 }
9434 ping_trans_color.gray*=0x0101;
9435 }
cristy3ed852e2009-09-05 21:47:34 +00009436 }
glennrp0fe50b42010-11-16 03:52:51 +00009437
cristy3ed852e2009-09-05 21:47:34 +00009438 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9439 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009440
glennrp136ee3a2011-04-27 15:47:45 +00009441 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009442 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009443 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009444
cristy3ed852e2009-09-05 21:47:34 +00009445 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009446 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009447
cristy3ed852e2009-09-05 21:47:34 +00009448 else
9449 {
glennrp5af765f2010-03-30 11:12:18 +00009450 ping_bit_depth=8;
9451 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009452 {
9453 if(!mng_info->write_png_depth)
9454 {
glennrp5af765f2010-03-30 11:12:18 +00009455 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009456
cristy35ef8242010-06-03 16:24:13 +00009457 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009458 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009459 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009460 }
9461 }
glennrp2b013e42010-11-24 16:55:50 +00009462
glennrp0fe50b42010-11-16 03:52:51 +00009463 else if (ping_color_type ==
9464 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009465 mng_info->IsPalette)
9466 {
cristy3ed852e2009-09-05 21:47:34 +00009467 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009468
cristy3ed852e2009-09-05 21:47:34 +00009469 int
9470 depth_4_ok=MagickTrue,
9471 depth_2_ok=MagickTrue,
9472 depth_1_ok=MagickTrue;
9473
cristybb503372010-05-27 20:51:26 +00009474 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009475 {
9476 unsigned char
9477 intensity;
9478
9479 intensity=ScaleQuantumToChar(image->colormap[i].red);
9480
9481 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9482 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9483 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9484 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009485 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009486 depth_1_ok=MagickFalse;
9487 }
glennrp2b013e42010-11-24 16:55:50 +00009488
cristy3ed852e2009-09-05 21:47:34 +00009489 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009490 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009491
cristy3ed852e2009-09-05 21:47:34 +00009492 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009493 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009494
cristy3ed852e2009-09-05 21:47:34 +00009495 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009496 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009497 }
9498 }
glennrp2b013e42010-11-24 16:55:50 +00009499
glennrp5af765f2010-03-30 11:12:18 +00009500 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009501 }
glennrp0fe50b42010-11-16 03:52:51 +00009502
cristy3ed852e2009-09-05 21:47:34 +00009503 else
glennrp0fe50b42010-11-16 03:52:51 +00009504
cristy3ed852e2009-09-05 21:47:34 +00009505 if (mng_info->IsPalette)
9506 {
glennrp17a14852010-05-10 03:01:59 +00009507 number_colors=image_colors;
9508
cristy3ed852e2009-09-05 21:47:34 +00009509 if (image_depth <= 8)
9510 {
cristy3ed852e2009-09-05 21:47:34 +00009511 /*
9512 Set image palette.
9513 */
glennrp5af765f2010-03-30 11:12:18 +00009514 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009515
glennrp58e01762011-01-07 15:28:54 +00009516 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009517 {
glennrp9c1eb072010-06-06 22:19:15 +00009518 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009519
glennrp3b51f0e2010-11-27 18:14:08 +00009520 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9522 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009523 }
glennrp0fe50b42010-11-16 03:52:51 +00009524
cristy3ed852e2009-09-05 21:47:34 +00009525 else
9526 {
cristybb503372010-05-27 20:51:26 +00009527 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009528 {
9529 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9530 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9531 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9532 }
glennrp0fe50b42010-11-16 03:52:51 +00009533
glennrp3b51f0e2010-11-27 18:14:08 +00009534 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009536 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009537 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009538
glennrp39992b42010-11-14 00:03:43 +00009539 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009540 }
glennrp0fe50b42010-11-16 03:52:51 +00009541
cristy3ed852e2009-09-05 21:47:34 +00009542 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009543 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009544 {
cristybefe4d22010-06-07 01:18:58 +00009545 size_t
9546 one;
9547
glennrp5af765f2010-03-30 11:12:18 +00009548 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009549 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009550
cristy94b11832011-09-08 19:46:03 +00009551 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009552 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009553 }
glennrp0fe50b42010-11-16 03:52:51 +00009554
glennrp5af765f2010-03-30 11:12:18 +00009555 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009556
glennrp58e01762011-01-07 15:28:54 +00009557 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009558 {
glennrp0fe50b42010-11-16 03:52:51 +00009559 /*
glennrpd6bf1612010-12-17 17:28:54 +00009560 * Set up trans_colors array.
9561 */
glennrp0fe50b42010-11-16 03:52:51 +00009562 assert(number_colors <= 256);
9563
glennrpd6bf1612010-12-17 17:28:54 +00009564 ping_num_trans=(unsigned short) (number_transparent +
9565 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009566
9567 if (ping_num_trans == 0)
9568 ping_have_tRNS=MagickFalse;
9569
glennrpd6bf1612010-12-17 17:28:54 +00009570 else
glennrp0fe50b42010-11-16 03:52:51 +00009571 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009572 if (logging != MagickFalse)
9573 {
9574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9575 " Scaling ping_trans_color (1)");
9576 }
glennrpd6bf1612010-12-17 17:28:54 +00009577 ping_have_tRNS=MagickTrue;
9578
9579 for (i=0; i < ping_num_trans; i++)
9580 {
cristy4c08aed2011-07-01 19:47:50 +00009581 ping_trans_alpha[i]= (png_byte)
9582 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009583 }
glennrp0fe50b42010-11-16 03:52:51 +00009584 }
9585 }
cristy3ed852e2009-09-05 21:47:34 +00009586 }
9587 }
glennrp0fe50b42010-11-16 03:52:51 +00009588
cristy3ed852e2009-09-05 21:47:34 +00009589 else
9590 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009591
cristy3ed852e2009-09-05 21:47:34 +00009592 if (image_depth < 8)
9593 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009594
cristy3ed852e2009-09-05 21:47:34 +00009595 if ((save_image_depth == 16) && (image_depth == 8))
9596 {
glennrp4f25bd02011-01-01 18:51:28 +00009597 if (logging != MagickFalse)
9598 {
9599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9600 " Scaling ping_trans_color from (%d,%d,%d)",
9601 (int) ping_trans_color.red,
9602 (int) ping_trans_color.green,
9603 (int) ping_trans_color.blue);
9604 }
9605
glennrp5af765f2010-03-30 11:12:18 +00009606 ping_trans_color.red*=0x0101;
9607 ping_trans_color.green*=0x0101;
9608 ping_trans_color.blue*=0x0101;
9609 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009610
9611 if (logging != MagickFalse)
9612 {
9613 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9614 " to (%d,%d,%d)",
9615 (int) ping_trans_color.red,
9616 (int) ping_trans_color.green,
9617 (int) ping_trans_color.blue);
9618 }
cristy3ed852e2009-09-05 21:47:34 +00009619 }
9620 }
9621
cristy4383ec82011-01-05 15:42:32 +00009622 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9623 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009624
cristy3ed852e2009-09-05 21:47:34 +00009625 /*
9626 Adjust background and transparency samples in sub-8-bit grayscale files.
9627 */
glennrp5af765f2010-03-30 11:12:18 +00009628 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009629 PNG_COLOR_TYPE_GRAY)
9630 {
9631 png_uint_16
9632 maxval;
9633
cristy35ef8242010-06-03 16:24:13 +00009634 size_t
9635 one=1;
9636
cristy22ffd972010-06-03 16:51:47 +00009637 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009638
glennrp4f25bd02011-01-01 18:51:28 +00009639 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009640 {
cristy3ed852e2009-09-05 21:47:34 +00009641
glennrpa521b2f2010-10-29 04:11:03 +00009642 ping_background.gray=(png_uint_16)
cristy101ab702011-10-13 13:06:32 +00009643 ((maxval/255.)*((GetPixelInfoIntensity(&image->background_color)))
glennrp847370c2011-07-05 17:37:15 +00009644 +.5);
cristy3ed852e2009-09-05 21:47:34 +00009645
9646 if (logging != MagickFalse)
9647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009648 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9650 " background_color index is %d",
9651 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009652
glennrp991d11d2010-11-12 21:55:28 +00009653 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009654 }
cristy3ed852e2009-09-05 21:47:34 +00009655
glennrp3e3e20f2011-06-09 04:21:43 +00009656 if (logging != MagickFalse)
9657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9658 " Scaling ping_trans_color.gray from %d",
9659 (int)ping_trans_color.gray);
9660
glennrp9be9b1c2011-06-09 12:21:45 +00009661 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009662 ping_trans_color.gray)+.5);
9663
9664 if (logging != MagickFalse)
9665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9666 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009667 }
glennrp17a14852010-05-10 03:01:59 +00009668
glennrp26f37912010-12-23 16:22:42 +00009669 if (ping_exclude_bKGD == MagickFalse)
9670 {
glennrp1273f7b2011-02-24 03:20:30 +00009671 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009672 {
9673 /*
9674 Identify which colormap entry is the background color.
9675 */
9676
glennrp17a14852010-05-10 03:01:59 +00009677 number_colors=image_colors;
9678
glennrpa521b2f2010-10-29 04:11:03 +00009679 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9680 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009681 break;
9682
9683 ping_background.index=(png_byte) i;
9684
glennrp3b51f0e2010-11-27 18:14:08 +00009685 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009686 {
9687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009688 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009689 }
glennrp0fe50b42010-11-16 03:52:51 +00009690
cristy13d07042010-11-21 20:56:18 +00009691 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009692 {
9693 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009694
9695 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009696 {
9697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9698 " background =(%d,%d,%d)",
9699 (int) ping_background.red,
9700 (int) ping_background.green,
9701 (int) ping_background.blue);
9702 }
9703 }
glennrpa521b2f2010-10-29 04:11:03 +00009704
glennrpd6bf1612010-12-17 17:28:54 +00009705 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009706 {
glennrp3b51f0e2010-11-27 18:14:08 +00009707 if (logging != MagickFalse)
9708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9709 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009710 ping_have_bKGD = MagickFalse;
9711 }
glennrp17a14852010-05-10 03:01:59 +00009712 }
glennrp26f37912010-12-23 16:22:42 +00009713 }
glennrp17a14852010-05-10 03:01:59 +00009714
cristy3ed852e2009-09-05 21:47:34 +00009715 if (logging != MagickFalse)
9716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009717 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009718 /*
9719 Initialize compression level and filtering.
9720 */
9721 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009722 {
9723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9724 " Setting up deflate compression");
9725
9726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9727 " Compression buffer size: 32768");
9728 }
9729
cristy3ed852e2009-09-05 21:47:34 +00009730 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009731
cristy3ed852e2009-09-05 21:47:34 +00009732 if (logging != MagickFalse)
9733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9734 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009735
cristy4054bfb2011-08-29 23:41:39 +00009736 png_set_compression_mem_level(ping, 9);
9737
glennrp10d739e2011-06-29 18:00:52 +00009738 /* Untangle the "-quality" setting:
9739
9740 Undefined is 0; the default is used.
9741 Default is 75
9742
9743 10's digit:
9744
9745 0: Use Z_HUFFMAN_ONLY strategy with the
9746 zlib default compression level
9747
9748 1-9: the zlib compression level
9749
9750 1's digit:
9751
9752 0-4: the PNG filter method
9753
9754 5: libpng adaptive filtering if compression level > 5
9755 libpng filter type "none" if compression level <= 5
9756 or if image is grayscale or palette
9757
9758 6: libpng adaptive filtering
9759
9760 7: "LOCO" filtering (intrapixel differing) if writing
9761 a MNG, othewise "none". Did not work in IM-6.7.0-9
9762 and earlier because of a missing "else".
9763
9764 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009765 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009766
9767 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009768 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009769
9770 Note that using the -quality option, not all combinations of
9771 PNG filter type, zlib compression level, and zlib compression
cristy5e6be1e2011-07-16 01:23:39 +00009772 strategy are possible. This will be addressed soon in a
cristy4054bfb2011-08-29 23:41:39 +00009773 release that accomodates "-define PNG:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +00009774
9775 */
9776
cristy3ed852e2009-09-05 21:47:34 +00009777 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9778 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009779
glennrp18682582011-06-30 18:11:47 +00009780 if (quality <= 9)
9781 {
9782 if (mng_info->write_png_compression_strategy == 0)
9783 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9784 }
9785
9786 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009787 {
9788 int
9789 level;
9790
cristybb503372010-05-27 20:51:26 +00009791 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009792
glennrp18682582011-06-30 18:11:47 +00009793 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009794 }
glennrp0fe50b42010-11-16 03:52:51 +00009795
glennrp18682582011-06-30 18:11:47 +00009796 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009797 {
glennrp18682582011-06-30 18:11:47 +00009798 if ((quality %10) == 8 || (quality %10) == 9)
9799 mng_info->write_png_compression_strategy=Z_RLE;
cristy3ed852e2009-09-05 21:47:34 +00009800 }
glennrp0fe50b42010-11-16 03:52:51 +00009801
glennrp18682582011-06-30 18:11:47 +00009802 if (mng_info->write_png_compression_filter == 0)
9803 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9804
cristy3ed852e2009-09-05 21:47:34 +00009805 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009806 {
glennrp18682582011-06-30 18:11:47 +00009807 if (mng_info->write_png_compression_level)
9808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9809 " Compression level: %d",
9810 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009811
glennrp18682582011-06-30 18:11:47 +00009812 if (mng_info->write_png_compression_strategy)
9813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9814 " Compression strategy: %d",
9815 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009816
glennrp18682582011-06-30 18:11:47 +00009817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9818 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009819
cristy4054bfb2011-08-29 23:41:39 +00009820 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +00009821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9822 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +00009823 else if (mng_info->write_png_compression_filter == 0 ||
9824 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +00009825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9826 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009827 else
9828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9829 " Base filter method: %d",
9830 (int) mng_info->write_png_compression_filter-1);
9831 }
glennrp2b013e42010-11-24 16:55:50 +00009832
glennrp18682582011-06-30 18:11:47 +00009833 if (mng_info->write_png_compression_level != 0)
9834 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9835
9836 if (mng_info->write_png_compression_filter == 6)
9837 {
9838 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9839 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9840 (quality < 50))
9841 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9842 else
9843 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9844 }
cristy4054bfb2011-08-29 23:41:39 +00009845 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +00009846 mng_info->write_png_compression_filter == 10)
9847 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9848
9849 else if (mng_info->write_png_compression_filter == 8)
9850 {
9851#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9852 if (mng_info->write_mng)
9853 {
9854 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9855 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9856 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9857 }
9858#endif
cristy4054bfb2011-08-29 23:41:39 +00009859 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +00009860 }
9861
9862 else if (mng_info->write_png_compression_filter == 9)
9863 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9864
9865 else if (mng_info->write_png_compression_filter != 0)
9866 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9867 mng_info->write_png_compression_filter-1);
9868
9869 if (mng_info->write_png_compression_strategy != 0)
9870 png_set_compression_strategy(ping,
9871 mng_info->write_png_compression_strategy-1);
9872
cristy0d57eec2011-09-04 22:13:56 +00009873 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9874 if (ping_exclude_sRGB != MagickFalse ||
9875 (image->rendering_intent == UndefinedIntent))
9876 {
9877 if ((ping_exclude_tEXt == MagickFalse ||
9878 ping_exclude_zTXt == MagickFalse) &&
9879 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009880 {
9881 ResetImageProfileIterator(image);
9882 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009883 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009884 profile=GetImageProfile(image,name);
9885
9886 if (profile != (StringInfo *) NULL)
9887 {
glennrp5af765f2010-03-30 11:12:18 +00009888#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009889 if ((LocaleCompare(name,"ICC") == 0) ||
9890 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009891 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009892
9893 if (ping_exclude_iCCP == MagickFalse)
9894 {
cristy9f027d12011-09-21 01:17:17 +00009895 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009896#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009897 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009898#else
9899 (png_const_bytep) GetStringInfoDatum(profile),
9900#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009901 (png_uint_32) GetStringInfoLength(profile));
9902 }
glennrp26f37912010-12-23 16:22:42 +00009903 }
glennrp0fe50b42010-11-16 03:52:51 +00009904
glennrpc8cbc5d2011-01-01 00:12:34 +00009905 else
cristy3ed852e2009-09-05 21:47:34 +00009906#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009907 if (ping_exclude_zCCP == MagickFalse)
9908 {
glennrpcf002022011-01-30 02:38:15 +00009909 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009910 (unsigned char *) name,(unsigned char *) name,
9911 GetStringInfoDatum(profile),
9912 (png_uint_32) GetStringInfoLength(profile));
9913 }
9914 }
glennrp0b206f52011-01-07 04:55:32 +00009915
glennrpc8cbc5d2011-01-01 00:12:34 +00009916 if (logging != MagickFalse)
9917 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9918 " Setting up text chunk with %s profile",name);
9919
9920 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009921 }
cristy0d57eec2011-09-04 22:13:56 +00009922 }
cristy3ed852e2009-09-05 21:47:34 +00009923 }
9924
9925#if defined(PNG_WRITE_sRGB_SUPPORTED)
9926 if ((mng_info->have_write_global_srgb == 0) &&
9927 ((image->rendering_intent != UndefinedIntent) ||
9928 (image->colorspace == sRGBColorspace)))
9929 {
glennrp26f37912010-12-23 16:22:42 +00009930 if (ping_exclude_sRGB == MagickFalse)
9931 {
9932 /*
9933 Note image rendering intent.
9934 */
9935 if (logging != MagickFalse)
9936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9937 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009938
glennrp26f37912010-12-23 16:22:42 +00009939 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009940 Magick_RenderingIntent_to_PNG_RenderingIntent(
9941 image->rendering_intent)));
glennrp26f37912010-12-23 16:22:42 +00009942 }
cristy3ed852e2009-09-05 21:47:34 +00009943 }
glennrp26f37912010-12-23 16:22:42 +00009944
glennrp5af765f2010-03-30 11:12:18 +00009945 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009946#endif
9947 {
glennrp2cc891a2010-12-24 13:44:32 +00009948 if (ping_exclude_gAMA == MagickFalse &&
9949 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009950 (image->gamma < .45 || image->gamma > .46)))
9951 {
cristy3ed852e2009-09-05 21:47:34 +00009952 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9953 {
9954 /*
9955 Note image gamma.
9956 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9957 */
9958 if (logging != MagickFalse)
9959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9960 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009961
cristy3ed852e2009-09-05 21:47:34 +00009962 png_set_gAMA(ping,ping_info,image->gamma);
9963 }
glennrp26f37912010-12-23 16:22:42 +00009964 }
glennrp2b013e42010-11-24 16:55:50 +00009965
glennrp26f37912010-12-23 16:22:42 +00009966 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009967 {
glennrp26f37912010-12-23 16:22:42 +00009968 if ((mng_info->have_write_global_chrm == 0) &&
9969 (image->chromaticity.red_primary.x != 0.0))
9970 {
9971 /*
9972 Note image chromaticity.
9973 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9974 */
9975 PrimaryInfo
9976 bp,
9977 gp,
9978 rp,
9979 wp;
cristy3ed852e2009-09-05 21:47:34 +00009980
glennrp26f37912010-12-23 16:22:42 +00009981 wp=image->chromaticity.white_point;
9982 rp=image->chromaticity.red_primary;
9983 gp=image->chromaticity.green_primary;
9984 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009985
glennrp26f37912010-12-23 16:22:42 +00009986 if (logging != MagickFalse)
9987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9988 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009989
glennrp26f37912010-12-23 16:22:42 +00009990 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9991 bp.x,bp.y);
9992 }
9993 }
cristy3ed852e2009-09-05 21:47:34 +00009994 }
glennrpdfd70802010-11-14 01:23:35 +00009995
glennrp5af765f2010-03-30 11:12:18 +00009996 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009997
9998 if (mng_info->write_mng)
9999 png_set_sig_bytes(ping,8);
10000
10001 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
10002
glennrpd6bf1612010-12-17 17:28:54 +000010003 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +000010004 {
10005 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +000010006 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010007 {
glennrp5af765f2010-03-30 11:12:18 +000010008 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +000010009
glennrp5af765f2010-03-30 11:12:18 +000010010 if (ping_bit_depth < 8)
10011 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +000010012 }
glennrp0fe50b42010-11-16 03:52:51 +000010013
cristy3ed852e2009-09-05 21:47:34 +000010014 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +000010015 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +000010016 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +000010017 }
10018
glennrp0e8ea192010-12-24 18:00:33 +000010019 if (ping_need_colortype_warning != MagickFalse ||
10020 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +000010021 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +000010022 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +000010023 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +000010024 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +000010025 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +000010026 {
10027 if (logging != MagickFalse)
10028 {
glennrp0e8ea192010-12-24 18:00:33 +000010029 if (ping_need_colortype_warning != MagickFalse)
10030 {
10031 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10032 " Image has transparency but tRNS chunk was excluded");
10033 }
10034
cristy3ed852e2009-09-05 21:47:34 +000010035 if (mng_info->write_png_depth)
10036 {
10037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10038 " Defined PNG:bit-depth=%u, Computed depth=%u",
10039 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +000010040 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +000010041 }
glennrp0e8ea192010-12-24 18:00:33 +000010042
cristy3ed852e2009-09-05 21:47:34 +000010043 if (mng_info->write_png_colortype)
10044 {
10045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10046 " Defined PNG:color-type=%u, Computed color type=%u",
10047 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +000010048 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010049 }
10050 }
glennrp0e8ea192010-12-24 18:00:33 +000010051
glennrp3bd2e412010-08-10 13:34:52 +000010052 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +000010053 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
10054 }
10055
glennrp58e01762011-01-07 15:28:54 +000010056 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010057 {
10058 /* Add an opaque matte channel */
10059 image->matte = MagickTrue;
cristye941a752011-10-15 01:52:48 +000010060 (void) SetImageAlpha(image,OpaqueAlpha,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010061
glennrpb4a13412010-05-05 12:47:19 +000010062 if (logging != MagickFalse)
10063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10064 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +000010065 }
10066
glennrp0e319732011-01-25 21:53:13 +000010067 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +000010068 {
glennrp991d11d2010-11-12 21:55:28 +000010069 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +000010070 {
glennrp991d11d2010-11-12 21:55:28 +000010071 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010072 if (logging != MagickFalse)
10073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10074 " Setting ping_have_tRNS=MagickTrue.");
10075 }
glennrpe9c26dc2010-05-30 01:56:35 +000010076 }
10077
cristy3ed852e2009-09-05 21:47:34 +000010078 if (logging != MagickFalse)
10079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10080 " Writing PNG header chunks");
10081
glennrp5af765f2010-03-30 11:12:18 +000010082 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10083 ping_bit_depth,ping_color_type,
10084 ping_interlace_method,ping_compression_method,
10085 ping_filter_method);
10086
glennrp39992b42010-11-14 00:03:43 +000010087 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10088 {
glennrpf09bded2011-01-08 01:15:59 +000010089 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010090
glennrp3b51f0e2010-11-27 18:14:08 +000010091 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +000010092 {
glennrp8640fb52010-11-23 15:48:26 +000010093 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +000010094 {
glennrpd6bf1612010-12-17 17:28:54 +000010095 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +000010096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010097 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10098 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010099 (int) palette[i].red,
10100 (int) palette[i].green,
10101 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +000010102 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010103 (int) ping_trans_alpha[i]);
10104 else
10105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010106 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010107 (int) i,
10108 (int) palette[i].red,
10109 (int) palette[i].green,
10110 (int) palette[i].blue);
10111 }
glennrp39992b42010-11-14 00:03:43 +000010112 }
glennrp39992b42010-11-14 00:03:43 +000010113 }
10114
glennrp26f37912010-12-23 16:22:42 +000010115 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010116 {
glennrp26f37912010-12-23 16:22:42 +000010117 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010118 {
glennrp26f37912010-12-23 16:22:42 +000010119 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010120 if (logging)
10121 {
10122 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10123 " Setting up bKGD chunk");
10124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10125 " background color = (%d,%d,%d)",
10126 (int) ping_background.red,
10127 (int) ping_background.green,
10128 (int) ping_background.blue);
10129 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10130 " index = %d, gray=%d",
10131 (int) ping_background.index,
10132 (int) ping_background.gray);
10133 }
10134 }
glennrp26f37912010-12-23 16:22:42 +000010135 }
10136
10137 if (ping_exclude_pHYs == MagickFalse)
10138 {
10139 if (ping_have_pHYs != MagickFalse)
10140 {
10141 png_set_pHYs(ping,ping_info,
10142 ping_pHYs_x_resolution,
10143 ping_pHYs_y_resolution,
10144 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010145
10146 if (logging)
10147 {
10148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10149 " Setting up pHYs chunk");
10150 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10151 " x_resolution=%lu",
10152 (unsigned long) ping_pHYs_x_resolution);
10153 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10154 " y_resolution=%lu",
10155 (unsigned long) ping_pHYs_y_resolution);
10156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10157 " unit_type=%lu",
10158 (unsigned long) ping_pHYs_unit_type);
10159 }
glennrp26f37912010-12-23 16:22:42 +000010160 }
glennrpdfd70802010-11-14 01:23:35 +000010161 }
10162
10163#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010164 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010165 {
glennrp26f37912010-12-23 16:22:42 +000010166 if (image->page.x || image->page.y)
10167 {
10168 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10169 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010170
glennrp26f37912010-12-23 16:22:42 +000010171 if (logging != MagickFalse)
10172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10173 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10174 (int) image->page.x, (int) image->page.y);
10175 }
glennrpdfd70802010-11-14 01:23:35 +000010176 }
10177#endif
10178
glennrpda8f3a72011-02-27 23:54:12 +000010179 if (mng_info->need_blob != MagickFalse)
10180 {
10181 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
10182 MagickFalse)
10183 png_error(ping,"WriteBlob Failed");
10184
10185 ping_have_blob=MagickTrue;
10186 }
10187
cristy3ed852e2009-09-05 21:47:34 +000010188 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010189
glennrp39992b42010-11-14 00:03:43 +000010190 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010191 {
glennrp3b51f0e2010-11-27 18:14:08 +000010192 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010193 {
10194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10195 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10196 }
10197
10198 if (ping_color_type == 3)
10199 (void) png_set_tRNS(ping, ping_info,
10200 ping_trans_alpha,
10201 ping_num_trans,
10202 NULL);
10203
10204 else
10205 {
10206 (void) png_set_tRNS(ping, ping_info,
10207 NULL,
10208 0,
10209 &ping_trans_color);
10210
glennrp3b51f0e2010-11-27 18:14:08 +000010211 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010212 {
10213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010214 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010215 (int) ping_trans_color.red,
10216 (int) ping_trans_color.green,
10217 (int) ping_trans_color.blue);
10218 }
10219 }
glennrp991d11d2010-11-12 21:55:28 +000010220 }
10221
cristy3ed852e2009-09-05 21:47:34 +000010222 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010223 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010224
cristy3ed852e2009-09-05 21:47:34 +000010225 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010226
cristy3ed852e2009-09-05 21:47:34 +000010227 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010228 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010229
glennrp26f37912010-12-23 16:22:42 +000010230 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010231 {
glennrp4f25bd02011-01-01 18:51:28 +000010232 if ((image->page.width != 0 && image->page.width != image->columns) ||
10233 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010234 {
10235 unsigned char
10236 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010237
glennrp26f37912010-12-23 16:22:42 +000010238 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10239 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010240 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010241 PNGLong(chunk+4,(png_uint_32) image->page.width);
10242 PNGLong(chunk+8,(png_uint_32) image->page.height);
10243 chunk[12]=0; /* unit = pixels */
10244 (void) WriteBlob(image,13,chunk);
10245 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10246 }
cristy3ed852e2009-09-05 21:47:34 +000010247 }
10248
10249#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010250 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010251#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010252 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010253#undef PNG_HAVE_IDAT
10254#endif
10255
10256 png_set_packing(ping);
10257 /*
10258 Allocate memory.
10259 */
10260 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010261 if (image_depth > 8)
10262 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010263 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010264 {
glennrpb4a13412010-05-05 12:47:19 +000010265 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010266 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010267 break;
glennrp0fe50b42010-11-16 03:52:51 +000010268
glennrpb4a13412010-05-05 12:47:19 +000010269 case PNG_COLOR_TYPE_GRAY_ALPHA:
10270 rowbytes*=2;
10271 break;
glennrp0fe50b42010-11-16 03:52:51 +000010272
glennrpb4a13412010-05-05 12:47:19 +000010273 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010274 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010275 break;
glennrp0fe50b42010-11-16 03:52:51 +000010276
glennrpb4a13412010-05-05 12:47:19 +000010277 default:
10278 break;
cristy3ed852e2009-09-05 21:47:34 +000010279 }
glennrp3b51f0e2010-11-27 18:14:08 +000010280
10281 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010282 {
10283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10284 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010285
glennrpb4a13412010-05-05 12:47:19 +000010286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010287 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010288 }
glennrpcf002022011-01-30 02:38:15 +000010289 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10290 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010291
glennrpcf002022011-01-30 02:38:15 +000010292 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010293 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010294
cristy3ed852e2009-09-05 21:47:34 +000010295 /*
10296 Initialize image scanlines.
10297 */
glennrp5af765f2010-03-30 11:12:18 +000010298 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010299 {
10300 /*
10301 PNG write failed.
10302 */
10303#ifdef PNG_DEBUG
10304 if (image_info->verbose)
10305 (void) printf("PNG write has failed.\n");
10306#endif
10307 png_destroy_write_struct(&ping,&ping_info);
10308 if (quantum_info != (QuantumInfo *) NULL)
10309 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010310 if (ping_pixels != (unsigned char *) NULL)
10311 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010312#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010313 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010314#endif
glennrpda8f3a72011-02-27 23:54:12 +000010315 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010316 (void) CloseBlob(image);
10317 image_info=DestroyImageInfo(image_info);
10318 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010319 return(MagickFalse);
10320 }
cristyed552522009-10-16 14:04:35 +000010321 quantum_info=AcquireQuantumInfo(image_info,image);
10322 if (quantum_info == (QuantumInfo *) NULL)
10323 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010324 quantum_info->format=UndefinedQuantumFormat;
10325 quantum_info->depth=image_depth;
10326 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010327
cristy3ed852e2009-09-05 21:47:34 +000010328 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010329 !mng_info->write_png32) &&
10330 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010331 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010332 image_matte == MagickFalse &&
10333 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010334 {
glennrp8bb3a022010-12-13 20:40:04 +000010335 /* Palette, Bilevel, or Opaque Monochrome */
cristy4c08aed2011-07-01 19:47:50 +000010336 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010337 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010338
cristy3ed852e2009-09-05 21:47:34 +000010339 quantum_info->depth=8;
10340 for (pass=0; pass < num_passes; pass++)
10341 {
10342 /*
10343 Convert PseudoClass image to a PNG monochrome image.
10344 */
cristybb503372010-05-27 20:51:26 +000010345 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010346 {
glennrpd71e86a2011-02-24 01:28:37 +000010347 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10349 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010350
cristy3ed852e2009-09-05 21:47:34 +000010351 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010352
cristy4c08aed2011-07-01 19:47:50 +000010353 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010354 break;
glennrp0fe50b42010-11-16 03:52:51 +000010355
cristy3ed852e2009-09-05 21:47:34 +000010356 if (mng_info->IsPalette)
10357 {
cristy4c08aed2011-07-01 19:47:50 +000010358 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010359 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010360 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10361 mng_info->write_png_depth &&
10362 mng_info->write_png_depth != old_bit_depth)
10363 {
10364 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010365 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010366 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010367 >> (8-old_bit_depth));
10368 }
10369 }
glennrp0fe50b42010-11-16 03:52:51 +000010370
cristy3ed852e2009-09-05 21:47:34 +000010371 else
10372 {
cristy4c08aed2011-07-01 19:47:50 +000010373 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010374 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010375 }
glennrp0fe50b42010-11-16 03:52:51 +000010376
cristy3ed852e2009-09-05 21:47:34 +000010377 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010378 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010379 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010380 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010381
glennrp3b51f0e2010-11-27 18:14:08 +000010382 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10384 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010385
glennrpcf002022011-01-30 02:38:15 +000010386 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010387 }
10388 if (image->previous == (Image *) NULL)
10389 {
10390 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10391 if (status == MagickFalse)
10392 break;
10393 }
10394 }
10395 }
glennrp0fe50b42010-11-16 03:52:51 +000010396
glennrp8bb3a022010-12-13 20:40:04 +000010397 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010398 {
glennrp0fe50b42010-11-16 03:52:51 +000010399 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010400 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010401 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010402 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010403 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010404 {
cristy4c08aed2011-07-01 19:47:50 +000010405 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010406 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010407
glennrp8bb3a022010-12-13 20:40:04 +000010408 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010409 {
glennrp8bb3a022010-12-13 20:40:04 +000010410
cristybb503372010-05-27 20:51:26 +000010411 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010412 {
10413 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010414
cristy4c08aed2011-07-01 19:47:50 +000010415 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010416 break;
glennrp2cc891a2010-12-24 13:44:32 +000010417
glennrp5af765f2010-03-30 11:12:18 +000010418 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010419 {
glennrp8bb3a022010-12-13 20:40:04 +000010420 if (mng_info->IsPalette)
cristy4c08aed2011-07-01 19:47:50 +000010421 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010422 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010423
glennrp8bb3a022010-12-13 20:40:04 +000010424 else
cristy4c08aed2011-07-01 19:47:50 +000010425 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010426 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010427
glennrp3b51f0e2010-11-27 18:14:08 +000010428 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010430 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010431 }
glennrp2cc891a2010-12-24 13:44:32 +000010432
glennrp8bb3a022010-12-13 20:40:04 +000010433 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10434 {
10435 if (logging != MagickFalse && y == 0)
10436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10437 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010438
cristy4c08aed2011-07-01 19:47:50 +000010439 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010440 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010441 }
glennrp2cc891a2010-12-24 13:44:32 +000010442
glennrp3b51f0e2010-11-27 18:14:08 +000010443 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010445 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010446
glennrpcf002022011-01-30 02:38:15 +000010447 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010448 }
glennrp2cc891a2010-12-24 13:44:32 +000010449
glennrp8bb3a022010-12-13 20:40:04 +000010450 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010451 {
glennrp8bb3a022010-12-13 20:40:04 +000010452 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10453 if (status == MagickFalse)
10454 break;
cristy3ed852e2009-09-05 21:47:34 +000010455 }
cristy3ed852e2009-09-05 21:47:34 +000010456 }
10457 }
glennrp8bb3a022010-12-13 20:40:04 +000010458
10459 else
10460 {
cristy4c08aed2011-07-01 19:47:50 +000010461 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010462 *p;
10463
10464 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010465 {
glennrp8bb3a022010-12-13 20:40:04 +000010466 if ((image_depth > 8) || (mng_info->write_png24 ||
10467 mng_info->write_png32 ||
10468 (!mng_info->write_png8 && !mng_info->IsPalette)))
10469 {
10470 for (y=0; y < (ssize_t) image->rows; y++)
10471 {
10472 p=GetVirtualPixels(image,0,y,image->columns,1,
10473 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010474
cristy4c08aed2011-07-01 19:47:50 +000010475 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010476 break;
glennrp2cc891a2010-12-24 13:44:32 +000010477
glennrp8bb3a022010-12-13 20:40:04 +000010478 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10479 {
10480 if (image->storage_class == DirectClass)
cristy4c08aed2011-07-01 19:47:50 +000010481 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010482 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010483
glennrp8bb3a022010-12-13 20:40:04 +000010484 else
cristy4c08aed2011-07-01 19:47:50 +000010485 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010486 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010487 }
glennrp2cc891a2010-12-24 13:44:32 +000010488
glennrp8bb3a022010-12-13 20:40:04 +000010489 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10490 {
cristy4c08aed2011-07-01 19:47:50 +000010491 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010492 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +000010493 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010494
glennrp8bb3a022010-12-13 20:40:04 +000010495 if (logging != MagickFalse && y == 0)
10496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10497 " Writing GRAY_ALPHA PNG pixels (3)");
10498 }
glennrp2cc891a2010-12-24 13:44:32 +000010499
glennrp8bb3a022010-12-13 20:40:04 +000010500 else if (image_matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +000010501 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010502 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010503
glennrp8bb3a022010-12-13 20:40:04 +000010504 else
cristy4c08aed2011-07-01 19:47:50 +000010505 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010506 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010507
glennrp8bb3a022010-12-13 20:40:04 +000010508 if (logging != MagickFalse && y == 0)
10509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10510 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010511
glennrpcf002022011-01-30 02:38:15 +000010512 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010513 }
10514 }
glennrp2cc891a2010-12-24 13:44:32 +000010515
glennrp8bb3a022010-12-13 20:40:04 +000010516 else
10517 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10518 mng_info->write_png32 ||
10519 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10520 {
10521 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10522 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10523 {
10524 if (logging != MagickFalse)
10525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10526 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010527
glennrp8bb3a022010-12-13 20:40:04 +000010528 quantum_info->depth=8;
10529 image_depth=8;
10530 }
glennrp2cc891a2010-12-24 13:44:32 +000010531
glennrp8bb3a022010-12-13 20:40:04 +000010532 for (y=0; y < (ssize_t) image->rows; y++)
10533 {
10534 if (logging != MagickFalse && y == 0)
10535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10536 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010537
glennrp770d1932011-03-06 22:11:17 +000010538 p=GetVirtualPixels(image,0,y,image->columns,1,
10539 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010540
cristy4c08aed2011-07-01 19:47:50 +000010541 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010542 break;
glennrp2cc891a2010-12-24 13:44:32 +000010543
glennrp8bb3a022010-12-13 20:40:04 +000010544 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010545 {
glennrp4bf89732011-03-21 13:48:28 +000010546 quantum_info->depth=image->depth;
10547
cristy4c08aed2011-07-01 19:47:50 +000010548 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010549 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +000010550 }
glennrp2cc891a2010-12-24 13:44:32 +000010551
glennrp8bb3a022010-12-13 20:40:04 +000010552 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10553 {
10554 if (logging != MagickFalse && y == 0)
10555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10556 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010557
cristy4c08aed2011-07-01 19:47:50 +000010558 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010559 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +000010560 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010561 }
glennrp2cc891a2010-12-24 13:44:32 +000010562
glennrp8bb3a022010-12-13 20:40:04 +000010563 else
glennrp8bb3a022010-12-13 20:40:04 +000010564 {
cristy4c08aed2011-07-01 19:47:50 +000010565 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +000010566 quantum_info,IndexQuantum,ping_pixels,&image->exception);
10567
10568 if (logging != MagickFalse && y <= 2)
10569 {
10570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010571 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010572
10573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10574 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10575 (int)ping_pixels[0],(int)ping_pixels[1]);
10576 }
glennrp8bb3a022010-12-13 20:40:04 +000010577 }
glennrpcf002022011-01-30 02:38:15 +000010578 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010579 }
10580 }
glennrp2cc891a2010-12-24 13:44:32 +000010581
glennrp8bb3a022010-12-13 20:40:04 +000010582 if (image->previous == (Image *) NULL)
10583 {
10584 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10585 if (status == MagickFalse)
10586 break;
10587 }
cristy3ed852e2009-09-05 21:47:34 +000010588 }
glennrp8bb3a022010-12-13 20:40:04 +000010589 }
10590 }
10591
cristyb32b90a2009-09-07 21:45:48 +000010592 if (quantum_info != (QuantumInfo *) NULL)
10593 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010594
10595 if (logging != MagickFalse)
10596 {
10597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010598 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010599
cristy3ed852e2009-09-05 21:47:34 +000010600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010601 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010602
cristy3ed852e2009-09-05 21:47:34 +000010603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010604 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010605
cristy3ed852e2009-09-05 21:47:34 +000010606 if (mng_info->write_png_depth)
10607 {
10608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10609 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
10610 }
glennrp0fe50b42010-11-16 03:52:51 +000010611
cristy3ed852e2009-09-05 21:47:34 +000010612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010613 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010614
cristy3ed852e2009-09-05 21:47:34 +000010615 if (mng_info->write_png_colortype)
10616 {
10617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10618 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
10619 }
glennrp0fe50b42010-11-16 03:52:51 +000010620
cristy3ed852e2009-09-05 21:47:34 +000010621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010622 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010623
cristy3ed852e2009-09-05 21:47:34 +000010624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010625 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010626 }
10627 /*
glennrpa0ed0092011-04-18 16:36:29 +000010628 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010629 */
glennrp823b55c2011-03-14 18:46:46 +000010630 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010631 {
glennrp26f37912010-12-23 16:22:42 +000010632 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010633 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010634 while (property != (const char *) NULL)
10635 {
10636 png_textp
10637 text;
glennrp2cc891a2010-12-24 13:44:32 +000010638
cristyd15e6592011-10-15 00:13:06 +000010639 value=GetImageProperty(image,property,exception);
glennrpa0ed0092011-04-18 16:36:29 +000010640
10641 /* Don't write any "png:" properties; those are just for "identify" */
10642 if (LocaleNCompare(property,"png:",4) != 0 &&
10643
10644 /* Suppress density and units if we wrote a pHYs chunk */
10645 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010646 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010647 LocaleCompare(property,"units") != 0) &&
10648
10649 /* Suppress the IM-generated Date:create and Date:modify */
10650 (ping_exclude_date == MagickFalse ||
10651 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010652 {
glennrpc70af4a2011-03-07 00:08:23 +000010653 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010654 {
glennrpc70af4a2011-03-07 00:08:23 +000010655 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10656 text[0].key=(char *) property;
10657 text[0].text=(char *) value;
10658 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010659
glennrpc70af4a2011-03-07 00:08:23 +000010660 if (ping_exclude_tEXt != MagickFalse)
10661 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10662
10663 else if (ping_exclude_zTXt != MagickFalse)
10664 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10665
10666 else
glennrp26f37912010-12-23 16:22:42 +000010667 {
glennrpc70af4a2011-03-07 00:08:23 +000010668 text[0].compression=image_info->compression == NoCompression ||
10669 (image_info->compression == UndefinedCompression &&
10670 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10671 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010672 }
glennrp2cc891a2010-12-24 13:44:32 +000010673
glennrpc70af4a2011-03-07 00:08:23 +000010674 if (logging != MagickFalse)
10675 {
10676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10677 " Setting up text chunk");
10678
10679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10680 " keyword: %s",text[0].key);
10681 }
10682
10683 png_set_text(ping,ping_info,text,1);
10684 png_free(ping,text);
10685 }
glennrp26f37912010-12-23 16:22:42 +000010686 }
10687 property=GetNextImageProperty(image);
10688 }
cristy3ed852e2009-09-05 21:47:34 +000010689 }
10690
10691 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010692 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010693
10694 if (logging != MagickFalse)
10695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10696 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010697
cristy3ed852e2009-09-05 21:47:34 +000010698 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010699
cristy3ed852e2009-09-05 21:47:34 +000010700 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10701 {
10702 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010703 (ping_width != mng_info->page.width) ||
10704 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010705 {
10706 unsigned char
10707 chunk[32];
10708
10709 /*
10710 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10711 */
10712 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10713 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010714 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010715 chunk[4]=4;
10716 chunk[5]=0; /* frame name separator (no name) */
10717 chunk[6]=1; /* flag for changing delay, for next frame only */
10718 chunk[7]=0; /* flag for changing frame timeout */
10719 chunk[8]=1; /* flag for changing frame clipping for next frame */
10720 chunk[9]=0; /* flag for changing frame sync_id */
10721 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10722 chunk[14]=0; /* clipping boundaries delta type */
10723 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10724 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010725 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010726 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10727 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010728 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010729 (void) WriteBlob(image,31,chunk);
10730 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10731 mng_info->old_framing_mode=4;
10732 mng_info->framing_mode=1;
10733 }
glennrp0fe50b42010-11-16 03:52:51 +000010734
cristy3ed852e2009-09-05 21:47:34 +000010735 else
10736 mng_info->framing_mode=3;
10737 }
10738 if (mng_info->write_mng && !mng_info->need_fram &&
10739 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +000010740 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010741 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010742 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010743
cristy3ed852e2009-09-05 21:47:34 +000010744 /*
10745 Free PNG resources.
10746 */
glennrp5af765f2010-03-30 11:12:18 +000010747
cristy3ed852e2009-09-05 21:47:34 +000010748 png_destroy_write_struct(&ping,&ping_info);
10749
glennrpcf002022011-01-30 02:38:15 +000010750 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010751
10752#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010753 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010754#endif
10755
glennrpda8f3a72011-02-27 23:54:12 +000010756 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010757 (void) CloseBlob(image);
10758
10759 image_info=DestroyImageInfo(image_info);
10760 image=DestroyImage(image);
10761
10762 /* Store bit depth actually written */
10763 s[0]=(char) ping_bit_depth;
10764 s[1]='\0';
10765
cristyd15e6592011-10-15 00:13:06 +000010766 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
glennrpb9cfe272010-12-21 15:08:06 +000010767
cristy3ed852e2009-09-05 21:47:34 +000010768 if (logging != MagickFalse)
10769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10770 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010771
cristy3ed852e2009-09-05 21:47:34 +000010772 return(MagickTrue);
10773/* End write one PNG image */
10774}
10775
10776/*
10777%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10778% %
10779% %
10780% %
10781% W r i t e P N G I m a g e %
10782% %
10783% %
10784% %
10785%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10786%
10787% WritePNGImage() writes a Portable Network Graphics (PNG) or
10788% Multiple-image Network Graphics (MNG) image file.
10789%
10790% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10791%
10792% The format of the WritePNGImage method is:
10793%
cristy1e178e72011-08-28 19:44:34 +000010794% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10795% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010796%
10797% A description of each parameter follows:
10798%
10799% o image_info: the image info.
10800%
10801% o image: The image.
10802%
cristy1e178e72011-08-28 19:44:34 +000010803% o exception: return any errors or warnings in this structure.
10804%
cristy3ed852e2009-09-05 21:47:34 +000010805% Returns MagickTrue on success, MagickFalse on failure.
10806%
10807% Communicating with the PNG encoder:
10808%
10809% While the datastream written is always in PNG format and normally would
10810% be given the "png" file extension, this method also writes the following
10811% pseudo-formats which are subsets of PNG:
10812%
glennrp5a39f372011-02-25 04:52:16 +000010813% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10814% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010815% is present, the tRNS chunk must only have values 0 and 255
10816% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010817% transparent). If other values are present they will be
10818% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010819% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000010820% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10821% of any resulting fully-transparent pixels is changed to
10822% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000010823%
10824% If you want better quantization or dithering of the colors
10825% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010826% PNG encoder. The pixels contain 8-bit indices even if
10827% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010828% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010829% PNG grayscale type might be slightly more efficient. Please
10830% note that writing to the PNG8 format may result in loss
10831% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010832%
10833% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10834% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010835% one of the colors as transparent. The only loss incurred
10836% is reduction of sample depth to 8. If the image has more
10837% than one transparent color, has semitransparent pixels, or
10838% has an opaque pixel with the same RGB components as the
10839% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010840%
10841% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10842% transparency is permitted, i.e., the alpha sample for
10843% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010844% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010845% The only loss in data is the reduction of the sample depth
10846% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010847%
10848% o -define: For more precise control of the PNG output, you can use the
10849% Image options "png:bit-depth" and "png:color-type". These
10850% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010851% from the application programming interfaces. The options
10852% are case-independent and are converted to lowercase before
10853% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010854%
10855% png:color-type can be 0, 2, 3, 4, or 6.
10856%
10857% When png:color-type is 0 (Grayscale), png:bit-depth can
10858% be 1, 2, 4, 8, or 16.
10859%
10860% When png:color-type is 2 (RGB), png:bit-depth can
10861% be 8 or 16.
10862%
10863% When png:color-type is 3 (Indexed), png:bit-depth can
10864% be 1, 2, 4, or 8. This refers to the number of bits
10865% used to store the index. The color samples always have
10866% bit-depth 8 in indexed PNG files.
10867%
10868% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10869% png:bit-depth can be 8 or 16.
10870%
glennrp5a39f372011-02-25 04:52:16 +000010871% If the image cannot be written without loss with the requested bit-depth
10872% and color-type, a PNG file will not be written, and the encoder will
10873% return MagickFalse.
10874%
cristy3ed852e2009-09-05 21:47:34 +000010875% Since image encoders should not be responsible for the "heavy lifting",
10876% the user should make sure that ImageMagick has already reduced the
10877% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010878% transparency prior to attempting to write the image with depth, color,
glennrp54dc0692011-07-01 19:02:13 +000010879% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010880%
cristy3ed852e2009-09-05 21:47:34 +000010881% Note that another definition, "png:bit-depth-written" exists, but it
10882% is not intended for external use. It is only used internally by the
10883% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10884%
10885% It is possible to request that the PNG encoder write previously-formatted
10886% ancillary chunks in the output PNG file, using the "-profile" commandline
10887% option as shown below or by setting the profile via a programming
10888% interface:
10889%
10890% -profile PNG-chunk-x:<file>
10891%
10892% where x is a location flag and <file> is a file containing the chunk
10893% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010894% This encoder will compute the chunk length and CRC, so those must not
10895% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010896%
10897% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10898% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10899% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010900% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010901%
glennrpbb8a7332010-11-13 15:17:35 +000010902% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010903%
glennrp3241bd02010-12-12 04:36:28 +000010904% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010905%
glennrpd6afd542010-11-19 01:53:05 +000010906% o 32-bit depth is reduced to 16.
10907% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10908% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010909% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010910% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010911% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010912% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10913% this can be done without loss and a larger bit depth N was not
10914% requested via the "-define PNG:bit-depth=N" option.
10915% o If matte channel is present but only one transparent color is
10916% present, RGB+tRNS is written instead of RGBA
10917% o Opaque matte channel is removed (or added, if color-type 4 or 6
10918% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010919%
cristy3ed852e2009-09-05 21:47:34 +000010920%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10921*/
10922static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +000010923 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010924{
10925 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010926 excluding,
10927 logging,
10928 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010929 status;
10930
10931 MngInfo
10932 *mng_info;
10933
10934 const char
10935 *value;
10936
10937 int
glennrp21f0e622011-01-07 16:20:57 +000010938 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010939 source;
10940
cristy3ed852e2009-09-05 21:47:34 +000010941 /*
10942 Open image file.
10943 */
10944 assert(image_info != (const ImageInfo *) NULL);
10945 assert(image_info->signature == MagickSignature);
10946 assert(image != (Image *) NULL);
10947 assert(image->signature == MagickSignature);
10948 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010949 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010950 /*
10951 Allocate a MngInfo structure.
10952 */
10953 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010954 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010955
cristy3ed852e2009-09-05 21:47:34 +000010956 if (mng_info == (MngInfo *) NULL)
10957 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010958
cristy3ed852e2009-09-05 21:47:34 +000010959 /*
10960 Initialize members of the MngInfo structure.
10961 */
10962 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10963 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010964 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010965 have_mng_structure=MagickTrue;
10966
10967 /* See if user has requested a specific PNG subformat */
10968
10969 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10970 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10971 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10972
10973 if (mng_info->write_png8)
10974 {
glennrp9c1eb072010-06-06 22:19:15 +000010975 mng_info->write_png_colortype = /* 3 */ 4;
10976 mng_info->write_png_depth = 8;
10977 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010978 }
10979
10980 if (mng_info->write_png24)
10981 {
glennrp9c1eb072010-06-06 22:19:15 +000010982 mng_info->write_png_colortype = /* 2 */ 3;
10983 mng_info->write_png_depth = 8;
10984 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010985
glennrp9c1eb072010-06-06 22:19:15 +000010986 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000010987 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010988
glennrp9c1eb072010-06-06 22:19:15 +000010989 else
cristy018f07f2011-09-04 21:15:19 +000010990 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010991
glennrp9c1eb072010-06-06 22:19:15 +000010992 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010993 }
10994
10995 if (mng_info->write_png32)
10996 {
glennrp9c1eb072010-06-06 22:19:15 +000010997 mng_info->write_png_colortype = /* 6 */ 7;
10998 mng_info->write_png_depth = 8;
10999 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011000
glennrp9c1eb072010-06-06 22:19:15 +000011001 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011002 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011003
glennrp9c1eb072010-06-06 22:19:15 +000011004 else
cristy018f07f2011-09-04 21:15:19 +000011005 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011006
glennrp9c1eb072010-06-06 22:19:15 +000011007 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000011008 }
11009
11010 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011011
cristy3ed852e2009-09-05 21:47:34 +000011012 if (value != (char *) NULL)
11013 {
11014 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011015 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011016
cristy3ed852e2009-09-05 21:47:34 +000011017 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011018 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011019
cristy3ed852e2009-09-05 21:47:34 +000011020 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011021 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011022
cristy3ed852e2009-09-05 21:47:34 +000011023 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011024 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011025
cristy3ed852e2009-09-05 21:47:34 +000011026 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011027 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011028
glennrpbb8a7332010-11-13 15:17:35 +000011029 else
11030 (void) ThrowMagickException(&image->exception,
11031 GetMagickModule(),CoderWarning,
11032 "ignoring invalid defined png:bit-depth",
11033 "=%s",value);
11034
cristy3ed852e2009-09-05 21:47:34 +000011035 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011037 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011038 }
glennrp0fe50b42010-11-16 03:52:51 +000011039
cristy3ed852e2009-09-05 21:47:34 +000011040 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011041
cristy3ed852e2009-09-05 21:47:34 +000011042 if (value != (char *) NULL)
11043 {
11044 /* We must store colortype+1 because 0 is a valid colortype */
11045 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011046 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011047
cristy3ed852e2009-09-05 21:47:34 +000011048 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011049 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011050
cristy3ed852e2009-09-05 21:47:34 +000011051 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011052 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011053
cristy3ed852e2009-09-05 21:47:34 +000011054 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011055 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011056
cristy3ed852e2009-09-05 21:47:34 +000011057 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011058 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011059
glennrpbb8a7332010-11-13 15:17:35 +000011060 else
11061 (void) ThrowMagickException(&image->exception,
11062 GetMagickModule(),CoderWarning,
11063 "ignoring invalid defined png:color-type",
11064 "=%s",value);
11065
cristy3ed852e2009-09-05 21:47:34 +000011066 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011068 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011069 }
11070
glennrp0e8ea192010-12-24 18:00:33 +000011071 /* Check for chunks to be excluded:
11072 *
glennrp0dff56c2011-01-29 19:10:02 +000011073 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011074 * listed in the "unused_chunks" array, above.
11075 *
11076 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
11077 * define (in the image properties or in the image artifacts)
11078 * or via a mng_info member. For convenience, in addition
11079 * to or instead of a comma-separated list of chunks, the
11080 * "exclude-chunk" string can be simply "all" or "none".
11081 *
11082 * The exclude-chunk define takes priority over the mng_info.
11083 *
11084 * A "PNG:include-chunk" define takes priority over both the
11085 * mng_info and the "PNG:exclude-chunk" define. Like the
11086 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011087 * well as a comma-separated list. Chunks that are unknown to
11088 * ImageMagick are always excluded, regardless of their "copy-safe"
11089 * status according to the PNG specification, and even if they
11090 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000011091 *
11092 * Finally, all chunks listed in the "unused_chunks" array are
11093 * automatically excluded, regardless of the other instructions
11094 * or lack thereof.
11095 *
11096 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11097 * will not be written and the gAMA chunk will only be written if it
11098 * is not between .45 and .46, or approximately (1.0/2.2).
11099 *
11100 * If you exclude tRNS and the image has transparency, the colortype
11101 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11102 *
11103 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011104 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011105 */
11106
glennrp26f37912010-12-23 16:22:42 +000011107 mng_info->ping_exclude_bKGD=MagickFalse;
11108 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011109 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011110 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11111 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011112 mng_info->ping_exclude_iCCP=MagickFalse;
11113 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11114 mng_info->ping_exclude_oFFs=MagickFalse;
11115 mng_info->ping_exclude_pHYs=MagickFalse;
11116 mng_info->ping_exclude_sRGB=MagickFalse;
11117 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011118 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011119 mng_info->ping_exclude_vpAg=MagickFalse;
11120 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11121 mng_info->ping_exclude_zTXt=MagickFalse;
11122
glennrp8d3d6e52011-04-19 04:39:51 +000011123 mng_info->ping_preserve_colormap=MagickFalse;
11124
11125 value=GetImageArtifact(image,"png:preserve-colormap");
11126 if (value == NULL)
11127 value=GetImageOption(image_info,"png:preserve-colormap");
11128 if (value != NULL)
11129 mng_info->ping_preserve_colormap=MagickTrue;
11130
glennrp18682582011-06-30 18:11:47 +000011131 /* Thes compression-level, compression-strategy, and compression-filter
11132 * defines take precedence over values from the -quality option.
11133 */
11134 value=GetImageArtifact(image,"png:compression-level");
11135 if (value == NULL)
11136 value=GetImageOption(image_info,"png:compression-level");
11137 if (value != NULL)
11138 {
glennrp18682582011-06-30 18:11:47 +000011139 /* We have to add 1 to everything because 0 is a valid input,
11140 * and we want to use 0 (the default) to mean undefined.
11141 */
11142 if (LocaleCompare(value,"0") == 0)
11143 mng_info->write_png_compression_level = 1;
11144
11145 if (LocaleCompare(value,"1") == 0)
11146 mng_info->write_png_compression_level = 2;
11147
11148 else if (LocaleCompare(value,"2") == 0)
11149 mng_info->write_png_compression_level = 3;
11150
11151 else if (LocaleCompare(value,"3") == 0)
11152 mng_info->write_png_compression_level = 4;
11153
11154 else if (LocaleCompare(value,"4") == 0)
11155 mng_info->write_png_compression_level = 5;
11156
11157 else if (LocaleCompare(value,"5") == 0)
11158 mng_info->write_png_compression_level = 6;
11159
11160 else if (LocaleCompare(value,"6") == 0)
11161 mng_info->write_png_compression_level = 7;
11162
11163 else if (LocaleCompare(value,"7") == 0)
11164 mng_info->write_png_compression_level = 8;
11165
11166 else if (LocaleCompare(value,"8") == 0)
11167 mng_info->write_png_compression_level = 9;
11168
11169 else if (LocaleCompare(value,"9") == 0)
11170 mng_info->write_png_compression_level = 10;
11171
11172 else
11173 (void) ThrowMagickException(&image->exception,
11174 GetMagickModule(),CoderWarning,
11175 "ignoring invalid defined png:compression-level",
11176 "=%s",value);
11177 }
11178
11179 value=GetImageArtifact(image,"png:compression-strategy");
11180 if (value == NULL)
11181 value=GetImageOption(image_info,"png:compression-strategy");
11182 if (value != NULL)
11183 {
11184
11185 if (LocaleCompare(value,"0") == 0)
11186 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11187
11188 else if (LocaleCompare(value,"1") == 0)
11189 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11190
11191 else if (LocaleCompare(value,"2") == 0)
11192 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11193
11194 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011195#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011196 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011197#else
11198 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11199#endif
glennrp18682582011-06-30 18:11:47 +000011200
11201 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011202#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011203 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011204#else
11205 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11206#endif
glennrp18682582011-06-30 18:11:47 +000011207
11208 else
11209 (void) ThrowMagickException(&image->exception,
11210 GetMagickModule(),CoderWarning,
11211 "ignoring invalid defined png:compression-strategy",
11212 "=%s",value);
11213 }
11214
11215 value=GetImageArtifact(image,"png:compression-filter");
11216 if (value == NULL)
11217 value=GetImageOption(image_info,"png:compression-filter");
11218 if (value != NULL)
11219 {
11220
11221 /* To do: combinations of filters allowed by libpng
11222 * masks 0x08 through 0xf8
11223 *
11224 * Implement this as a comma-separated list of 0,1,2,3,4,5
11225 * where 5 is a special case meaning PNG_ALL_FILTERS.
11226 */
11227
11228 if (LocaleCompare(value,"0") == 0)
11229 mng_info->write_png_compression_filter = 1;
11230
11231 if (LocaleCompare(value,"1") == 0)
11232 mng_info->write_png_compression_filter = 2;
11233
11234 else if (LocaleCompare(value,"2") == 0)
11235 mng_info->write_png_compression_filter = 3;
11236
11237 else if (LocaleCompare(value,"3") == 0)
11238 mng_info->write_png_compression_filter = 4;
11239
11240 else if (LocaleCompare(value,"4") == 0)
11241 mng_info->write_png_compression_filter = 5;
11242
11243 else if (LocaleCompare(value,"5") == 0)
11244 mng_info->write_png_compression_filter = 6;
11245
glennrp18682582011-06-30 18:11:47 +000011246 else
11247 (void) ThrowMagickException(&image->exception,
11248 GetMagickModule(),CoderWarning,
11249 "ignoring invalid defined png:compression-filter",
11250 "=%s",value);
11251 }
11252
glennrp03812ae2010-12-24 01:31:34 +000011253 excluding=MagickFalse;
11254
glennrp5c7cf4e2010-12-24 00:30:00 +000011255 for (source=0; source<1; source++)
11256 {
11257 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011258 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011259 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011260
11261 if (value == NULL)
11262 value=GetImageArtifact(image,"png:exclude-chunks");
11263 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011264 else
glennrpacba0042010-12-24 14:27:26 +000011265 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011266 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011267
glennrpacba0042010-12-24 14:27:26 +000011268 if (value == NULL)
11269 value=GetImageOption(image_info,"png:exclude-chunks");
11270 }
11271
glennrp03812ae2010-12-24 01:31:34 +000011272 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011273 {
glennrp03812ae2010-12-24 01:31:34 +000011274
11275 size_t
11276 last;
11277
11278 excluding=MagickTrue;
11279
11280 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011281 {
11282 if (source == 0)
11283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11284 " png:exclude-chunk=%s found in image artifacts.\n", value);
11285 else
11286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11287 " png:exclude-chunk=%s found in image properties.\n", value);
11288 }
glennrp03812ae2010-12-24 01:31:34 +000011289
11290 last=strlen(value);
11291
11292 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011293 {
glennrp03812ae2010-12-24 01:31:34 +000011294
11295 if (LocaleNCompare(value+i,"all",3) == 0)
11296 {
11297 mng_info->ping_exclude_bKGD=MagickTrue;
11298 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011299 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011300 mng_info->ping_exclude_EXIF=MagickTrue;
11301 mng_info->ping_exclude_gAMA=MagickTrue;
11302 mng_info->ping_exclude_iCCP=MagickTrue;
11303 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11304 mng_info->ping_exclude_oFFs=MagickTrue;
11305 mng_info->ping_exclude_pHYs=MagickTrue;
11306 mng_info->ping_exclude_sRGB=MagickTrue;
11307 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011308 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011309 mng_info->ping_exclude_vpAg=MagickTrue;
11310 mng_info->ping_exclude_zCCP=MagickTrue;
11311 mng_info->ping_exclude_zTXt=MagickTrue;
11312 i--;
11313 }
glennrp2cc891a2010-12-24 13:44:32 +000011314
glennrp03812ae2010-12-24 01:31:34 +000011315 if (LocaleNCompare(value+i,"none",4) == 0)
11316 {
11317 mng_info->ping_exclude_bKGD=MagickFalse;
11318 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011319 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011320 mng_info->ping_exclude_EXIF=MagickFalse;
11321 mng_info->ping_exclude_gAMA=MagickFalse;
11322 mng_info->ping_exclude_iCCP=MagickFalse;
11323 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11324 mng_info->ping_exclude_oFFs=MagickFalse;
11325 mng_info->ping_exclude_pHYs=MagickFalse;
11326 mng_info->ping_exclude_sRGB=MagickFalse;
11327 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011328 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011329 mng_info->ping_exclude_vpAg=MagickFalse;
11330 mng_info->ping_exclude_zCCP=MagickFalse;
11331 mng_info->ping_exclude_zTXt=MagickFalse;
11332 }
glennrp2cc891a2010-12-24 13:44:32 +000011333
glennrp03812ae2010-12-24 01:31:34 +000011334 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11335 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011336
glennrp03812ae2010-12-24 01:31:34 +000011337 if (LocaleNCompare(value+i,"chrm",4) == 0)
11338 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011339
glennrpa0ed0092011-04-18 16:36:29 +000011340 if (LocaleNCompare(value+i,"date",4) == 0)
11341 mng_info->ping_exclude_date=MagickTrue;
11342
glennrp03812ae2010-12-24 01:31:34 +000011343 if (LocaleNCompare(value+i,"exif",4) == 0)
11344 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011345
glennrp03812ae2010-12-24 01:31:34 +000011346 if (LocaleNCompare(value+i,"gama",4) == 0)
11347 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011348
glennrp03812ae2010-12-24 01:31:34 +000011349 if (LocaleNCompare(value+i,"iccp",4) == 0)
11350 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011351
glennrp03812ae2010-12-24 01:31:34 +000011352 /*
11353 if (LocaleNCompare(value+i,"itxt",4) == 0)
11354 mng_info->ping_exclude_iTXt=MagickTrue;
11355 */
glennrp2cc891a2010-12-24 13:44:32 +000011356
glennrp03812ae2010-12-24 01:31:34 +000011357 if (LocaleNCompare(value+i,"gama",4) == 0)
11358 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011359
glennrp03812ae2010-12-24 01:31:34 +000011360 if (LocaleNCompare(value+i,"offs",4) == 0)
11361 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011362
glennrp03812ae2010-12-24 01:31:34 +000011363 if (LocaleNCompare(value+i,"phys",4) == 0)
11364 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011365
glennrpa1e3b7b2010-12-24 16:37:33 +000011366 if (LocaleNCompare(value+i,"srgb",4) == 0)
11367 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011368
glennrp03812ae2010-12-24 01:31:34 +000011369 if (LocaleNCompare(value+i,"text",4) == 0)
11370 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011371
glennrpa1e3b7b2010-12-24 16:37:33 +000011372 if (LocaleNCompare(value+i,"trns",4) == 0)
11373 mng_info->ping_exclude_tRNS=MagickTrue;
11374
glennrp03812ae2010-12-24 01:31:34 +000011375 if (LocaleNCompare(value+i,"vpag",4) == 0)
11376 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011377
glennrp03812ae2010-12-24 01:31:34 +000011378 if (LocaleNCompare(value+i,"zccp",4) == 0)
11379 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011380
glennrp03812ae2010-12-24 01:31:34 +000011381 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11382 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011383
glennrp03812ae2010-12-24 01:31:34 +000011384 }
glennrpce91ed52010-12-23 22:37:49 +000011385 }
glennrp26f37912010-12-23 16:22:42 +000011386 }
11387
glennrp5c7cf4e2010-12-24 00:30:00 +000011388 for (source=0; source<1; source++)
11389 {
11390 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011391 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011392 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011393
11394 if (value == NULL)
11395 value=GetImageArtifact(image,"png:include-chunks");
11396 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011397 else
glennrpacba0042010-12-24 14:27:26 +000011398 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011399 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011400
glennrpacba0042010-12-24 14:27:26 +000011401 if (value == NULL)
11402 value=GetImageOption(image_info,"png:include-chunks");
11403 }
11404
glennrp03812ae2010-12-24 01:31:34 +000011405 if (value != NULL)
11406 {
11407 size_t
11408 last;
glennrp26f37912010-12-23 16:22:42 +000011409
glennrp03812ae2010-12-24 01:31:34 +000011410 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011411
glennrp03812ae2010-12-24 01:31:34 +000011412 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011413 {
11414 if (source == 0)
11415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11416 " png:include-chunk=%s found in image artifacts.\n", value);
11417 else
11418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11419 " png:include-chunk=%s found in image properties.\n", value);
11420 }
glennrp03812ae2010-12-24 01:31:34 +000011421
11422 last=strlen(value);
11423
11424 for (i=0; i<(int) last; i+=5)
11425 {
11426 if (LocaleNCompare(value+i,"all",3) == 0)
11427 {
11428 mng_info->ping_exclude_bKGD=MagickFalse;
11429 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011430 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011431 mng_info->ping_exclude_EXIF=MagickFalse;
11432 mng_info->ping_exclude_gAMA=MagickFalse;
11433 mng_info->ping_exclude_iCCP=MagickFalse;
11434 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11435 mng_info->ping_exclude_oFFs=MagickFalse;
11436 mng_info->ping_exclude_pHYs=MagickFalse;
11437 mng_info->ping_exclude_sRGB=MagickFalse;
11438 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011439 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011440 mng_info->ping_exclude_vpAg=MagickFalse;
11441 mng_info->ping_exclude_zCCP=MagickFalse;
11442 mng_info->ping_exclude_zTXt=MagickFalse;
11443 i--;
11444 }
glennrp2cc891a2010-12-24 13:44:32 +000011445
glennrp03812ae2010-12-24 01:31:34 +000011446 if (LocaleNCompare(value+i,"none",4) == 0)
11447 {
11448 mng_info->ping_exclude_bKGD=MagickTrue;
11449 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011450 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011451 mng_info->ping_exclude_EXIF=MagickTrue;
11452 mng_info->ping_exclude_gAMA=MagickTrue;
11453 mng_info->ping_exclude_iCCP=MagickTrue;
11454 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11455 mng_info->ping_exclude_oFFs=MagickTrue;
11456 mng_info->ping_exclude_pHYs=MagickTrue;
11457 mng_info->ping_exclude_sRGB=MagickTrue;
11458 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011459 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011460 mng_info->ping_exclude_vpAg=MagickTrue;
11461 mng_info->ping_exclude_zCCP=MagickTrue;
11462 mng_info->ping_exclude_zTXt=MagickTrue;
11463 }
glennrp2cc891a2010-12-24 13:44:32 +000011464
glennrp03812ae2010-12-24 01:31:34 +000011465 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11466 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011467
glennrp03812ae2010-12-24 01:31:34 +000011468 if (LocaleNCompare(value+i,"chrm",4) == 0)
11469 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011470
glennrpa0ed0092011-04-18 16:36:29 +000011471 if (LocaleNCompare(value+i,"date",4) == 0)
11472 mng_info->ping_exclude_date=MagickFalse;
11473
glennrp03812ae2010-12-24 01:31:34 +000011474 if (LocaleNCompare(value+i,"exif",4) == 0)
11475 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011476
glennrp03812ae2010-12-24 01:31:34 +000011477 if (LocaleNCompare(value+i,"gama",4) == 0)
11478 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011479
glennrp03812ae2010-12-24 01:31:34 +000011480 if (LocaleNCompare(value+i,"iccp",4) == 0)
11481 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011482
glennrp03812ae2010-12-24 01:31:34 +000011483 /*
11484 if (LocaleNCompare(value+i,"itxt",4) == 0)
11485 mng_info->ping_exclude_iTXt=MagickFalse;
11486 */
glennrp2cc891a2010-12-24 13:44:32 +000011487
glennrp03812ae2010-12-24 01:31:34 +000011488 if (LocaleNCompare(value+i,"gama",4) == 0)
11489 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011490
glennrp03812ae2010-12-24 01:31:34 +000011491 if (LocaleNCompare(value+i,"offs",4) == 0)
11492 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011493
glennrp03812ae2010-12-24 01:31:34 +000011494 if (LocaleNCompare(value+i,"phys",4) == 0)
11495 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011496
glennrpa1e3b7b2010-12-24 16:37:33 +000011497 if (LocaleNCompare(value+i,"srgb",4) == 0)
11498 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011499
glennrp03812ae2010-12-24 01:31:34 +000011500 if (LocaleNCompare(value+i,"text",4) == 0)
11501 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011502
glennrpa1e3b7b2010-12-24 16:37:33 +000011503 if (LocaleNCompare(value+i,"trns",4) == 0)
11504 mng_info->ping_exclude_tRNS=MagickFalse;
11505
glennrp03812ae2010-12-24 01:31:34 +000011506 if (LocaleNCompare(value+i,"vpag",4) == 0)
11507 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011508
glennrp03812ae2010-12-24 01:31:34 +000011509 if (LocaleNCompare(value+i,"zccp",4) == 0)
11510 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011511
glennrp03812ae2010-12-24 01:31:34 +000011512 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11513 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011514
glennrp03812ae2010-12-24 01:31:34 +000011515 }
glennrpce91ed52010-12-23 22:37:49 +000011516 }
glennrp26f37912010-12-23 16:22:42 +000011517 }
11518
glennrp03812ae2010-12-24 01:31:34 +000011519 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011520 {
11521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11522 " Chunks to be excluded from the output PNG:");
11523 if (mng_info->ping_exclude_bKGD != MagickFalse)
11524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11525 " bKGD");
11526 if (mng_info->ping_exclude_cHRM != MagickFalse)
11527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11528 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011529 if (mng_info->ping_exclude_date != MagickFalse)
11530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11531 " date");
glennrp26f37912010-12-23 16:22:42 +000011532 if (mng_info->ping_exclude_EXIF != MagickFalse)
11533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11534 " EXIF");
11535 if (mng_info->ping_exclude_gAMA != MagickFalse)
11536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11537 " gAMA");
11538 if (mng_info->ping_exclude_iCCP != MagickFalse)
11539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11540 " iCCP");
11541/*
11542 if (mng_info->ping_exclude_iTXt != MagickFalse)
11543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11544 " iTXt");
11545*/
11546 if (mng_info->ping_exclude_oFFs != MagickFalse)
11547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11548 " oFFs");
11549 if (mng_info->ping_exclude_pHYs != MagickFalse)
11550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11551 " pHYs");
11552 if (mng_info->ping_exclude_sRGB != MagickFalse)
11553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11554 " sRGB");
11555 if (mng_info->ping_exclude_tEXt != MagickFalse)
11556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11557 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011558 if (mng_info->ping_exclude_tRNS != MagickFalse)
11559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11560 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011561 if (mng_info->ping_exclude_vpAg != MagickFalse)
11562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11563 " vpAg");
11564 if (mng_info->ping_exclude_zCCP != MagickFalse)
11565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11566 " zCCP");
11567 if (mng_info->ping_exclude_zTXt != MagickFalse)
11568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11569 " zTXt");
11570 }
11571
glennrpb9cfe272010-12-21 15:08:06 +000011572 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011573
cristy018f07f2011-09-04 21:15:19 +000011574 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011575
11576 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011577
cristy3ed852e2009-09-05 21:47:34 +000011578 if (logging != MagickFalse)
11579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011580
cristy3ed852e2009-09-05 21:47:34 +000011581 return(status);
11582}
11583
11584#if defined(JNG_SUPPORTED)
11585
11586/* Write one JNG image */
11587static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +000011588 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011589{
11590 Image
11591 *jpeg_image;
11592
11593 ImageInfo
11594 *jpeg_image_info;
11595
11596 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011597 logging,
cristy3ed852e2009-09-05 21:47:34 +000011598 status;
11599
11600 size_t
11601 length;
11602
11603 unsigned char
11604 *blob,
11605 chunk[80],
11606 *p;
11607
11608 unsigned int
11609 jng_alpha_compression_method,
11610 jng_alpha_sample_depth,
11611 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011612 transparent;
11613
cristybb503372010-05-27 20:51:26 +000011614 size_t
cristy3ed852e2009-09-05 21:47:34 +000011615 jng_quality;
11616
11617 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011618 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011619
11620 blob=(unsigned char *) NULL;
11621 jpeg_image=(Image *) NULL;
11622 jpeg_image_info=(ImageInfo *) NULL;
11623
11624 status=MagickTrue;
11625 transparent=image_info->type==GrayscaleMatteType ||
11626 image_info->type==TrueColorMatteType;
11627 jng_color_type=10;
11628 jng_alpha_sample_depth=0;
11629 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11630 jng_alpha_compression_method=0;
11631
11632 if (image->matte != MagickFalse)
11633 {
11634 /* if any pixels are transparent */
11635 transparent=MagickTrue;
11636 if (image_info->compression==JPEGCompression)
11637 jng_alpha_compression_method=8;
11638 }
11639
11640 if (transparent)
11641 {
cristybd5a96c2011-08-21 00:04:26 +000011642 ChannelType
11643 channel_mask;
11644
cristy3ed852e2009-09-05 21:47:34 +000011645 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011646
cristy3ed852e2009-09-05 21:47:34 +000011647 /* Create JPEG blob, image, and image_info */
11648 if (logging != MagickFalse)
11649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +000011650 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011651
cristy3ed852e2009-09-05 21:47:34 +000011652 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011653
cristy3ed852e2009-09-05 21:47:34 +000011654 if (jpeg_image_info == (ImageInfo *) NULL)
11655 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011656
cristy3ed852e2009-09-05 21:47:34 +000011657 if (logging != MagickFalse)
11658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11659 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011660
cristy3ed852e2009-09-05 21:47:34 +000011661 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011662
cristy3ed852e2009-09-05 21:47:34 +000011663 if (jpeg_image == (Image *) NULL)
11664 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011665
cristy3ed852e2009-09-05 21:47:34 +000011666 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristybd5a96c2011-08-21 00:04:26 +000011667 channel_mask=SetPixelChannelMask(jpeg_image,AlphaChannel);
cristye941a752011-10-15 01:52:48 +000011668 status=SeparateImage(jpeg_image,exception);
cristybd5a96c2011-08-21 00:04:26 +000011669 (void) SetPixelChannelMap(jpeg_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +000011670 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011671
cristy3ed852e2009-09-05 21:47:34 +000011672 if (jng_quality >= 1000)
11673 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000011674
cristy3ed852e2009-09-05 21:47:34 +000011675 else
11676 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000011677
cristy3ed852e2009-09-05 21:47:34 +000011678 jpeg_image_info->type=GrayscaleType;
cristy018f07f2011-09-04 21:15:19 +000011679 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000011680 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011681 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011682 "%s",jpeg_image->filename);
11683 }
11684
11685 /* To do: check bit depth of PNG alpha channel */
11686
11687 /* Check if image is grayscale. */
11688 if (image_info->type != TrueColorMatteType && image_info->type !=
11689 TrueColorType && ImageIsGray(image))
11690 jng_color_type-=2;
11691
11692 if (transparent)
11693 {
11694 if (jng_alpha_compression_method==0)
11695 {
11696 const char
11697 *value;
11698
cristy4c08aed2011-07-01 19:47:50 +000011699 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011700 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11701 &image->exception);
11702 if (logging != MagickFalse)
11703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11704 " Creating PNG blob.");
11705 length=0;
11706
11707 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11708 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11709 jpeg_image_info->interlace=NoInterlace;
11710
11711 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11712 &image->exception);
11713
11714 /* Retrieve sample depth used */
cristyd15e6592011-10-15 00:13:06 +000011715 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
cristy3ed852e2009-09-05 21:47:34 +000011716 if (value != (char *) NULL)
11717 jng_alpha_sample_depth= (unsigned int) value[0];
11718 }
11719 else
11720 {
cristy4c08aed2011-07-01 19:47:50 +000011721 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011722
11723 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11724 &image->exception);
11725
11726 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11727 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11728 jpeg_image_info->interlace=NoInterlace;
11729 if (logging != MagickFalse)
11730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11731 " Creating blob.");
11732 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000011733 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000011734 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011735
cristy3ed852e2009-09-05 21:47:34 +000011736 if (logging != MagickFalse)
11737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011738 " Successfully read jpeg_image into a blob, length=%.20g.",
11739 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011740
11741 }
11742 /* Destroy JPEG image and image_info */
11743 jpeg_image=DestroyImage(jpeg_image);
11744 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11745 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11746 }
11747
11748 /* Write JHDR chunk */
11749 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11750 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011751 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011752 PNGLong(chunk+4,(png_uint_32) image->columns);
11753 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011754 chunk[12]=jng_color_type;
11755 chunk[13]=8; /* sample depth */
11756 chunk[14]=8; /*jng_image_compression_method */
11757 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11758 chunk[16]=jng_alpha_sample_depth;
11759 chunk[17]=jng_alpha_compression_method;
11760 chunk[18]=0; /*jng_alpha_filter_method */
11761 chunk[19]=0; /*jng_alpha_interlace_method */
11762 (void) WriteBlob(image,20,chunk);
11763 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11764 if (logging != MagickFalse)
11765 {
11766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011767 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011768
cristy3ed852e2009-09-05 21:47:34 +000011769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011770 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011771
cristy3ed852e2009-09-05 21:47:34 +000011772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11773 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011774
cristy3ed852e2009-09-05 21:47:34 +000011775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11776 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011777
cristy3ed852e2009-09-05 21:47:34 +000011778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11779 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011780
cristy3ed852e2009-09-05 21:47:34 +000011781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11782 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011783
cristy3ed852e2009-09-05 21:47:34 +000011784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11785 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011786
cristy3ed852e2009-09-05 21:47:34 +000011787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11788 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011789
cristy3ed852e2009-09-05 21:47:34 +000011790 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11791 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011792
cristy3ed852e2009-09-05 21:47:34 +000011793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11794 " JNG alpha interlace:%5d",0);
11795 }
11796
glennrp0fe50b42010-11-16 03:52:51 +000011797 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011798 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011799
11800 /*
11801 Write leading ancillary chunks
11802 */
11803
11804 if (transparent)
11805 {
11806 /*
11807 Write JNG bKGD chunk
11808 */
11809
11810 unsigned char
11811 blue,
11812 green,
11813 red;
11814
cristybb503372010-05-27 20:51:26 +000011815 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011816 num_bytes;
11817
11818 if (jng_color_type == 8 || jng_color_type == 12)
11819 num_bytes=6L;
11820 else
11821 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011822 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011823 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011824 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011825 red=ScaleQuantumToChar(image->background_color.red);
11826 green=ScaleQuantumToChar(image->background_color.green);
11827 blue=ScaleQuantumToChar(image->background_color.blue);
11828 *(chunk+4)=0;
11829 *(chunk+5)=red;
11830 *(chunk+6)=0;
11831 *(chunk+7)=green;
11832 *(chunk+8)=0;
11833 *(chunk+9)=blue;
11834 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11835 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11836 }
11837
11838 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11839 {
11840 /*
11841 Write JNG sRGB chunk
11842 */
11843 (void) WriteBlobMSBULong(image,1L);
11844 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011845 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011846
cristy3ed852e2009-09-05 21:47:34 +000011847 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011848 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011849 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011850 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011851
cristy3ed852e2009-09-05 21:47:34 +000011852 else
glennrpe610a072010-08-05 17:08:46 +000011853 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011854 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011855 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011856
cristy3ed852e2009-09-05 21:47:34 +000011857 (void) WriteBlob(image,5,chunk);
11858 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11859 }
11860 else
11861 {
11862 if (image->gamma != 0.0)
11863 {
11864 /*
11865 Write JNG gAMA chunk
11866 */
11867 (void) WriteBlobMSBULong(image,4L);
11868 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011869 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011870 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011871 (void) WriteBlob(image,8,chunk);
11872 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11873 }
glennrp0fe50b42010-11-16 03:52:51 +000011874
cristy3ed852e2009-09-05 21:47:34 +000011875 if ((mng_info->equal_chrms == MagickFalse) &&
11876 (image->chromaticity.red_primary.x != 0.0))
11877 {
11878 PrimaryInfo
11879 primary;
11880
11881 /*
11882 Write JNG cHRM chunk
11883 */
11884 (void) WriteBlobMSBULong(image,32L);
11885 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011886 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011887 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011888 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11889 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011890 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011891 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11892 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011893 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011894 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11895 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011896 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011897 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11898 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011899 (void) WriteBlob(image,36,chunk);
11900 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11901 }
11902 }
glennrp0fe50b42010-11-16 03:52:51 +000011903
cristy3ed852e2009-09-05 21:47:34 +000011904 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11905 {
11906 /*
11907 Write JNG pHYs chunk
11908 */
11909 (void) WriteBlobMSBULong(image,9L);
11910 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011911 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011912 if (image->units == PixelsPerInchResolution)
11913 {
cristy35ef8242010-06-03 16:24:13 +000011914 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011915 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011916
cristy35ef8242010-06-03 16:24:13 +000011917 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011918 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011919
cristy3ed852e2009-09-05 21:47:34 +000011920 chunk[12]=1;
11921 }
glennrp0fe50b42010-11-16 03:52:51 +000011922
cristy3ed852e2009-09-05 21:47:34 +000011923 else
11924 {
11925 if (image->units == PixelsPerCentimeterResolution)
11926 {
cristy35ef8242010-06-03 16:24:13 +000011927 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011928 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011929
cristy35ef8242010-06-03 16:24:13 +000011930 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011931 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011932
cristy3ed852e2009-09-05 21:47:34 +000011933 chunk[12]=1;
11934 }
glennrp0fe50b42010-11-16 03:52:51 +000011935
cristy3ed852e2009-09-05 21:47:34 +000011936 else
11937 {
cristy35ef8242010-06-03 16:24:13 +000011938 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11939 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011940 chunk[12]=0;
11941 }
11942 }
11943 (void) WriteBlob(image,13,chunk);
11944 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11945 }
11946
11947 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11948 {
11949 /*
11950 Write JNG oFFs chunk
11951 */
11952 (void) WriteBlobMSBULong(image,9L);
11953 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011954 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011955 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11956 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011957 chunk[12]=0;
11958 (void) WriteBlob(image,13,chunk);
11959 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11960 }
11961 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11962 {
11963 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11964 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011965 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011966 PNGLong(chunk+4,(png_uint_32) image->page.width);
11967 PNGLong(chunk+8,(png_uint_32) image->page.height);
11968 chunk[12]=0; /* unit = pixels */
11969 (void) WriteBlob(image,13,chunk);
11970 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11971 }
11972
11973
11974 if (transparent)
11975 {
11976 if (jng_alpha_compression_method==0)
11977 {
cristybb503372010-05-27 20:51:26 +000011978 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011979 i;
11980
cristybb503372010-05-27 20:51:26 +000011981 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011982 len;
11983
11984 /* Write IDAT chunk header */
11985 if (logging != MagickFalse)
11986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011987 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000011988 length);
cristy3ed852e2009-09-05 21:47:34 +000011989
11990 /* Copy IDAT chunks */
11991 len=0;
11992 p=blob+8;
cristybb503372010-05-27 20:51:26 +000011993 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000011994 {
11995 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11996 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000011997
cristy3ed852e2009-09-05 21:47:34 +000011998 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11999 {
12000 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000012001 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000012002 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000012003 (void) WriteBlob(image,(size_t) len+4,p);
12004 (void) WriteBlobMSBULong(image,
12005 crc32(0,p,(uInt) len+4));
12006 }
glennrp0fe50b42010-11-16 03:52:51 +000012007
cristy3ed852e2009-09-05 21:47:34 +000012008 else
12009 {
12010 if (logging != MagickFalse)
12011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012012 " Skipping %c%c%c%c chunk, length=%.20g.",
12013 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012014 }
12015 p+=(8+len);
12016 }
12017 }
12018 else
12019 {
12020 /* Write JDAA chunk header */
12021 if (logging != MagickFalse)
12022 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012023 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012024 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012025 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012026 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012027 /* Write JDAT chunk(s) data */
12028 (void) WriteBlob(image,4,chunk);
12029 (void) WriteBlob(image,length,blob);
12030 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12031 (uInt) length));
12032 }
12033 blob=(unsigned char *) RelinquishMagickMemory(blob);
12034 }
12035
12036 /* Encode image as a JPEG blob */
12037 if (logging != MagickFalse)
12038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12039 " Creating jpeg_image_info.");
12040 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12041 if (jpeg_image_info == (ImageInfo *) NULL)
12042 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12043
12044 if (logging != MagickFalse)
12045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12046 " Creating jpeg_image.");
12047
12048 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12049 if (jpeg_image == (Image *) NULL)
12050 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12051 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12052
12053 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012054 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012055 jpeg_image->filename);
12056
12057 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12058 &image->exception);
12059
12060 if (logging != MagickFalse)
12061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012062 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12063 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012064
12065 if (jng_color_type == 8 || jng_color_type == 12)
12066 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012067
cristy3ed852e2009-09-05 21:47:34 +000012068 jpeg_image_info->quality=jng_quality % 1000;
12069 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12070 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012071
cristy3ed852e2009-09-05 21:47:34 +000012072 if (logging != MagickFalse)
12073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12074 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012075
cristy3ed852e2009-09-05 21:47:34 +000012076 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000012077
cristy3ed852e2009-09-05 21:47:34 +000012078 if (logging != MagickFalse)
12079 {
12080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012081 " Successfully read jpeg_image into a blob, length=%.20g.",
12082 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012083
12084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012085 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012086 }
glennrp0fe50b42010-11-16 03:52:51 +000012087
cristy3ed852e2009-09-05 21:47:34 +000012088 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012089 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012090 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012091 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012092 (void) WriteBlob(image,4,chunk);
12093 (void) WriteBlob(image,length,blob);
12094 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12095
12096 jpeg_image=DestroyImage(jpeg_image);
12097 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12098 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12099 blob=(unsigned char *) RelinquishMagickMemory(blob);
12100
12101 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012102 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012103
12104 /* Write IEND chunk */
12105 (void) WriteBlobMSBULong(image,0L);
12106 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012107 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012108 (void) WriteBlob(image,4,chunk);
12109 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12110
12111 if (logging != MagickFalse)
12112 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12113 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012114
cristy3ed852e2009-09-05 21:47:34 +000012115 return(status);
12116}
12117
12118
12119/*
12120%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12121% %
12122% %
12123% %
12124% W r i t e J N G I m a g e %
12125% %
12126% %
12127% %
12128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12129%
12130% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12131%
12132% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12133%
12134% The format of the WriteJNGImage method is:
12135%
cristy1e178e72011-08-28 19:44:34 +000012136% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12137% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012138%
12139% A description of each parameter follows:
12140%
12141% o image_info: the image info.
12142%
12143% o image: The image.
12144%
cristy1e178e72011-08-28 19:44:34 +000012145% o exception: return any errors or warnings in this structure.
12146%
cristy3ed852e2009-09-05 21:47:34 +000012147%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12148*/
cristy1e178e72011-08-28 19:44:34 +000012149static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12150 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012151{
12152 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012153 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012154 logging,
cristy3ed852e2009-09-05 21:47:34 +000012155 status;
12156
12157 MngInfo
12158 *mng_info;
12159
cristy3ed852e2009-09-05 21:47:34 +000012160 /*
12161 Open image file.
12162 */
12163 assert(image_info != (const ImageInfo *) NULL);
12164 assert(image_info->signature == MagickSignature);
12165 assert(image != (Image *) NULL);
12166 assert(image->signature == MagickSignature);
12167 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012168 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012169 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12170 if (status == MagickFalse)
12171 return(status);
12172
12173 /*
12174 Allocate a MngInfo structure.
12175 */
12176 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012177 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012178 if (mng_info == (MngInfo *) NULL)
12179 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12180 /*
12181 Initialize members of the MngInfo structure.
12182 */
12183 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12184 mng_info->image=image;
12185 have_mng_structure=MagickTrue;
12186
12187 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12188
cristy018f07f2011-09-04 21:15:19 +000012189 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012190 (void) CloseBlob(image);
12191
12192 (void) CatchImageException(image);
12193 MngInfoFreeStruct(mng_info,&have_mng_structure);
12194 if (logging != MagickFalse)
12195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12196 return(status);
12197}
12198#endif
12199
cristy1e178e72011-08-28 19:44:34 +000012200static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12201 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012202{
12203 const char
12204 *option;
12205
12206 Image
12207 *next_image;
12208
12209 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012210 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012211 status;
12212
glennrp03812ae2010-12-24 01:31:34 +000012213 volatile MagickBooleanType
12214 logging;
12215
cristy3ed852e2009-09-05 21:47:34 +000012216 MngInfo
12217 *mng_info;
12218
12219 int
cristy3ed852e2009-09-05 21:47:34 +000012220 image_count,
12221 need_iterations,
12222 need_matte;
12223
12224 volatile int
12225#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12226 defined(PNG_MNG_FEATURES_SUPPORTED)
12227 need_local_plte,
12228#endif
12229 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012230 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012231 use_global_plte;
12232
cristybb503372010-05-27 20:51:26 +000012233 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012234 i;
12235
12236 unsigned char
12237 chunk[800];
12238
12239 volatile unsigned int
12240 write_jng,
12241 write_mng;
12242
cristybb503372010-05-27 20:51:26 +000012243 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012244 scene;
12245
cristybb503372010-05-27 20:51:26 +000012246 size_t
cristy3ed852e2009-09-05 21:47:34 +000012247 final_delay=0,
12248 initial_delay;
12249
glennrpd5045b42010-03-24 12:40:35 +000012250#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012251 if (image_info->verbose)
12252 printf("Your PNG library (libpng-%s) is rather old.\n",
12253 PNG_LIBPNG_VER_STRING);
12254#endif
12255
12256 /*
12257 Open image file.
12258 */
12259 assert(image_info != (const ImageInfo *) NULL);
12260 assert(image_info->signature == MagickSignature);
12261 assert(image != (Image *) NULL);
12262 assert(image->signature == MagickSignature);
12263 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012264 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012265 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12266 if (status == MagickFalse)
12267 return(status);
12268
12269 /*
12270 Allocate a MngInfo structure.
12271 */
12272 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012273 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012274 if (mng_info == (MngInfo *) NULL)
12275 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12276 /*
12277 Initialize members of the MngInfo structure.
12278 */
12279 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12280 mng_info->image=image;
12281 have_mng_structure=MagickTrue;
12282 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12283
12284 /*
12285 * See if user has requested a specific PNG subformat to be used
12286 * for all of the PNGs in the MNG being written, e.g.,
12287 *
12288 * convert *.png png8:animation.mng
12289 *
12290 * To do: check -define png:bit_depth and png:color_type as well,
12291 * or perhaps use mng:bit_depth and mng:color_type instead for
12292 * global settings.
12293 */
12294
12295 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12296 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12297 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12298
12299 write_jng=MagickFalse;
12300 if (image_info->compression == JPEGCompression)
12301 write_jng=MagickTrue;
12302
12303 mng_info->adjoin=image_info->adjoin &&
12304 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12305
cristy3ed852e2009-09-05 21:47:34 +000012306 if (logging != MagickFalse)
12307 {
12308 /* Log some info about the input */
12309 Image
12310 *p;
12311
12312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12313 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012314
cristy3ed852e2009-09-05 21:47:34 +000012315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012316 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012317
cristy3ed852e2009-09-05 21:47:34 +000012318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12319 " Type: %d",image_info->type);
12320
12321 scene=0;
12322 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12323 {
12324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012325 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012326
cristy3ed852e2009-09-05 21:47:34 +000012327 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012328 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012329
cristy3ed852e2009-09-05 21:47:34 +000012330 if (p->matte)
12331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12332 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012333
cristy3ed852e2009-09-05 21:47:34 +000012334 else
12335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12336 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012337
cristy3ed852e2009-09-05 21:47:34 +000012338 if (p->storage_class == PseudoClass)
12339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12340 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012341
cristy3ed852e2009-09-05 21:47:34 +000012342 else
12343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12344 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012345
cristy3ed852e2009-09-05 21:47:34 +000012346 if (p->colors)
12347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012348 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012349
cristy3ed852e2009-09-05 21:47:34 +000012350 else
12351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12352 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012353
cristy3ed852e2009-09-05 21:47:34 +000012354 if (mng_info->adjoin == MagickFalse)
12355 break;
12356 }
12357 }
12358
cristy3ed852e2009-09-05 21:47:34 +000012359 use_global_plte=MagickFalse;
12360 all_images_are_gray=MagickFalse;
12361#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12362 need_local_plte=MagickTrue;
12363#endif
12364 need_defi=MagickFalse;
12365 need_matte=MagickFalse;
12366 mng_info->framing_mode=1;
12367 mng_info->old_framing_mode=1;
12368
12369 if (write_mng)
12370 if (image_info->page != (char *) NULL)
12371 {
12372 /*
12373 Determine image bounding box.
12374 */
12375 SetGeometry(image,&mng_info->page);
12376 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12377 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12378 }
12379 if (write_mng)
12380 {
12381 unsigned int
12382 need_geom;
12383
12384 unsigned short
12385 red,
12386 green,
12387 blue;
12388
12389 mng_info->page=image->page;
12390 need_geom=MagickTrue;
12391 if (mng_info->page.width || mng_info->page.height)
12392 need_geom=MagickFalse;
12393 /*
12394 Check all the scenes.
12395 */
12396 initial_delay=image->delay;
12397 need_iterations=MagickFalse;
12398 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12399 mng_info->equal_physs=MagickTrue,
12400 mng_info->equal_gammas=MagickTrue;
12401 mng_info->equal_srgbs=MagickTrue;
12402 mng_info->equal_backgrounds=MagickTrue;
12403 image_count=0;
12404#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12405 defined(PNG_MNG_FEATURES_SUPPORTED)
12406 all_images_are_gray=MagickTrue;
12407 mng_info->equal_palettes=MagickFalse;
12408 need_local_plte=MagickFalse;
12409#endif
12410 for (next_image=image; next_image != (Image *) NULL; )
12411 {
12412 if (need_geom)
12413 {
12414 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12415 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012416
cristy3ed852e2009-09-05 21:47:34 +000012417 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12418 mng_info->page.height=next_image->rows+next_image->page.y;
12419 }
glennrp0fe50b42010-11-16 03:52:51 +000012420
cristy3ed852e2009-09-05 21:47:34 +000012421 if (next_image->page.x || next_image->page.y)
12422 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012423
cristy3ed852e2009-09-05 21:47:34 +000012424 if (next_image->matte)
12425 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012426
cristy3ed852e2009-09-05 21:47:34 +000012427 if ((int) next_image->dispose >= BackgroundDispose)
12428 if (next_image->matte || next_image->page.x || next_image->page.y ||
12429 ((next_image->columns < mng_info->page.width) &&
12430 (next_image->rows < mng_info->page.height)))
12431 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012432
cristy3ed852e2009-09-05 21:47:34 +000012433 if (next_image->iterations)
12434 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012435
cristy3ed852e2009-09-05 21:47:34 +000012436 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012437
cristy3ed852e2009-09-05 21:47:34 +000012438 if (final_delay != initial_delay || final_delay > 1UL*
12439 next_image->ticks_per_second)
12440 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012441
cristy3ed852e2009-09-05 21:47:34 +000012442#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12443 defined(PNG_MNG_FEATURES_SUPPORTED)
12444 /*
12445 check for global palette possibility.
12446 */
12447 if (image->matte != MagickFalse)
12448 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012449
cristy3ed852e2009-09-05 21:47:34 +000012450 if (need_local_plte == 0)
12451 {
12452 if (ImageIsGray(image) == MagickFalse)
12453 all_images_are_gray=MagickFalse;
12454 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12455 if (use_global_plte == 0)
12456 use_global_plte=mng_info->equal_palettes;
12457 need_local_plte=!mng_info->equal_palettes;
12458 }
12459#endif
12460 if (GetNextImageInList(next_image) != (Image *) NULL)
12461 {
12462 if (next_image->background_color.red !=
12463 next_image->next->background_color.red ||
12464 next_image->background_color.green !=
12465 next_image->next->background_color.green ||
12466 next_image->background_color.blue !=
12467 next_image->next->background_color.blue)
12468 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012469
cristy3ed852e2009-09-05 21:47:34 +000012470 if (next_image->gamma != next_image->next->gamma)
12471 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012472
cristy3ed852e2009-09-05 21:47:34 +000012473 if (next_image->rendering_intent !=
12474 next_image->next->rendering_intent)
12475 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012476
cristy3ed852e2009-09-05 21:47:34 +000012477 if ((next_image->units != next_image->next->units) ||
12478 (next_image->x_resolution != next_image->next->x_resolution) ||
12479 (next_image->y_resolution != next_image->next->y_resolution))
12480 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012481
cristy3ed852e2009-09-05 21:47:34 +000012482 if (mng_info->equal_chrms)
12483 {
12484 if (next_image->chromaticity.red_primary.x !=
12485 next_image->next->chromaticity.red_primary.x ||
12486 next_image->chromaticity.red_primary.y !=
12487 next_image->next->chromaticity.red_primary.y ||
12488 next_image->chromaticity.green_primary.x !=
12489 next_image->next->chromaticity.green_primary.x ||
12490 next_image->chromaticity.green_primary.y !=
12491 next_image->next->chromaticity.green_primary.y ||
12492 next_image->chromaticity.blue_primary.x !=
12493 next_image->next->chromaticity.blue_primary.x ||
12494 next_image->chromaticity.blue_primary.y !=
12495 next_image->next->chromaticity.blue_primary.y ||
12496 next_image->chromaticity.white_point.x !=
12497 next_image->next->chromaticity.white_point.x ||
12498 next_image->chromaticity.white_point.y !=
12499 next_image->next->chromaticity.white_point.y)
12500 mng_info->equal_chrms=MagickFalse;
12501 }
12502 }
12503 image_count++;
12504 next_image=GetNextImageInList(next_image);
12505 }
12506 if (image_count < 2)
12507 {
12508 mng_info->equal_backgrounds=MagickFalse;
12509 mng_info->equal_chrms=MagickFalse;
12510 mng_info->equal_gammas=MagickFalse;
12511 mng_info->equal_srgbs=MagickFalse;
12512 mng_info->equal_physs=MagickFalse;
12513 use_global_plte=MagickFalse;
12514#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12515 need_local_plte=MagickTrue;
12516#endif
12517 need_iterations=MagickFalse;
12518 }
glennrp0fe50b42010-11-16 03:52:51 +000012519
cristy3ed852e2009-09-05 21:47:34 +000012520 if (mng_info->need_fram == MagickFalse)
12521 {
12522 /*
12523 Only certain framing rates 100/n are exactly representable without
12524 the FRAM chunk but we'll allow some slop in VLC files
12525 */
12526 if (final_delay == 0)
12527 {
12528 if (need_iterations != MagickFalse)
12529 {
12530 /*
12531 It's probably a GIF with loop; don't run it *too* fast.
12532 */
glennrp02617122010-07-28 13:07:35 +000012533 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012534 {
12535 final_delay=10;
12536 (void) ThrowMagickException(&image->exception,
12537 GetMagickModule(),CoderWarning,
12538 "input has zero delay between all frames; assuming",
12539 " 10 cs `%s'","");
12540 }
cristy3ed852e2009-09-05 21:47:34 +000012541 }
12542 else
12543 mng_info->ticks_per_second=0;
12544 }
12545 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012546 mng_info->ticks_per_second=(png_uint_32)
12547 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012548 if (final_delay > 50)
12549 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012550
cristy3ed852e2009-09-05 21:47:34 +000012551 if (final_delay > 75)
12552 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012553
cristy3ed852e2009-09-05 21:47:34 +000012554 if (final_delay > 125)
12555 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012556
cristy3ed852e2009-09-05 21:47:34 +000012557 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12558 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12559 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12560 1UL*image->ticks_per_second))
12561 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12562 }
glennrp0fe50b42010-11-16 03:52:51 +000012563
cristy3ed852e2009-09-05 21:47:34 +000012564 if (mng_info->need_fram != MagickFalse)
12565 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12566 /*
12567 If pseudocolor, we should also check to see if all the
12568 palettes are identical and write a global PLTE if they are.
12569 ../glennrp Feb 99.
12570 */
12571 /*
12572 Write the MNG version 1.0 signature and MHDR chunk.
12573 */
12574 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12575 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12576 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012577 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012578 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12579 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012580 PNGLong(chunk+12,mng_info->ticks_per_second);
12581 PNGLong(chunk+16,0L); /* layer count=unknown */
12582 PNGLong(chunk+20,0L); /* frame count=unknown */
12583 PNGLong(chunk+24,0L); /* play time=unknown */
12584 if (write_jng)
12585 {
12586 if (need_matte)
12587 {
12588 if (need_defi || mng_info->need_fram || use_global_plte)
12589 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012590
cristy3ed852e2009-09-05 21:47:34 +000012591 else
12592 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12593 }
glennrp0fe50b42010-11-16 03:52:51 +000012594
cristy3ed852e2009-09-05 21:47:34 +000012595 else
12596 {
12597 if (need_defi || mng_info->need_fram || use_global_plte)
12598 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012599
cristy3ed852e2009-09-05 21:47:34 +000012600 else
12601 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12602 }
12603 }
glennrp0fe50b42010-11-16 03:52:51 +000012604
cristy3ed852e2009-09-05 21:47:34 +000012605 else
12606 {
12607 if (need_matte)
12608 {
12609 if (need_defi || mng_info->need_fram || use_global_plte)
12610 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012611
cristy3ed852e2009-09-05 21:47:34 +000012612 else
12613 PNGLong(chunk+28,9L); /* simplicity=VLC */
12614 }
glennrp0fe50b42010-11-16 03:52:51 +000012615
cristy3ed852e2009-09-05 21:47:34 +000012616 else
12617 {
12618 if (need_defi || mng_info->need_fram || use_global_plte)
12619 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012620
cristy3ed852e2009-09-05 21:47:34 +000012621 else
12622 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12623 }
12624 }
12625 (void) WriteBlob(image,32,chunk);
12626 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12627 option=GetImageOption(image_info,"mng:need-cacheoff");
12628 if (option != (const char *) NULL)
12629 {
12630 size_t
12631 length;
12632
12633 /*
12634 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12635 */
12636 PNGType(chunk,mng_nEED);
12637 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012638 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012639 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012640 length+=4;
12641 (void) WriteBlob(image,length,chunk);
12642 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12643 }
12644 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12645 (GetNextImageInList(image) != (Image *) NULL) &&
12646 (image->iterations != 1))
12647 {
12648 /*
12649 Write MNG TERM chunk
12650 */
12651 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12652 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012653 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012654 chunk[4]=3; /* repeat animation */
12655 chunk[5]=0; /* show last frame when done */
12656 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12657 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012658
cristy3ed852e2009-09-05 21:47:34 +000012659 if (image->iterations == 0)
12660 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012661
cristy3ed852e2009-09-05 21:47:34 +000012662 else
12663 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012664
cristy3ed852e2009-09-05 21:47:34 +000012665 if (logging != MagickFalse)
12666 {
12667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012668 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12669 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012670
cristy3ed852e2009-09-05 21:47:34 +000012671 if (image->iterations == 0)
12672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012673 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012674
cristy3ed852e2009-09-05 21:47:34 +000012675 else
12676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012677 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012678 }
12679 (void) WriteBlob(image,14,chunk);
12680 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12681 }
12682 /*
12683 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12684 */
12685 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12686 mng_info->equal_srgbs)
12687 {
12688 /*
12689 Write MNG sRGB chunk
12690 */
12691 (void) WriteBlobMSBULong(image,1L);
12692 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012693 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012694
cristy3ed852e2009-09-05 21:47:34 +000012695 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012696 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012697 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012698 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012699
cristy3ed852e2009-09-05 21:47:34 +000012700 else
glennrpe610a072010-08-05 17:08:46 +000012701 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012702 Magick_RenderingIntent_to_PNG_RenderingIntent(
12703 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012704
cristy3ed852e2009-09-05 21:47:34 +000012705 (void) WriteBlob(image,5,chunk);
12706 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12707 mng_info->have_write_global_srgb=MagickTrue;
12708 }
glennrp0fe50b42010-11-16 03:52:51 +000012709
cristy3ed852e2009-09-05 21:47:34 +000012710 else
12711 {
12712 if (image->gamma && mng_info->equal_gammas)
12713 {
12714 /*
12715 Write MNG gAMA chunk
12716 */
12717 (void) WriteBlobMSBULong(image,4L);
12718 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012719 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012720 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012721 (void) WriteBlob(image,8,chunk);
12722 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12723 mng_info->have_write_global_gama=MagickTrue;
12724 }
12725 if (mng_info->equal_chrms)
12726 {
12727 PrimaryInfo
12728 primary;
12729
12730 /*
12731 Write MNG cHRM chunk
12732 */
12733 (void) WriteBlobMSBULong(image,32L);
12734 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012735 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012736 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012737 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12738 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012739 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012740 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12741 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012742 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012743 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12744 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012745 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012746 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12747 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012748 (void) WriteBlob(image,36,chunk);
12749 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12750 mng_info->have_write_global_chrm=MagickTrue;
12751 }
12752 }
12753 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
12754 {
12755 /*
12756 Write MNG pHYs chunk
12757 */
12758 (void) WriteBlobMSBULong(image,9L);
12759 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012760 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012761
cristy3ed852e2009-09-05 21:47:34 +000012762 if (image->units == PixelsPerInchResolution)
12763 {
cristy35ef8242010-06-03 16:24:13 +000012764 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012765 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012766
cristy35ef8242010-06-03 16:24:13 +000012767 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012768 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012769
cristy3ed852e2009-09-05 21:47:34 +000012770 chunk[12]=1;
12771 }
glennrp0fe50b42010-11-16 03:52:51 +000012772
cristy3ed852e2009-09-05 21:47:34 +000012773 else
12774 {
12775 if (image->units == PixelsPerCentimeterResolution)
12776 {
cristy35ef8242010-06-03 16:24:13 +000012777 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012778 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012779
cristy35ef8242010-06-03 16:24:13 +000012780 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012781 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012782
cristy3ed852e2009-09-05 21:47:34 +000012783 chunk[12]=1;
12784 }
glennrp0fe50b42010-11-16 03:52:51 +000012785
cristy3ed852e2009-09-05 21:47:34 +000012786 else
12787 {
cristy35ef8242010-06-03 16:24:13 +000012788 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
12789 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012790 chunk[12]=0;
12791 }
12792 }
12793 (void) WriteBlob(image,13,chunk);
12794 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12795 }
12796 /*
12797 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12798 or does not cover the entire frame.
12799 */
12800 if (write_mng && (image->matte || image->page.x > 0 ||
12801 image->page.y > 0 || (image->page.width &&
12802 (image->page.width+image->page.x < mng_info->page.width))
12803 || (image->page.height && (image->page.height+image->page.y
12804 < mng_info->page.height))))
12805 {
12806 (void) WriteBlobMSBULong(image,6L);
12807 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012808 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012809 red=ScaleQuantumToShort(image->background_color.red);
12810 green=ScaleQuantumToShort(image->background_color.green);
12811 blue=ScaleQuantumToShort(image->background_color.blue);
12812 PNGShort(chunk+4,red);
12813 PNGShort(chunk+6,green);
12814 PNGShort(chunk+8,blue);
12815 (void) WriteBlob(image,10,chunk);
12816 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12817 if (mng_info->equal_backgrounds)
12818 {
12819 (void) WriteBlobMSBULong(image,6L);
12820 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012821 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012822 (void) WriteBlob(image,10,chunk);
12823 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12824 }
12825 }
12826
12827#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12828 if ((need_local_plte == MagickFalse) &&
12829 (image->storage_class == PseudoClass) &&
12830 (all_images_are_gray == MagickFalse))
12831 {
cristybb503372010-05-27 20:51:26 +000012832 size_t
cristy3ed852e2009-09-05 21:47:34 +000012833 data_length;
12834
12835 /*
12836 Write MNG PLTE chunk
12837 */
12838 data_length=3*image->colors;
12839 (void) WriteBlobMSBULong(image,data_length);
12840 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012841 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012842
cristybb503372010-05-27 20:51:26 +000012843 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012844 {
cristy5f07f702011-09-26 17:29:10 +000012845 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12846 image->colormap[i].red) & 0xff);
12847 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12848 image->colormap[i].green) & 0xff);
12849 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12850 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000012851 }
glennrp0fe50b42010-11-16 03:52:51 +000012852
cristy3ed852e2009-09-05 21:47:34 +000012853 (void) WriteBlob(image,data_length+4,chunk);
12854 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12855 mng_info->have_write_global_plte=MagickTrue;
12856 }
12857#endif
12858 }
12859 scene=0;
12860 mng_info->delay=0;
12861#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12862 defined(PNG_MNG_FEATURES_SUPPORTED)
12863 mng_info->equal_palettes=MagickFalse;
12864#endif
12865 do
12866 {
12867 if (mng_info->adjoin)
12868 {
12869#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12870 defined(PNG_MNG_FEATURES_SUPPORTED)
12871 /*
12872 If we aren't using a global palette for the entire MNG, check to
12873 see if we can use one for two or more consecutive images.
12874 */
12875 if (need_local_plte && use_global_plte && !all_images_are_gray)
12876 {
12877 if (mng_info->IsPalette)
12878 {
12879 /*
12880 When equal_palettes is true, this image has the same palette
12881 as the previous PseudoClass image
12882 */
12883 mng_info->have_write_global_plte=mng_info->equal_palettes;
12884 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12885 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12886 {
12887 /*
12888 Write MNG PLTE chunk
12889 */
cristybb503372010-05-27 20:51:26 +000012890 size_t
cristy3ed852e2009-09-05 21:47:34 +000012891 data_length;
12892
12893 data_length=3*image->colors;
12894 (void) WriteBlobMSBULong(image,data_length);
12895 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012896 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012897
cristybb503372010-05-27 20:51:26 +000012898 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012899 {
12900 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12901 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12902 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12903 }
glennrp0fe50b42010-11-16 03:52:51 +000012904
cristy3ed852e2009-09-05 21:47:34 +000012905 (void) WriteBlob(image,data_length+4,chunk);
12906 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12907 (uInt) (data_length+4)));
12908 mng_info->have_write_global_plte=MagickTrue;
12909 }
12910 }
12911 else
12912 mng_info->have_write_global_plte=MagickFalse;
12913 }
12914#endif
12915 if (need_defi)
12916 {
cristybb503372010-05-27 20:51:26 +000012917 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012918 previous_x,
12919 previous_y;
12920
12921 if (scene)
12922 {
12923 previous_x=mng_info->page.x;
12924 previous_y=mng_info->page.y;
12925 }
12926 else
12927 {
12928 previous_x=0;
12929 previous_y=0;
12930 }
12931 mng_info->page=image->page;
12932 if ((mng_info->page.x != previous_x) ||
12933 (mng_info->page.y != previous_y))
12934 {
12935 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12936 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012937 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012938 chunk[4]=0; /* object 0 MSB */
12939 chunk[5]=0; /* object 0 LSB */
12940 chunk[6]=0; /* visible */
12941 chunk[7]=0; /* abstract */
12942 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12943 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12944 (void) WriteBlob(image,16,chunk);
12945 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12946 }
12947 }
12948 }
12949
12950 mng_info->write_mng=write_mng;
12951
12952 if ((int) image->dispose >= 3)
12953 mng_info->framing_mode=3;
12954
12955 if (mng_info->need_fram && mng_info->adjoin &&
12956 ((image->delay != mng_info->delay) ||
12957 (mng_info->framing_mode != mng_info->old_framing_mode)))
12958 {
12959 if (image->delay == mng_info->delay)
12960 {
12961 /*
12962 Write a MNG FRAM chunk with the new framing mode.
12963 */
12964 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12965 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012966 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012967 chunk[4]=(unsigned char) mng_info->framing_mode;
12968 (void) WriteBlob(image,5,chunk);
12969 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12970 }
12971 else
12972 {
12973 /*
12974 Write a MNG FRAM chunk with the delay.
12975 */
12976 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12977 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012978 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012979 chunk[4]=(unsigned char) mng_info->framing_mode;
12980 chunk[5]=0; /* frame name separator (no name) */
12981 chunk[6]=2; /* flag for changing default delay */
12982 chunk[7]=0; /* flag for changing frame timeout */
12983 chunk[8]=0; /* flag for changing frame clipping */
12984 chunk[9]=0; /* flag for changing frame sync_id */
12985 PNGLong(chunk+10,(png_uint_32)
12986 ((mng_info->ticks_per_second*
12987 image->delay)/MagickMax(image->ticks_per_second,1)));
12988 (void) WriteBlob(image,14,chunk);
12989 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000012990 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000012991 }
12992 mng_info->old_framing_mode=mng_info->framing_mode;
12993 }
12994
12995#if defined(JNG_SUPPORTED)
12996 if (image_info->compression == JPEGCompression)
12997 {
12998 ImageInfo
12999 *write_info;
13000
13001 if (logging != MagickFalse)
13002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13003 " Writing JNG object.");
13004 /* To do: specify the desired alpha compression method. */
13005 write_info=CloneImageInfo(image_info);
13006 write_info->compression=UndefinedCompression;
cristy018f07f2011-09-04 21:15:19 +000013007 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013008 write_info=DestroyImageInfo(write_info);
13009 }
13010 else
13011#endif
13012 {
13013 if (logging != MagickFalse)
13014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13015 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013016
glennrpb9cfe272010-12-21 15:08:06 +000013017 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013018 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013019
13020 /* We don't want any ancillary chunks written */
13021 mng_info->ping_exclude_bKGD=MagickTrue;
13022 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013023 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013024 mng_info->ping_exclude_EXIF=MagickTrue;
13025 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013026 mng_info->ping_exclude_iCCP=MagickTrue;
13027 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13028 mng_info->ping_exclude_oFFs=MagickTrue;
13029 mng_info->ping_exclude_pHYs=MagickTrue;
13030 mng_info->ping_exclude_sRGB=MagickTrue;
13031 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013032 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013033 mng_info->ping_exclude_vpAg=MagickTrue;
13034 mng_info->ping_exclude_zCCP=MagickTrue;
13035 mng_info->ping_exclude_zTXt=MagickTrue;
13036
cristy018f07f2011-09-04 21:15:19 +000013037 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013038 }
13039
13040 if (status == MagickFalse)
13041 {
13042 MngInfoFreeStruct(mng_info,&have_mng_structure);
13043 (void) CloseBlob(image);
13044 return(MagickFalse);
13045 }
13046 (void) CatchImageException(image);
13047 if (GetNextImageInList(image) == (Image *) NULL)
13048 break;
13049 image=SyncNextImageInList(image);
13050 status=SetImageProgress(image,SaveImagesTag,scene++,
13051 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013052
cristy3ed852e2009-09-05 21:47:34 +000013053 if (status == MagickFalse)
13054 break;
glennrp0fe50b42010-11-16 03:52:51 +000013055
cristy3ed852e2009-09-05 21:47:34 +000013056 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013057
cristy3ed852e2009-09-05 21:47:34 +000013058 if (write_mng)
13059 {
13060 while (GetPreviousImageInList(image) != (Image *) NULL)
13061 image=GetPreviousImageInList(image);
13062 /*
13063 Write the MEND chunk.
13064 */
13065 (void) WriteBlobMSBULong(image,0x00000000L);
13066 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013067 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013068 (void) WriteBlob(image,4,chunk);
13069 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13070 }
13071 /*
13072 Relinquish resources.
13073 */
13074 (void) CloseBlob(image);
13075 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013076
cristy3ed852e2009-09-05 21:47:34 +000013077 if (logging != MagickFalse)
13078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013079
cristy3ed852e2009-09-05 21:47:34 +000013080 return(MagickTrue);
13081}
glennrpd5045b42010-03-24 12:40:35 +000013082#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013083
cristy3ed852e2009-09-05 21:47:34 +000013084static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13085{
13086 image=image;
13087 printf("Your PNG library is too old: You have libpng-%s\n",
13088 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013089
cristy3ed852e2009-09-05 21:47:34 +000013090 ThrowBinaryException(CoderError,"PNG library is too old",
13091 image_info->filename);
13092}
glennrp39992b42010-11-14 00:03:43 +000013093
cristy3ed852e2009-09-05 21:47:34 +000013094static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13095{
13096 return(WritePNGImage(image_info,image));
13097}
glennrpd5045b42010-03-24 12:40:35 +000013098#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013099#endif