blob: a7108910f5fcd37553f649d664c447a74205acf3 [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,
cristy3ed852e2009-09-05 21:47:34 +00001801 png_textp text,int ii)
1802{
cristybb503372010-05-27 20:51:26 +00001803 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001804 i;
1805
1806 register unsigned char
1807 *dp;
1808
1809 register png_charp
1810 sp;
1811
1812 png_uint_32
1813 length,
1814 nibbles;
1815
1816 StringInfo
1817 *profile;
1818
glennrp0c3e06b2010-11-19 13:45:02 +00001819 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001820 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1821 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1822 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1823 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1824 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1825 13,14,15};
1826
1827 sp=text[ii].text+1;
1828 /* look for newline */
1829 while (*sp != '\n')
1830 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001831
cristy3ed852e2009-09-05 21:47:34 +00001832 /* look for length */
1833 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1834 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001835
cristyf2f27272009-12-17 14:48:46 +00001836 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001837
glennrp97f90e22011-02-22 05:47:58 +00001838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1839 " length: %lu",(unsigned long) length);
1840
cristy3ed852e2009-09-05 21:47:34 +00001841 while (*sp != ' ' && *sp != '\n')
1842 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001843
cristy3ed852e2009-09-05 21:47:34 +00001844 /* allocate space */
1845 if (length == 0)
1846 {
1847 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1848 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1849 return(MagickFalse);
1850 }
glennrp0fe50b42010-11-16 03:52:51 +00001851
cristy8723e4b2011-09-01 13:11:19 +00001852 profile=BlobToStringInfo((const void *) NULL,length);
glennrp0fe50b42010-11-16 03:52:51 +00001853
cristy3ed852e2009-09-05 21:47:34 +00001854 if (profile == (StringInfo *) NULL)
1855 {
1856 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1857 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1858 "unable to copy profile");
1859 return(MagickFalse);
1860 }
glennrp0fe50b42010-11-16 03:52:51 +00001861
cristy3ed852e2009-09-05 21:47:34 +00001862 /* copy profile, skipping white space and column 1 "=" signs */
1863 dp=GetStringInfoDatum(profile);
1864 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001865
cristybb503372010-05-27 20:51:26 +00001866 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001867 {
1868 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1869 {
1870 if (*sp == '\0')
1871 {
1872 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1873 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1874 profile=DestroyStringInfo(profile);
1875 return(MagickFalse);
1876 }
1877 sp++;
1878 }
glennrp0fe50b42010-11-16 03:52:51 +00001879
cristy3ed852e2009-09-05 21:47:34 +00001880 if (i%2 == 0)
1881 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001882
cristy3ed852e2009-09-05 21:47:34 +00001883 else
1884 (*dp++)+=unhex[(int) *sp++];
1885 }
1886 /*
1887 We have already read "Raw profile type.
1888 */
1889 (void) SetImageProfile(image,&text[ii].key[17],profile);
1890 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001891
cristy3ed852e2009-09-05 21:47:34 +00001892 if (image_info->verbose)
1893 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001894
cristy3ed852e2009-09-05 21:47:34 +00001895 return MagickTrue;
1896}
1897
1898#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1899static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1900{
1901 Image
1902 *image;
1903
1904
1905 /* The unknown chunk structure contains the chunk data:
1906 png_byte name[5];
1907 png_byte *data;
1908 png_size_t size;
1909
1910 Note that libpng has already taken care of the CRC handling.
1911 */
1912
1913
1914 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1915 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1916 return(0); /* Did not recognize */
1917
1918 /* recognized vpAg */
1919
1920 if (chunk->size != 9)
1921 return(-1); /* Error return */
1922
1923 if (chunk->data[8] != 0)
1924 return(0); /* ImageMagick requires pixel units */
1925
1926 image=(Image *) png_get_user_chunk_ptr(ping);
1927
cristybb503372010-05-27 20:51:26 +00001928 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001929 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001930
cristybb503372010-05-27 20:51:26 +00001931 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001932 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1933
1934 /* Return one of the following: */
1935 /* return(-n); chunk had an error */
1936 /* return(0); did not recognize */
1937 /* return(n); success */
1938
1939 return(1);
1940
1941}
1942#endif
1943
1944/*
1945%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1946% %
1947% %
1948% %
1949% R e a d O n e P N G I m a g e %
1950% %
1951% %
1952% %
1953%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1954%
1955% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1956% (minus the 8-byte signature) and returns it. It allocates the memory
1957% necessary for the new Image structure and returns a pointer to the new
1958% image.
1959%
1960% The format of the ReadOnePNGImage method is:
1961%
1962% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1963% ExceptionInfo *exception)
1964%
1965% A description of each parameter follows:
1966%
1967% o mng_info: Specifies a pointer to a MngInfo structure.
1968%
1969% o image_info: the image info.
1970%
1971% o exception: return any errors or warnings in this structure.
1972%
1973*/
1974static Image *ReadOnePNGImage(MngInfo *mng_info,
1975 const ImageInfo *image_info, ExceptionInfo *exception)
1976{
1977 /* Read one PNG image */
1978
glennrpcc95c3f2011-04-18 16:46:48 +00001979 /* To do: Read the tIME chunk into the date:modify property */
1980 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1981
cristy3ed852e2009-09-05 21:47:34 +00001982 Image
1983 *image;
1984
1985 int
glennrp4eb39312011-03-30 21:34:55 +00001986 intent,
glennrpcb395ac2011-03-30 19:50:23 +00001987 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00001988 num_text,
glennrp4eb39312011-03-30 21:34:55 +00001989 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00001990 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00001991 pass,
1992 ping_bit_depth,
1993 ping_color_type,
1994 ping_interlace_method,
1995 ping_compression_method,
1996 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00001997 ping_num_trans,
1998 unit_type;
1999
2000 double
2001 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002002
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);
2308 (void) SetImageProfile(image,"icc",profile);
2309 profile=DestroyStringInfo(profile);
2310 }
2311 }
2312#endif
2313#if defined(PNG_READ_sRGB_SUPPORTED)
2314 {
cristy3ed852e2009-09-05 21:47:34 +00002315 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00002316 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2317 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00002318
cristy3ed852e2009-09-05 21:47:34 +00002319 if (png_get_sRGB(ping,ping_info,&intent))
2320 {
glennrpcf002022011-01-30 02:38:15 +00002321 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2322 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002323
cristy3ed852e2009-09-05 21:47:34 +00002324 if (logging != MagickFalse)
2325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002326 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002327 }
2328 }
2329#endif
2330 {
glennrpfaa852b2010-03-30 12:17:00 +00002331 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2332 if (mng_info->have_global_gama)
2333 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002334
cristy3ed852e2009-09-05 21:47:34 +00002335 if (png_get_gAMA(ping,ping_info,&file_gamma))
2336 {
2337 image->gamma=(float) file_gamma;
2338 if (logging != MagickFalse)
2339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2340 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2341 }
2342 }
glennrpfaa852b2010-03-30 12:17:00 +00002343 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2344 {
2345 if (mng_info->have_global_chrm != MagickFalse)
2346 {
2347 (void) png_set_cHRM(ping,ping_info,
2348 mng_info->global_chrm.white_point.x,
2349 mng_info->global_chrm.white_point.y,
2350 mng_info->global_chrm.red_primary.x,
2351 mng_info->global_chrm.red_primary.y,
2352 mng_info->global_chrm.green_primary.x,
2353 mng_info->global_chrm.green_primary.y,
2354 mng_info->global_chrm.blue_primary.x,
2355 mng_info->global_chrm.blue_primary.y);
2356 }
2357 }
glennrp0fe50b42010-11-16 03:52:51 +00002358
glennrpfaa852b2010-03-30 12:17:00 +00002359 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002360 {
2361 (void) png_get_cHRM(ping,ping_info,
2362 &image->chromaticity.white_point.x,
2363 &image->chromaticity.white_point.y,
2364 &image->chromaticity.red_primary.x,
2365 &image->chromaticity.red_primary.y,
2366 &image->chromaticity.green_primary.x,
2367 &image->chromaticity.green_primary.y,
2368 &image->chromaticity.blue_primary.x,
2369 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002370
cristy3ed852e2009-09-05 21:47:34 +00002371 if (logging != MagickFalse)
2372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2373 " Reading PNG cHRM chunk.");
2374 }
glennrp0fe50b42010-11-16 03:52:51 +00002375
glennrpe610a072010-08-05 17:08:46 +00002376 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002377 {
glennrpe610a072010-08-05 17:08:46 +00002378 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002379 Magick_RenderingIntent_to_PNG_RenderingIntent
2380 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00002381 png_set_gAMA(ping,ping_info,0.45455f);
2382 png_set_cHRM(ping,ping_info,
2383 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2384 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002385 }
cristy3ed852e2009-09-05 21:47:34 +00002386#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002387 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002388 {
cristy905ef802011-02-23 00:29:18 +00002389 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2390 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002391
cristy3ed852e2009-09-05 21:47:34 +00002392 if (logging != MagickFalse)
2393 if (image->page.x || image->page.y)
2394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002395 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2396 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002397 }
2398#endif
2399#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002400 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2401 {
2402 if (mng_info->have_global_phys)
2403 {
2404 png_set_pHYs(ping,ping_info,
2405 mng_info->global_x_pixels_per_unit,
2406 mng_info->global_y_pixels_per_unit,
2407 mng_info->global_phys_unit_type);
2408 }
2409 }
2410
2411 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002412 {
cristy3ed852e2009-09-05 21:47:34 +00002413 /*
2414 Set image resolution.
2415 */
2416 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002417 &unit_type);
2418 image->x_resolution=(double) x_resolution;
2419 image->y_resolution=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002420
cristy3ed852e2009-09-05 21:47:34 +00002421 if (unit_type == PNG_RESOLUTION_METER)
2422 {
2423 image->units=PixelsPerCentimeterResolution;
2424 image->x_resolution=(double) x_resolution/100.0;
2425 image->y_resolution=(double) y_resolution/100.0;
2426 }
glennrp0fe50b42010-11-16 03:52:51 +00002427
cristy3ed852e2009-09-05 21:47:34 +00002428 if (logging != MagickFalse)
2429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002430 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2431 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002432 }
cristy3ed852e2009-09-05 21:47:34 +00002433#endif
glennrp823b55c2011-03-14 18:46:46 +00002434
glennrpfaa852b2010-03-30 12:17:00 +00002435 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002436 {
2437 int
2438 number_colors;
2439
2440 png_colorp
2441 palette;
2442
2443 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002444
cristy3ed852e2009-09-05 21:47:34 +00002445 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002446 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002447 {
2448 if (mng_info->global_plte_length)
2449 {
2450 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2451 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002452
glennrpfaa852b2010-03-30 12:17:00 +00002453 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002454 if (mng_info->global_trns_length)
2455 {
2456 if (mng_info->global_trns_length >
2457 mng_info->global_plte_length)
2458 (void) ThrowMagickException(&image->exception,
2459 GetMagickModule(),CoderError,
2460 "global tRNS has more entries than global PLTE",
2461 "`%s'",image_info->filename);
2462 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2463 (int) mng_info->global_trns_length,NULL);
2464 }
glennrpbfd9e612011-04-22 14:02:20 +00002465#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002466 if (
2467#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2468 mng_info->have_saved_bkgd_index ||
2469#endif
glennrpfaa852b2010-03-30 12:17:00 +00002470 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002471 {
2472 png_color_16
2473 background;
2474
2475#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2476 if (mng_info->have_saved_bkgd_index)
2477 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002478#endif
glennrpfaa852b2010-03-30 12:17:00 +00002479 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2480 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002481
cristy3ed852e2009-09-05 21:47:34 +00002482 background.red=(png_uint_16)
2483 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002484
cristy3ed852e2009-09-05 21:47:34 +00002485 background.green=(png_uint_16)
2486 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002487
cristy3ed852e2009-09-05 21:47:34 +00002488 background.blue=(png_uint_16)
2489 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002490
glennrpc6c391a2011-04-27 02:23:56 +00002491 background.gray=(png_uint_16)
2492 mng_info->global_plte[background.index].green;
2493
cristy3ed852e2009-09-05 21:47:34 +00002494 png_set_bKGD(ping,ping_info,&background);
2495 }
2496#endif
2497 }
2498 else
2499 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2500 CoderError,"No global PLTE in file","`%s'",
2501 image_info->filename);
2502 }
2503 }
2504
glennrpbfd9e612011-04-22 14:02:20 +00002505#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002506 if (mng_info->have_global_bkgd &&
2507 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002508 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002509
glennrpfaa852b2010-03-30 12:17:00 +00002510 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002511 {
glennrpbfd9e612011-04-22 14:02:20 +00002512 unsigned int
2513 bkgd_scale;
2514
cristy3ed852e2009-09-05 21:47:34 +00002515 /*
2516 Set image background color.
2517 */
2518 if (logging != MagickFalse)
2519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2520 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002521
glennrpbfd9e612011-04-22 14:02:20 +00002522 /* Scale background components to 16-bit, then scale
2523 * to quantum depth
2524 */
2525 if (logging != MagickFalse)
2526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2527 " raw ping_background=(%d,%d,%d).",ping_background->red,
2528 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002529
glennrpbfd9e612011-04-22 14:02:20 +00002530 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002531
glennrpbfd9e612011-04-22 14:02:20 +00002532 if (ping_bit_depth == 1)
2533 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002534
glennrpbfd9e612011-04-22 14:02:20 +00002535 else if (ping_bit_depth == 2)
2536 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002537
glennrpbfd9e612011-04-22 14:02:20 +00002538 else if (ping_bit_depth == 4)
2539 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002540
glennrpbfd9e612011-04-22 14:02:20 +00002541 if (ping_bit_depth <= 8)
2542 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002543
glennrpbfd9e612011-04-22 14:02:20 +00002544 ping_background->red *= bkgd_scale;
2545 ping_background->green *= bkgd_scale;
2546 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002547
glennrpbfd9e612011-04-22 14:02:20 +00002548 if (logging != MagickFalse)
2549 {
glennrp2cbb4482010-06-02 04:37:24 +00002550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2551 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002552
glennrp2cbb4482010-06-02 04:37:24 +00002553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2554 " ping_background=(%d,%d,%d).",ping_background->red,
2555 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002556 }
glennrp2cbb4482010-06-02 04:37:24 +00002557
glennrpbfd9e612011-04-22 14:02:20 +00002558 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002559 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002560
glennrpbfd9e612011-04-22 14:02:20 +00002561 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002562 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002563
glennrpbfd9e612011-04-22 14:02:20 +00002564 image->background_color.blue=
2565 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002566
cristy4c08aed2011-07-01 19:47:50 +00002567 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002568
glennrpbfd9e612011-04-22 14:02:20 +00002569 if (logging != MagickFalse)
2570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2571 " image->background_color=(%.20g,%.20g,%.20g).",
2572 (double) image->background_color.red,
2573 (double) image->background_color.green,
2574 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002575 }
glennrpbfd9e612011-04-22 14:02:20 +00002576#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002577
glennrpfaa852b2010-03-30 12:17:00 +00002578 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002579 {
2580 /*
glennrpa6a06632011-01-19 15:15:34 +00002581 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002582 */
2583 int
2584 max_sample;
2585
cristy35ef8242010-06-03 16:24:13 +00002586 size_t
2587 one=1;
2588
cristy3ed852e2009-09-05 21:47:34 +00002589 if (logging != MagickFalse)
2590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2591 " Reading PNG tRNS chunk.");
2592
cristyf9cca6a2010-06-04 23:49:28 +00002593 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002594
glennrpfaa852b2010-03-30 12:17:00 +00002595 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2596 (int)ping_trans_color->gray > max_sample) ||
2597 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2598 ((int)ping_trans_color->red > max_sample ||
2599 (int)ping_trans_color->green > max_sample ||
2600 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002601 {
2602 if (logging != MagickFalse)
2603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2604 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002605 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002606 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002607 image->matte=MagickFalse;
2608 }
2609 else
2610 {
glennrpa6a06632011-01-19 15:15:34 +00002611 int
2612 scale_to_short;
2613
2614 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2615
2616 /* Scale transparent_color to short */
2617 transparent_color.red= scale_to_short*ping_trans_color->red;
2618 transparent_color.green= scale_to_short*ping_trans_color->green;
2619 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy4c08aed2011-07-01 19:47:50 +00002620 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002621
glennrpfaa852b2010-03-30 12:17:00 +00002622 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002623 {
glennrp0f111982010-07-07 20:18:33 +00002624 if (logging != MagickFalse)
2625 {
2626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2627 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002628
glennrp0f111982010-07-07 20:18:33 +00002629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
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);
2769 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
glennrp147bc912011-03-30 18:47:21 +00002770
cristy3b6fd2e2011-05-20 12:53:50 +00002771 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
glennrp147bc912011-03-30 18:47:21 +00002772 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2773
cristy3b6fd2e2011-05-20 12:53:50 +00002774 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
glennrp147bc912011-03-30 18:47:21 +00002775 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2776
cristy3b6fd2e2011-05-20 12:53:50 +00002777 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002778 (int) ping_interlace_method);
2779 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
glennrpcb395ac2011-03-30 19:50:23 +00002780 }
glennrp147bc912011-03-30 18:47:21 +00002781
cristy3ed852e2009-09-05 21:47:34 +00002782 /*
2783 Read image scanlines.
2784 */
2785 if (image->delay != 0)
2786 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002787
glennrp0ca69b12010-07-26 01:57:52 +00002788 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002789 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2790 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002791 {
2792 if (logging != MagickFalse)
2793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002794 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002795 mng_info->scenes_found-1);
2796 png_destroy_read_struct(&ping,&ping_info,&end_info);
2797#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002798 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002799#endif
2800 if (logging != MagickFalse)
2801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2802 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002803
cristy3ed852e2009-09-05 21:47:34 +00002804 return(image);
2805 }
glennrp0fe50b42010-11-16 03:52:51 +00002806
cristy3ed852e2009-09-05 21:47:34 +00002807 if (logging != MagickFalse)
2808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2809 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002810
cristy3ed852e2009-09-05 21:47:34 +00002811 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002812 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2813 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002814
cristy3ed852e2009-09-05 21:47:34 +00002815 else
glennrpcf002022011-01-30 02:38:15 +00002816 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2817 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002818
glennrpcf002022011-01-30 02:38:15 +00002819 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002820 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002821
cristy3ed852e2009-09-05 21:47:34 +00002822 if (logging != MagickFalse)
2823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2824 " Converting PNG pixels to pixel packets");
2825 /*
2826 Convert PNG pixels to pixel packets.
2827 */
glennrpfaa852b2010-03-30 12:17:00 +00002828 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002829 {
2830 /*
2831 PNG image is corrupt.
2832 */
2833 png_destroy_read_struct(&ping,&ping_info,&end_info);
2834#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002835 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002836#endif
2837 if (quantum_info != (QuantumInfo *) NULL)
2838 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002839
glennrpcf002022011-01-30 02:38:15 +00002840 if (ping_pixels != (unsigned char *) NULL)
2841 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002842
cristy3ed852e2009-09-05 21:47:34 +00002843 if (logging != MagickFalse)
2844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2845 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002846
cristy3ed852e2009-09-05 21:47:34 +00002847 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002848 {
2849 InheritException(exception,&image->exception);
2850 image->columns=0;
2851 }
glennrp0fe50b42010-11-16 03:52:51 +00002852
cristy3ed852e2009-09-05 21:47:34 +00002853 return(GetFirstImageInList(image));
2854 }
glennrp0fe50b42010-11-16 03:52:51 +00002855
cristyed552522009-10-16 14:04:35 +00002856 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002857
cristyed552522009-10-16 14:04:35 +00002858 if (quantum_info == (QuantumInfo *) NULL)
2859 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002860
glennrpc8cbc5d2011-01-01 00:12:34 +00002861 {
2862
2863 MagickBooleanType
2864 found_transparent_pixel;
2865
2866 found_transparent_pixel=MagickFalse;
2867
cristy3ed852e2009-09-05 21:47:34 +00002868 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002869 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002870 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002871 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002872 /*
2873 Convert image to DirectClass pixel packets.
2874 */
glennrp67b9c1a2011-04-22 18:47:36 +00002875#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2876 int
2877 depth;
2878
2879 depth=(ssize_t) ping_bit_depth;
2880#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002881 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2882 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2883 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2884 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002885
glennrpc8cbc5d2011-01-01 00:12:34 +00002886 for (y=0; y < (ssize_t) image->rows; y++)
2887 {
2888 if (num_passes > 1)
2889 row_offset=ping_rowbytes*y;
2890
2891 else
2892 row_offset=0;
2893
glennrpcf002022011-01-30 02:38:15 +00002894 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002895 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2896
cristyacd2ed22011-08-30 01:44:23 +00002897 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00002898 break;
2899
glennrpc8cbc5d2011-01-01 00:12:34 +00002900 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2901 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002902 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002903
2904 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2905 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002906 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002907
2908 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2909 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002910 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002911
2912 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2913 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002914 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002915
2916 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2917 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002918 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002919
glennrpc8cbc5d2011-01-01 00:12:34 +00002920 if (found_transparent_pixel == MagickFalse)
2921 {
2922 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002923 if (y== 0 && logging != MagickFalse)
2924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2925 " Looking for cheap transparent pixel");
2926
glennrpc8cbc5d2011-01-01 00:12:34 +00002927 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2928 {
glennrp5aa37f62011-01-02 03:07:57 +00002929 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2930 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy4c08aed2011-07-01 19:47:50 +00002931 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00002932 {
glennrpa6a06632011-01-19 15:15:34 +00002933 if (logging != MagickFalse)
2934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2935 " ...got one.");
2936
glennrpc8cbc5d2011-01-01 00:12:34 +00002937 found_transparent_pixel = MagickTrue;
2938 break;
2939 }
glennrp4f25bd02011-01-01 18:51:28 +00002940 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2941 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp847370c2011-07-05 17:37:15 +00002942 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2943 transparent_color.red &&
2944 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2945 transparent_color.green &&
2946 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2947 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002948 {
glennrpa6a06632011-01-19 15:15:34 +00002949 if (logging != MagickFalse)
2950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2951 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002952 found_transparent_pixel = MagickTrue;
2953 break;
2954 }
cristyed231572011-07-14 02:18:59 +00002955 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00002956 }
2957 }
2958
2959 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2960 {
2961 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2962 image->rows);
2963
2964 if (status == MagickFalse)
2965 break;
2966 }
2967 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2968 break;
2969 }
2970
2971 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2972 {
2973 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00002974 if (status == MagickFalse)
2975 break;
2976 }
cristy3ed852e2009-09-05 21:47:34 +00002977 }
cristy3ed852e2009-09-05 21:47:34 +00002978 }
glennrp0fe50b42010-11-16 03:52:51 +00002979
cristy3ed852e2009-09-05 21:47:34 +00002980 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00002981
cristy3ed852e2009-09-05 21:47:34 +00002982 for (pass=0; pass < num_passes; pass++)
2983 {
2984 Quantum
2985 *quantum_scanline;
2986
2987 register Quantum
2988 *r;
2989
2990 /*
2991 Convert grayscale image to PseudoClass pixel packets.
2992 */
glennrpc17d96f2011-06-27 01:20:11 +00002993 if (logging != MagickFalse)
2994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2995 " Converting grayscale pixels to pixel packets");
cristy4c08aed2011-07-01 19:47:50 +00002996
glennrpfaa852b2010-03-30 12:17:00 +00002997 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00002998 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002999
cristy3ed852e2009-09-05 21:47:34 +00003000 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3001 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003002
cristy3ed852e2009-09-05 21:47:34 +00003003 if (quantum_scanline == (Quantum *) NULL)
3004 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003005
cristybb503372010-05-27 20:51:26 +00003006 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003007 {
3008 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003009 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003010
cristy3ed852e2009-09-05 21:47:34 +00003011 else
3012 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003013
glennrpcf002022011-01-30 02:38:15 +00003014 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00003015 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003016
cristyacd2ed22011-08-30 01:44:23 +00003017 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003018 break;
glennrp0fe50b42010-11-16 03:52:51 +00003019
glennrpcf002022011-01-30 02:38:15 +00003020 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003021 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003022
glennrpfaa852b2010-03-30 12:17:00 +00003023 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003024 {
3025 case 1:
3026 {
cristybb503372010-05-27 20:51:26 +00003027 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003028 bit;
3029
cristybb503372010-05-27 20:51:26 +00003030 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003031 {
3032 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003033 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003034 p++;
3035 }
glennrp0fe50b42010-11-16 03:52:51 +00003036
cristy3ed852e2009-09-05 21:47:34 +00003037 if ((image->columns % 8) != 0)
3038 {
cristybb503372010-05-27 20:51:26 +00003039 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003040 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003041 }
glennrp0fe50b42010-11-16 03:52:51 +00003042
cristy3ed852e2009-09-05 21:47:34 +00003043 break;
3044 }
glennrp47b9dd52010-11-24 18:12:06 +00003045
cristy3ed852e2009-09-05 21:47:34 +00003046 case 2:
3047 {
cristybb503372010-05-27 20:51:26 +00003048 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003049 {
glennrpa18d5bc2011-04-23 14:51:34 +00003050 *r++=(*p >> 6) & 0x03;
3051 *r++=(*p >> 4) & 0x03;
3052 *r++=(*p >> 2) & 0x03;
3053 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003054 }
glennrp0fe50b42010-11-16 03:52:51 +00003055
cristy3ed852e2009-09-05 21:47:34 +00003056 if ((image->columns % 4) != 0)
3057 {
cristybb503372010-05-27 20:51:26 +00003058 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003059 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003060 }
glennrp0fe50b42010-11-16 03:52:51 +00003061
cristy3ed852e2009-09-05 21:47:34 +00003062 break;
3063 }
glennrp47b9dd52010-11-24 18:12:06 +00003064
cristy3ed852e2009-09-05 21:47:34 +00003065 case 4:
3066 {
cristybb503372010-05-27 20:51:26 +00003067 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003068 {
glennrpa18d5bc2011-04-23 14:51:34 +00003069 *r++=(*p >> 4) & 0x0f;
3070 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003071 }
glennrp0fe50b42010-11-16 03:52:51 +00003072
cristy3ed852e2009-09-05 21:47:34 +00003073 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003074 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003075
cristy3ed852e2009-09-05 21:47:34 +00003076 break;
3077 }
glennrp47b9dd52010-11-24 18:12:06 +00003078
cristy3ed852e2009-09-05 21:47:34 +00003079 case 8:
3080 {
glennrpfaa852b2010-03-30 12:17:00 +00003081 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003082 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003083 {
glennrpa18d5bc2011-04-23 14:51:34 +00003084 *r++=*p++;
cristy4c08aed2011-07-01 19:47:50 +00003085 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3086 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003087 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003088 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003089 }
glennrp0fe50b42010-11-16 03:52:51 +00003090
cristy3ed852e2009-09-05 21:47:34 +00003091 else
cristybb503372010-05-27 20:51:26 +00003092 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003093 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003094
cristy3ed852e2009-09-05 21:47:34 +00003095 break;
3096 }
glennrp47b9dd52010-11-24 18:12:06 +00003097
cristy3ed852e2009-09-05 21:47:34 +00003098 case 16:
3099 {
cristybb503372010-05-27 20:51:26 +00003100 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003101 {
glennrpc17d96f2011-06-27 01:20:11 +00003102#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003103 size_t
3104 quantum;
3105
3106 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003107 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003108
3109 else
glennrpc17d96f2011-06-27 01:20:11 +00003110 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003111
glennrp58f77c72011-04-23 14:09:09 +00003112 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003113 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003114 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003115
3116 if (ping_color_type == 4)
3117 {
glennrpc17d96f2011-06-27 01:20:11 +00003118 if (image->colors > 256)
3119 quantum=((*p++) << 8);
3120 else
3121 quantum=0;
3122
glennrp58f77c72011-04-23 14:09:09 +00003123 quantum|=(*p++);
cristy4c08aed2011-07-01 19:47:50 +00003124 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3125 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003126 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003127 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003128 }
glennrp58f77c72011-04-23 14:09:09 +00003129
3130#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3131 *r++=(*p++);
3132 p++; /* strip low byte */
3133
3134 if (ping_color_type == 4)
3135 {
cristy4c08aed2011-07-01 19:47:50 +00003136 SetPixelAlpha(image,*p++,q);
3137 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003138 found_transparent_pixel = MagickTrue;
3139 p++;
cristyed231572011-07-14 02:18:59 +00003140 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003141 }
cristy3ed852e2009-09-05 21:47:34 +00003142#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003143 }
glennrp47b9dd52010-11-24 18:12:06 +00003144
cristy3ed852e2009-09-05 21:47:34 +00003145 break;
3146 }
glennrp47b9dd52010-11-24 18:12:06 +00003147
cristy3ed852e2009-09-05 21:47:34 +00003148 default:
3149 break;
3150 }
glennrp3faa9a32011-04-23 14:00:25 +00003151
cristy3ed852e2009-09-05 21:47:34 +00003152 /*
3153 Transfer image scanline.
3154 */
3155 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003156
cristy4c08aed2011-07-01 19:47:50 +00003157 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3158
cristyacd2ed22011-08-30 01:44:23 +00003159 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00003160 break;
cristybb503372010-05-27 20:51:26 +00003161 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00003162 {
3163 SetPixelIndex(image,*r++,q);
cristyed231572011-07-14 02:18:59 +00003164 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00003165 }
glennrp0fe50b42010-11-16 03:52:51 +00003166
cristy3ed852e2009-09-05 21:47:34 +00003167 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3168 break;
glennrp0fe50b42010-11-16 03:52:51 +00003169
cristy7a287bf2010-02-14 02:18:09 +00003170 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3171 {
cristycee97112010-05-28 00:44:52 +00003172 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003173 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003174
cristy7a287bf2010-02-14 02:18:09 +00003175 if (status == MagickFalse)
3176 break;
3177 }
cristy3ed852e2009-09-05 21:47:34 +00003178 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003179
cristy7a287bf2010-02-14 02:18:09 +00003180 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003181 {
3182 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003183
cristy3ed852e2009-09-05 21:47:34 +00003184 if (status == MagickFalse)
3185 break;
3186 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003187
cristy3ed852e2009-09-05 21:47:34 +00003188 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3189 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003190
3191 image->matte=found_transparent_pixel;
3192
3193 if (logging != MagickFalse)
3194 {
3195 if (found_transparent_pixel != MagickFalse)
3196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3197 " Found transparent pixel");
3198 else
glennrp5aa37f62011-01-02 03:07:57 +00003199 {
3200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3201 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003202
glennrp5aa37f62011-01-02 03:07:57 +00003203 ping_color_type&=0x03;
3204 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003205 }
3206 }
3207
cristyb32b90a2009-09-07 21:45:48 +00003208 if (quantum_info != (QuantumInfo *) NULL)
3209 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00003210
cristy5c6f7892010-05-05 22:53:29 +00003211 if (image->storage_class == PseudoClass)
3212 {
cristyaeb2cbc2010-05-07 13:28:58 +00003213 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003214 matte;
3215
3216 matte=image->matte;
3217 image->matte=MagickFalse;
3218 (void) SyncImage(image);
cristyaeb2cbc2010-05-07 13:28:58 +00003219 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003220 }
glennrp47b9dd52010-11-24 18:12:06 +00003221
glennrp4eb39312011-03-30 21:34:55 +00003222 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003223
3224 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003225 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003226 {
3227 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003228 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003229 image->colors=2;
3230 (void) SetImageBackgroundColor(image);
3231#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003232 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003233#endif
3234 if (logging != MagickFalse)
3235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3236 " exit ReadOnePNGImage() early.");
3237 return(image);
3238 }
glennrp47b9dd52010-11-24 18:12:06 +00003239
glennrpfaa852b2010-03-30 12:17:00 +00003240 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003241 {
3242 ClassType
3243 storage_class;
3244
3245 /*
3246 Image has a transparent background.
3247 */
3248 storage_class=image->storage_class;
3249 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003250
glennrp3c218112010-11-27 15:31:26 +00003251/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003252
glennrp0fe50b42010-11-16 03:52:51 +00003253 if (storage_class == PseudoClass)
3254 {
3255 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003256 {
glennrp0fe50b42010-11-16 03:52:51 +00003257 for (x=0; x < ping_num_trans; x++)
3258 {
cristy4c08aed2011-07-01 19:47:50 +00003259 image->colormap[x].alpha =
3260 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003261 }
glennrpc11cf6a2010-03-20 16:46:19 +00003262 }
glennrp47b9dd52010-11-24 18:12:06 +00003263
glennrp0fe50b42010-11-16 03:52:51 +00003264 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3265 {
3266 for (x=0; x < (int) image->colors; x++)
3267 {
3268 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy4c08aed2011-07-01 19:47:50 +00003269 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003270 {
cristy4c08aed2011-07-01 19:47:50 +00003271 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003272 }
3273 }
3274 }
3275 (void) SyncImage(image);
3276 }
glennrp47b9dd52010-11-24 18:12:06 +00003277
glennrpa6a06632011-01-19 15:15:34 +00003278#if 1 /* Should have already been done above, but glennrp problem P10
3279 * needs this.
3280 */
glennrp0fe50b42010-11-16 03:52:51 +00003281 else
3282 {
3283 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003284 {
glennrp0fe50b42010-11-16 03:52:51 +00003285 image->storage_class=storage_class;
3286 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3287
cristyacd2ed22011-08-30 01:44:23 +00003288 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003289 break;
3290
glennrp0fe50b42010-11-16 03:52:51 +00003291
glennrpa6a06632011-01-19 15:15:34 +00003292 /* Caution: on a Q8 build, this does not distinguish between
3293 * 16-bit colors that differ only in the low byte
3294 */
glennrp0fe50b42010-11-16 03:52:51 +00003295 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3296 {
glennrp847370c2011-07-05 17:37:15 +00003297 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3298 transparent_color.red &&
3299 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3300 transparent_color.green &&
3301 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3302 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003303 {
cristy4c08aed2011-07-01 19:47:50 +00003304 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003305 }
glennrp0fe50b42010-11-16 03:52:51 +00003306
glennrp67b9c1a2011-04-22 18:47:36 +00003307#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003308 else
glennrp4f25bd02011-01-01 18:51:28 +00003309 {
cristy4c08aed2011-07-01 19:47:50 +00003310 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003311 }
glennrpa6a06632011-01-19 15:15:34 +00003312#endif
glennrp0fe50b42010-11-16 03:52:51 +00003313
cristyed231572011-07-14 02:18:59 +00003314 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003315 }
3316
3317 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3318 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003319 }
glennrp0fe50b42010-11-16 03:52:51 +00003320 }
glennrpa6a06632011-01-19 15:15:34 +00003321#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003322
cristy3ed852e2009-09-05 21:47:34 +00003323 image->storage_class=DirectClass;
3324 }
glennrp3c218112010-11-27 15:31:26 +00003325
cristyb40fc462010-08-08 00:49:49 +00003326 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3327 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3328 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00003329
cristyeb3b22a2011-03-31 20:16:11 +00003330 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003331 {
3332 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003333 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3334 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003335 else
glennrpa0ed0092011-04-18 16:36:29 +00003336 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3337 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003338
glennrp4eb39312011-03-30 21:34:55 +00003339 if (status != MagickFalse)
3340 for (i=0; i < (ssize_t) num_text; i++)
3341 {
3342 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003343
glennrp4eb39312011-03-30 21:34:55 +00003344 if (logging != MagickFalse)
3345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3346 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003347
glennrp4eb39312011-03-30 21:34:55 +00003348 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003349 {
glennrp4eb39312011-03-30 21:34:55 +00003350 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
3351 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003352 }
glennrp0fe50b42010-11-16 03:52:51 +00003353
glennrp4eb39312011-03-30 21:34:55 +00003354 else
3355 {
3356 char
3357 *value;
3358
3359 length=text[i].text_length;
3360 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3361 sizeof(*value));
3362 if (value == (char *) NULL)
3363 {
3364 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3365 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3366 image->filename);
3367 break;
3368 }
3369 *value='\0';
3370 (void) ConcatenateMagickString(value,text[i].text,length+2);
3371
3372 /* Don't save "density" or "units" property if we have a pHYs
3373 * chunk
3374 */
3375 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3376 (LocaleCompare(text[i].key,"density") != 0 &&
3377 LocaleCompare(text[i].key,"units") != 0))
3378 (void) SetImageProperty(image,text[i].key,value);
3379
3380 if (logging != MagickFalse)
3381 {
3382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3383 " length: %lu",(unsigned long) length);
3384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3385 " Keyword: %s",text[i].key);
3386 }
3387
3388 value=DestroyString(value);
3389 }
3390 }
3391 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003392 }
glennrp3c218112010-11-27 15:31:26 +00003393
cristy3ed852e2009-09-05 21:47:34 +00003394#ifdef MNG_OBJECT_BUFFERS
3395 /*
3396 Store the object if necessary.
3397 */
3398 if (object_id && !mng_info->frozen[object_id])
3399 {
3400 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3401 {
3402 /*
3403 create a new object buffer.
3404 */
3405 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003406 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003407
cristy3ed852e2009-09-05 21:47:34 +00003408 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3409 {
3410 mng_info->ob[object_id]->image=(Image *) NULL;
3411 mng_info->ob[object_id]->reference_count=1;
3412 }
3413 }
glennrp47b9dd52010-11-24 18:12:06 +00003414
cristy3ed852e2009-09-05 21:47:34 +00003415 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3416 mng_info->ob[object_id]->frozen)
3417 {
3418 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3419 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3420 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3421 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003422
cristy3ed852e2009-09-05 21:47:34 +00003423 if (mng_info->ob[object_id]->frozen)
3424 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3425 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3426 "`%s'",image->filename);
3427 }
glennrp0fe50b42010-11-16 03:52:51 +00003428
cristy3ed852e2009-09-05 21:47:34 +00003429 else
3430 {
cristy3ed852e2009-09-05 21:47:34 +00003431
3432 if (mng_info->ob[object_id]->image != (Image *) NULL)
3433 mng_info->ob[object_id]->image=DestroyImage
3434 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003435
cristy3ed852e2009-09-05 21:47:34 +00003436 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3437 &image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00003438
cristy3ed852e2009-09-05 21:47:34 +00003439 if (mng_info->ob[object_id]->image != (Image *) NULL)
3440 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003441
cristy3ed852e2009-09-05 21:47:34 +00003442 else
3443 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3444 ResourceLimitError,"Cloning image for object buffer failed",
3445 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003446
glennrpfaa852b2010-03-30 12:17:00 +00003447 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003448 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003449
glennrpfaa852b2010-03-30 12:17:00 +00003450 mng_info->ob[object_id]->width=ping_width;
3451 mng_info->ob[object_id]->height=ping_height;
3452 mng_info->ob[object_id]->color_type=ping_color_type;
3453 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3454 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3455 mng_info->ob[object_id]->compression_method=
3456 ping_compression_method;
3457 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003458
glennrpfaa852b2010-03-30 12:17:00 +00003459 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003460 {
3461 int
3462 number_colors;
3463
3464 png_colorp
3465 plte;
3466
3467 /*
3468 Copy the PLTE to the object buffer.
3469 */
3470 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3471 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003472
cristy3ed852e2009-09-05 21:47:34 +00003473 for (i=0; i < number_colors; i++)
3474 {
3475 mng_info->ob[object_id]->plte[i]=plte[i];
3476 }
3477 }
glennrp47b9dd52010-11-24 18:12:06 +00003478
cristy3ed852e2009-09-05 21:47:34 +00003479 else
3480 mng_info->ob[object_id]->plte_length=0;
3481 }
3482 }
3483#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003484
3485 /* Set image->matte to MagickTrue if the input colortype supports
3486 * alpha or if a valid tRNS chunk is present, no matter whether there
3487 * is actual transparency present.
3488 */
3489 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3490 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3491 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3492 MagickTrue : MagickFalse;
3493
glennrpcb395ac2011-03-30 19:50:23 +00003494 /* Set more properties for identify to retrieve */
3495 {
3496 char
3497 msg[MaxTextExtent];
3498
glennrp4eb39312011-03-30 21:34:55 +00003499 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003500 {
3501 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003502 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003503 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrpcb395ac2011-03-30 19:50:23 +00003504 (void) SetImageProperty(image,"PNG:text ",msg);
3505 }
3506
3507 if (num_raw_profiles != 0)
3508 {
cristy3b6fd2e2011-05-20 12:53:50 +00003509 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003510 "%d were found", num_raw_profiles);
3511 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3512 }
3513
glennrpcb395ac2011-03-30 19:50:23 +00003514 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003515 {
cristy3b6fd2e2011-05-20 12:53:50 +00003516 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003517 "chunk was found (see Chromaticity, above)");
3518 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3519 }
glennrpcb395ac2011-03-30 19:50:23 +00003520
3521 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003522 {
cristy3b6fd2e2011-05-20 12:53:50 +00003523 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003524 "chunk was found (see Background color, above)");
3525 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3526 }
3527
cristy3b6fd2e2011-05-20 12:53:50 +00003528 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003529 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003530
3531 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3532 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3533
glennrpcb395ac2011-03-30 19:50:23 +00003534 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3535 (void) SetImageProperty(image,"PNG:tRNS ",msg);
glennrp4eb39312011-03-30 21:34:55 +00003536
3537#if defined(PNG_sRGB_SUPPORTED)
3538 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3539 {
cristy3b6fd2e2011-05-20 12:53:50 +00003540 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003541 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003542 (int) intent);
3543 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3544 }
3545#endif
3546
3547 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3548 {
cristy3b6fd2e2011-05-20 12:53:50 +00003549 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003550 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003551 file_gamma);
3552 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3553 }
3554
3555#if defined(PNG_pHYs_SUPPORTED)
3556 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3557 {
cristy3b6fd2e2011-05-20 12:53:50 +00003558 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003559 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003560 (double) x_resolution,(double) y_resolution, unit_type);
3561 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3562 }
3563#endif
3564
3565#if defined(PNG_oFFs_SUPPORTED)
3566 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3567 {
cristy3b6fd2e2011-05-20 12:53:50 +00003568 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003569 (double) image->page.x,(double) image->page.y);
3570 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3571 }
3572#endif
3573
glennrp07523c72011-03-31 18:12:10 +00003574 if ((image->page.width != 0 && image->page.width != image->columns) ||
3575 (image->page.height != 0 && image->page.height != image->rows))
3576 {
cristy3b6fd2e2011-05-20 12:53:50 +00003577 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003578 "width=%.20g, height=%.20g",
3579 (double) image->page.width,(double) image->page.height);
3580 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3581 }
glennrpcb395ac2011-03-30 19:50:23 +00003582 }
3583
cristy3ed852e2009-09-05 21:47:34 +00003584 /*
3585 Relinquish resources.
3586 */
3587 png_destroy_read_struct(&ping,&ping_info,&end_info);
3588
glennrpcf002022011-01-30 02:38:15 +00003589 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003590#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003591 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003592#endif
3593
3594 if (logging != MagickFalse)
3595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3596 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003597
cristy3ed852e2009-09-05 21:47:34 +00003598 return(image);
3599
3600/* end of reading one PNG image */
3601}
3602
3603static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3604{
3605 Image
3606 *image,
3607 *previous;
3608
3609 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003610 have_mng_structure,
3611 logging,
cristy3ed852e2009-09-05 21:47:34 +00003612 status;
3613
3614 MngInfo
3615 *mng_info;
3616
3617 char
3618 magic_number[MaxTextExtent];
3619
cristy3ed852e2009-09-05 21:47:34 +00003620 ssize_t
3621 count;
3622
3623 /*
3624 Open image file.
3625 */
3626 assert(image_info != (const ImageInfo *) NULL);
3627 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003628
cristy3ed852e2009-09-05 21:47:34 +00003629 if (image_info->debug != MagickFalse)
3630 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3631 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003632
cristy3ed852e2009-09-05 21:47:34 +00003633 assert(exception != (ExceptionInfo *) NULL);
3634 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003635 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy9950d572011-10-01 18:22:35 +00003636 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003637 mng_info=(MngInfo *) NULL;
3638 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003639
cristy3ed852e2009-09-05 21:47:34 +00003640 if (status == MagickFalse)
3641 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003642
cristy3ed852e2009-09-05 21:47:34 +00003643 /*
3644 Verify PNG signature.
3645 */
3646 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003647
glennrpdde35db2011-02-21 12:06:32 +00003648 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003649 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003650
cristy3ed852e2009-09-05 21:47:34 +00003651 /*
3652 Allocate a MngInfo structure.
3653 */
3654 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003655 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003656
cristy3ed852e2009-09-05 21:47:34 +00003657 if (mng_info == (MngInfo *) NULL)
3658 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003659
cristy3ed852e2009-09-05 21:47:34 +00003660 /*
3661 Initialize members of the MngInfo structure.
3662 */
3663 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3664 mng_info->image=image;
3665 have_mng_structure=MagickTrue;
3666
3667 previous=image;
3668 image=ReadOnePNGImage(mng_info,image_info,exception);
3669 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003670
cristy3ed852e2009-09-05 21:47:34 +00003671 if (image == (Image *) NULL)
3672 {
3673 if (previous != (Image *) NULL)
3674 {
3675 if (previous->signature != MagickSignature)
3676 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003677
cristy3ed852e2009-09-05 21:47:34 +00003678 (void) CloseBlob(previous);
3679 (void) DestroyImageList(previous);
3680 }
glennrp0fe50b42010-11-16 03:52:51 +00003681
cristy3ed852e2009-09-05 21:47:34 +00003682 if (logging != MagickFalse)
3683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3684 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003685
cristy3ed852e2009-09-05 21:47:34 +00003686 return((Image *) NULL);
3687 }
glennrp47b9dd52010-11-24 18:12:06 +00003688
cristy3ed852e2009-09-05 21:47:34 +00003689 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003690
cristy3ed852e2009-09-05 21:47:34 +00003691 if ((image->columns == 0) || (image->rows == 0))
3692 {
3693 if (logging != MagickFalse)
3694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3695 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003696
cristy3ed852e2009-09-05 21:47:34 +00003697 ThrowReaderException(CorruptImageError,"CorruptImage");
3698 }
glennrp47b9dd52010-11-24 18:12:06 +00003699
cristy3ed852e2009-09-05 21:47:34 +00003700 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3701 {
cristy018f07f2011-09-04 21:15:19 +00003702 (void) SetImageType(image,TrueColorType,exception);
cristy3ed852e2009-09-05 21:47:34 +00003703 image->matte=MagickFalse;
3704 }
glennrp0fe50b42010-11-16 03:52:51 +00003705
cristy3ed852e2009-09-05 21:47:34 +00003706 if (LocaleCompare(image_info->magick,"PNG32") == 0)
cristy018f07f2011-09-04 21:15:19 +00003707 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003708
cristy3ed852e2009-09-05 21:47:34 +00003709 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3711 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3712 (double) image->page.width,(double) image->page.height,
3713 (double) image->page.x,(double) image->page.y);
3714
3715 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003717
cristy3ed852e2009-09-05 21:47:34 +00003718 return(image);
3719}
3720
3721
3722
3723#if defined(JNG_SUPPORTED)
3724/*
3725%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3726% %
3727% %
3728% %
3729% R e a d O n e J N G I m a g e %
3730% %
3731% %
3732% %
3733%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3734%
3735% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3736% (minus the 8-byte signature) and returns it. It allocates the memory
3737% necessary for the new Image structure and returns a pointer to the new
3738% image.
3739%
3740% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3741%
3742% The format of the ReadOneJNGImage method is:
3743%
3744% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3745% ExceptionInfo *exception)
3746%
3747% A description of each parameter follows:
3748%
3749% o mng_info: Specifies a pointer to a MngInfo structure.
3750%
3751% o image_info: the image info.
3752%
3753% o exception: return any errors or warnings in this structure.
3754%
3755*/
3756static Image *ReadOneJNGImage(MngInfo *mng_info,
3757 const ImageInfo *image_info, ExceptionInfo *exception)
3758{
3759 Image
3760 *alpha_image,
3761 *color_image,
3762 *image,
3763 *jng_image;
3764
3765 ImageInfo
3766 *alpha_image_info,
3767 *color_image_info;
3768
cristy4383ec82011-01-05 15:42:32 +00003769 MagickBooleanType
3770 logging;
3771
cristybb503372010-05-27 20:51:26 +00003772 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003773 y;
3774
3775 MagickBooleanType
3776 status;
3777
3778 png_uint_32
3779 jng_height,
3780 jng_width;
3781
3782 png_byte
3783 jng_color_type,
3784 jng_image_sample_depth,
3785 jng_image_compression_method,
3786 jng_image_interlace_method,
3787 jng_alpha_sample_depth,
3788 jng_alpha_compression_method,
3789 jng_alpha_filter_method,
3790 jng_alpha_interlace_method;
3791
cristy4c08aed2011-07-01 19:47:50 +00003792 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003793 *s;
3794
cristybb503372010-05-27 20:51:26 +00003795 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003796 i,
3797 x;
3798
cristy4c08aed2011-07-01 19:47:50 +00003799 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003800 *q;
3801
3802 register unsigned char
3803 *p;
3804
3805 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003806 read_JSEP,
3807 reading_idat,
3808 skip_to_iend;
3809
cristybb503372010-05-27 20:51:26 +00003810 size_t
cristy3ed852e2009-09-05 21:47:34 +00003811 length;
3812
3813 jng_alpha_compression_method=0;
3814 jng_alpha_sample_depth=8;
3815 jng_color_type=0;
3816 jng_height=0;
3817 jng_width=0;
3818 alpha_image=(Image *) NULL;
3819 color_image=(Image *) NULL;
3820 alpha_image_info=(ImageInfo *) NULL;
3821 color_image_info=(ImageInfo *) NULL;
3822
3823 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003824 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003825
3826 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003827
cristy4c08aed2011-07-01 19:47:50 +00003828 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003829 {
3830 /*
3831 Allocate next image structure.
3832 */
3833 if (logging != MagickFalse)
3834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3835 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003836
cristy9950d572011-10-01 18:22:35 +00003837 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003838
cristy3ed852e2009-09-05 21:47:34 +00003839 if (GetNextImageInList(image) == (Image *) NULL)
3840 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003841
cristy3ed852e2009-09-05 21:47:34 +00003842 image=SyncNextImageInList(image);
3843 }
3844 mng_info->image=image;
3845
3846 /*
3847 Signature bytes have already been read.
3848 */
3849
3850 read_JSEP=MagickFalse;
3851 reading_idat=MagickFalse;
3852 skip_to_iend=MagickFalse;
3853 for (;;)
3854 {
3855 char
3856 type[MaxTextExtent];
3857
3858 unsigned char
3859 *chunk;
3860
3861 unsigned int
3862 count;
3863
3864 /*
3865 Read a new JNG chunk.
3866 */
3867 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3868 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003869
cristy3ed852e2009-09-05 21:47:34 +00003870 if (status == MagickFalse)
3871 break;
glennrp0fe50b42010-11-16 03:52:51 +00003872
cristy3ed852e2009-09-05 21:47:34 +00003873 type[0]='\0';
3874 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3875 length=ReadBlobMSBLong(image);
3876 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3877
3878 if (logging != MagickFalse)
3879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003880 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3881 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003882
3883 if (length > PNG_UINT_31_MAX || count == 0)
3884 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003885
cristy3ed852e2009-09-05 21:47:34 +00003886 p=NULL;
3887 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003888
cristy3ed852e2009-09-05 21:47:34 +00003889 if (length)
3890 {
3891 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003892
cristy3ed852e2009-09-05 21:47:34 +00003893 if (chunk == (unsigned char *) NULL)
3894 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003895
cristybb503372010-05-27 20:51:26 +00003896 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003897 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003898
cristy3ed852e2009-09-05 21:47:34 +00003899 p=chunk;
3900 }
glennrp47b9dd52010-11-24 18:12:06 +00003901
cristy3ed852e2009-09-05 21:47:34 +00003902 (void) ReadBlobMSBLong(image); /* read crc word */
3903
3904 if (skip_to_iend)
3905 {
3906 if (length)
3907 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003908
cristy3ed852e2009-09-05 21:47:34 +00003909 continue;
3910 }
3911
3912 if (memcmp(type,mng_JHDR,4) == 0)
3913 {
3914 if (length == 16)
3915 {
cristybb503372010-05-27 20:51:26 +00003916 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003917 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003918 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003919 (p[6] << 8) | p[7]);
3920 jng_color_type=p[8];
3921 jng_image_sample_depth=p[9];
3922 jng_image_compression_method=p[10];
3923 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003924
cristy3ed852e2009-09-05 21:47:34 +00003925 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3926 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003927
cristy3ed852e2009-09-05 21:47:34 +00003928 jng_alpha_sample_depth=p[12];
3929 jng_alpha_compression_method=p[13];
3930 jng_alpha_filter_method=p[14];
3931 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003932
cristy3ed852e2009-09-05 21:47:34 +00003933 if (logging != MagickFalse)
3934 {
3935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003936 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003937
cristy3ed852e2009-09-05 21:47:34 +00003938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003939 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003940
cristy3ed852e2009-09-05 21:47:34 +00003941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3942 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003943
cristy3ed852e2009-09-05 21:47:34 +00003944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3945 " jng_image_sample_depth: %3d",
3946 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003947
cristy3ed852e2009-09-05 21:47:34 +00003948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3949 " jng_image_compression_method:%3d",
3950 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003951
cristy3ed852e2009-09-05 21:47:34 +00003952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3953 " jng_image_interlace_method: %3d",
3954 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003955
cristy3ed852e2009-09-05 21:47:34 +00003956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3957 " jng_alpha_sample_depth: %3d",
3958 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003959
cristy3ed852e2009-09-05 21:47:34 +00003960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3961 " jng_alpha_compression_method:%3d",
3962 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003963
cristy3ed852e2009-09-05 21:47:34 +00003964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3965 " jng_alpha_filter_method: %3d",
3966 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00003967
cristy3ed852e2009-09-05 21:47:34 +00003968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3969 " jng_alpha_interlace_method: %3d",
3970 jng_alpha_interlace_method);
3971 }
3972 }
glennrp47b9dd52010-11-24 18:12:06 +00003973
cristy3ed852e2009-09-05 21:47:34 +00003974 if (length)
3975 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003976
cristy3ed852e2009-09-05 21:47:34 +00003977 continue;
3978 }
3979
3980
3981 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3982 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3983 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3984 {
3985 /*
3986 o create color_image
3987 o open color_blob, attached to color_image
3988 o if (color type has alpha)
3989 open alpha_blob, attached to alpha_image
3990 */
3991
cristy73bd4a52010-10-05 11:24:23 +00003992 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003993
cristy3ed852e2009-09-05 21:47:34 +00003994 if (color_image_info == (ImageInfo *) NULL)
3995 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003996
cristy3ed852e2009-09-05 21:47:34 +00003997 GetImageInfo(color_image_info);
cristy9950d572011-10-01 18:22:35 +00003998 color_image=AcquireImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003999
cristy3ed852e2009-09-05 21:47:34 +00004000 if (color_image == (Image *) NULL)
4001 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4002
4003 if (logging != MagickFalse)
4004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4005 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004006
cristy3ed852e2009-09-05 21:47:34 +00004007 (void) AcquireUniqueFilename(color_image->filename);
4008 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4009 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004010
cristy3ed852e2009-09-05 21:47:34 +00004011 if (status == MagickFalse)
4012 return((Image *) NULL);
4013
4014 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4015 {
4016 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004017 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004018
cristy3ed852e2009-09-05 21:47:34 +00004019 if (alpha_image_info == (ImageInfo *) NULL)
4020 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004021
cristy3ed852e2009-09-05 21:47:34 +00004022 GetImageInfo(alpha_image_info);
cristy9950d572011-10-01 18:22:35 +00004023 alpha_image=AcquireImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004024
cristy3ed852e2009-09-05 21:47:34 +00004025 if (alpha_image == (Image *) NULL)
4026 {
4027 alpha_image=DestroyImage(alpha_image);
4028 ThrowReaderException(ResourceLimitError,
4029 "MemoryAllocationFailed");
4030 }
glennrp0fe50b42010-11-16 03:52:51 +00004031
cristy3ed852e2009-09-05 21:47:34 +00004032 if (logging != MagickFalse)
4033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4034 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004035
cristy3ed852e2009-09-05 21:47:34 +00004036 (void) AcquireUniqueFilename(alpha_image->filename);
4037 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4038 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004039
cristy3ed852e2009-09-05 21:47:34 +00004040 if (status == MagickFalse)
4041 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004042
cristy3ed852e2009-09-05 21:47:34 +00004043 if (jng_alpha_compression_method == 0)
4044 {
4045 unsigned char
4046 data[18];
4047
4048 if (logging != MagickFalse)
4049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4050 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004051
cristy3ed852e2009-09-05 21:47:34 +00004052 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4053 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004054
cristy3ed852e2009-09-05 21:47:34 +00004055 (void) WriteBlobMSBULong(alpha_image,13L);
4056 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004057 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004058 PNGLong(data+4,jng_width);
4059 PNGLong(data+8,jng_height);
4060 data[12]=jng_alpha_sample_depth;
4061 data[13]=0; /* color_type gray */
4062 data[14]=0; /* compression method 0 */
4063 data[15]=0; /* filter_method 0 */
4064 data[16]=0; /* interlace_method 0 */
4065 (void) WriteBlob(alpha_image,17,data);
4066 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4067 }
4068 }
4069 reading_idat=MagickTrue;
4070 }
4071
4072 if (memcmp(type,mng_JDAT,4) == 0)
4073 {
glennrp47b9dd52010-11-24 18:12:06 +00004074 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004075
4076 if (logging != MagickFalse)
4077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4078 " Copying JDAT chunk data to color_blob.");
4079
4080 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004081
cristy3ed852e2009-09-05 21:47:34 +00004082 if (length)
4083 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004084
cristy3ed852e2009-09-05 21:47:34 +00004085 continue;
4086 }
4087
4088 if (memcmp(type,mng_IDAT,4) == 0)
4089 {
4090 png_byte
4091 data[5];
4092
glennrp47b9dd52010-11-24 18:12:06 +00004093 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004094
4095 if (image_info->ping == MagickFalse)
4096 {
4097 if (logging != MagickFalse)
4098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4099 " Copying IDAT chunk data to alpha_blob.");
4100
cristybb503372010-05-27 20:51:26 +00004101 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004102 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004103 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004104 (void) WriteBlob(alpha_image,4,data);
4105 (void) WriteBlob(alpha_image,length,chunk);
4106 (void) WriteBlobMSBULong(alpha_image,
4107 crc32(crc32(0,data,4),chunk,(uInt) length));
4108 }
glennrp0fe50b42010-11-16 03:52:51 +00004109
cristy3ed852e2009-09-05 21:47:34 +00004110 if (length)
4111 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004112
cristy3ed852e2009-09-05 21:47:34 +00004113 continue;
4114 }
4115
4116 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4117 {
glennrp47b9dd52010-11-24 18:12:06 +00004118 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004119
4120 if (image_info->ping == MagickFalse)
4121 {
4122 if (logging != MagickFalse)
4123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4124 " Copying JDAA chunk data to alpha_blob.");
4125
4126 (void) WriteBlob(alpha_image,length,chunk);
4127 }
glennrp0fe50b42010-11-16 03:52:51 +00004128
cristy3ed852e2009-09-05 21:47:34 +00004129 if (length)
4130 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004131
cristy3ed852e2009-09-05 21:47:34 +00004132 continue;
4133 }
4134
4135 if (memcmp(type,mng_JSEP,4) == 0)
4136 {
4137 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004138
cristy3ed852e2009-09-05 21:47:34 +00004139 if (length)
4140 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004141
cristy3ed852e2009-09-05 21:47:34 +00004142 continue;
4143 }
4144
4145 if (memcmp(type,mng_bKGD,4) == 0)
4146 {
4147 if (length == 2)
4148 {
4149 image->background_color.red=ScaleCharToQuantum(p[1]);
4150 image->background_color.green=image->background_color.red;
4151 image->background_color.blue=image->background_color.red;
4152 }
glennrp0fe50b42010-11-16 03:52:51 +00004153
cristy3ed852e2009-09-05 21:47:34 +00004154 if (length == 6)
4155 {
4156 image->background_color.red=ScaleCharToQuantum(p[1]);
4157 image->background_color.green=ScaleCharToQuantum(p[3]);
4158 image->background_color.blue=ScaleCharToQuantum(p[5]);
4159 }
glennrp0fe50b42010-11-16 03:52:51 +00004160
cristy3ed852e2009-09-05 21:47:34 +00004161 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4162 continue;
4163 }
4164
4165 if (memcmp(type,mng_gAMA,4) == 0)
4166 {
4167 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004168 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004169
cristy3ed852e2009-09-05 21:47:34 +00004170 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4171 continue;
4172 }
4173
4174 if (memcmp(type,mng_cHRM,4) == 0)
4175 {
4176 if (length == 32)
4177 {
cristy8182b072010-05-30 20:10:53 +00004178 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4179 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4180 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4181 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4182 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4183 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4184 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4185 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004186 }
glennrp47b9dd52010-11-24 18:12:06 +00004187
cristy3ed852e2009-09-05 21:47:34 +00004188 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4189 continue;
4190 }
4191
4192 if (memcmp(type,mng_sRGB,4) == 0)
4193 {
4194 if (length == 1)
4195 {
glennrpe610a072010-08-05 17:08:46 +00004196 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004197 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004198 image->gamma=0.45455f;
4199 image->chromaticity.red_primary.x=0.6400f;
4200 image->chromaticity.red_primary.y=0.3300f;
4201 image->chromaticity.green_primary.x=0.3000f;
4202 image->chromaticity.green_primary.y=0.6000f;
4203 image->chromaticity.blue_primary.x=0.1500f;
4204 image->chromaticity.blue_primary.y=0.0600f;
4205 image->chromaticity.white_point.x=0.3127f;
4206 image->chromaticity.white_point.y=0.3290f;
4207 }
glennrp47b9dd52010-11-24 18:12:06 +00004208
cristy3ed852e2009-09-05 21:47:34 +00004209 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4210 continue;
4211 }
4212
4213 if (memcmp(type,mng_oFFs,4) == 0)
4214 {
4215 if (length > 8)
4216 {
glennrp5eae7602011-02-22 15:21:32 +00004217 image->page.x=(ssize_t) mng_get_long(p);
4218 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004219
cristy3ed852e2009-09-05 21:47:34 +00004220 if ((int) p[8] != 0)
4221 {
4222 image->page.x/=10000;
4223 image->page.y/=10000;
4224 }
4225 }
glennrp47b9dd52010-11-24 18:12:06 +00004226
cristy3ed852e2009-09-05 21:47:34 +00004227 if (length)
4228 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004229
cristy3ed852e2009-09-05 21:47:34 +00004230 continue;
4231 }
4232
4233 if (memcmp(type,mng_pHYs,4) == 0)
4234 {
4235 if (length > 8)
4236 {
cristy8182b072010-05-30 20:10:53 +00004237 image->x_resolution=(double) mng_get_long(p);
4238 image->y_resolution=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004239 if ((int) p[8] == PNG_RESOLUTION_METER)
4240 {
4241 image->units=PixelsPerCentimeterResolution;
4242 image->x_resolution=image->x_resolution/100.0f;
4243 image->y_resolution=image->y_resolution/100.0f;
4244 }
4245 }
glennrp0fe50b42010-11-16 03:52:51 +00004246
cristy3ed852e2009-09-05 21:47:34 +00004247 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4248 continue;
4249 }
4250
4251#if 0
4252 if (memcmp(type,mng_iCCP,4) == 0)
4253 {
glennrpfd05d622011-02-25 04:10:33 +00004254 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004255 if (length)
4256 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004257
cristy3ed852e2009-09-05 21:47:34 +00004258 continue;
4259 }
4260#endif
4261
4262 if (length)
4263 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4264
4265 if (memcmp(type,mng_IEND,4))
4266 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004267
cristy3ed852e2009-09-05 21:47:34 +00004268 break;
4269 }
4270
4271
4272 /* IEND found */
4273
4274 /*
4275 Finish up reading image data:
4276
4277 o read main image from color_blob.
4278
4279 o close color_blob.
4280
4281 o if (color_type has alpha)
4282 if alpha_encoding is PNG
4283 read secondary image from alpha_blob via ReadPNG
4284 if alpha_encoding is JPEG
4285 read secondary image from alpha_blob via ReadJPEG
4286
4287 o close alpha_blob.
4288
4289 o copy intensity of secondary image into
cristy4c08aed2011-07-01 19:47:50 +00004290 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004291
4292 o destroy the secondary image.
4293 */
4294
4295 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004296
cristy3ed852e2009-09-05 21:47:34 +00004297 if (logging != MagickFalse)
4298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4299 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004300
cristy3b6fd2e2011-05-20 12:53:50 +00004301 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004302 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004303
cristy3ed852e2009-09-05 21:47:34 +00004304 color_image_info->ping=MagickFalse; /* To do: avoid this */
4305 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004306
cristy3ed852e2009-09-05 21:47:34 +00004307 if (jng_image == (Image *) NULL)
4308 return((Image *) NULL);
4309
4310 (void) RelinquishUniqueFileResource(color_image->filename);
4311 color_image=DestroyImage(color_image);
4312 color_image_info=DestroyImageInfo(color_image_info);
4313
4314 if (jng_image == (Image *) NULL)
4315 return((Image *) NULL);
4316
4317 if (logging != MagickFalse)
4318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4319 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004320
cristy3ed852e2009-09-05 21:47:34 +00004321 image->rows=jng_height;
4322 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004323
cristybb503372010-05-27 20:51:26 +00004324 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004325 {
4326 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
4327 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004328 for (x=(ssize_t) image->columns; x != 0; x--)
4329 {
4330 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4331 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4332 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004333 q+=GetPixelChannels(image);
4334 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004335 }
glennrp47b9dd52010-11-24 18:12:06 +00004336
cristy3ed852e2009-09-05 21:47:34 +00004337 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4338 break;
4339 }
glennrp0fe50b42010-11-16 03:52:51 +00004340
cristy3ed852e2009-09-05 21:47:34 +00004341 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004342
cristy3ed852e2009-09-05 21:47:34 +00004343 if (image_info->ping == MagickFalse)
4344 {
4345 if (jng_color_type >= 12)
4346 {
4347 if (jng_alpha_compression_method == 0)
4348 {
4349 png_byte
4350 data[5];
4351 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4352 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004353 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004354 (void) WriteBlob(alpha_image,4,data);
4355 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4356 }
glennrp0fe50b42010-11-16 03:52:51 +00004357
cristy3ed852e2009-09-05 21:47:34 +00004358 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004359
cristy3ed852e2009-09-05 21:47:34 +00004360 if (logging != MagickFalse)
4361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00004362 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004363
cristy3b6fd2e2011-05-20 12:53:50 +00004364 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004365 "%s",alpha_image->filename);
4366
4367 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004368
cristy3ed852e2009-09-05 21:47:34 +00004369 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004370 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004371 {
4372 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristy4c08aed2011-07-01 19:47:50 +00004373 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +00004374 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004375
cristy3ed852e2009-09-05 21:47:34 +00004376 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00004377 for (x=(ssize_t) image->columns; x != 0; x--)
4378 {
4379 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004380 q+=GetPixelChannels(image);
4381 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004382 }
glennrp0fe50b42010-11-16 03:52:51 +00004383
cristy3ed852e2009-09-05 21:47:34 +00004384 else
cristy4c08aed2011-07-01 19:47:50 +00004385 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004386 {
cristy4c08aed2011-07-01 19:47:50 +00004387 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4388 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004389 image->matte=MagickTrue;
cristyed231572011-07-14 02:18:59 +00004390 q+=GetPixelChannels(image);
4391 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004392 }
glennrp0fe50b42010-11-16 03:52:51 +00004393
cristy3ed852e2009-09-05 21:47:34 +00004394 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4395 break;
4396 }
4397 (void) RelinquishUniqueFileResource(alpha_image->filename);
4398 alpha_image=DestroyImage(alpha_image);
4399 alpha_image_info=DestroyImageInfo(alpha_image_info);
4400 if (jng_image != (Image *) NULL)
4401 jng_image=DestroyImage(jng_image);
4402 }
4403 }
4404
glennrp47b9dd52010-11-24 18:12:06 +00004405 /* Read the JNG image. */
4406
cristy3ed852e2009-09-05 21:47:34 +00004407 if (mng_info->mng_type == 0)
4408 {
4409 mng_info->mng_width=jng_width;
4410 mng_info->mng_height=jng_height;
4411 }
glennrp0fe50b42010-11-16 03:52:51 +00004412
cristy3ed852e2009-09-05 21:47:34 +00004413 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004414 {
4415 image->page.width=jng_width;
4416 image->page.height=jng_height;
4417 }
4418
cristy3ed852e2009-09-05 21:47:34 +00004419 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004420 {
4421 image->page.x=mng_info->x_off[mng_info->object_id];
4422 image->page.y=mng_info->y_off[mng_info->object_id];
4423 }
4424
cristy3ed852e2009-09-05 21:47:34 +00004425 else
glennrp0fe50b42010-11-16 03:52:51 +00004426 {
4427 image->page.y=mng_info->y_off[mng_info->object_id];
4428 }
4429
cristy3ed852e2009-09-05 21:47:34 +00004430 mng_info->image_found++;
4431 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4432 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004433
cristy3ed852e2009-09-05 21:47:34 +00004434 if (logging != MagickFalse)
4435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4436 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004437
cristy3ed852e2009-09-05 21:47:34 +00004438 return(image);
4439}
4440
4441/*
4442%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4443% %
4444% %
4445% %
4446% R e a d J N G I m a g e %
4447% %
4448% %
4449% %
4450%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4451%
4452% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4453% (including the 8-byte signature) and returns it. It allocates the memory
4454% necessary for the new Image structure and returns a pointer to the new
4455% image.
4456%
4457% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4458%
4459% The format of the ReadJNGImage method is:
4460%
4461% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4462% *exception)
4463%
4464% A description of each parameter follows:
4465%
4466% o image_info: the image info.
4467%
4468% o exception: return any errors or warnings in this structure.
4469%
4470*/
4471
4472static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4473{
4474 Image
4475 *image,
4476 *previous;
4477
4478 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004479 have_mng_structure,
4480 logging,
cristy3ed852e2009-09-05 21:47:34 +00004481 status;
4482
4483 MngInfo
4484 *mng_info;
4485
4486 char
4487 magic_number[MaxTextExtent];
4488
cristy3ed852e2009-09-05 21:47:34 +00004489 size_t
4490 count;
4491
4492 /*
4493 Open image file.
4494 */
4495 assert(image_info != (const ImageInfo *) NULL);
4496 assert(image_info->signature == MagickSignature);
4497 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4498 assert(exception != (ExceptionInfo *) NULL);
4499 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004500 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy9950d572011-10-01 18:22:35 +00004501 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004502 mng_info=(MngInfo *) NULL;
4503 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004504
cristy3ed852e2009-09-05 21:47:34 +00004505 if (status == MagickFalse)
4506 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004507
cristy3ed852e2009-09-05 21:47:34 +00004508 if (LocaleCompare(image_info->magick,"JNG") != 0)
4509 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004510
glennrp47b9dd52010-11-24 18:12:06 +00004511 /* Verify JNG signature. */
4512
cristy3ed852e2009-09-05 21:47:34 +00004513 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004514
glennrp3b8763e2011-02-21 12:08:18 +00004515 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004516 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004517
glennrp47b9dd52010-11-24 18:12:06 +00004518 /* Allocate a MngInfo structure. */
4519
cristy3ed852e2009-09-05 21:47:34 +00004520 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004521 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004522
cristy3ed852e2009-09-05 21:47:34 +00004523 if (mng_info == (MngInfo *) NULL)
4524 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004525
glennrp47b9dd52010-11-24 18:12:06 +00004526 /* Initialize members of the MngInfo structure. */
4527
cristy3ed852e2009-09-05 21:47:34 +00004528 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4529 have_mng_structure=MagickTrue;
4530
4531 mng_info->image=image;
4532 previous=image;
4533 image=ReadOneJNGImage(mng_info,image_info,exception);
4534 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004535
cristy3ed852e2009-09-05 21:47:34 +00004536 if (image == (Image *) NULL)
4537 {
4538 if (IsImageObject(previous) != MagickFalse)
4539 {
4540 (void) CloseBlob(previous);
4541 (void) DestroyImageList(previous);
4542 }
glennrp0fe50b42010-11-16 03:52:51 +00004543
cristy3ed852e2009-09-05 21:47:34 +00004544 if (logging != MagickFalse)
4545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4546 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004547
cristy3ed852e2009-09-05 21:47:34 +00004548 return((Image *) NULL);
4549 }
4550 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004551
cristy3ed852e2009-09-05 21:47:34 +00004552 if (image->columns == 0 || image->rows == 0)
4553 {
4554 if (logging != MagickFalse)
4555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4556 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004557
cristy3ed852e2009-09-05 21:47:34 +00004558 ThrowReaderException(CorruptImageError,"CorruptImage");
4559 }
glennrp0fe50b42010-11-16 03:52:51 +00004560
cristy3ed852e2009-09-05 21:47:34 +00004561 if (logging != MagickFalse)
4562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004563
cristy3ed852e2009-09-05 21:47:34 +00004564 return(image);
4565}
4566#endif
4567
4568static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4569{
4570 char
4571 page_geometry[MaxTextExtent];
4572
4573 Image
4574 *image,
4575 *previous;
4576
cristy4383ec82011-01-05 15:42:32 +00004577 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004578 logging,
4579 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004580
cristy3ed852e2009-09-05 21:47:34 +00004581 volatile int
4582 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004583 object_id,
4584 term_chunk_found,
4585 skip_to_iend;
4586
cristybb503372010-05-27 20:51:26 +00004587 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004588 image_count=0;
4589
4590 MagickBooleanType
4591 status;
4592
4593 MagickOffsetType
4594 offset;
4595
4596 MngInfo
4597 *mng_info;
4598
4599 MngBox
4600 default_fb,
4601 fb,
4602 previous_fb;
4603
4604#if defined(MNG_INSERT_LAYERS)
cristy101ab702011-10-13 13:06:32 +00004605 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004606 mng_background_color;
4607#endif
4608
4609 register unsigned char
4610 *p;
4611
cristybb503372010-05-27 20:51:26 +00004612 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004613 i;
4614
4615 size_t
4616 count;
4617
cristybb503372010-05-27 20:51:26 +00004618 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004619 loop_level;
4620
4621 volatile short
4622 skipping_loop;
4623
4624#if defined(MNG_INSERT_LAYERS)
4625 unsigned int
4626 mandatory_back=0;
4627#endif
4628
4629 volatile unsigned int
4630#ifdef MNG_OBJECT_BUFFERS
4631 mng_background_object=0,
4632#endif
4633 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4634
cristybb503372010-05-27 20:51:26 +00004635 size_t
cristy3ed852e2009-09-05 21:47:34 +00004636 default_frame_timeout,
4637 frame_timeout,
4638#if defined(MNG_INSERT_LAYERS)
4639 image_height,
4640 image_width,
4641#endif
4642 length;
4643
glennrp38ea0832010-06-02 18:50:28 +00004644 /* These delays are all measured in image ticks_per_second,
4645 * not in MNG ticks_per_second
4646 */
cristybb503372010-05-27 20:51:26 +00004647 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004648 default_frame_delay,
4649 final_delay,
4650 final_image_delay,
4651 frame_delay,
4652#if defined(MNG_INSERT_LAYERS)
4653 insert_layers,
4654#endif
4655 mng_iterations=1,
4656 simplicity=0,
4657 subframe_height=0,
4658 subframe_width=0;
4659
4660 previous_fb.top=0;
4661 previous_fb.bottom=0;
4662 previous_fb.left=0;
4663 previous_fb.right=0;
4664 default_fb.top=0;
4665 default_fb.bottom=0;
4666 default_fb.left=0;
4667 default_fb.right=0;
4668
glennrp47b9dd52010-11-24 18:12:06 +00004669 /* Open image file. */
4670
cristy3ed852e2009-09-05 21:47:34 +00004671 assert(image_info != (const ImageInfo *) NULL);
4672 assert(image_info->signature == MagickSignature);
4673 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4674 assert(exception != (ExceptionInfo *) NULL);
4675 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004676 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy9950d572011-10-01 18:22:35 +00004677 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004678 mng_info=(MngInfo *) NULL;
4679 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004680
cristy3ed852e2009-09-05 21:47:34 +00004681 if (status == MagickFalse)
4682 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004683
cristy3ed852e2009-09-05 21:47:34 +00004684 first_mng_object=MagickFalse;
4685 skipping_loop=(-1);
4686 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004687
4688 /* Allocate a MngInfo structure. */
4689
cristy73bd4a52010-10-05 11:24:23 +00004690 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004691
cristy3ed852e2009-09-05 21:47:34 +00004692 if (mng_info == (MngInfo *) NULL)
4693 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004694
glennrp47b9dd52010-11-24 18:12:06 +00004695 /* Initialize members of the MngInfo structure. */
4696
cristy3ed852e2009-09-05 21:47:34 +00004697 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4698 mng_info->image=image;
4699 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004700
4701 if (LocaleCompare(image_info->magick,"MNG") == 0)
4702 {
4703 char
4704 magic_number[MaxTextExtent];
4705
glennrp47b9dd52010-11-24 18:12:06 +00004706 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004707 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4708 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4709 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004710
4711 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004712 for (i=0; i < MNG_MAX_OBJECTS; i++)
4713 {
cristybb503372010-05-27 20:51:26 +00004714 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4715 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004716 }
4717 mng_info->exists[0]=MagickTrue;
4718 }
glennrp47b9dd52010-11-24 18:12:06 +00004719
cristy3ed852e2009-09-05 21:47:34 +00004720 first_mng_object=MagickTrue;
4721 mng_type=0;
4722#if defined(MNG_INSERT_LAYERS)
4723 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4724#endif
4725 default_frame_delay=0;
4726 default_frame_timeout=0;
4727 frame_delay=0;
4728 final_delay=1;
4729 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4730 object_id=0;
4731 skip_to_iend=MagickFalse;
4732 term_chunk_found=MagickFalse;
4733 mng_info->framing_mode=1;
4734#if defined(MNG_INSERT_LAYERS)
4735 mandatory_back=MagickFalse;
4736#endif
4737#if defined(MNG_INSERT_LAYERS)
4738 mng_background_color=image->background_color;
4739#endif
4740 default_fb=mng_info->frame;
4741 previous_fb=mng_info->frame;
4742 do
4743 {
4744 char
4745 type[MaxTextExtent];
4746
4747 if (LocaleCompare(image_info->magick,"MNG") == 0)
4748 {
4749 unsigned char
4750 *chunk;
4751
4752 /*
4753 Read a new chunk.
4754 */
4755 type[0]='\0';
4756 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4757 length=ReadBlobMSBLong(image);
4758 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4759
4760 if (logging != MagickFalse)
4761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004762 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4763 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004764
4765 if (length > PNG_UINT_31_MAX)
4766 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004767
cristy3ed852e2009-09-05 21:47:34 +00004768 if (count == 0)
4769 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004770
cristy3ed852e2009-09-05 21:47:34 +00004771 p=NULL;
4772 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004773
cristy3ed852e2009-09-05 21:47:34 +00004774 if (length)
4775 {
4776 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004777
cristy3ed852e2009-09-05 21:47:34 +00004778 if (chunk == (unsigned char *) NULL)
4779 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004780
cristybb503372010-05-27 20:51:26 +00004781 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004782 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004783
cristy3ed852e2009-09-05 21:47:34 +00004784 p=chunk;
4785 }
glennrp0fe50b42010-11-16 03:52:51 +00004786
cristy3ed852e2009-09-05 21:47:34 +00004787 (void) ReadBlobMSBLong(image); /* read crc word */
4788
4789#if !defined(JNG_SUPPORTED)
4790 if (memcmp(type,mng_JHDR,4) == 0)
4791 {
4792 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004793
cristy3ed852e2009-09-05 21:47:34 +00004794 if (mng_info->jhdr_warning == 0)
4795 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4796 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004797
cristy3ed852e2009-09-05 21:47:34 +00004798 mng_info->jhdr_warning++;
4799 }
4800#endif
4801 if (memcmp(type,mng_DHDR,4) == 0)
4802 {
4803 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004804
cristy3ed852e2009-09-05 21:47:34 +00004805 if (mng_info->dhdr_warning == 0)
4806 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4807 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004808
cristy3ed852e2009-09-05 21:47:34 +00004809 mng_info->dhdr_warning++;
4810 }
4811 if (memcmp(type,mng_MEND,4) == 0)
4812 break;
glennrp47b9dd52010-11-24 18:12:06 +00004813
cristy3ed852e2009-09-05 21:47:34 +00004814 if (skip_to_iend)
4815 {
4816 if (memcmp(type,mng_IEND,4) == 0)
4817 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004818
cristy3ed852e2009-09-05 21:47:34 +00004819 if (length)
4820 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004821
cristy3ed852e2009-09-05 21:47:34 +00004822 if (logging != MagickFalse)
4823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4824 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004825
cristy3ed852e2009-09-05 21:47:34 +00004826 continue;
4827 }
glennrp0fe50b42010-11-16 03:52:51 +00004828
cristy3ed852e2009-09-05 21:47:34 +00004829 if (memcmp(type,mng_MHDR,4) == 0)
4830 {
cristybb503372010-05-27 20:51:26 +00004831 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004832 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004833
cristybb503372010-05-27 20:51:26 +00004834 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004835 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004836
cristy3ed852e2009-09-05 21:47:34 +00004837 if (logging != MagickFalse)
4838 {
4839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004840 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004842 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004843 }
glennrp0fe50b42010-11-16 03:52:51 +00004844
cristy3ed852e2009-09-05 21:47:34 +00004845 p+=8;
cristy8182b072010-05-30 20:10:53 +00004846 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004847
cristy3ed852e2009-09-05 21:47:34 +00004848 if (mng_info->ticks_per_second == 0)
4849 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004850
cristy3ed852e2009-09-05 21:47:34 +00004851 else
4852 default_frame_delay=1UL*image->ticks_per_second/
4853 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004854
cristy3ed852e2009-09-05 21:47:34 +00004855 frame_delay=default_frame_delay;
4856 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004857
cristy3ed852e2009-09-05 21:47:34 +00004858 if (length > 16)
4859 {
4860 p+=16;
cristy8182b072010-05-30 20:10:53 +00004861 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004862 }
glennrp0fe50b42010-11-16 03:52:51 +00004863
cristy3ed852e2009-09-05 21:47:34 +00004864 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004865
cristy3ed852e2009-09-05 21:47:34 +00004866 if ((simplicity != 0) && ((simplicity | 11) == 11))
4867 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004868
cristy3ed852e2009-09-05 21:47:34 +00004869 if ((simplicity != 0) && ((simplicity | 9) == 9))
4870 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004871
cristy3ed852e2009-09-05 21:47:34 +00004872#if defined(MNG_INSERT_LAYERS)
4873 if (mng_type != 3)
4874 insert_layers=MagickTrue;
4875#endif
cristy4c08aed2011-07-01 19:47:50 +00004876 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004877 {
glennrp47b9dd52010-11-24 18:12:06 +00004878 /* Allocate next image structure. */
cristy9950d572011-10-01 18:22:35 +00004879 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004880
cristy3ed852e2009-09-05 21:47:34 +00004881 if (GetNextImageInList(image) == (Image *) NULL)
4882 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004883
cristy3ed852e2009-09-05 21:47:34 +00004884 image=SyncNextImageInList(image);
4885 mng_info->image=image;
4886 }
4887
4888 if ((mng_info->mng_width > 65535L) ||
4889 (mng_info->mng_height > 65535L))
4890 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004891
cristy3b6fd2e2011-05-20 12:53:50 +00004892 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004893 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004894 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004895
cristy3ed852e2009-09-05 21:47:34 +00004896 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004897 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004898 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004899 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004900 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004901
cristy3ed852e2009-09-05 21:47:34 +00004902 for (i=0; i < MNG_MAX_OBJECTS; i++)
4903 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004904
cristy3ed852e2009-09-05 21:47:34 +00004905 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4906 continue;
4907 }
4908
4909 if (memcmp(type,mng_TERM,4) == 0)
4910 {
4911 int
4912 repeat=0;
4913
4914
4915 if (length)
4916 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004917
cristy3ed852e2009-09-05 21:47:34 +00004918 if (repeat == 3)
4919 {
cristy8182b072010-05-30 20:10:53 +00004920 final_delay=(png_uint_32) mng_get_long(&p[2]);
4921 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004922
cristy3ed852e2009-09-05 21:47:34 +00004923 if (mng_iterations == PNG_UINT_31_MAX)
4924 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004925
cristy3ed852e2009-09-05 21:47:34 +00004926 image->iterations=mng_iterations;
4927 term_chunk_found=MagickTrue;
4928 }
glennrp0fe50b42010-11-16 03:52:51 +00004929
cristy3ed852e2009-09-05 21:47:34 +00004930 if (logging != MagickFalse)
4931 {
4932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4933 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004934
cristy3ed852e2009-09-05 21:47:34 +00004935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004936 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004937
cristy3ed852e2009-09-05 21:47:34 +00004938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004939 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004940 }
glennrp0fe50b42010-11-16 03:52:51 +00004941
cristy3ed852e2009-09-05 21:47:34 +00004942 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4943 continue;
4944 }
4945 if (memcmp(type,mng_DEFI,4) == 0)
4946 {
4947 if (mng_type == 3)
4948 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4949 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4950 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004951
cristy3ed852e2009-09-05 21:47:34 +00004952 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004953
cristy3ed852e2009-09-05 21:47:34 +00004954 if (mng_type == 2 && object_id != 0)
4955 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4956 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4957 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004958
cristy3ed852e2009-09-05 21:47:34 +00004959 if (object_id > MNG_MAX_OBJECTS)
4960 {
4961 /*
4962 Instead ofsuing a warning we should allocate a larger
4963 MngInfo structure and continue.
4964 */
4965 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4966 CoderError,"object id too large","`%s'",image->filename);
4967 object_id=MNG_MAX_OBJECTS;
4968 }
glennrp0fe50b42010-11-16 03:52:51 +00004969
cristy3ed852e2009-09-05 21:47:34 +00004970 if (mng_info->exists[object_id])
4971 if (mng_info->frozen[object_id])
4972 {
4973 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4974 (void) ThrowMagickException(&image->exception,
4975 GetMagickModule(),CoderError,
4976 "DEFI cannot redefine a frozen MNG object","`%s'",
4977 image->filename);
4978 continue;
4979 }
glennrp0fe50b42010-11-16 03:52:51 +00004980
cristy3ed852e2009-09-05 21:47:34 +00004981 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004982
cristy3ed852e2009-09-05 21:47:34 +00004983 if (length > 2)
4984 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00004985
cristy3ed852e2009-09-05 21:47:34 +00004986 /*
4987 Extract object offset info.
4988 */
4989 if (length > 11)
4990 {
glennrp0fe50b42010-11-16 03:52:51 +00004991 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4992 (p[5] << 16) | (p[6] << 8) | p[7]);
4993
4994 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4995 (p[9] << 16) | (p[10] << 8) | p[11]);
4996
cristy3ed852e2009-09-05 21:47:34 +00004997 if (logging != MagickFalse)
4998 {
4999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005000 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005001 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005002
cristy3ed852e2009-09-05 21:47:34 +00005003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005004 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005005 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005006 }
5007 }
glennrp0fe50b42010-11-16 03:52:51 +00005008
cristy3ed852e2009-09-05 21:47:34 +00005009 /*
5010 Extract object clipping info.
5011 */
5012 if (length > 27)
5013 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5014 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005015
cristy3ed852e2009-09-05 21:47:34 +00005016 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5017 continue;
5018 }
5019 if (memcmp(type,mng_bKGD,4) == 0)
5020 {
5021 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005022
cristy3ed852e2009-09-05 21:47:34 +00005023 if (length > 5)
5024 {
5025 mng_info->mng_global_bkgd.red=
5026 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005027
cristy3ed852e2009-09-05 21:47:34 +00005028 mng_info->mng_global_bkgd.green=
5029 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005030
cristy3ed852e2009-09-05 21:47:34 +00005031 mng_info->mng_global_bkgd.blue=
5032 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005033
cristy3ed852e2009-09-05 21:47:34 +00005034 mng_info->have_global_bkgd=MagickTrue;
5035 }
glennrp0fe50b42010-11-16 03:52:51 +00005036
cristy3ed852e2009-09-05 21:47:34 +00005037 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5038 continue;
5039 }
5040 if (memcmp(type,mng_BACK,4) == 0)
5041 {
5042#if defined(MNG_INSERT_LAYERS)
5043 if (length > 6)
5044 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005045
cristy3ed852e2009-09-05 21:47:34 +00005046 else
5047 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005048
cristy3ed852e2009-09-05 21:47:34 +00005049 if (mandatory_back && length > 5)
5050 {
5051 mng_background_color.red=
5052 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005053
cristy3ed852e2009-09-05 21:47:34 +00005054 mng_background_color.green=
5055 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005056
cristy3ed852e2009-09-05 21:47:34 +00005057 mng_background_color.blue=
5058 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005059
cristy4c08aed2011-07-01 19:47:50 +00005060 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005061 }
glennrp0fe50b42010-11-16 03:52:51 +00005062
cristy3ed852e2009-09-05 21:47:34 +00005063#ifdef MNG_OBJECT_BUFFERS
5064 if (length > 8)
5065 mng_background_object=(p[7] << 8) | p[8];
5066#endif
5067#endif
5068 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5069 continue;
5070 }
glennrp47b9dd52010-11-24 18:12:06 +00005071
cristy3ed852e2009-09-05 21:47:34 +00005072 if (memcmp(type,mng_PLTE,4) == 0)
5073 {
glennrp47b9dd52010-11-24 18:12:06 +00005074 /* Read global PLTE. */
5075
cristy3ed852e2009-09-05 21:47:34 +00005076 if (length && (length < 769))
5077 {
5078 if (mng_info->global_plte == (png_colorp) NULL)
5079 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5080 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005081
cristybb503372010-05-27 20:51:26 +00005082 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005083 {
5084 mng_info->global_plte[i].red=p[3*i];
5085 mng_info->global_plte[i].green=p[3*i+1];
5086 mng_info->global_plte[i].blue=p[3*i+2];
5087 }
glennrp0fe50b42010-11-16 03:52:51 +00005088
cristy35ef8242010-06-03 16:24:13 +00005089 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005090 }
5091#ifdef MNG_LOOSE
5092 for ( ; i < 256; i++)
5093 {
5094 mng_info->global_plte[i].red=i;
5095 mng_info->global_plte[i].green=i;
5096 mng_info->global_plte[i].blue=i;
5097 }
glennrp0fe50b42010-11-16 03:52:51 +00005098
cristy3ed852e2009-09-05 21:47:34 +00005099 if (length)
5100 mng_info->global_plte_length=256;
5101#endif
5102 else
5103 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005104
cristy3ed852e2009-09-05 21:47:34 +00005105 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5106 continue;
5107 }
glennrp47b9dd52010-11-24 18:12:06 +00005108
cristy3ed852e2009-09-05 21:47:34 +00005109 if (memcmp(type,mng_tRNS,4) == 0)
5110 {
5111 /* read global tRNS */
5112
5113 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005114 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005115 mng_info->global_trns[i]=p[i];
5116
5117#ifdef MNG_LOOSE
5118 for ( ; i < 256; i++)
5119 mng_info->global_trns[i]=255;
5120#endif
cristy12560f32010-06-03 16:51:08 +00005121 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005122 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5123 continue;
5124 }
5125 if (memcmp(type,mng_gAMA,4) == 0)
5126 {
5127 if (length == 4)
5128 {
cristybb503372010-05-27 20:51:26 +00005129 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005130 igamma;
5131
cristy8182b072010-05-30 20:10:53 +00005132 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005133 mng_info->global_gamma=((float) igamma)*0.00001;
5134 mng_info->have_global_gama=MagickTrue;
5135 }
glennrp0fe50b42010-11-16 03:52:51 +00005136
cristy3ed852e2009-09-05 21:47:34 +00005137 else
5138 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005139
cristy3ed852e2009-09-05 21:47:34 +00005140 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5141 continue;
5142 }
5143
5144 if (memcmp(type,mng_cHRM,4) == 0)
5145 {
glennrp47b9dd52010-11-24 18:12:06 +00005146 /* Read global cHRM */
5147
cristy3ed852e2009-09-05 21:47:34 +00005148 if (length == 32)
5149 {
cristy8182b072010-05-30 20:10:53 +00005150 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5151 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5152 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005153 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005154 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005155 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005156 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005157 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005158 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005159 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005160 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005161 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005162 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005163 mng_info->have_global_chrm=MagickTrue;
5164 }
5165 else
5166 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005167
cristy3ed852e2009-09-05 21:47:34 +00005168 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5169 continue;
5170 }
glennrp47b9dd52010-11-24 18:12:06 +00005171
cristy3ed852e2009-09-05 21:47:34 +00005172 if (memcmp(type,mng_sRGB,4) == 0)
5173 {
5174 /*
5175 Read global sRGB.
5176 */
5177 if (length)
5178 {
glennrpe610a072010-08-05 17:08:46 +00005179 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005180 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005181 mng_info->have_global_srgb=MagickTrue;
5182 }
5183 else
5184 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005185
cristy3ed852e2009-09-05 21:47:34 +00005186 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5187 continue;
5188 }
glennrp47b9dd52010-11-24 18:12:06 +00005189
cristy3ed852e2009-09-05 21:47:34 +00005190 if (memcmp(type,mng_iCCP,4) == 0)
5191 {
glennrpfd05d622011-02-25 04:10:33 +00005192 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005193
5194 /*
5195 Read global iCCP.
5196 */
5197 if (length)
5198 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005199
cristy3ed852e2009-09-05 21:47:34 +00005200 continue;
5201 }
glennrp47b9dd52010-11-24 18:12:06 +00005202
cristy3ed852e2009-09-05 21:47:34 +00005203 if (memcmp(type,mng_FRAM,4) == 0)
5204 {
5205 if (mng_type == 3)
5206 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5207 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5208 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005209
cristy3ed852e2009-09-05 21:47:34 +00005210 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5211 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005212
cristy3ed852e2009-09-05 21:47:34 +00005213 frame_delay=default_frame_delay;
5214 frame_timeout=default_frame_timeout;
5215 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005216
cristy3ed852e2009-09-05 21:47:34 +00005217 if (length)
5218 if (p[0])
5219 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005220
cristy3ed852e2009-09-05 21:47:34 +00005221 if (logging != MagickFalse)
5222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5223 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005224
cristy3ed852e2009-09-05 21:47:34 +00005225 if (length > 6)
5226 {
glennrp47b9dd52010-11-24 18:12:06 +00005227 /* Note the delay and frame clipping boundaries. */
5228
cristy3ed852e2009-09-05 21:47:34 +00005229 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005230
cristybb503372010-05-27 20:51:26 +00005231 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005232 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005233
cristy3ed852e2009-09-05 21:47:34 +00005234 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005235
cristybb503372010-05-27 20:51:26 +00005236 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005237 {
5238 int
5239 change_delay,
5240 change_timeout,
5241 change_clipping;
5242
5243 change_delay=(*p++);
5244 change_timeout=(*p++);
5245 change_clipping=(*p++);
5246 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005247
cristy3ed852e2009-09-05 21:47:34 +00005248 if (change_delay)
5249 {
cristy8182b072010-05-30 20:10:53 +00005250 frame_delay=1UL*image->ticks_per_second*
5251 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005252
cristy8182b072010-05-30 20:10:53 +00005253 if (mng_info->ticks_per_second != 0)
5254 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005255
glennrpbb010dd2010-06-01 13:07:15 +00005256 else
5257 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005258
cristy3ed852e2009-09-05 21:47:34 +00005259 if (change_delay == 2)
5260 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005261
cristy3ed852e2009-09-05 21:47:34 +00005262 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005263
cristy3ed852e2009-09-05 21:47:34 +00005264 if (logging != MagickFalse)
5265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005266 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005267 }
glennrp47b9dd52010-11-24 18:12:06 +00005268
cristy3ed852e2009-09-05 21:47:34 +00005269 if (change_timeout)
5270 {
glennrpbb010dd2010-06-01 13:07:15 +00005271 frame_timeout=1UL*image->ticks_per_second*
5272 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005273
glennrpbb010dd2010-06-01 13:07:15 +00005274 if (mng_info->ticks_per_second != 0)
5275 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005276
glennrpbb010dd2010-06-01 13:07:15 +00005277 else
5278 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005279
cristy3ed852e2009-09-05 21:47:34 +00005280 if (change_delay == 2)
5281 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005282
cristy3ed852e2009-09-05 21:47:34 +00005283 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005284
cristy3ed852e2009-09-05 21:47:34 +00005285 if (logging != MagickFalse)
5286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005287 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005288 }
glennrp47b9dd52010-11-24 18:12:06 +00005289
cristy3ed852e2009-09-05 21:47:34 +00005290 if (change_clipping)
5291 {
5292 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5293 p+=17;
5294 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005295
cristy3ed852e2009-09-05 21:47:34 +00005296 if (logging != MagickFalse)
5297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005298 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005299 (double) fb.left,(double) fb.right,(double) fb.top,
5300 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005301
cristy3ed852e2009-09-05 21:47:34 +00005302 if (change_clipping == 2)
5303 default_fb=fb;
5304 }
5305 }
5306 }
5307 mng_info->clip=fb;
5308 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005309
cristybb503372010-05-27 20:51:26 +00005310 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005311 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005312
cristybb503372010-05-27 20:51:26 +00005313 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005314 -mng_info->clip.top);
5315 /*
5316 Insert a background layer behind the frame if framing_mode is 4.
5317 */
5318#if defined(MNG_INSERT_LAYERS)
5319 if (logging != MagickFalse)
5320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005321 " subframe_width=%.20g, subframe_height=%.20g",(double)
5322 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005323
cristy3ed852e2009-09-05 21:47:34 +00005324 if (insert_layers && (mng_info->framing_mode == 4) &&
5325 (subframe_width) && (subframe_height))
5326 {
glennrp47b9dd52010-11-24 18:12:06 +00005327 /* Allocate next image structure. */
cristy4c08aed2011-07-01 19:47:50 +00005328 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005329 {
cristy9950d572011-10-01 18:22:35 +00005330 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005331
cristy3ed852e2009-09-05 21:47:34 +00005332 if (GetNextImageInList(image) == (Image *) NULL)
5333 {
5334 image=DestroyImageList(image);
5335 MngInfoFreeStruct(mng_info,&have_mng_structure);
5336 return((Image *) NULL);
5337 }
glennrp47b9dd52010-11-24 18:12:06 +00005338
cristy3ed852e2009-09-05 21:47:34 +00005339 image=SyncNextImageInList(image);
5340 }
glennrp0fe50b42010-11-16 03:52:51 +00005341
cristy3ed852e2009-09-05 21:47:34 +00005342 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005343
cristy3ed852e2009-09-05 21:47:34 +00005344 if (term_chunk_found)
5345 {
5346 image->start_loop=MagickTrue;
5347 image->iterations=mng_iterations;
5348 term_chunk_found=MagickFalse;
5349 }
glennrp0fe50b42010-11-16 03:52:51 +00005350
cristy3ed852e2009-09-05 21:47:34 +00005351 else
5352 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005353
cristy3ed852e2009-09-05 21:47:34 +00005354 image->columns=subframe_width;
5355 image->rows=subframe_height;
5356 image->page.width=subframe_width;
5357 image->page.height=subframe_height;
5358 image->page.x=mng_info->clip.left;
5359 image->page.y=mng_info->clip.top;
5360 image->background_color=mng_background_color;
5361 image->matte=MagickFalse;
5362 image->delay=0;
5363 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005364
cristy3ed852e2009-09-05 21:47:34 +00005365 if (logging != MagickFalse)
5366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005367 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005368 (double) mng_info->clip.left,(double) mng_info->clip.right,
5369 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005370 }
5371#endif
5372 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5373 continue;
5374 }
5375 if (memcmp(type,mng_CLIP,4) == 0)
5376 {
5377 unsigned int
5378 first_object,
5379 last_object;
5380
5381 /*
5382 Read CLIP.
5383 */
5384 first_object=(p[0] << 8) | p[1];
5385 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005386
cristy3ed852e2009-09-05 21:47:34 +00005387 for (i=(int) first_object; i <= (int) last_object; i++)
5388 {
5389 if (mng_info->exists[i] && !mng_info->frozen[i])
5390 {
5391 MngBox
5392 box;
5393
5394 box=mng_info->object_clip[i];
5395 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5396 }
5397 }
glennrp47b9dd52010-11-24 18:12:06 +00005398
cristy3ed852e2009-09-05 21:47:34 +00005399 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5400 continue;
5401 }
5402 if (memcmp(type,mng_SAVE,4) == 0)
5403 {
5404 for (i=1; i < MNG_MAX_OBJECTS; i++)
5405 if (mng_info->exists[i])
5406 {
5407 mng_info->frozen[i]=MagickTrue;
5408#ifdef MNG_OBJECT_BUFFERS
5409 if (mng_info->ob[i] != (MngBuffer *) NULL)
5410 mng_info->ob[i]->frozen=MagickTrue;
5411#endif
5412 }
glennrp0fe50b42010-11-16 03:52:51 +00005413
cristy3ed852e2009-09-05 21:47:34 +00005414 if (length)
5415 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005416
cristy3ed852e2009-09-05 21:47:34 +00005417 continue;
5418 }
5419
5420 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5421 {
glennrp47b9dd52010-11-24 18:12:06 +00005422 /* Read DISC or SEEK. */
5423
cristy3ed852e2009-09-05 21:47:34 +00005424 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5425 {
5426 for (i=1; i < MNG_MAX_OBJECTS; i++)
5427 MngInfoDiscardObject(mng_info,i);
5428 }
glennrp0fe50b42010-11-16 03:52:51 +00005429
cristy3ed852e2009-09-05 21:47:34 +00005430 else
5431 {
cristybb503372010-05-27 20:51:26 +00005432 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005433 j;
5434
cristybb503372010-05-27 20:51:26 +00005435 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005436 {
5437 i=p[j] << 8 | p[j+1];
5438 MngInfoDiscardObject(mng_info,i);
5439 }
5440 }
glennrp0fe50b42010-11-16 03:52:51 +00005441
cristy3ed852e2009-09-05 21:47:34 +00005442 if (length)
5443 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005444
cristy3ed852e2009-09-05 21:47:34 +00005445 continue;
5446 }
glennrp47b9dd52010-11-24 18:12:06 +00005447
cristy3ed852e2009-09-05 21:47:34 +00005448 if (memcmp(type,mng_MOVE,4) == 0)
5449 {
cristybb503372010-05-27 20:51:26 +00005450 size_t
cristy3ed852e2009-09-05 21:47:34 +00005451 first_object,
5452 last_object;
5453
glennrp47b9dd52010-11-24 18:12:06 +00005454 /* read MOVE */
5455
cristy3ed852e2009-09-05 21:47:34 +00005456 first_object=(p[0] << 8) | p[1];
5457 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005458 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005459 {
5460 if (mng_info->exists[i] && !mng_info->frozen[i])
5461 {
5462 MngPair
5463 new_pair;
5464
5465 MngPair
5466 old_pair;
5467
5468 old_pair.a=mng_info->x_off[i];
5469 old_pair.b=mng_info->y_off[i];
5470 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5471 mng_info->x_off[i]=new_pair.a;
5472 mng_info->y_off[i]=new_pair.b;
5473 }
5474 }
glennrp47b9dd52010-11-24 18:12:06 +00005475
cristy3ed852e2009-09-05 21:47:34 +00005476 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5477 continue;
5478 }
5479
5480 if (memcmp(type,mng_LOOP,4) == 0)
5481 {
cristybb503372010-05-27 20:51:26 +00005482 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005483 loop_level=chunk[0];
5484 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005485
5486 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005487 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005488
cristy3ed852e2009-09-05 21:47:34 +00005489 if (logging != MagickFalse)
5490 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005491 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5492 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005493
cristy3ed852e2009-09-05 21:47:34 +00005494 if (loop_iters == 0)
5495 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005496
cristy3ed852e2009-09-05 21:47:34 +00005497 else
5498 {
5499 mng_info->loop_jump[loop_level]=TellBlob(image);
5500 mng_info->loop_count[loop_level]=loop_iters;
5501 }
glennrp0fe50b42010-11-16 03:52:51 +00005502
cristy3ed852e2009-09-05 21:47:34 +00005503 mng_info->loop_iteration[loop_level]=0;
5504 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5505 continue;
5506 }
glennrp47b9dd52010-11-24 18:12:06 +00005507
cristy3ed852e2009-09-05 21:47:34 +00005508 if (memcmp(type,mng_ENDL,4) == 0)
5509 {
5510 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005511
cristy3ed852e2009-09-05 21:47:34 +00005512 if (skipping_loop > 0)
5513 {
5514 if (skipping_loop == loop_level)
5515 {
5516 /*
5517 Found end of zero-iteration loop.
5518 */
5519 skipping_loop=(-1);
5520 mng_info->loop_active[loop_level]=0;
5521 }
5522 }
glennrp47b9dd52010-11-24 18:12:06 +00005523
cristy3ed852e2009-09-05 21:47:34 +00005524 else
5525 {
5526 if (mng_info->loop_active[loop_level] == 1)
5527 {
5528 mng_info->loop_count[loop_level]--;
5529 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005530
cristy3ed852e2009-09-05 21:47:34 +00005531 if (logging != MagickFalse)
5532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005533 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005534 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005535 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005536
cristy3ed852e2009-09-05 21:47:34 +00005537 if (mng_info->loop_count[loop_level] != 0)
5538 {
5539 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5540 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005541
cristy3ed852e2009-09-05 21:47:34 +00005542 if (offset < 0)
5543 ThrowReaderException(CorruptImageError,
5544 "ImproperImageHeader");
5545 }
glennrp47b9dd52010-11-24 18:12:06 +00005546
cristy3ed852e2009-09-05 21:47:34 +00005547 else
5548 {
5549 short
5550 last_level;
5551
5552 /*
5553 Finished loop.
5554 */
5555 mng_info->loop_active[loop_level]=0;
5556 last_level=(-1);
5557 for (i=0; i < loop_level; i++)
5558 if (mng_info->loop_active[i] == 1)
5559 last_level=(short) i;
5560 loop_level=last_level;
5561 }
5562 }
5563 }
glennrp47b9dd52010-11-24 18:12:06 +00005564
cristy3ed852e2009-09-05 21:47:34 +00005565 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5566 continue;
5567 }
glennrp47b9dd52010-11-24 18:12:06 +00005568
cristy3ed852e2009-09-05 21:47:34 +00005569 if (memcmp(type,mng_CLON,4) == 0)
5570 {
5571 if (mng_info->clon_warning == 0)
5572 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5573 CoderError,"CLON is not implemented yet","`%s'",
5574 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005575
cristy3ed852e2009-09-05 21:47:34 +00005576 mng_info->clon_warning++;
5577 }
glennrp47b9dd52010-11-24 18:12:06 +00005578
cristy3ed852e2009-09-05 21:47:34 +00005579 if (memcmp(type,mng_MAGN,4) == 0)
5580 {
5581 png_uint_16
5582 magn_first,
5583 magn_last,
5584 magn_mb,
5585 magn_ml,
5586 magn_mr,
5587 magn_mt,
5588 magn_mx,
5589 magn_my,
5590 magn_methx,
5591 magn_methy;
5592
5593 if (length > 1)
5594 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005595
cristy3ed852e2009-09-05 21:47:34 +00005596 else
5597 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005598
cristy3ed852e2009-09-05 21:47:34 +00005599 if (length > 3)
5600 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005601
cristy3ed852e2009-09-05 21:47:34 +00005602 else
5603 magn_last=magn_first;
5604#ifndef MNG_OBJECT_BUFFERS
5605 if (magn_first || magn_last)
5606 if (mng_info->magn_warning == 0)
5607 {
5608 (void) ThrowMagickException(&image->exception,
5609 GetMagickModule(),CoderError,
5610 "MAGN is not implemented yet for nonzero objects",
5611 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005612
cristy3ed852e2009-09-05 21:47:34 +00005613 mng_info->magn_warning++;
5614 }
5615#endif
5616 if (length > 4)
5617 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005618
cristy3ed852e2009-09-05 21:47:34 +00005619 else
5620 magn_methx=0;
5621
5622 if (length > 6)
5623 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005624
cristy3ed852e2009-09-05 21:47:34 +00005625 else
5626 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005627
cristy3ed852e2009-09-05 21:47:34 +00005628 if (magn_mx == 0)
5629 magn_mx=1;
5630
5631 if (length > 8)
5632 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005633
cristy3ed852e2009-09-05 21:47:34 +00005634 else
5635 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005636
cristy3ed852e2009-09-05 21:47:34 +00005637 if (magn_my == 0)
5638 magn_my=1;
5639
5640 if (length > 10)
5641 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005642
cristy3ed852e2009-09-05 21:47:34 +00005643 else
5644 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005645
cristy3ed852e2009-09-05 21:47:34 +00005646 if (magn_ml == 0)
5647 magn_ml=1;
5648
5649 if (length > 12)
5650 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005651
cristy3ed852e2009-09-05 21:47:34 +00005652 else
5653 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005654
cristy3ed852e2009-09-05 21:47:34 +00005655 if (magn_mr == 0)
5656 magn_mr=1;
5657
5658 if (length > 14)
5659 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005660
cristy3ed852e2009-09-05 21:47:34 +00005661 else
5662 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005663
cristy3ed852e2009-09-05 21:47:34 +00005664 if (magn_mt == 0)
5665 magn_mt=1;
5666
5667 if (length > 16)
5668 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005669
cristy3ed852e2009-09-05 21:47:34 +00005670 else
5671 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005672
cristy3ed852e2009-09-05 21:47:34 +00005673 if (magn_mb == 0)
5674 magn_mb=1;
5675
5676 if (length > 17)
5677 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005678
cristy3ed852e2009-09-05 21:47:34 +00005679 else
5680 magn_methy=magn_methx;
5681
glennrp47b9dd52010-11-24 18:12:06 +00005682
cristy3ed852e2009-09-05 21:47:34 +00005683 if (magn_methx > 5 || magn_methy > 5)
5684 if (mng_info->magn_warning == 0)
5685 {
5686 (void) ThrowMagickException(&image->exception,
5687 GetMagickModule(),CoderError,
5688 "Unknown MAGN method in MNG datastream","`%s'",
5689 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005690
cristy3ed852e2009-09-05 21:47:34 +00005691 mng_info->magn_warning++;
5692 }
5693#ifdef MNG_OBJECT_BUFFERS
5694 /* Magnify existing objects in the range magn_first to magn_last */
5695#endif
5696 if (magn_first == 0 || magn_last == 0)
5697 {
5698 /* Save the magnification factors for object 0 */
5699 mng_info->magn_mb=magn_mb;
5700 mng_info->magn_ml=magn_ml;
5701 mng_info->magn_mr=magn_mr;
5702 mng_info->magn_mt=magn_mt;
5703 mng_info->magn_mx=magn_mx;
5704 mng_info->magn_my=magn_my;
5705 mng_info->magn_methx=magn_methx;
5706 mng_info->magn_methy=magn_methy;
5707 }
5708 }
glennrp47b9dd52010-11-24 18:12:06 +00005709
cristy3ed852e2009-09-05 21:47:34 +00005710 if (memcmp(type,mng_PAST,4) == 0)
5711 {
5712 if (mng_info->past_warning == 0)
5713 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5714 CoderError,"PAST is not implemented yet","`%s'",
5715 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005716
cristy3ed852e2009-09-05 21:47:34 +00005717 mng_info->past_warning++;
5718 }
glennrp47b9dd52010-11-24 18:12:06 +00005719
cristy3ed852e2009-09-05 21:47:34 +00005720 if (memcmp(type,mng_SHOW,4) == 0)
5721 {
5722 if (mng_info->show_warning == 0)
5723 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5724 CoderError,"SHOW is not implemented yet","`%s'",
5725 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005726
cristy3ed852e2009-09-05 21:47:34 +00005727 mng_info->show_warning++;
5728 }
glennrp47b9dd52010-11-24 18:12:06 +00005729
cristy3ed852e2009-09-05 21:47:34 +00005730 if (memcmp(type,mng_sBIT,4) == 0)
5731 {
5732 if (length < 4)
5733 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005734
cristy3ed852e2009-09-05 21:47:34 +00005735 else
5736 {
5737 mng_info->global_sbit.gray=p[0];
5738 mng_info->global_sbit.red=p[0];
5739 mng_info->global_sbit.green=p[1];
5740 mng_info->global_sbit.blue=p[2];
5741 mng_info->global_sbit.alpha=p[3];
5742 mng_info->have_global_sbit=MagickTrue;
5743 }
5744 }
5745 if (memcmp(type,mng_pHYs,4) == 0)
5746 {
5747 if (length > 8)
5748 {
5749 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005750 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005751 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005752 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005753 mng_info->global_phys_unit_type=p[8];
5754 mng_info->have_global_phys=MagickTrue;
5755 }
glennrp47b9dd52010-11-24 18:12:06 +00005756
cristy3ed852e2009-09-05 21:47:34 +00005757 else
5758 mng_info->have_global_phys=MagickFalse;
5759 }
5760 if (memcmp(type,mng_pHYg,4) == 0)
5761 {
5762 if (mng_info->phyg_warning == 0)
5763 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5764 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005765
cristy3ed852e2009-09-05 21:47:34 +00005766 mng_info->phyg_warning++;
5767 }
5768 if (memcmp(type,mng_BASI,4) == 0)
5769 {
5770 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005771
cristy3ed852e2009-09-05 21:47:34 +00005772 if (mng_info->basi_warning == 0)
5773 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5774 CoderError,"BASI is not implemented yet","`%s'",
5775 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005776
cristy3ed852e2009-09-05 21:47:34 +00005777 mng_info->basi_warning++;
5778#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005779 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005780 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005781 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005782 (p[6] << 8) | p[7]);
5783 basi_color_type=p[8];
5784 basi_compression_method=p[9];
5785 basi_filter_type=p[10];
5786 basi_interlace_method=p[11];
5787 if (length > 11)
5788 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005789
cristy3ed852e2009-09-05 21:47:34 +00005790 else
5791 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005792
cristy3ed852e2009-09-05 21:47:34 +00005793 if (length > 13)
5794 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005795
cristy3ed852e2009-09-05 21:47:34 +00005796 else
5797 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005798
cristy3ed852e2009-09-05 21:47:34 +00005799 if (length > 15)
5800 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005801
cristy3ed852e2009-09-05 21:47:34 +00005802 else
5803 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005804
cristy3ed852e2009-09-05 21:47:34 +00005805 if (length > 17)
5806 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005807
cristy3ed852e2009-09-05 21:47:34 +00005808 else
5809 {
5810 if (basi_sample_depth == 16)
5811 basi_alpha=65535L;
5812 else
5813 basi_alpha=255;
5814 }
glennrp47b9dd52010-11-24 18:12:06 +00005815
cristy3ed852e2009-09-05 21:47:34 +00005816 if (length > 19)
5817 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005818
cristy3ed852e2009-09-05 21:47:34 +00005819 else
5820 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005821
cristy3ed852e2009-09-05 21:47:34 +00005822#endif
5823 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5824 continue;
5825 }
glennrp47b9dd52010-11-24 18:12:06 +00005826
cristy3ed852e2009-09-05 21:47:34 +00005827 if (memcmp(type,mng_IHDR,4)
5828#if defined(JNG_SUPPORTED)
5829 && memcmp(type,mng_JHDR,4)
5830#endif
5831 )
5832 {
5833 /* Not an IHDR or JHDR chunk */
5834 if (length)
5835 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005836
cristy3ed852e2009-09-05 21:47:34 +00005837 continue;
5838 }
5839/* Process IHDR */
5840 if (logging != MagickFalse)
5841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5842 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005843
cristy3ed852e2009-09-05 21:47:34 +00005844 mng_info->exists[object_id]=MagickTrue;
5845 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005846
cristy3ed852e2009-09-05 21:47:34 +00005847 if (mng_info->invisible[object_id])
5848 {
5849 if (logging != MagickFalse)
5850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5851 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005852
cristy3ed852e2009-09-05 21:47:34 +00005853 skip_to_iend=MagickTrue;
5854 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5855 continue;
5856 }
5857#if defined(MNG_INSERT_LAYERS)
5858 if (length < 8)
5859 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005860
cristy8182b072010-05-30 20:10:53 +00005861 image_width=(size_t) mng_get_long(p);
5862 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005863#endif
5864 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5865
5866 /*
5867 Insert a transparent background layer behind the entire animation
5868 if it is not full screen.
5869 */
5870#if defined(MNG_INSERT_LAYERS)
5871 if (insert_layers && mng_type && first_mng_object)
5872 {
5873 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5874 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005875 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005876 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005877 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005878 {
cristy4c08aed2011-07-01 19:47:50 +00005879 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005880 {
5881 /*
5882 Allocate next image structure.
5883 */
cristy9950d572011-10-01 18:22:35 +00005884 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005885
cristy3ed852e2009-09-05 21:47:34 +00005886 if (GetNextImageInList(image) == (Image *) NULL)
5887 {
5888 image=DestroyImageList(image);
5889 MngInfoFreeStruct(mng_info,&have_mng_structure);
5890 return((Image *) NULL);
5891 }
glennrp47b9dd52010-11-24 18:12:06 +00005892
cristy3ed852e2009-09-05 21:47:34 +00005893 image=SyncNextImageInList(image);
5894 }
5895 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005896
cristy3ed852e2009-09-05 21:47:34 +00005897 if (term_chunk_found)
5898 {
5899 image->start_loop=MagickTrue;
5900 image->iterations=mng_iterations;
5901 term_chunk_found=MagickFalse;
5902 }
glennrp47b9dd52010-11-24 18:12:06 +00005903
cristy3ed852e2009-09-05 21:47:34 +00005904 else
5905 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005906
5907 /* Make a background rectangle. */
5908
cristy3ed852e2009-09-05 21:47:34 +00005909 image->delay=0;
5910 image->columns=mng_info->mng_width;
5911 image->rows=mng_info->mng_height;
5912 image->page.width=mng_info->mng_width;
5913 image->page.height=mng_info->mng_height;
5914 image->page.x=0;
5915 image->page.y=0;
5916 image->background_color=mng_background_color;
5917 (void) SetImageBackgroundColor(image);
5918 if (logging != MagickFalse)
5919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005920 " Inserted transparent background layer, W=%.20g, H=%.20g",
5921 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005922 }
5923 }
5924 /*
5925 Insert a background layer behind the upcoming image if
5926 framing_mode is 3, and we haven't already inserted one.
5927 */
5928 if (insert_layers && (mng_info->framing_mode == 3) &&
5929 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5930 (simplicity & 0x08)))
5931 {
cristy4c08aed2011-07-01 19:47:50 +00005932 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005933 {
5934 /*
5935 Allocate next image structure.
5936 */
cristy9950d572011-10-01 18:22:35 +00005937 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005938
cristy3ed852e2009-09-05 21:47:34 +00005939 if (GetNextImageInList(image) == (Image *) NULL)
5940 {
5941 image=DestroyImageList(image);
5942 MngInfoFreeStruct(mng_info,&have_mng_structure);
5943 return((Image *) NULL);
5944 }
glennrp47b9dd52010-11-24 18:12:06 +00005945
cristy3ed852e2009-09-05 21:47:34 +00005946 image=SyncNextImageInList(image);
5947 }
glennrp0fe50b42010-11-16 03:52:51 +00005948
cristy3ed852e2009-09-05 21:47:34 +00005949 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005950
cristy3ed852e2009-09-05 21:47:34 +00005951 if (term_chunk_found)
5952 {
5953 image->start_loop=MagickTrue;
5954 image->iterations=mng_iterations;
5955 term_chunk_found=MagickFalse;
5956 }
glennrp0fe50b42010-11-16 03:52:51 +00005957
cristy3ed852e2009-09-05 21:47:34 +00005958 else
5959 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005960
cristy3ed852e2009-09-05 21:47:34 +00005961 image->delay=0;
5962 image->columns=subframe_width;
5963 image->rows=subframe_height;
5964 image->page.width=subframe_width;
5965 image->page.height=subframe_height;
5966 image->page.x=mng_info->clip.left;
5967 image->page.y=mng_info->clip.top;
5968 image->background_color=mng_background_color;
5969 image->matte=MagickFalse;
5970 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005971
cristy3ed852e2009-09-05 21:47:34 +00005972 if (logging != MagickFalse)
5973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005974 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005975 (double) mng_info->clip.left,(double) mng_info->clip.right,
5976 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005977 }
5978#endif /* MNG_INSERT_LAYERS */
5979 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005980
cristy4c08aed2011-07-01 19:47:50 +00005981 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005982 {
5983 /*
5984 Allocate next image structure.
5985 */
cristy9950d572011-10-01 18:22:35 +00005986 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005987
cristy3ed852e2009-09-05 21:47:34 +00005988 if (GetNextImageInList(image) == (Image *) NULL)
5989 {
5990 image=DestroyImageList(image);
5991 MngInfoFreeStruct(mng_info,&have_mng_structure);
5992 return((Image *) NULL);
5993 }
glennrp47b9dd52010-11-24 18:12:06 +00005994
cristy3ed852e2009-09-05 21:47:34 +00005995 image=SyncNextImageInList(image);
5996 }
5997 mng_info->image=image;
5998 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5999 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006000
cristy3ed852e2009-09-05 21:47:34 +00006001 if (status == MagickFalse)
6002 break;
glennrp0fe50b42010-11-16 03:52:51 +00006003
cristy3ed852e2009-09-05 21:47:34 +00006004 if (term_chunk_found)
6005 {
6006 image->start_loop=MagickTrue;
6007 term_chunk_found=MagickFalse;
6008 }
glennrp0fe50b42010-11-16 03:52:51 +00006009
cristy3ed852e2009-09-05 21:47:34 +00006010 else
6011 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006012
cristy3ed852e2009-09-05 21:47:34 +00006013 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6014 {
6015 image->delay=frame_delay;
6016 frame_delay=default_frame_delay;
6017 }
glennrp0fe50b42010-11-16 03:52:51 +00006018
cristy3ed852e2009-09-05 21:47:34 +00006019 else
6020 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006021
cristy3ed852e2009-09-05 21:47:34 +00006022 image->page.width=mng_info->mng_width;
6023 image->page.height=mng_info->mng_height;
6024 image->page.x=mng_info->x_off[object_id];
6025 image->page.y=mng_info->y_off[object_id];
6026 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006027
cristy3ed852e2009-09-05 21:47:34 +00006028 /*
6029 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6030 */
glennrp47b9dd52010-11-24 18:12:06 +00006031
cristy3ed852e2009-09-05 21:47:34 +00006032 if (logging != MagickFalse)
6033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6034 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6035 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006036
cristybb503372010-05-27 20:51:26 +00006037 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006038
cristy3ed852e2009-09-05 21:47:34 +00006039 if (offset < 0)
6040 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6041 }
6042
6043 previous=image;
6044 mng_info->image=image;
6045 mng_info->mng_type=mng_type;
6046 mng_info->object_id=object_id;
6047
6048 if (memcmp(type,mng_IHDR,4) == 0)
6049 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006050
cristy3ed852e2009-09-05 21:47:34 +00006051#if defined(JNG_SUPPORTED)
6052 else
6053 image=ReadOneJNGImage(mng_info,image_info,exception);
6054#endif
6055
6056 if (image == (Image *) NULL)
6057 {
6058 if (IsImageObject(previous) != MagickFalse)
6059 {
6060 (void) DestroyImageList(previous);
6061 (void) CloseBlob(previous);
6062 }
glennrp47b9dd52010-11-24 18:12:06 +00006063
cristy3ed852e2009-09-05 21:47:34 +00006064 MngInfoFreeStruct(mng_info,&have_mng_structure);
6065 return((Image *) NULL);
6066 }
glennrp0fe50b42010-11-16 03:52:51 +00006067
cristy3ed852e2009-09-05 21:47:34 +00006068 if (image->columns == 0 || image->rows == 0)
6069 {
6070 (void) CloseBlob(image);
6071 image=DestroyImageList(image);
6072 MngInfoFreeStruct(mng_info,&have_mng_structure);
6073 return((Image *) NULL);
6074 }
glennrp0fe50b42010-11-16 03:52:51 +00006075
cristy3ed852e2009-09-05 21:47:34 +00006076 mng_info->image=image;
6077
6078 if (mng_type)
6079 {
6080 MngBox
6081 crop_box;
6082
6083 if (mng_info->magn_methx || mng_info->magn_methy)
6084 {
6085 png_uint_32
6086 magnified_height,
6087 magnified_width;
6088
6089 if (logging != MagickFalse)
6090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6091 " Processing MNG MAGN chunk");
6092
6093 if (mng_info->magn_methx == 1)
6094 {
6095 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006096
cristy3ed852e2009-09-05 21:47:34 +00006097 if (image->columns > 1)
6098 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006099
cristy3ed852e2009-09-05 21:47:34 +00006100 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006101 magnified_width += (png_uint_32)
6102 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006103 }
glennrp47b9dd52010-11-24 18:12:06 +00006104
cristy3ed852e2009-09-05 21:47:34 +00006105 else
6106 {
cristy4e5bc842010-06-09 13:56:01 +00006107 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006108
cristy3ed852e2009-09-05 21:47:34 +00006109 if (image->columns > 1)
6110 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006111
cristy3ed852e2009-09-05 21:47:34 +00006112 if (image->columns > 2)
6113 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006114
cristy3ed852e2009-09-05 21:47:34 +00006115 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006116 magnified_width += (png_uint_32)
6117 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006118 }
glennrp47b9dd52010-11-24 18:12:06 +00006119
cristy3ed852e2009-09-05 21:47:34 +00006120 if (mng_info->magn_methy == 1)
6121 {
6122 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006123
cristy3ed852e2009-09-05 21:47:34 +00006124 if (image->rows > 1)
6125 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006126
cristy3ed852e2009-09-05 21:47:34 +00006127 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006128 magnified_height += (png_uint_32)
6129 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006130 }
glennrp47b9dd52010-11-24 18:12:06 +00006131
cristy3ed852e2009-09-05 21:47:34 +00006132 else
6133 {
cristy4e5bc842010-06-09 13:56:01 +00006134 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006135
cristy3ed852e2009-09-05 21:47:34 +00006136 if (image->rows > 1)
6137 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006138
cristy3ed852e2009-09-05 21:47:34 +00006139 if (image->rows > 2)
6140 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006141
cristy3ed852e2009-09-05 21:47:34 +00006142 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006143 magnified_height += (png_uint_32)
6144 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006145 }
glennrp47b9dd52010-11-24 18:12:06 +00006146
cristy3ed852e2009-09-05 21:47:34 +00006147 if (magnified_height > image->rows ||
6148 magnified_width > image->columns)
6149 {
6150 Image
6151 *large_image;
6152
6153 int
6154 yy;
6155
cristy4c08aed2011-07-01 19:47:50 +00006156 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006157 *next,
6158 *prev;
6159
6160 png_uint_16
6161 magn_methx,
6162 magn_methy;
6163
cristy4c08aed2011-07-01 19:47:50 +00006164 ssize_t
6165 m,
6166 y;
6167
6168 register Quantum
6169 *n,
6170 *q;
6171
6172 register ssize_t
6173 x;
6174
glennrp47b9dd52010-11-24 18:12:06 +00006175 /* Allocate next image structure. */
6176
cristy3ed852e2009-09-05 21:47:34 +00006177 if (logging != MagickFalse)
6178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6179 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006180
cristy9950d572011-10-01 18:22:35 +00006181 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006182
cristy3ed852e2009-09-05 21:47:34 +00006183 if (GetNextImageInList(image) == (Image *) NULL)
6184 {
6185 image=DestroyImageList(image);
6186 MngInfoFreeStruct(mng_info,&have_mng_structure);
6187 return((Image *) NULL);
6188 }
6189
6190 large_image=SyncNextImageInList(image);
6191
6192 large_image->columns=magnified_width;
6193 large_image->rows=magnified_height;
6194
6195 magn_methx=mng_info->magn_methx;
6196 magn_methy=mng_info->magn_methy;
6197
glennrp3faa9a32011-04-23 14:00:25 +00006198#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006199#define QM unsigned short
6200 if (magn_methx != 1 || magn_methy != 1)
6201 {
6202 /*
6203 Scale pixels to unsigned shorts to prevent
6204 overflow of intermediate values of interpolations
6205 */
cristybb503372010-05-27 20:51:26 +00006206 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006207 {
6208 q=GetAuthenticPixels(image,0,y,image->columns,1,
6209 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006210
cristybb503372010-05-27 20:51:26 +00006211 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006212 {
cristy4c08aed2011-07-01 19:47:50 +00006213 SetPixelRed(image,ScaleQuantumToShort(
6214 GetPixelRed(image,q)),q);
6215 SetPixelGreen(image,ScaleQuantumToShort(
6216 GetPixelGreen(image,q)),q);
6217 SetPixelBlue(image,ScaleQuantumToShort(
6218 GetPixelBlue(image,q)),q);
6219 SetPixelAlpha(image,ScaleQuantumToShort(
6220 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006221 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006222 }
glennrp47b9dd52010-11-24 18:12:06 +00006223
cristy3ed852e2009-09-05 21:47:34 +00006224 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6225 break;
6226 }
6227 }
6228#else
6229#define QM Quantum
6230#endif
6231
6232 if (image->matte != MagickFalse)
6233 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006234
cristy3ed852e2009-09-05 21:47:34 +00006235 else
6236 {
cristy4c08aed2011-07-01 19:47:50 +00006237 large_image->background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00006238 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006239
cristy3ed852e2009-09-05 21:47:34 +00006240 if (magn_methx == 4)
6241 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006242
cristy3ed852e2009-09-05 21:47:34 +00006243 if (magn_methx == 5)
6244 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006245
cristy3ed852e2009-09-05 21:47:34 +00006246 if (magn_methy == 4)
6247 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006248
cristy3ed852e2009-09-05 21:47:34 +00006249 if (magn_methy == 5)
6250 magn_methy=3;
6251 }
6252
6253 /* magnify the rows into the right side of the large image */
6254
6255 if (logging != MagickFalse)
6256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006257 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006258 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006259 yy=0;
6260 length=(size_t) image->columns;
cristy4c08aed2011-07-01 19:47:50 +00006261 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6262 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006263
cristy4c08aed2011-07-01 19:47:50 +00006264 if ((prev == (Quantum *) NULL) ||
6265 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006266 {
6267 image=DestroyImageList(image);
6268 MngInfoFreeStruct(mng_info,&have_mng_structure);
6269 ThrowReaderException(ResourceLimitError,
6270 "MemoryAllocationFailed");
6271 }
glennrp47b9dd52010-11-24 18:12:06 +00006272
cristy3ed852e2009-09-05 21:47:34 +00006273 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6274 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006275
cristybb503372010-05-27 20:51:26 +00006276 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006277 {
6278 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006279 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006280
cristybb503372010-05-27 20:51:26 +00006281 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6282 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006283
cristybb503372010-05-27 20:51:26 +00006284 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6285 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006286
cristybb503372010-05-27 20:51:26 +00006287 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006288 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006289
cristy3ed852e2009-09-05 21:47:34 +00006290 else
cristybb503372010-05-27 20:51:26 +00006291 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006292
cristy3ed852e2009-09-05 21:47:34 +00006293 n=prev;
6294 prev=next;
6295 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006296
cristybb503372010-05-27 20:51:26 +00006297 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006298 {
6299 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6300 exception);
6301 (void) CopyMagickMemory(next,n,length);
6302 }
glennrp47b9dd52010-11-24 18:12:06 +00006303
cristy3ed852e2009-09-05 21:47:34 +00006304 for (i=0; i < m; i++, yy++)
6305 {
cristy4c08aed2011-07-01 19:47:50 +00006306 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006307 *pixels;
6308
cristybb503372010-05-27 20:51:26 +00006309 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006310 pixels=prev;
6311 n=next;
6312 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006313 1,exception);
cristy3ed852e2009-09-05 21:47:34 +00006314 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00006315
cristybb503372010-05-27 20:51:26 +00006316 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006317 {
glennrpfd05d622011-02-25 04:10:33 +00006318 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006319 /*
6320 if (image->storage_class == PseudoClass)
6321 {
6322 }
6323 */
6324
6325 if (magn_methy <= 1)
6326 {
glennrpbb4f99d2011-05-22 11:13:17 +00006327 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006328 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
glennrp847370c2011-07-05 17:37:15 +00006329 SetPixelGreen(large_image,GetPixelGreen(image,
6330 pixels),q);
6331 SetPixelBlue(large_image,GetPixelBlue(image,
6332 pixels),q);
6333 SetPixelAlpha(large_image,GetPixelAlpha(image,
6334 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006335 }
glennrp47b9dd52010-11-24 18:12:06 +00006336
cristy3ed852e2009-09-05 21:47:34 +00006337 else if (magn_methy == 2 || magn_methy == 4)
6338 {
6339 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006340 {
glennrp847370c2011-07-05 17:37:15 +00006341 SetPixelRed(large_image,GetPixelRed(image,
6342 pixels),q);
6343 SetPixelGreen(large_image,GetPixelGreen(image,
6344 pixels),q);
6345 SetPixelBlue(large_image,GetPixelBlue(image,
6346 pixels),q);
6347 SetPixelAlpha(large_image,GetPixelAlpha(image,
6348 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006349 }
glennrp47b9dd52010-11-24 18:12:06 +00006350
cristy3ed852e2009-09-05 21:47:34 +00006351 else
6352 {
6353 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006354 SetPixelRed(large_image,((QM) (((ssize_t)
6355 (2*i*(GetPixelRed(image,n)
6356 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006357 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006358 +GetPixelRed(image,pixels)))),q);
6359 SetPixelGreen(large_image,((QM) (((ssize_t)
6360 (2*i*(GetPixelGreen(image,n)
6361 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006362 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006363 +GetPixelGreen(image,pixels)))),q);
6364 SetPixelBlue(large_image,((QM) (((ssize_t)
6365 (2*i*(GetPixelBlue(image,n)
6366 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006367 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006368 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006369
cristy3ed852e2009-09-05 21:47:34 +00006370 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006371 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6372 (2*i*(GetPixelAlpha(image,n)
6373 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006374 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006375 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006376 }
glennrp47b9dd52010-11-24 18:12:06 +00006377
cristy3ed852e2009-09-05 21:47:34 +00006378 if (magn_methy == 4)
6379 {
6380 /* Replicate nearest */
6381 if (i <= ((m+1) << 1))
glennrp847370c2011-07-05 17:37:15 +00006382 SetPixelAlpha(large_image,GetPixelAlpha(image,
6383 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006384 else
glennrp847370c2011-07-05 17:37:15 +00006385 SetPixelAlpha(large_image,GetPixelAlpha(image,
6386 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006387 }
6388 }
glennrp47b9dd52010-11-24 18:12:06 +00006389
cristy3ed852e2009-09-05 21:47:34 +00006390 else /* if (magn_methy == 3 || magn_methy == 5) */
6391 {
6392 /* Replicate nearest */
6393 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006394 {
glennrp847370c2011-07-05 17:37:15 +00006395 SetPixelRed(large_image,GetPixelRed(image,
6396 pixels),q);
6397 SetPixelGreen(large_image,GetPixelGreen(image,
6398 pixels),q);
6399 SetPixelBlue(large_image,GetPixelBlue(image,
6400 pixels),q);
6401 SetPixelAlpha(large_image,GetPixelAlpha(image,
6402 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006403 }
glennrp47b9dd52010-11-24 18:12:06 +00006404
cristy3ed852e2009-09-05 21:47:34 +00006405 else
glennrpbb4f99d2011-05-22 11:13:17 +00006406 {
cristy4c08aed2011-07-01 19:47:50 +00006407 SetPixelRed(large_image,GetPixelRed(image,n),q);
glennrp847370c2011-07-05 17:37:15 +00006408 SetPixelGreen(large_image,GetPixelGreen(image,n),
6409 q);
6410 SetPixelBlue(large_image,GetPixelBlue(image,n),
6411 q);
6412 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6413 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006414 }
glennrp47b9dd52010-11-24 18:12:06 +00006415
cristy3ed852e2009-09-05 21:47:34 +00006416 if (magn_methy == 5)
6417 {
cristy4c08aed2011-07-01 19:47:50 +00006418 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6419 (GetPixelAlpha(image,n)
6420 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006421 +m))/((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006422 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006423 }
6424 }
cristyed231572011-07-14 02:18:59 +00006425 n+=GetPixelChannels(image);
6426 q+=GetPixelChannels(large_image);
6427 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006428 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006429
cristy3ed852e2009-09-05 21:47:34 +00006430 if (SyncAuthenticPixels(large_image,exception) == 0)
6431 break;
glennrp47b9dd52010-11-24 18:12:06 +00006432
cristy3ed852e2009-09-05 21:47:34 +00006433 } /* i */
6434 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006435
cristy4c08aed2011-07-01 19:47:50 +00006436 prev=(Quantum *) RelinquishMagickMemory(prev);
6437 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006438
6439 length=image->columns;
6440
6441 if (logging != MagickFalse)
6442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6443 " Delete original image");
6444
6445 DeleteImageFromList(&image);
6446
6447 image=large_image;
6448
6449 mng_info->image=image;
6450
6451 /* magnify the columns */
6452 if (logging != MagickFalse)
6453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006454 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006455
cristybb503372010-05-27 20:51:26 +00006456 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006457 {
cristy4c08aed2011-07-01 19:47:50 +00006458 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006459 *pixels;
6460
6461 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristyed231572011-07-14 02:18:59 +00006462 pixels=q+(image->columns-length)*GetPixelChannels(image);
6463 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006464
cristybb503372010-05-27 20:51:26 +00006465 for (x=(ssize_t) (image->columns-length);
6466 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006467 {
cristyed231572011-07-14 02:18:59 +00006468 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006469
cristybb503372010-05-27 20:51:26 +00006470 if (x == (ssize_t) (image->columns-length))
6471 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006472
cristybb503372010-05-27 20:51:26 +00006473 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6474 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006475
cristybb503372010-05-27 20:51:26 +00006476 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6477 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006478
cristybb503372010-05-27 20:51:26 +00006479 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006480 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006481
cristy3ed852e2009-09-05 21:47:34 +00006482 else
cristybb503372010-05-27 20:51:26 +00006483 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006484
cristy3ed852e2009-09-05 21:47:34 +00006485 for (i=0; i < m; i++)
6486 {
6487 if (magn_methx <= 1)
6488 {
6489 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006490 SetPixelRed(image,GetPixelRed(image,pixels),q);
6491 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6492 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6493 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006494 }
glennrp47b9dd52010-11-24 18:12:06 +00006495
cristy3ed852e2009-09-05 21:47:34 +00006496 else if (magn_methx == 2 || magn_methx == 4)
6497 {
6498 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006499 {
cristy4c08aed2011-07-01 19:47:50 +00006500 SetPixelRed(image,GetPixelRed(image,pixels),q);
6501 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6502 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6503 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006504 }
glennrp47b9dd52010-11-24 18:12:06 +00006505
cristyed231572011-07-14 02:18:59 +00006506 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006507 else
6508 {
6509 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006510 SetPixelRed(image,(QM) ((2*i*(
6511 GetPixelRed(image,n)
6512 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006513 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006514 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006515
cristy4c08aed2011-07-01 19:47:50 +00006516 SetPixelGreen(image,(QM) ((2*i*(
6517 GetPixelGreen(image,n)
6518 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006519 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006520 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006521
cristy4c08aed2011-07-01 19:47:50 +00006522 SetPixelBlue(image,(QM) ((2*i*(
6523 GetPixelBlue(image,n)
6524 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006525 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006526 GetPixelBlue(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006527 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006528 SetPixelAlpha(image,(QM) ((2*i*(
6529 GetPixelAlpha(image,n)
6530 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006531 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006532 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006533 }
glennrp47b9dd52010-11-24 18:12:06 +00006534
cristy3ed852e2009-09-05 21:47:34 +00006535 if (magn_methx == 4)
6536 {
6537 /* Replicate nearest */
6538 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006539 {
cristy4c08aed2011-07-01 19:47:50 +00006540 SetPixelAlpha(image,
6541 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006542 }
cristy3ed852e2009-09-05 21:47:34 +00006543 else
glennrpbb4f99d2011-05-22 11:13:17 +00006544 {
cristy4c08aed2011-07-01 19:47:50 +00006545 SetPixelAlpha(image,
6546 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006547 }
cristy3ed852e2009-09-05 21:47:34 +00006548 }
6549 }
glennrp47b9dd52010-11-24 18:12:06 +00006550
cristy3ed852e2009-09-05 21:47:34 +00006551 else /* if (magn_methx == 3 || magn_methx == 5) */
6552 {
6553 /* Replicate nearest */
6554 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006555 {
cristy4c08aed2011-07-01 19:47:50 +00006556 SetPixelRed(image,GetPixelRed(image,pixels),q);
6557 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6558 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6559 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006560 }
glennrp47b9dd52010-11-24 18:12:06 +00006561
cristy3ed852e2009-09-05 21:47:34 +00006562 else
glennrpbb4f99d2011-05-22 11:13:17 +00006563 {
cristy4c08aed2011-07-01 19:47:50 +00006564 SetPixelRed(image,GetPixelRed(image,n),q);
6565 SetPixelGreen(image,GetPixelGreen(image,n),q);
6566 SetPixelBlue(image,GetPixelBlue(image,n),q);
6567 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006568 }
glennrp47b9dd52010-11-24 18:12:06 +00006569
cristy3ed852e2009-09-05 21:47:34 +00006570 if (magn_methx == 5)
6571 {
6572 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006573 SetPixelAlpha(image,
6574 (QM) ((2*i*( GetPixelAlpha(image,n)
6575 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006576 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006577 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006578 }
6579 }
cristyed231572011-07-14 02:18:59 +00006580 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006581 }
cristyed231572011-07-14 02:18:59 +00006582 n+=GetPixelChannels(image);
6583 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006584 }
glennrp47b9dd52010-11-24 18:12:06 +00006585
cristy3ed852e2009-09-05 21:47:34 +00006586 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6587 break;
6588 }
glennrp3faa9a32011-04-23 14:00:25 +00006589#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006590 if (magn_methx != 1 || magn_methy != 1)
6591 {
6592 /*
6593 Rescale pixels to Quantum
6594 */
cristybb503372010-05-27 20:51:26 +00006595 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006596 {
6597 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006598
cristybb503372010-05-27 20:51:26 +00006599 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006600 {
cristy4c08aed2011-07-01 19:47:50 +00006601 SetPixelRed(image,ScaleShortToQuantum(
6602 GetPixelRed(image,q)),q);
6603 SetPixelGreen(image,ScaleShortToQuantum(
6604 GetPixelGreen(image,q)),q);
6605 SetPixelBlue(image,ScaleShortToQuantum(
6606 GetPixelBlue(image,q)),q);
6607 SetPixelAlpha(image,ScaleShortToQuantum(
6608 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006609 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006610 }
glennrp47b9dd52010-11-24 18:12:06 +00006611
cristy3ed852e2009-09-05 21:47:34 +00006612 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6613 break;
6614 }
6615 }
6616#endif
6617 if (logging != MagickFalse)
6618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6619 " Finished MAGN processing");
6620 }
6621 }
6622
6623 /*
6624 Crop_box is with respect to the upper left corner of the MNG.
6625 */
6626 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6627 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6628 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6629 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6630 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6631 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6632 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6633 if ((crop_box.left != (mng_info->image_box.left
6634 +mng_info->x_off[object_id])) ||
6635 (crop_box.right != (mng_info->image_box.right
6636 +mng_info->x_off[object_id])) ||
6637 (crop_box.top != (mng_info->image_box.top
6638 +mng_info->y_off[object_id])) ||
6639 (crop_box.bottom != (mng_info->image_box.bottom
6640 +mng_info->y_off[object_id])))
6641 {
6642 if (logging != MagickFalse)
6643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6644 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006645
cristy3ed852e2009-09-05 21:47:34 +00006646 if ((crop_box.left < crop_box.right) &&
6647 (crop_box.top < crop_box.bottom))
6648 {
6649 Image
6650 *im;
6651
6652 RectangleInfo
6653 crop_info;
6654
6655 /*
6656 Crop_info is with respect to the upper left corner of
6657 the image.
6658 */
6659 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6660 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006661 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6662 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006663 image->page.width=image->columns;
6664 image->page.height=image->rows;
6665 image->page.x=0;
6666 image->page.y=0;
6667 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006668
cristy3ed852e2009-09-05 21:47:34 +00006669 if (im != (Image *) NULL)
6670 {
6671 image->columns=im->columns;
6672 image->rows=im->rows;
6673 im=DestroyImage(im);
6674 image->page.width=image->columns;
6675 image->page.height=image->rows;
6676 image->page.x=crop_box.left;
6677 image->page.y=crop_box.top;
6678 }
6679 }
glennrp47b9dd52010-11-24 18:12:06 +00006680
cristy3ed852e2009-09-05 21:47:34 +00006681 else
6682 {
6683 /*
6684 No pixels in crop area. The MNG spec still requires
6685 a layer, though, so make a single transparent pixel in
6686 the top left corner.
6687 */
6688 image->columns=1;
6689 image->rows=1;
6690 image->colors=2;
6691 (void) SetImageBackgroundColor(image);
6692 image->page.width=1;
6693 image->page.height=1;
6694 image->page.x=0;
6695 image->page.y=0;
6696 }
6697 }
6698#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6699 image=mng_info->image;
6700#endif
6701 }
6702
glennrp2b013e42010-11-24 16:55:50 +00006703#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6704 /* PNG does not handle depths greater than 16 so reduce it even
6705 * if lossy
6706 */
6707 if (image->depth > 16)
6708 image->depth=16;
6709#endif
6710
glennrp3faa9a32011-04-23 14:00:25 +00006711#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006712 if (LosslessReduceDepthOK(image) != MagickFalse)
6713 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006714#endif
glennrpd6afd542010-11-19 01:53:05 +00006715
cristy3ed852e2009-09-05 21:47:34 +00006716 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006717
cristy3ed852e2009-09-05 21:47:34 +00006718 if (image_info->number_scenes != 0)
6719 {
6720 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006721 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006722 break;
6723 }
glennrpd6afd542010-11-19 01:53:05 +00006724
cristy3ed852e2009-09-05 21:47:34 +00006725 if (logging != MagickFalse)
6726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6727 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006728
cristy3ed852e2009-09-05 21:47:34 +00006729 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006730
cristy3ed852e2009-09-05 21:47:34 +00006731 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006732
cristy3ed852e2009-09-05 21:47:34 +00006733 if (logging != MagickFalse)
6734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6735 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006736
cristy3ed852e2009-09-05 21:47:34 +00006737#if defined(MNG_INSERT_LAYERS)
6738 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6739 (mng_info->mng_height))
6740 {
6741 /*
6742 Insert a background layer if nothing else was found.
6743 */
6744 if (logging != MagickFalse)
6745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6746 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006747
cristy4c08aed2011-07-01 19:47:50 +00006748 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006749 {
6750 /*
6751 Allocate next image structure.
6752 */
cristy9950d572011-10-01 18:22:35 +00006753 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006754 if (GetNextImageInList(image) == (Image *) NULL)
6755 {
6756 image=DestroyImageList(image);
6757 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006758
cristy3ed852e2009-09-05 21:47:34 +00006759 if (logging != MagickFalse)
6760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6761 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006762
cristy3ed852e2009-09-05 21:47:34 +00006763 return((Image *) NULL);
6764 }
6765 image=SyncNextImageInList(image);
6766 }
6767 image->columns=mng_info->mng_width;
6768 image->rows=mng_info->mng_height;
6769 image->page.width=mng_info->mng_width;
6770 image->page.height=mng_info->mng_height;
6771 image->page.x=0;
6772 image->page.y=0;
6773 image->background_color=mng_background_color;
6774 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006775
cristy3ed852e2009-09-05 21:47:34 +00006776 if (image_info->ping == MagickFalse)
6777 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006778
cristy3ed852e2009-09-05 21:47:34 +00006779 mng_info->image_found++;
6780 }
6781#endif
6782 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006783
cristy3ed852e2009-09-05 21:47:34 +00006784 if (mng_iterations == 1)
6785 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006786
cristy3ed852e2009-09-05 21:47:34 +00006787 while (GetPreviousImageInList(image) != (Image *) NULL)
6788 {
6789 image_count++;
6790 if (image_count > 10*mng_info->image_found)
6791 {
6792 if (logging != MagickFalse)
6793 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006794
cristy3ed852e2009-09-05 21:47:34 +00006795 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6796 CoderError,"Linked list is corrupted, beginning of list not found",
6797 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006798
cristy3ed852e2009-09-05 21:47:34 +00006799 return((Image *) NULL);
6800 }
glennrp0fe50b42010-11-16 03:52:51 +00006801
cristy3ed852e2009-09-05 21:47:34 +00006802 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006803
cristy3ed852e2009-09-05 21:47:34 +00006804 if (GetNextImageInList(image) == (Image *) NULL)
6805 {
6806 if (logging != MagickFalse)
6807 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006808
cristy3ed852e2009-09-05 21:47:34 +00006809 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6810 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6811 image_info->filename);
6812 }
6813 }
glennrp47b9dd52010-11-24 18:12:06 +00006814
cristy3ed852e2009-09-05 21:47:34 +00006815 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6816 GetNextImageInList(image) ==
6817 (Image *) NULL)
6818 {
6819 if (logging != MagickFalse)
6820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6821 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006822
cristy3ed852e2009-09-05 21:47:34 +00006823 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6824 CoderError,"image->next for first image is NULL but shouldn't be.",
6825 "`%s'",image_info->filename);
6826 }
glennrp47b9dd52010-11-24 18:12:06 +00006827
cristy3ed852e2009-09-05 21:47:34 +00006828 if (mng_info->image_found == 0)
6829 {
6830 if (logging != MagickFalse)
6831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6832 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006833
cristy3ed852e2009-09-05 21:47:34 +00006834 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6835 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006836
cristy3ed852e2009-09-05 21:47:34 +00006837 if (image != (Image *) NULL)
6838 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006839
cristy3ed852e2009-09-05 21:47:34 +00006840 MngInfoFreeStruct(mng_info,&have_mng_structure);
6841 return((Image *) NULL);
6842 }
6843
6844 if (mng_info->ticks_per_second)
6845 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6846 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006847
cristy3ed852e2009-09-05 21:47:34 +00006848 else
6849 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006850
cristy3ed852e2009-09-05 21:47:34 +00006851 /* Find final nonzero image delay */
6852 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006853
cristy3ed852e2009-09-05 21:47:34 +00006854 while (GetNextImageInList(image) != (Image *) NULL)
6855 {
6856 if (image->delay)
6857 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006858
cristy3ed852e2009-09-05 21:47:34 +00006859 image=GetNextImageInList(image);
6860 }
glennrp0fe50b42010-11-16 03:52:51 +00006861
cristy3ed852e2009-09-05 21:47:34 +00006862 if (final_delay < final_image_delay)
6863 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006864
cristy3ed852e2009-09-05 21:47:34 +00006865 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006866
cristy3ed852e2009-09-05 21:47:34 +00006867 if (logging != MagickFalse)
6868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006869 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6870 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006871
cristy3ed852e2009-09-05 21:47:34 +00006872 if (logging != MagickFalse)
6873 {
6874 int
6875 scene;
6876
6877 scene=0;
6878 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006879
cristy3ed852e2009-09-05 21:47:34 +00006880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6881 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006882
cristy3ed852e2009-09-05 21:47:34 +00006883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006884 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006885
cristy3ed852e2009-09-05 21:47:34 +00006886 while (GetNextImageInList(image) != (Image *) NULL)
6887 {
6888 image=GetNextImageInList(image);
6889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006890 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006891 }
6892 }
6893
6894 image=GetFirstImageInList(image);
6895#ifdef MNG_COALESCE_LAYERS
6896 if (insert_layers)
6897 {
6898 Image
6899 *next_image,
6900 *next;
6901
cristybb503372010-05-27 20:51:26 +00006902 size_t
cristy3ed852e2009-09-05 21:47:34 +00006903 scene;
6904
6905 if (logging != MagickFalse)
6906 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006907
cristy3ed852e2009-09-05 21:47:34 +00006908 scene=image->scene;
6909 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006910
cristy3ed852e2009-09-05 21:47:34 +00006911 if (next_image == (Image *) NULL)
6912 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006913
cristy3ed852e2009-09-05 21:47:34 +00006914 image=DestroyImageList(image);
6915 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006916
cristy3ed852e2009-09-05 21:47:34 +00006917 for (next=image; next != (Image *) NULL; next=next_image)
6918 {
6919 next->page.width=mng_info->mng_width;
6920 next->page.height=mng_info->mng_height;
6921 next->page.x=0;
6922 next->page.y=0;
6923 next->scene=scene++;
6924 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006925
cristy3ed852e2009-09-05 21:47:34 +00006926 if (next_image == (Image *) NULL)
6927 break;
glennrp47b9dd52010-11-24 18:12:06 +00006928
cristy3ed852e2009-09-05 21:47:34 +00006929 if (next->delay == 0)
6930 {
6931 scene--;
6932 next_image->previous=GetPreviousImageInList(next);
6933 if (GetPreviousImageInList(next) == (Image *) NULL)
6934 image=next_image;
6935 else
6936 next->previous->next=next_image;
6937 next=DestroyImage(next);
6938 }
6939 }
6940 }
6941#endif
6942
6943 while (GetNextImageInList(image) != (Image *) NULL)
6944 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006945
cristy3ed852e2009-09-05 21:47:34 +00006946 image->dispose=BackgroundDispose;
6947
6948 if (logging != MagickFalse)
6949 {
6950 int
6951 scene;
6952
6953 scene=0;
6954 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006955
cristy3ed852e2009-09-05 21:47:34 +00006956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6957 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006958
cristy3ed852e2009-09-05 21:47:34 +00006959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006960 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6961 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006962
cristy3ed852e2009-09-05 21:47:34 +00006963 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006964 {
6965 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006966
cristyf2faecf2010-05-28 19:19:36 +00006967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006968 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6969 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006970 }
6971 }
glennrp47b9dd52010-11-24 18:12:06 +00006972
cristy3ed852e2009-09-05 21:47:34 +00006973 image=GetFirstImageInList(image);
6974 MngInfoFreeStruct(mng_info,&have_mng_structure);
6975 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006976
cristy3ed852e2009-09-05 21:47:34 +00006977 if (logging != MagickFalse)
6978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006979
cristy3ed852e2009-09-05 21:47:34 +00006980 return(GetFirstImageInList(image));
6981}
glennrp25c1e2b2010-03-25 01:39:56 +00006982#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006983static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6984{
6985 printf("Your PNG library is too old: You have libpng-%s\n",
6986 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006987
cristy3ed852e2009-09-05 21:47:34 +00006988 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6989 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006990
cristy3ed852e2009-09-05 21:47:34 +00006991 return(Image *) NULL;
6992}
glennrp47b9dd52010-11-24 18:12:06 +00006993
cristy3ed852e2009-09-05 21:47:34 +00006994static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6995{
6996 return(ReadPNGImage(image_info,exception));
6997}
glennrp25c1e2b2010-03-25 01:39:56 +00006998#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006999#endif
7000
7001/*
7002%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7003% %
7004% %
7005% %
7006% R e g i s t e r P N G I m a g e %
7007% %
7008% %
7009% %
7010%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7011%
7012% RegisterPNGImage() adds properties for the PNG image format to
7013% the list of supported formats. The properties include the image format
7014% tag, a method to read and/or write the format, whether the format
7015% supports the saving of more than one frame to the same file or blob,
7016% whether the format supports native in-memory I/O, and a brief
7017% description of the format.
7018%
7019% The format of the RegisterPNGImage method is:
7020%
cristybb503372010-05-27 20:51:26 +00007021% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007022%
7023*/
cristybb503372010-05-27 20:51:26 +00007024ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007025{
7026 char
7027 version[MaxTextExtent];
7028
7029 MagickInfo
7030 *entry;
7031
7032 static const char
7033 *PNGNote=
7034 {
7035 "See http://www.libpng.org/ for details about the PNG format."
7036 },
glennrp47b9dd52010-11-24 18:12:06 +00007037
cristy3ed852e2009-09-05 21:47:34 +00007038 *JNGNote=
7039 {
7040 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7041 "format."
7042 },
glennrp47b9dd52010-11-24 18:12:06 +00007043
cristy3ed852e2009-09-05 21:47:34 +00007044 *MNGNote=
7045 {
7046 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7047 "format."
7048 };
7049
7050 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007051
cristy3ed852e2009-09-05 21:47:34 +00007052#if defined(PNG_LIBPNG_VER_STRING)
7053 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7054 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007055
cristy3ed852e2009-09-05 21:47:34 +00007056 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7057 {
7058 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7059 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7060 MaxTextExtent);
7061 }
7062#endif
glennrp47b9dd52010-11-24 18:12:06 +00007063
cristy3ed852e2009-09-05 21:47:34 +00007064 entry=SetMagickInfo("MNG");
7065 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007066
cristy3ed852e2009-09-05 21:47:34 +00007067#if defined(MAGICKCORE_PNG_DELEGATE)
7068 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7069 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7070#endif
glennrp47b9dd52010-11-24 18:12:06 +00007071
cristy3ed852e2009-09-05 21:47:34 +00007072 entry->magick=(IsImageFormatHandler *) IsMNG;
7073 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007074
cristy3ed852e2009-09-05 21:47:34 +00007075 if (*version != '\0')
7076 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007077
cristy3ed852e2009-09-05 21:47:34 +00007078 entry->module=ConstantString("PNG");
7079 entry->note=ConstantString(MNGNote);
7080 (void) RegisterMagickInfo(entry);
7081
7082 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007083
cristy3ed852e2009-09-05 21:47:34 +00007084#if defined(MAGICKCORE_PNG_DELEGATE)
7085 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7086 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7087#endif
glennrp47b9dd52010-11-24 18:12:06 +00007088
cristy3ed852e2009-09-05 21:47:34 +00007089 entry->magick=(IsImageFormatHandler *) IsPNG;
7090 entry->adjoin=MagickFalse;
7091 entry->description=ConstantString("Portable Network Graphics");
7092 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007093
cristy3ed852e2009-09-05 21:47:34 +00007094 if (*version != '\0')
7095 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007096
cristy3ed852e2009-09-05 21:47:34 +00007097 entry->note=ConstantString(PNGNote);
7098 (void) RegisterMagickInfo(entry);
7099
7100 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007101
cristy3ed852e2009-09-05 21:47:34 +00007102#if defined(MAGICKCORE_PNG_DELEGATE)
7103 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7104 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7105#endif
glennrp47b9dd52010-11-24 18:12:06 +00007106
cristy3ed852e2009-09-05 21:47:34 +00007107 entry->magick=(IsImageFormatHandler *) IsPNG;
7108 entry->adjoin=MagickFalse;
7109 entry->description=ConstantString(
7110 "8-bit indexed with optional binary transparency");
7111 entry->module=ConstantString("PNG");
7112 (void) RegisterMagickInfo(entry);
7113
7114 entry=SetMagickInfo("PNG24");
7115 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007116
cristy3ed852e2009-09-05 21:47:34 +00007117#if defined(ZLIB_VERSION)
7118 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7119 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007120
cristy3ed852e2009-09-05 21:47:34 +00007121 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7122 {
7123 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7124 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7125 }
7126#endif
glennrp47b9dd52010-11-24 18:12:06 +00007127
cristy3ed852e2009-09-05 21:47:34 +00007128 if (*version != '\0')
7129 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007130
cristy3ed852e2009-09-05 21:47:34 +00007131#if defined(MAGICKCORE_PNG_DELEGATE)
7132 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7133 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7134#endif
glennrp47b9dd52010-11-24 18:12:06 +00007135
cristy3ed852e2009-09-05 21:47:34 +00007136 entry->magick=(IsImageFormatHandler *) IsPNG;
7137 entry->adjoin=MagickFalse;
7138 entry->description=ConstantString("opaque 24-bit RGB");
7139 entry->module=ConstantString("PNG");
7140 (void) RegisterMagickInfo(entry);
7141
7142 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007143
cristy3ed852e2009-09-05 21:47:34 +00007144#if defined(MAGICKCORE_PNG_DELEGATE)
7145 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7146 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7147#endif
glennrp47b9dd52010-11-24 18:12:06 +00007148
cristy3ed852e2009-09-05 21:47:34 +00007149 entry->magick=(IsImageFormatHandler *) IsPNG;
7150 entry->adjoin=MagickFalse;
7151 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7152 entry->module=ConstantString("PNG");
7153 (void) RegisterMagickInfo(entry);
7154
7155 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007156
cristy3ed852e2009-09-05 21:47:34 +00007157#if defined(JNG_SUPPORTED)
7158#if defined(MAGICKCORE_PNG_DELEGATE)
7159 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7160 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7161#endif
7162#endif
glennrp47b9dd52010-11-24 18:12:06 +00007163
cristy3ed852e2009-09-05 21:47:34 +00007164 entry->magick=(IsImageFormatHandler *) IsJNG;
7165 entry->adjoin=MagickFalse;
7166 entry->description=ConstantString("JPEG Network Graphics");
7167 entry->module=ConstantString("PNG");
7168 entry->note=ConstantString(JNGNote);
7169 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007170
cristy18b17442009-10-25 18:36:48 +00007171#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007172 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007173#endif
glennrp47b9dd52010-11-24 18:12:06 +00007174
cristy3ed852e2009-09-05 21:47:34 +00007175 return(MagickImageCoderSignature);
7176}
7177
7178/*
7179%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7180% %
7181% %
7182% %
7183% U n r e g i s t e r P N G I m a g e %
7184% %
7185% %
7186% %
7187%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7188%
7189% UnregisterPNGImage() removes format registrations made by the
7190% PNG module from the list of supported formats.
7191%
7192% The format of the UnregisterPNGImage method is:
7193%
7194% UnregisterPNGImage(void)
7195%
7196*/
7197ModuleExport void UnregisterPNGImage(void)
7198{
7199 (void) UnregisterMagickInfo("MNG");
7200 (void) UnregisterMagickInfo("PNG");
7201 (void) UnregisterMagickInfo("PNG8");
7202 (void) UnregisterMagickInfo("PNG24");
7203 (void) UnregisterMagickInfo("PNG32");
7204 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007205
cristy3ed852e2009-09-05 21:47:34 +00007206#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007207 if (ping_semaphore != (SemaphoreInfo *) NULL)
7208 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007209#endif
7210}
7211
7212#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007213#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007214/*
7215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7216% %
7217% %
7218% %
7219% W r i t e M N G I m a g e %
7220% %
7221% %
7222% %
7223%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7224%
7225% WriteMNGImage() writes an image in the Portable Network Graphics
7226% Group's "Multiple-image Network Graphics" encoded image format.
7227%
7228% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7229%
7230% The format of the WriteMNGImage method is:
7231%
cristy1e178e72011-08-28 19:44:34 +00007232% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7233% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007234%
7235% A description of each parameter follows.
7236%
7237% o image_info: the image info.
7238%
7239% o image: The image.
7240%
cristy1e178e72011-08-28 19:44:34 +00007241% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007242%
7243% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7244% "To do" under ReadPNGImage):
7245%
cristy3ed852e2009-09-05 21:47:34 +00007246% Preserve all unknown and not-yet-handled known chunks found in input
7247% PNG file and copy them into output PNG files according to the PNG
7248% copying rules.
7249%
7250% Write the iCCP chunk at MNG level when (icc profile length > 0)
7251%
7252% Improve selection of color type (use indexed-colour or indexed-colour
7253% with tRNS when 256 or fewer unique RGBA values are present).
7254%
7255% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7256% This will be complicated if we limit ourselves to generating MNG-LC
7257% files. For now we ignore disposal method 3 and simply overlay the next
7258% image on it.
7259%
7260% Check for identical PLTE's or PLTE/tRNS combinations and use a
7261% global MNG PLTE or PLTE/tRNS combination when appropriate.
7262% [mostly done 15 June 1999 but still need to take care of tRNS]
7263%
7264% Check for identical sRGB and replace with a global sRGB (and remove
7265% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7266% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7267% local gAMA/cHRM with local sRGB if appropriate).
7268%
7269% Check for identical sBIT chunks and write global ones.
7270%
7271% Provide option to skip writing the signature tEXt chunks.
7272%
7273% Use signatures to detect identical objects and reuse the first
7274% instance of such objects instead of writing duplicate objects.
7275%
7276% Use a smaller-than-32k value of compression window size when
7277% appropriate.
7278%
7279% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7280% ancillary text chunks and save profiles.
7281%
7282% Provide an option to force LC files (to ensure exact framing rate)
7283% instead of VLC.
7284%
7285% Provide an option to force VLC files instead of LC, even when offsets
7286% are present. This will involve expanding the embedded images with a
7287% transparent region at the top and/or left.
7288*/
7289
cristy3ed852e2009-09-05 21:47:34 +00007290static void
glennrpcf002022011-01-30 02:38:15 +00007291Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007292 png_info *ping_info, unsigned char *profile_type, unsigned char
7293 *profile_description, unsigned char *profile_data, png_uint_32 length)
7294{
cristy3ed852e2009-09-05 21:47:34 +00007295 png_textp
7296 text;
7297
cristybb503372010-05-27 20:51:26 +00007298 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007299 i;
7300
7301 unsigned char
7302 *sp;
7303
7304 png_charp
7305 dp;
7306
7307 png_uint_32
7308 allocated_length,
7309 description_length;
7310
7311 unsigned char
7312 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007313
cristy3ed852e2009-09-05 21:47:34 +00007314 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7315 return;
7316
7317 if (image_info->verbose)
7318 {
glennrp0fe50b42010-11-16 03:52:51 +00007319 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7320 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007321 }
glennrp0fe50b42010-11-16 03:52:51 +00007322
cristy3ed852e2009-09-05 21:47:34 +00007323 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7324 description_length=(png_uint_32) strlen((const char *) profile_description);
7325 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7326 + description_length);
7327 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7328 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7329 text[0].key[0]='\0';
7330 (void) ConcatenateMagickString(text[0].key,
7331 "Raw profile type ",MaxTextExtent);
7332 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7333 sp=profile_data;
7334 dp=text[0].text;
7335 *dp++='\n';
7336 (void) CopyMagickString(dp,(const char *) profile_description,
7337 allocated_length);
7338 dp+=description_length;
7339 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007340 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007341 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007342 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007343
cristybb503372010-05-27 20:51:26 +00007344 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007345 {
7346 if (i%36 == 0)
7347 *dp++='\n';
7348 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7349 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7350 }
glennrp47b9dd52010-11-24 18:12:06 +00007351
cristy3ed852e2009-09-05 21:47:34 +00007352 *dp++='\n';
7353 *dp='\0';
7354 text[0].text_length=(png_size_t) (dp-text[0].text);
7355 text[0].compression=image_info->compression == NoCompression ||
7356 (image_info->compression == UndefinedCompression &&
7357 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007358
cristy3ed852e2009-09-05 21:47:34 +00007359 if (text[0].text_length <= allocated_length)
7360 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007361
cristy3ed852e2009-09-05 21:47:34 +00007362 png_free(ping,text[0].text);
7363 png_free(ping,text[0].key);
7364 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007365}
7366
glennrpcf002022011-01-30 02:38:15 +00007367static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007368 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007369{
7370 char
7371 *name;
7372
7373 const StringInfo
7374 *profile;
7375
7376 unsigned char
7377 *data;
7378
7379 png_uint_32 length;
7380
7381 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007382
7383 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7384 {
cristy3ed852e2009-09-05 21:47:34 +00007385 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007386
cristy3ed852e2009-09-05 21:47:34 +00007387 if (profile != (const StringInfo *) NULL)
7388 {
7389 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007390 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007391
glennrp47b9dd52010-11-24 18:12:06 +00007392 if (LocaleNCompare(name,string,11) == 0)
7393 {
7394 if (logging != MagickFalse)
7395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7396 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007397
glennrpcf002022011-01-30 02:38:15 +00007398 ping_profile=CloneStringInfo(profile);
7399 data=GetStringInfoDatum(ping_profile),
7400 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007401 data[4]=data[3];
7402 data[3]=data[2];
7403 data[2]=data[1];
7404 data[1]=data[0];
7405 (void) WriteBlobMSBULong(image,length-5); /* data length */
7406 (void) WriteBlob(image,length-1,data+1);
7407 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007408 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007409 }
cristy3ed852e2009-09-05 21:47:34 +00007410 }
glennrp47b9dd52010-11-24 18:12:06 +00007411
cristy3ed852e2009-09-05 21:47:34 +00007412 name=GetNextImageProfile(image);
7413 }
glennrp47b9dd52010-11-24 18:12:06 +00007414
cristy3ed852e2009-09-05 21:47:34 +00007415 return(MagickTrue);
7416}
7417
glennrpb9cfe272010-12-21 15:08:06 +00007418
cristy3ed852e2009-09-05 21:47:34 +00007419/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007420static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +00007421 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007422{
7423 Image
7424 *image;
7425
7426 ImageInfo
7427 *image_info;
7428
cristy3ed852e2009-09-05 21:47:34 +00007429 char
7430 s[2];
7431
7432 const char
7433 *name,
7434 *property,
7435 *value;
7436
7437 const StringInfo
7438 *profile;
7439
cristy3ed852e2009-09-05 21:47:34 +00007440 int
cristy3ed852e2009-09-05 21:47:34 +00007441 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007442 pass;
7443
glennrpe9c26dc2010-05-30 01:56:35 +00007444 png_byte
7445 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007446
glennrp39992b42010-11-14 00:03:43 +00007447 png_color
7448 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007449
glennrp5af765f2010-03-30 11:12:18 +00007450 png_color_16
7451 ping_background,
7452 ping_trans_color;
7453
cristy3ed852e2009-09-05 21:47:34 +00007454 png_info
7455 *ping_info;
7456
7457 png_struct
7458 *ping;
7459
glennrp5af765f2010-03-30 11:12:18 +00007460 png_uint_32
7461 ping_height,
7462 ping_width;
7463
cristybb503372010-05-27 20:51:26 +00007464 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007465 y;
7466
7467 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007468 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007469 logging,
glennrp58e01762011-01-07 15:28:54 +00007470 matte,
7471
glennrpda8f3a72011-02-27 23:54:12 +00007472 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007473 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007474 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007475 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007476 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007477 ping_have_bKGD,
7478 ping_have_pHYs,
7479 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007480
7481 ping_exclude_bKGD,
7482 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007483 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007484 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007485 ping_exclude_gAMA,
7486 ping_exclude_iCCP,
7487 /* ping_exclude_iTXt, */
7488 ping_exclude_oFFs,
7489 ping_exclude_pHYs,
7490 ping_exclude_sRGB,
7491 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007492 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007493 ping_exclude_vpAg,
7494 ping_exclude_zCCP, /* hex-encoded iCCP */
7495 ping_exclude_zTXt,
7496
glennrp8d3d6e52011-04-19 04:39:51 +00007497 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007498 ping_need_colortype_warning,
7499
glennrp82b3c532011-03-22 19:20:54 +00007500 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007501 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007502 tried_333,
7503 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007504
7505 QuantumInfo
7506 *quantum_info;
7507
cristybb503372010-05-27 20:51:26 +00007508 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007509 i,
7510 x;
7511
7512 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007513 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007514
glennrp5af765f2010-03-30 11:12:18 +00007515 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007516 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007517 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007518 ping_color_type,
7519 ping_interlace_method,
7520 ping_compression_method,
7521 ping_filter_method,
7522 ping_num_trans;
7523
cristybb503372010-05-27 20:51:26 +00007524 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007525 image_depth,
7526 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007527
cristybb503372010-05-27 20:51:26 +00007528 size_t
cristy3ed852e2009-09-05 21:47:34 +00007529 quality,
7530 rowbytes,
7531 save_image_depth;
7532
glennrpdfd70802010-11-14 01:23:35 +00007533 int
glennrpfd05d622011-02-25 04:10:33 +00007534 j,
glennrpf09bded2011-01-08 01:15:59 +00007535 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007536 number_opaque,
7537 number_semitransparent,
7538 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007539 ping_pHYs_unit_type;
7540
7541 png_uint_32
7542 ping_pHYs_x_resolution,
7543 ping_pHYs_y_resolution;
7544
cristy3ed852e2009-09-05 21:47:34 +00007545 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007546 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007547
glennrpb9cfe272010-12-21 15:08:06 +00007548 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7549 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007550 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007551 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007552
cristy3ed852e2009-09-05 21:47:34 +00007553#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007554 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007555#endif
7556
glennrp5af765f2010-03-30 11:12:18 +00007557 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007558 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007559 ping_color_type=0,
7560 ping_interlace_method=0,
7561 ping_compression_method=0,
7562 ping_filter_method=0,
7563 ping_num_trans = 0;
7564
7565 ping_background.red = 0;
7566 ping_background.green = 0;
7567 ping_background.blue = 0;
7568 ping_background.gray = 0;
7569 ping_background.index = 0;
7570
7571 ping_trans_color.red=0;
7572 ping_trans_color.green=0;
7573 ping_trans_color.blue=0;
7574 ping_trans_color.gray=0;
7575
glennrpdfd70802010-11-14 01:23:35 +00007576 ping_pHYs_unit_type = 0;
7577 ping_pHYs_x_resolution = 0;
7578 ping_pHYs_y_resolution = 0;
7579
glennrpda8f3a72011-02-27 23:54:12 +00007580 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007581 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007582 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007583 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007584 ping_have_bKGD=MagickFalse;
7585 ping_have_pHYs=MagickFalse;
7586 ping_have_tRNS=MagickFalse;
7587
glennrp0e8ea192010-12-24 18:00:33 +00007588 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7589 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007590 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007591 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007592 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007593 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7594 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7595 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7596 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7597 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7598 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007599 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007600 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7601 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7602 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7603
glennrp8d3d6e52011-04-19 04:39:51 +00007604 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007605 ping_need_colortype_warning = MagickFalse;
7606
cristy0d57eec2011-09-04 22:13:56 +00007607 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7608 * i.e., eliminate the ICC profile and set image->rendering_intent.
7609 * Note that this will not involve any changes to the actual pixels
7610 * but merely passes information to applications that read the resulting
7611 * PNG image.
7612 */
7613 if (ping_exclude_sRGB == MagickFalse)
7614 {
7615 char
7616 *name;
7617
7618 const StringInfo
7619 *profile;
7620
7621 ResetImageProfileIterator(image);
7622 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7623 {
7624 profile=GetImageProfile(image,name);
7625
7626 if (profile != (StringInfo *) NULL)
7627 {
7628 if ((LocaleCompare(name,"ICC") == 0) ||
glennrp29a106e2011-09-06 17:11:42 +00007629 (LocaleCompare(name,"ICM") == 0))
7630 {
glennrpee7b4c02011-10-04 01:21:09 +00007631 int
7632 icheck;
7633
7634 /* 0: not a known sRGB profile
7635 * 1: HP-Microsoft sRGB v2
7636 * 2: ICC sRGB v4 perceptual
7637 * 3: ICC sRGB v2 perceptual no black-compensation
7638 */
7639 png_uint_32
7640 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7641 check_len[4] = {0, 3144, 60960, 3052};
7642
7643 png_uint_32
7644 length,
7645 profile_crc;
7646
cristy0d57eec2011-09-04 22:13:56 +00007647 unsigned char
7648 *data;
7649
glennrp29a106e2011-09-06 17:11:42 +00007650 length=(png_uint_32) GetStringInfoLength(profile);
7651
glennrpee7b4c02011-10-04 01:21:09 +00007652 for (icheck=3; icheck > 0; icheck--)
cristy0d57eec2011-09-04 22:13:56 +00007653 {
glennrpee7b4c02011-10-04 01:21:09 +00007654 if (length == check_len[icheck])
glennrp29a106e2011-09-06 17:11:42 +00007655 {
glennrpee7b4c02011-10-04 01:21:09 +00007656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7657 " Got a %lu-byte ICC profile (potentially sRGB)",
7658 (unsigned long) length);
glennrp29a106e2011-09-06 17:11:42 +00007659
glennrpee7b4c02011-10-04 01:21:09 +00007660 data=GetStringInfoDatum(profile);
7661 profile_crc=crc32(0,data,length);
glennrp29a106e2011-09-06 17:11:42 +00007662
glennrpee7b4c02011-10-04 01:21:09 +00007663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe54cd4b2011-10-04 01:31:35 +00007664 " with crc=%8x",(unsigned int) profile_crc);
glennrpee7b4c02011-10-04 01:21:09 +00007665
7666 if (profile_crc == check_crc[icheck])
7667 {
7668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7669 " It is sRGB.");
7670 if (image->rendering_intent==UndefinedIntent)
7671 image->rendering_intent=PerceptualIntent;
7672 break;
7673 }
glennrp29a106e2011-09-06 17:11:42 +00007674 }
glennrp29a106e2011-09-06 17:11:42 +00007675 }
glennrpee7b4c02011-10-04 01:21:09 +00007676 if (icheck == 0)
glennrp29a106e2011-09-06 17:11:42 +00007677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpee7b4c02011-10-04 01:21:09 +00007678 " Got a %lu-byte ICC profile",
glennrp29a106e2011-09-06 17:11:42 +00007679 (unsigned long) length);
7680 }
cristy0d57eec2011-09-04 22:13:56 +00007681 }
7682 name=GetNextImageProfile(image);
7683 }
7684 }
7685
glennrp8bb3a022010-12-13 20:40:04 +00007686 number_opaque = 0;
7687 number_semitransparent = 0;
7688 number_transparent = 0;
7689
glennrpfd05d622011-02-25 04:10:33 +00007690 if (logging != MagickFalse)
7691 {
7692 if (image->storage_class == UndefinedClass)
7693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7694 " storage_class=UndefinedClass");
7695 if (image->storage_class == DirectClass)
7696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7697 " storage_class=DirectClass");
7698 if (image->storage_class == PseudoClass)
7699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7700 " storage_class=PseudoClass");
7701 }
glennrp28af3712011-04-06 18:07:30 +00007702
glennrp7e65e932011-08-19 02:31:16 +00007703 if (image->storage_class == PseudoClass &&
7704 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7705 (mng_info->write_png_colortype != 0 &&
7706 mng_info->write_png_colortype != 4)))
7707 {
7708 (void) SyncImage(image);
7709 image->storage_class = DirectClass;
7710 }
7711
glennrpc6c391a2011-04-27 02:23:56 +00007712 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007713 {
glennrpc6c391a2011-04-27 02:23:56 +00007714 if (image->storage_class != PseudoClass && image->colormap != NULL)
7715 {
7716 /* Free the bogus colormap; it can cause trouble later */
7717 if (logging != MagickFalse)
7718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7719 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00007720 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00007721 image->colormap=NULL;
7722 }
glennrp28af3712011-04-06 18:07:30 +00007723 }
glennrpbb4f99d2011-05-22 11:13:17 +00007724
cristy510d06a2011-07-06 23:43:54 +00007725 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007726 (void) TransformImageColorspace(image,RGBColorspace);
glennrp0fe50b42010-11-16 03:52:51 +00007727
glennrp3241bd02010-12-12 04:36:28 +00007728 /*
7729 Sometimes we get PseudoClass images whose RGB values don't match
7730 the colors in the colormap. This code syncs the RGB values.
7731 */
7732 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7733 (void) SyncImage(image);
7734
glennrpa6a06632011-01-19 15:15:34 +00007735#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7736 if (image->depth > 8)
7737 {
7738 if (logging != MagickFalse)
7739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7740 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7741
7742 image->depth=8;
7743 }
7744#endif
7745
glennrp8e58efd2011-05-20 12:16:29 +00007746 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007747 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7748 {
cristy4c08aed2011-07-01 19:47:50 +00007749 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007750 *r;
7751
7752 ExceptionInfo
7753 *exception;
7754
7755 exception=(&image->exception);
7756
7757 if (image->depth > 8)
7758 {
7759#if MAGICKCORE_QUANTUM_DEPTH > 16
7760 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007761 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007762
7763 for (y=0; y < (ssize_t) image->rows; y++)
7764 {
7765 r=GetAuthenticPixels(image,0,y,image->columns,1,
7766 exception);
7767
cristy4c08aed2011-07-01 19:47:50 +00007768 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007769 break;
7770
7771 for (x=0; x < (ssize_t) image->columns; x++)
7772 {
glennrp54cf7972011-08-06 14:28:09 +00007773 LBR16PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007774 r++;
7775 }
glennrpbb4f99d2011-05-22 11:13:17 +00007776
glennrp8e58efd2011-05-20 12:16:29 +00007777 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7778 break;
7779 }
7780
7781 if (image->storage_class == PseudoClass && image->colormap != NULL)
7782 {
cristy3e08f112011-05-24 13:19:30 +00007783 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007784 {
glennrp91d99252011-06-25 14:30:13 +00007785 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007786 }
7787 }
7788#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7789 }
7790
7791 else if (image->depth > 4)
7792 {
7793#if MAGICKCORE_QUANTUM_DEPTH > 8
7794 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007795 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007796
7797 for (y=0; y < (ssize_t) image->rows; y++)
7798 {
7799 r=GetAuthenticPixels(image,0,y,image->columns,1,
7800 exception);
7801
cristy4c08aed2011-07-01 19:47:50 +00007802 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007803 break;
7804
7805 for (x=0; x < (ssize_t) image->columns; x++)
7806 {
glennrp54cf7972011-08-06 14:28:09 +00007807 LBR08PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007808 r++;
7809 }
glennrpbb4f99d2011-05-22 11:13:17 +00007810
glennrp8e58efd2011-05-20 12:16:29 +00007811 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7812 break;
7813 }
7814
7815 if (image->storage_class == PseudoClass && image->colormap != NULL)
7816 {
cristy3e08f112011-05-24 13:19:30 +00007817 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007818 {
glennrp91d99252011-06-25 14:30:13 +00007819 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007820 }
7821 }
7822#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7823 }
7824 else
7825 if (image->depth > 2)
7826 {
7827 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007828 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007829
7830 for (y=0; y < (ssize_t) image->rows; y++)
7831 {
7832 r=GetAuthenticPixels(image,0,y,image->columns,1,
7833 exception);
7834
cristy4c08aed2011-07-01 19:47:50 +00007835 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007836 break;
7837
7838 for (x=0; x < (ssize_t) image->columns; x++)
7839 {
glennrp54cf7972011-08-06 14:28:09 +00007840 LBR04PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007841 r++;
7842 }
glennrpbb4f99d2011-05-22 11:13:17 +00007843
glennrp8e58efd2011-05-20 12:16:29 +00007844 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7845 break;
7846 }
7847
7848 if (image->storage_class == PseudoClass && image->colormap != NULL)
7849 {
cristy3e08f112011-05-24 13:19:30 +00007850 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007851 {
glennrp91d99252011-06-25 14:30:13 +00007852 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007853 }
7854 }
7855 }
7856
7857 else if (image->depth > 1)
7858 {
7859 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007860 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007861
7862 for (y=0; y < (ssize_t) image->rows; y++)
7863 {
7864 r=GetAuthenticPixels(image,0,y,image->columns,1,
7865 exception);
7866
cristy4c08aed2011-07-01 19:47:50 +00007867 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007868 break;
7869
7870 for (x=0; x < (ssize_t) image->columns; x++)
7871 {
glennrp54cf7972011-08-06 14:28:09 +00007872 LBR02PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007873 r++;
7874 }
glennrpbb4f99d2011-05-22 11:13:17 +00007875
glennrp8e58efd2011-05-20 12:16:29 +00007876 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7877 break;
7878 }
7879
7880 if (image->storage_class == PseudoClass && image->colormap != NULL)
7881 {
cristy3e08f112011-05-24 13:19:30 +00007882 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007883 {
glennrp91d99252011-06-25 14:30:13 +00007884 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007885 }
7886 }
7887 }
7888 else
7889 {
7890 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007891 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007892
7893 for (y=0; y < (ssize_t) image->rows; y++)
7894 {
7895 r=GetAuthenticPixels(image,0,y,image->columns,1,
7896 exception);
7897
cristy4c08aed2011-07-01 19:47:50 +00007898 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007899 break;
7900
7901 for (x=0; x < (ssize_t) image->columns; x++)
7902 {
glennrp54cf7972011-08-06 14:28:09 +00007903 LBR01PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007904 r++;
7905 }
glennrpbb4f99d2011-05-22 11:13:17 +00007906
glennrp8e58efd2011-05-20 12:16:29 +00007907 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7908 break;
7909 }
7910
7911 if (image->storage_class == PseudoClass && image->colormap != NULL)
7912 {
cristy3e08f112011-05-24 13:19:30 +00007913 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007914 {
glennrp91d99252011-06-25 14:30:13 +00007915 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007916 }
7917 }
7918 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007919 }
7920
glennrp67b9c1a2011-04-22 18:47:36 +00007921 /* To do: set to next higher multiple of 8 */
7922 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007923 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007924
glennrp2b013e42010-11-24 16:55:50 +00007925#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7926 /* PNG does not handle depths greater than 16 so reduce it even
7927 * if lossy
7928 */
glennrp8e58efd2011-05-20 12:16:29 +00007929 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007930 image->depth=16;
7931#endif
7932
glennrp3faa9a32011-04-23 14:00:25 +00007933#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007934 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007935 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007936 image->depth = 8;
7937#endif
7938
glennrpc8c2f062011-02-25 19:00:33 +00007939 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007940 * we reduce the transparency to binary and run again, then if there
7941 * 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 +00007942 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7943 * palette. Then (To do) we take care of a final reduction that is only
7944 * needed if there are still 256 colors present and one of them has both
7945 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007946 */
glennrp82b3c532011-03-22 19:20:54 +00007947
glennrp8ca51ad2011-05-12 21:22:32 +00007948 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007949 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007950 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007951
glennrp8ca51ad2011-05-12 21:22:32 +00007952 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007953 {
7954 /* BUILD_PALETTE
7955 *
7956 * Sometimes we get DirectClass images that have 256 colors or fewer.
7957 * This code will build a colormap.
7958 *
7959 * Also, sometimes we get PseudoClass images with an out-of-date
7960 * colormap. This code will replace the colormap with a new one.
7961 * Sometimes we get PseudoClass images that have more than 256 colors.
7962 * This code will delete the colormap and change the image to
7963 * DirectClass.
7964 *
cristy4c08aed2011-07-01 19:47:50 +00007965 * If image->matte is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00007966 * even though it sometimes contains left-over non-opaque values.
7967 *
7968 * Also we gather some information (number of opaque, transparent,
7969 * and semitransparent pixels, and whether the image has any non-gray
7970 * pixels or only black-and-white pixels) that we might need later.
7971 *
7972 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7973 * we need to check for bogus non-opaque values, at least.
7974 */
glennrp3c218112010-11-27 15:31:26 +00007975
glennrpd71e86a2011-02-24 01:28:37 +00007976 ExceptionInfo
7977 *exception;
glennrp3c218112010-11-27 15:31:26 +00007978
glennrpd71e86a2011-02-24 01:28:37 +00007979 int
7980 n;
glennrp3c218112010-11-27 15:31:26 +00007981
cristy101ab702011-10-13 13:06:32 +00007982 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00007983 opaque[260],
7984 semitransparent[260],
7985 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007986
cristy4c08aed2011-07-01 19:47:50 +00007987 register const Quantum
7988 *s;
glennrp8bb3a022010-12-13 20:40:04 +00007989
cristy4c08aed2011-07-01 19:47:50 +00007990 register Quantum
7991 *q,
glennrpfd05d622011-02-25 04:10:33 +00007992 *r;
7993
glennrpd71e86a2011-02-24 01:28:37 +00007994 if (logging != MagickFalse)
7995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7996 " Enter BUILD_PALETTE:");
7997
7998 if (logging != MagickFalse)
7999 {
glennrp03812ae2010-12-24 01:31:34 +00008000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008001 " image->columns=%.20g",(double) image->columns);
8002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8003 " image->rows=%.20g",(double) image->rows);
8004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8005 " image->matte=%.20g",(double) image->matte);
8006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8007 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008008
glennrpfd05d622011-02-25 04:10:33 +00008009 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008010 {
8011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008012 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008014 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008015
glennrpd71e86a2011-02-24 01:28:37 +00008016 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008017 {
glennrpd71e86a2011-02-24 01:28:37 +00008018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8019 " %d (%d,%d,%d,%d)",
8020 (int) i,
8021 (int) image->colormap[i].red,
8022 (int) image->colormap[i].green,
8023 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008024 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008025 }
glennrp2cc891a2010-12-24 13:44:32 +00008026
glennrpd71e86a2011-02-24 01:28:37 +00008027 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8028 {
8029 if (i > 255)
8030 {
8031 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8032 " %d (%d,%d,%d,%d)",
8033 (int) i,
8034 (int) image->colormap[i].red,
8035 (int) image->colormap[i].green,
8036 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008037 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008038 }
8039 }
glennrp03812ae2010-12-24 01:31:34 +00008040 }
glennrp7ddcc222010-12-11 05:01:05 +00008041
glennrpd71e86a2011-02-24 01:28:37 +00008042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8043 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008044
glennrpd71e86a2011-02-24 01:28:37 +00008045 if (image->colors == 0)
8046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8047 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008048
glennrp8d3d6e52011-04-19 04:39:51 +00008049 if (ping_preserve_colormap == MagickFalse)
8050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8051 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008052 }
8053
8054 exception=(&image->exception);
8055
8056 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008057 number_opaque = 0;
8058 number_semitransparent = 0;
8059 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008060
8061 for (y=0; y < (ssize_t) image->rows; y++)
8062 {
8063 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8064
cristyacd2ed22011-08-30 01:44:23 +00008065 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008066 break;
8067
8068 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008069 {
glennrp4737d522011-04-29 03:33:42 +00008070 if (image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008071 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008072 {
8073 if (number_opaque < 259)
8074 {
8075 if (number_opaque == 0)
8076 {
cristy101ab702011-10-13 13:06:32 +00008077 GetPixelInfoPixel(image, q, opaque);
cristy4c08aed2011-07-01 19:47:50 +00008078 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008079 number_opaque=1;
8080 }
glennrp2cc891a2010-12-24 13:44:32 +00008081
glennrpd71e86a2011-02-24 01:28:37 +00008082 for (i=0; i< (ssize_t) number_opaque; i++)
8083 {
cristy4c08aed2011-07-01 19:47:50 +00008084 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008085 break;
8086 }
glennrp7ddcc222010-12-11 05:01:05 +00008087
glennrpd71e86a2011-02-24 01:28:37 +00008088 if (i == (ssize_t) number_opaque &&
8089 number_opaque < 259)
8090 {
8091 number_opaque++;
cristy101ab702011-10-13 13:06:32 +00008092 GetPixelInfoPixel(image, q, opaque+i);
cristy4c08aed2011-07-01 19:47:50 +00008093 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008094 }
8095 }
8096 }
cristy4c08aed2011-07-01 19:47:50 +00008097 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008098 {
8099 if (number_transparent < 259)
8100 {
8101 if (number_transparent == 0)
8102 {
cristy101ab702011-10-13 13:06:32 +00008103 GetPixelInfoPixel(image, q, transparent);
cristy4c08aed2011-07-01 19:47:50 +00008104 ping_trans_color.red=(unsigned short)
8105 GetPixelRed(image,q);
8106 ping_trans_color.green=(unsigned short)
8107 GetPixelGreen(image,q);
8108 ping_trans_color.blue=(unsigned short)
8109 GetPixelBlue(image,q);
8110 ping_trans_color.gray=(unsigned short)
8111 GetPixelRed(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008112 number_transparent = 1;
8113 }
8114
8115 for (i=0; i< (ssize_t) number_transparent; i++)
8116 {
cristy4c08aed2011-07-01 19:47:50 +00008117 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008118 break;
8119 }
8120
8121 if (i == (ssize_t) number_transparent &&
8122 number_transparent < 259)
8123 {
8124 number_transparent++;
cristy101ab702011-10-13 13:06:32 +00008125 GetPixelInfoPixel(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008126 }
8127 }
8128 }
8129 else
8130 {
8131 if (number_semitransparent < 259)
8132 {
8133 if (number_semitransparent == 0)
8134 {
cristy101ab702011-10-13 13:06:32 +00008135 GetPixelInfoPixel(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008136 number_semitransparent = 1;
8137 }
8138
8139 for (i=0; i< (ssize_t) number_semitransparent; i++)
8140 {
cristy4c08aed2011-07-01 19:47:50 +00008141 if (IsPixelEquivalent(image,q, semitransparent+i)
8142 && GetPixelAlpha(image,q) ==
8143 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008144 break;
8145 }
8146
8147 if (i == (ssize_t) number_semitransparent &&
8148 number_semitransparent < 259)
8149 {
8150 number_semitransparent++;
cristy101ab702011-10-13 13:06:32 +00008151 GetPixelInfoPixel(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008152 }
8153 }
8154 }
cristyed231572011-07-14 02:18:59 +00008155 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008156 }
8157 }
8158
cristy4054bfb2011-08-29 23:41:39 +00008159 if (mng_info->write_png8 == MagickFalse &&
8160 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008161 {
8162 /* Add the background color to the palette, if it
8163 * isn't already there.
8164 */
glennrpc6c391a2011-04-27 02:23:56 +00008165 if (logging != MagickFalse)
8166 {
8167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8168 " Check colormap for background (%d,%d,%d)",
8169 (int) image->background_color.red,
8170 (int) image->background_color.green,
8171 (int) image->background_color.blue);
8172 }
glennrpd71e86a2011-02-24 01:28:37 +00008173 for (i=0; i<number_opaque; i++)
8174 {
glennrpca7ad3a2011-04-26 04:44:54 +00008175 if (opaque[i].red == image->background_color.red &&
8176 opaque[i].green == image->background_color.green &&
8177 opaque[i].blue == image->background_color.blue)
8178 break;
glennrpd71e86a2011-02-24 01:28:37 +00008179 }
glennrpd71e86a2011-02-24 01:28:37 +00008180 if (number_opaque < 259 && i == number_opaque)
8181 {
glennrp8e045c82011-04-27 16:40:27 +00008182 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008183 ping_background.index = i;
8184 if (logging != MagickFalse)
8185 {
8186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8187 " background_color index is %d",(int) i);
8188 }
8189
glennrpd71e86a2011-02-24 01:28:37 +00008190 }
glennrpa080bc32011-03-11 18:03:44 +00008191 else if (logging != MagickFalse)
8192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8193 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008194 }
8195
8196 image_colors=number_opaque+number_transparent+number_semitransparent;
8197
glennrpa080bc32011-03-11 18:03:44 +00008198 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8199 {
8200 /* No room for the background color; remove it. */
8201 number_opaque--;
8202 image_colors--;
8203 }
8204
glennrpd71e86a2011-02-24 01:28:37 +00008205 if (logging != MagickFalse)
8206 {
8207 if (image_colors > 256)
8208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8209 " image has more than 256 colors");
8210
8211 else
8212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8213 " image has %d colors",image_colors);
8214 }
8215
glennrp8d3d6e52011-04-19 04:39:51 +00008216 if (ping_preserve_colormap != MagickFalse)
8217 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008218
glennrpfd05d622011-02-25 04:10:33 +00008219 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008220 {
8221 ping_have_color=MagickFalse;
8222 ping_have_non_bw=MagickFalse;
8223
8224 if(image_colors > 256)
8225 {
8226 for (y=0; y < (ssize_t) image->rows; y++)
8227 {
8228 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8229
cristyacd2ed22011-08-30 01:44:23 +00008230 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008231 break;
8232
glennrpe5e6b802011-07-20 14:44:40 +00008233 s=q;
8234 for (x=0; x < (ssize_t) image->columns; x++)
8235 {
8236 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8237 GetPixelRed(image,s) != GetPixelBlue(image,s))
8238 {
8239 ping_have_color=MagickTrue;
8240 ping_have_non_bw=MagickTrue;
8241 break;
8242 }
8243 s+=GetPixelChannels(image);
8244 }
8245
8246 if (ping_have_color != MagickFalse)
8247 break;
8248
glennrpd71e86a2011-02-24 01:28:37 +00008249 /* Worst case is black-and-white; we are looking at every
8250 * pixel twice.
8251 */
8252
glennrpd71e86a2011-02-24 01:28:37 +00008253 if (ping_have_non_bw == MagickFalse)
8254 {
8255 s=q;
8256 for (x=0; x < (ssize_t) image->columns; x++)
8257 {
cristy4c08aed2011-07-01 19:47:50 +00008258 if (GetPixelRed(image,s) != 0 &&
8259 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008260 {
8261 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008262 break;
glennrpd71e86a2011-02-24 01:28:37 +00008263 }
cristyed231572011-07-14 02:18:59 +00008264 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008265 }
glennrpe5e6b802011-07-20 14:44:40 +00008266 }
glennrpd71e86a2011-02-24 01:28:37 +00008267 }
glennrpbb4f99d2011-05-22 11:13:17 +00008268 }
8269 }
glennrpd71e86a2011-02-24 01:28:37 +00008270
8271 if (image_colors < 257)
8272 {
cristy101ab702011-10-13 13:06:32 +00008273 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008274 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008275
glennrpd71e86a2011-02-24 01:28:37 +00008276 /*
8277 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008278 */
8279
glennrpd71e86a2011-02-24 01:28:37 +00008280 if (logging != MagickFalse)
8281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8282 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008283
glennrpd71e86a2011-02-24 01:28:37 +00008284 /* Sort palette, transparent first */;
8285
8286 n = 0;
8287
8288 for (i=0; i<number_transparent; i++)
8289 colormap[n++] = transparent[i];
8290
8291 for (i=0; i<number_semitransparent; i++)
8292 colormap[n++] = semitransparent[i];
8293
8294 for (i=0; i<number_opaque; i++)
8295 colormap[n++] = opaque[i];
8296
glennrpc6c391a2011-04-27 02:23:56 +00008297 ping_background.index +=
8298 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008299
glennrpd71e86a2011-02-24 01:28:37 +00008300 /* image_colors < 257; search the colormap instead of the pixels
8301 * to get ping_have_color and ping_have_non_bw
8302 */
8303 for (i=0; i<n; i++)
8304 {
8305 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008306 {
glennrpd71e86a2011-02-24 01:28:37 +00008307 if (colormap[i].red != colormap[i].green ||
8308 colormap[i].red != colormap[i].blue)
8309 {
8310 ping_have_color=MagickTrue;
8311 ping_have_non_bw=MagickTrue;
8312 break;
8313 }
8314 }
8315
8316 if (ping_have_non_bw == MagickFalse)
8317 {
8318 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008319 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008320 }
glennrp8bb3a022010-12-13 20:40:04 +00008321 }
8322
glennrpd71e86a2011-02-24 01:28:37 +00008323 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8324 (number_transparent == 0 && number_semitransparent == 0)) &&
8325 (((mng_info->write_png_colortype-1) ==
8326 PNG_COLOR_TYPE_PALETTE) ||
8327 (mng_info->write_png_colortype == 0)))
8328 {
glennrp6185c532011-01-14 17:58:40 +00008329 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008330 {
glennrpd71e86a2011-02-24 01:28:37 +00008331 if (n != (ssize_t) image_colors)
8332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8333 " image_colors (%d) and n (%d) don't match",
8334 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008335
glennrpd71e86a2011-02-24 01:28:37 +00008336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8337 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008338 }
glennrp03812ae2010-12-24 01:31:34 +00008339
glennrpd71e86a2011-02-24 01:28:37 +00008340 image->colors = image_colors;
8341
cristy018f07f2011-09-04 21:15:19 +00008342 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008343 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008344 ThrowWriterException(ResourceLimitError,
8345 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008346
8347 for (i=0; i< (ssize_t) image_colors; i++)
8348 image->colormap[i] = colormap[i];
8349
8350 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008351 {
glennrpd71e86a2011-02-24 01:28:37 +00008352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8353 " image->colors=%d (%d)",
8354 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008355
glennrpd71e86a2011-02-24 01:28:37 +00008356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8357 " Update the pixel indexes");
8358 }
glennrp6185c532011-01-14 17:58:40 +00008359
glennrpfd05d622011-02-25 04:10:33 +00008360 /* Sync the pixel indices with the new colormap */
8361
glennrpd71e86a2011-02-24 01:28:37 +00008362 for (y=0; y < (ssize_t) image->rows; y++)
8363 {
8364 q=GetAuthenticPixels(image,0,y,image->columns,1,
8365 exception);
glennrp6185c532011-01-14 17:58:40 +00008366
cristyacd2ed22011-08-30 01:44:23 +00008367 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008368 break;
glennrp6185c532011-01-14 17:58:40 +00008369
glennrpbb4f99d2011-05-22 11:13:17 +00008370
glennrpd71e86a2011-02-24 01:28:37 +00008371 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008372 {
glennrpd71e86a2011-02-24 01:28:37 +00008373 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008374 {
glennrpd71e86a2011-02-24 01:28:37 +00008375 if ((image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008376 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8377 image->colormap[i].red == GetPixelRed(image,q) &&
8378 image->colormap[i].green == GetPixelGreen(image,q) &&
8379 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008380 {
cristy4c08aed2011-07-01 19:47:50 +00008381 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008382 break;
glennrp6185c532011-01-14 17:58:40 +00008383 }
glennrp6185c532011-01-14 17:58:40 +00008384 }
cristyed231572011-07-14 02:18:59 +00008385 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008386 }
glennrp6185c532011-01-14 17:58:40 +00008387
glennrpd71e86a2011-02-24 01:28:37 +00008388 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8389 break;
8390 }
8391 }
8392 }
8393
8394 if (logging != MagickFalse)
8395 {
8396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8397 " image->colors=%d", (int) image->colors);
8398
8399 if (image->colormap != NULL)
8400 {
8401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008402 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008403
8404 for (i=0; i < (ssize_t) image->colors; i++)
8405 {
cristy72988482011-03-29 16:34:38 +00008406 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008407 {
8408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8409 " %d (%d,%d,%d,%d)",
8410 (int) i,
8411 (int) image->colormap[i].red,
8412 (int) image->colormap[i].green,
8413 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008414 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008415 }
glennrp6185c532011-01-14 17:58:40 +00008416 }
8417 }
glennrp03812ae2010-12-24 01:31:34 +00008418
glennrpd71e86a2011-02-24 01:28:37 +00008419 if (number_transparent < 257)
8420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8421 " number_transparent = %d",
8422 number_transparent);
8423 else
glennrp03812ae2010-12-24 01:31:34 +00008424
glennrpd71e86a2011-02-24 01:28:37 +00008425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8426 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008427
glennrpd71e86a2011-02-24 01:28:37 +00008428 if (number_opaque < 257)
8429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8430 " number_opaque = %d",
8431 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008432
glennrpd71e86a2011-02-24 01:28:37 +00008433 else
8434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8435 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008436
glennrpd71e86a2011-02-24 01:28:37 +00008437 if (number_semitransparent < 257)
8438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8439 " number_semitransparent = %d",
8440 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008441
glennrpd71e86a2011-02-24 01:28:37 +00008442 else
8443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8444 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008445
glennrpd71e86a2011-02-24 01:28:37 +00008446 if (ping_have_non_bw == MagickFalse)
8447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8448 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008449
glennrpd71e86a2011-02-24 01:28:37 +00008450 else if (ping_have_color == MagickFalse)
8451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8452 " All pixels and the background are gray");
8453
8454 else
8455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8456 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008457
glennrp03812ae2010-12-24 01:31:34 +00008458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8459 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008460 }
glennrpfd05d622011-02-25 04:10:33 +00008461
glennrpc8c2f062011-02-25 19:00:33 +00008462 if (mng_info->write_png8 == MagickFalse)
8463 break;
glennrpfd05d622011-02-25 04:10:33 +00008464
glennrpc8c2f062011-02-25 19:00:33 +00008465 /* Make any reductions necessary for the PNG8 format */
8466 if (image_colors <= 256 &&
8467 image_colors != 0 && image->colormap != NULL &&
8468 number_semitransparent == 0 &&
8469 number_transparent <= 1)
8470 break;
8471
8472 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008473 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8474 * transparent color so if more than one is transparent we merge
8475 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008476 */
glennrp130fc452011-08-20 03:43:18 +00008477 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008478 {
8479 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8480 " Thresholding the alpha channel to binary");
8481
8482 for (y=0; y < (ssize_t) image->rows; y++)
8483 {
8484 r=GetAuthenticPixels(image,0,y,image->columns,1,
8485 exception);
8486
cristy4c08aed2011-07-01 19:47:50 +00008487 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008488 break;
8489
8490 for (x=0; x < (ssize_t) image->columns; x++)
8491 {
glennrpf73547f2011-08-20 04:40:26 +00008492 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008493 {
cristy101ab702011-10-13 13:06:32 +00008494 SetPixelPixelInfo(image,&image->background_color,r);
cristy4c08aed2011-07-01 19:47:50 +00008495 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008496 }
8497 else
cristy4c08aed2011-07-01 19:47:50 +00008498 SetPixelAlpha(image,OpaqueAlpha,r);
cristyed231572011-07-14 02:18:59 +00008499 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008500 }
glennrpbb4f99d2011-05-22 11:13:17 +00008501
glennrpc8c2f062011-02-25 19:00:33 +00008502 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8503 break;
8504
8505 if (image_colors != 0 && image_colors <= 256 &&
8506 image->colormap != NULL)
8507 for (i=0; i<image_colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00008508 image->colormap[i].alpha =
8509 (image->colormap[i].alpha > TransparentAlpha/2 ?
8510 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008511 }
8512 continue;
8513 }
8514
8515 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008516 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8517 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8518 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008519 */
glennrpd3371642011-03-22 19:42:23 +00008520 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8521 {
8522 if (logging != MagickFalse)
8523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8524 " Quantizing the background color to 4-4-4");
8525
8526 tried_444 = MagickTrue;
8527
glennrp91d99252011-06-25 14:30:13 +00008528 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008529
8530 if (logging != MagickFalse)
8531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8532 " Quantizing the pixel colors to 4-4-4");
8533
8534 if (image->colormap == NULL)
8535 {
8536 for (y=0; y < (ssize_t) image->rows; y++)
8537 {
8538 r=GetAuthenticPixels(image,0,y,image->columns,1,
8539 exception);
8540
cristy4c08aed2011-07-01 19:47:50 +00008541 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008542 break;
8543
8544 for (x=0; x < (ssize_t) image->columns; x++)
8545 {
cristy4c08aed2011-07-01 19:47:50 +00008546 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008547 LBR04PixelRGB(r);
glennrpd3371642011-03-22 19:42:23 +00008548 r++;
8549 }
glennrpbb4f99d2011-05-22 11:13:17 +00008550
glennrpd3371642011-03-22 19:42:23 +00008551 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8552 break;
8553 }
8554 }
8555
8556 else /* Should not reach this; colormap already exists and
8557 must be <= 256 */
8558 {
8559 if (logging != MagickFalse)
8560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8561 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008562
glennrpd3371642011-03-22 19:42:23 +00008563 for (i=0; i<image_colors; i++)
8564 {
glennrp91d99252011-06-25 14:30:13 +00008565 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008566 }
8567 }
8568 continue;
8569 }
8570
glennrp82b3c532011-03-22 19:20:54 +00008571 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8572 {
8573 if (logging != MagickFalse)
8574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8575 " Quantizing the background color to 3-3-3");
8576
8577 tried_333 = MagickTrue;
8578
glennrp91d99252011-06-25 14:30:13 +00008579 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008580
8581 if (logging != MagickFalse)
8582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008583 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008584
8585 if (image->colormap == NULL)
8586 {
8587 for (y=0; y < (ssize_t) image->rows; y++)
8588 {
8589 r=GetAuthenticPixels(image,0,y,image->columns,1,
8590 exception);
8591
cristy4c08aed2011-07-01 19:47:50 +00008592 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008593 break;
8594
8595 for (x=0; x < (ssize_t) image->columns; x++)
8596 {
cristy4c08aed2011-07-01 19:47:50 +00008597 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8598 LBR03RGB(r);
glennrp82b3c532011-03-22 19:20:54 +00008599 r++;
8600 }
glennrpbb4f99d2011-05-22 11:13:17 +00008601
glennrp82b3c532011-03-22 19:20:54 +00008602 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8603 break;
8604 }
8605 }
8606
8607 else /* Should not reach this; colormap already exists and
8608 must be <= 256 */
8609 {
8610 if (logging != MagickFalse)
8611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008612 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008613 for (i=0; i<image_colors; i++)
8614 {
glennrp91d99252011-06-25 14:30:13 +00008615 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008616 }
glennrpd3371642011-03-22 19:42:23 +00008617 }
8618 continue;
glennrp82b3c532011-03-22 19:20:54 +00008619 }
glennrpc8c2f062011-02-25 19:00:33 +00008620
glennrp8ca51ad2011-05-12 21:22:32 +00008621 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008622 {
8623 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008625 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008626
glennrp8ca51ad2011-05-12 21:22:32 +00008627 tried_332 = MagickTrue;
8628
glennrp3faa9a32011-04-23 14:00:25 +00008629 /* Red and green were already done so we only quantize the blue
8630 * channel
8631 */
8632
glennrp91d99252011-06-25 14:30:13 +00008633 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008634
glennrpc8c2f062011-02-25 19:00:33 +00008635 if (logging != MagickFalse)
8636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008637 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008638
glennrpc8c2f062011-02-25 19:00:33 +00008639 if (image->colormap == NULL)
8640 {
8641 for (y=0; y < (ssize_t) image->rows; y++)
8642 {
8643 r=GetAuthenticPixels(image,0,y,image->columns,1,
8644 exception);
8645
cristy4c08aed2011-07-01 19:47:50 +00008646 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008647 break;
8648
8649 for (x=0; x < (ssize_t) image->columns; x++)
8650 {
cristy4c08aed2011-07-01 19:47:50 +00008651 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008652 LBR02PixelBlue(r);
glennrp52a479c2011-02-26 21:14:38 +00008653 r++;
glennrpc8c2f062011-02-25 19:00:33 +00008654 }
glennrpbb4f99d2011-05-22 11:13:17 +00008655
glennrpc8c2f062011-02-25 19:00:33 +00008656 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8657 break;
8658 }
8659 }
glennrpfd05d622011-02-25 04:10:33 +00008660
glennrpc8c2f062011-02-25 19:00:33 +00008661 else /* Should not reach this; colormap already exists and
8662 must be <= 256 */
8663 {
8664 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008666 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008667 for (i=0; i<image_colors; i++)
8668 {
glennrp91d99252011-06-25 14:30:13 +00008669 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008670 }
8671 }
8672 continue;
8673 }
8674 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008675
8676 if (image_colors == 0 || image_colors > 256)
8677 {
8678 /* Take care of special case with 256 colors + 1 transparent
8679 * color. We don't need to quantize to 2-3-2-1; we only need to
8680 * eliminate one color, so we'll merge the two darkest red
8681 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8682 */
8683 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8684 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8685 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8686 {
8687 image->background_color.red=ScaleCharToQuantum(0x24);
8688 }
glennrpbb4f99d2011-05-22 11:13:17 +00008689
glennrp8ca51ad2011-05-12 21:22:32 +00008690 if (image->colormap == NULL)
8691 {
8692 for (y=0; y < (ssize_t) image->rows; y++)
8693 {
8694 r=GetAuthenticPixels(image,0,y,image->columns,1,
8695 exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008696
cristy4c08aed2011-07-01 19:47:50 +00008697 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008698 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008699
glennrp8ca51ad2011-05-12 21:22:32 +00008700 for (x=0; x < (ssize_t) image->columns; x++)
8701 {
cristy4c08aed2011-07-01 19:47:50 +00008702 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8703 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8704 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8705 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008706 {
cristy4c08aed2011-07-01 19:47:50 +00008707 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008708 }
cristyed231572011-07-14 02:18:59 +00008709 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008710 }
glennrpbb4f99d2011-05-22 11:13:17 +00008711
glennrp8ca51ad2011-05-12 21:22:32 +00008712 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8713 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008714
glennrp8ca51ad2011-05-12 21:22:32 +00008715 }
8716 }
8717
8718 else
8719 {
8720 for (i=0; i<image_colors; i++)
8721 {
8722 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8723 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8724 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8725 {
8726 image->colormap[i].red=ScaleCharToQuantum(0x24);
8727 }
8728 }
8729 }
8730 }
glennrpd71e86a2011-02-24 01:28:37 +00008731 }
glennrpfd05d622011-02-25 04:10:33 +00008732 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008733
glennrpfd05d622011-02-25 04:10:33 +00008734 /* If we are excluding the tRNS chunk and there is transparency,
8735 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8736 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008737 */
glennrp0e8ea192010-12-24 18:00:33 +00008738 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8739 (number_transparent != 0 || number_semitransparent != 0))
8740 {
glennrpd17915c2011-04-29 14:24:22 +00008741 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008742
8743 if (ping_have_color == MagickFalse)
8744 mng_info->write_png_colortype = 5;
8745
8746 else
8747 mng_info->write_png_colortype = 7;
8748
glennrp8d579662011-02-23 02:05:02 +00008749 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008750 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008751 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008752
glennrp0e8ea192010-12-24 18:00:33 +00008753 }
8754
glennrpfd05d622011-02-25 04:10:33 +00008755 /* See if cheap transparency is possible. It is only possible
8756 * when there is a single transparent color, no semitransparent
8757 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008758 * as the transparent color. We only need this information if
8759 * we are writing a PNG with colortype 0 or 2, and we have not
8760 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008761 */
glennrp5a39f372011-02-25 04:52:16 +00008762 if (number_transparent == 1 &&
8763 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008764 {
8765 ping_have_cheap_transparency = MagickTrue;
8766
8767 if (number_semitransparent != 0)
8768 ping_have_cheap_transparency = MagickFalse;
8769
8770 else if (image_colors == 0 || image_colors > 256 ||
8771 image->colormap == NULL)
8772 {
8773 ExceptionInfo
8774 *exception;
8775
cristy4c08aed2011-07-01 19:47:50 +00008776 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008777 *q;
8778
8779 exception=(&image->exception);
8780
8781 for (y=0; y < (ssize_t) image->rows; y++)
8782 {
8783 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8784
cristyacd2ed22011-08-30 01:44:23 +00008785 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008786 break;
8787
8788 for (x=0; x < (ssize_t) image->columns; x++)
8789 {
cristy4c08aed2011-07-01 19:47:50 +00008790 if (GetPixelAlpha(image,q) != TransparentAlpha &&
glennrp847370c2011-07-05 17:37:15 +00008791 (unsigned short) GetPixelRed(image,q) ==
8792 ping_trans_color.red &&
8793 (unsigned short) GetPixelGreen(image,q) ==
8794 ping_trans_color.green &&
8795 (unsigned short) GetPixelBlue(image,q) ==
8796 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008797 {
8798 ping_have_cheap_transparency = MagickFalse;
8799 break;
8800 }
8801
cristyed231572011-07-14 02:18:59 +00008802 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008803 }
glennrpbb4f99d2011-05-22 11:13:17 +00008804
glennrpfd05d622011-02-25 04:10:33 +00008805 if (ping_have_cheap_transparency == MagickFalse)
8806 break;
8807 }
8808 }
8809 else
8810 {
glennrp67b9c1a2011-04-22 18:47:36 +00008811 /* Assuming that image->colormap[0] is the one transparent color
8812 * and that all others are opaque.
8813 */
glennrpfd05d622011-02-25 04:10:33 +00008814 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008815 for (i=1; i<image_colors; i++)
8816 if (image->colormap[i].red == image->colormap[0].red &&
8817 image->colormap[i].green == image->colormap[0].green &&
8818 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008819 {
glennrp67b9c1a2011-04-22 18:47:36 +00008820 ping_have_cheap_transparency = MagickFalse;
8821 break;
glennrpfd05d622011-02-25 04:10:33 +00008822 }
8823 }
glennrpbb4f99d2011-05-22 11:13:17 +00008824
glennrpfd05d622011-02-25 04:10:33 +00008825 if (logging != MagickFalse)
8826 {
8827 if (ping_have_cheap_transparency == MagickFalse)
8828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8829 " Cheap transparency is not possible.");
8830
8831 else
8832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8833 " Cheap transparency is possible.");
8834 }
8835 }
8836 else
8837 ping_have_cheap_transparency = MagickFalse;
8838
glennrp8640fb52010-11-23 15:48:26 +00008839 image_depth=image->depth;
8840
glennrp26c990a2010-11-23 02:23:20 +00008841 quantum_info = (QuantumInfo *) NULL;
8842 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008843 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008844 image_matte=image->matte;
8845
glennrp0fe50b42010-11-16 03:52:51 +00008846 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008847 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008848
glennrp52a479c2011-02-26 21:14:38 +00008849 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8850 (image->colors == 0 || image->colormap == NULL))
8851 {
glennrp52a479c2011-02-26 21:14:38 +00008852 image_info=DestroyImageInfo(image_info);
8853 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008854 (void) ThrowMagickException(&IMimage->exception,
8855 GetMagickModule(),CoderError,
8856 "Cannot write PNG8 or color-type 3; colormap is NULL",
8857 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008858#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8859 UnlockSemaphoreInfo(ping_semaphore);
8860#endif
8861 return(MagickFalse);
8862 }
8863
cristy3ed852e2009-09-05 21:47:34 +00008864 /*
8865 Allocate the PNG structures
8866 */
8867#ifdef PNG_USER_MEM_SUPPORTED
8868 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008869 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8870 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008871
cristy3ed852e2009-09-05 21:47:34 +00008872#else
8873 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008874 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008875
cristy3ed852e2009-09-05 21:47:34 +00008876#endif
8877 if (ping == (png_struct *) NULL)
8878 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008879
cristy3ed852e2009-09-05 21:47:34 +00008880 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008881
cristy3ed852e2009-09-05 21:47:34 +00008882 if (ping_info == (png_info *) NULL)
8883 {
8884 png_destroy_write_struct(&ping,(png_info **) NULL);
8885 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8886 }
glennrp0fe50b42010-11-16 03:52:51 +00008887
cristy3ed852e2009-09-05 21:47:34 +00008888 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008889 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008890
glennrp5af765f2010-03-30 11:12:18 +00008891 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008892 {
8893 /*
8894 PNG write failed.
8895 */
8896#ifdef PNG_DEBUG
8897 if (image_info->verbose)
8898 (void) printf("PNG write has failed.\n");
8899#endif
8900 png_destroy_write_struct(&ping,&ping_info);
8901#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008902 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008903#endif
glennrpda8f3a72011-02-27 23:54:12 +00008904 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008905 (void) CloseBlob(image);
8906 image_info=DestroyImageInfo(image_info);
8907 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008908 return(MagickFalse);
8909 }
8910 /*
8911 Prepare PNG for writing.
8912 */
8913#if defined(PNG_MNG_FEATURES_SUPPORTED)
8914 if (mng_info->write_mng)
8915 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008916
cristy3ed852e2009-09-05 21:47:34 +00008917#else
8918# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8919 if (mng_info->write_mng)
8920 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008921
cristy3ed852e2009-09-05 21:47:34 +00008922# endif
8923#endif
glennrp2b013e42010-11-24 16:55:50 +00008924
cristy3ed852e2009-09-05 21:47:34 +00008925 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008926
cristy4e5bc842010-06-09 13:56:01 +00008927 ping_width=(png_uint_32) image->columns;
8928 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008929
cristy3ed852e2009-09-05 21:47:34 +00008930 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8931 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008932
cristy3ed852e2009-09-05 21:47:34 +00008933 if (mng_info->write_png_depth != 0)
8934 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008935
cristy3ed852e2009-09-05 21:47:34 +00008936 /* Adjust requested depth to next higher valid depth if necessary */
8937 if (image_depth > 8)
8938 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008939
cristy3ed852e2009-09-05 21:47:34 +00008940 if ((image_depth > 4) && (image_depth < 8))
8941 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008942
cristy3ed852e2009-09-05 21:47:34 +00008943 if (image_depth == 3)
8944 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008945
cristy3ed852e2009-09-05 21:47:34 +00008946 if (logging != MagickFalse)
8947 {
8948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008949 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008951 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008953 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008954 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008955 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008957 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008958 }
glennrp8640fb52010-11-23 15:48:26 +00008959
cristy3ed852e2009-09-05 21:47:34 +00008960 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008961 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008962
glennrp26f37912010-12-23 16:22:42 +00008963
cristy3ed852e2009-09-05 21:47:34 +00008964#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008965 if (ping_exclude_pHYs == MagickFalse)
8966 {
cristy3ed852e2009-09-05 21:47:34 +00008967 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8968 (!mng_info->write_mng || !mng_info->equal_physs))
8969 {
glennrp0fe50b42010-11-16 03:52:51 +00008970 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8972 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008973
8974 if (image->units == PixelsPerInchResolution)
8975 {
glennrpdfd70802010-11-14 01:23:35 +00008976 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008977 ping_pHYs_x_resolution=
8978 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8979 ping_pHYs_y_resolution=
8980 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008981 }
glennrpdfd70802010-11-14 01:23:35 +00008982
cristy3ed852e2009-09-05 21:47:34 +00008983 else if (image->units == PixelsPerCentimeterResolution)
8984 {
glennrpdfd70802010-11-14 01:23:35 +00008985 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008986 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8987 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008988 }
glennrp991d11d2010-11-12 21:55:28 +00008989
cristy3ed852e2009-09-05 21:47:34 +00008990 else
8991 {
glennrpdfd70802010-11-14 01:23:35 +00008992 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8993 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8994 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00008995 }
glennrp991d11d2010-11-12 21:55:28 +00008996
glennrp823b55c2011-03-14 18:46:46 +00008997 if (logging != MagickFalse)
8998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8999 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9000 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9001 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009002 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009003 }
glennrp26f37912010-12-23 16:22:42 +00009004 }
cristy3ed852e2009-09-05 21:47:34 +00009005#endif
glennrpa521b2f2010-10-29 04:11:03 +00009006
glennrp26f37912010-12-23 16:22:42 +00009007 if (ping_exclude_bKGD == MagickFalse)
9008 {
glennrpa521b2f2010-10-29 04:11:03 +00009009 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009010 {
glennrpa521b2f2010-10-29 04:11:03 +00009011 unsigned int
9012 mask;
cristy3ed852e2009-09-05 21:47:34 +00009013
glennrpa521b2f2010-10-29 04:11:03 +00009014 mask=0xffff;
9015 if (ping_bit_depth == 8)
9016 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009017
glennrpa521b2f2010-10-29 04:11:03 +00009018 if (ping_bit_depth == 4)
9019 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009020
glennrpa521b2f2010-10-29 04:11:03 +00009021 if (ping_bit_depth == 2)
9022 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009023
glennrpa521b2f2010-10-29 04:11:03 +00009024 if (ping_bit_depth == 1)
9025 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009026
glennrpa521b2f2010-10-29 04:11:03 +00009027 ping_background.red=(png_uint_16)
9028 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009029
glennrpa521b2f2010-10-29 04:11:03 +00009030 ping_background.green=(png_uint_16)
9031 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009032
glennrpa521b2f2010-10-29 04:11:03 +00009033 ping_background.blue=(png_uint_16)
9034 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009035
9036 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009037 }
cristy3ed852e2009-09-05 21:47:34 +00009038
glennrp0fe50b42010-11-16 03:52:51 +00009039 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009040 {
9041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9042 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9044 " background_color index is %d",
9045 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009046
9047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9048 " ping_bit_depth=%d",ping_bit_depth);
9049 }
glennrp0fe50b42010-11-16 03:52:51 +00009050
9051 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009052 }
glennrp0fe50b42010-11-16 03:52:51 +00009053
cristy3ed852e2009-09-05 21:47:34 +00009054 /*
9055 Select the color type.
9056 */
9057 matte=image_matte;
9058 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009059
glennrp1273f7b2011-02-24 03:20:30 +00009060 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009061 {
glennrp0fe50b42010-11-16 03:52:51 +00009062
glennrpfd05d622011-02-25 04:10:33 +00009063 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009064 for reducing the sample depth from 8. */
9065
glennrp0fe50b42010-11-16 03:52:51 +00009066 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009067
glennrp8bb3a022010-12-13 20:40:04 +00009068 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009069
9070 /*
9071 Set image palette.
9072 */
9073 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9074
glennrp0fe50b42010-11-16 03:52:51 +00009075 if (logging != MagickFalse)
9076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9077 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009078 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009079
9080 for (i=0; i < (ssize_t) number_colors; i++)
9081 {
9082 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9083 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9084 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9085 if (logging != MagickFalse)
9086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009087#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009088 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009089#else
9090 " %5ld (%5d,%5d,%5d)",
9091#endif
glennrp0fe50b42010-11-16 03:52:51 +00009092 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9093
9094 }
glennrp2b013e42010-11-24 16:55:50 +00009095
glennrp8bb3a022010-12-13 20:40:04 +00009096 ping_have_PLTE=MagickTrue;
9097 image_depth=ping_bit_depth;
9098 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009099
glennrp58e01762011-01-07 15:28:54 +00009100 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009101 {
glennrp0fe50b42010-11-16 03:52:51 +00009102 /*
9103 Identify which colormap entry is transparent.
9104 */
9105 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009106 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009107
glennrp8bb3a022010-12-13 20:40:04 +00009108 for (i=0; i < (ssize_t) number_transparent; i++)
9109 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009110
glennrp0fe50b42010-11-16 03:52:51 +00009111
glennrp2cc891a2010-12-24 13:44:32 +00009112 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009113 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009114
9115 if (ping_num_trans == 0)
9116 ping_have_tRNS=MagickFalse;
9117
glennrp8bb3a022010-12-13 20:40:04 +00009118 else
9119 ping_have_tRNS=MagickTrue;
9120 }
glennrp0fe50b42010-11-16 03:52:51 +00009121
glennrp1273f7b2011-02-24 03:20:30 +00009122 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009123 {
glennrp1273f7b2011-02-24 03:20:30 +00009124 /*
9125 * Identify which colormap entry is the background color.
9126 */
9127
glennrp4f25bd02011-01-01 18:51:28 +00009128 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9129 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9130 break;
glennrp0fe50b42010-11-16 03:52:51 +00009131
glennrp4f25bd02011-01-01 18:51:28 +00009132 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009133
9134 if (logging != MagickFalse)
9135 {
9136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9137 " background_color index is %d",
9138 (int) ping_background.index);
9139 }
glennrp4f25bd02011-01-01 18:51:28 +00009140 }
cristy3ed852e2009-09-05 21:47:34 +00009141 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009142
glennrp7e65e932011-08-19 02:31:16 +00009143 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009144 {
9145 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009146 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009147 }
glennrp0fe50b42010-11-16 03:52:51 +00009148
glennrp7e65e932011-08-19 02:31:16 +00009149 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009150 {
9151 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009152 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009153 }
glennrp0fe50b42010-11-16 03:52:51 +00009154
glennrp8bb3a022010-12-13 20:40:04 +00009155 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009156 {
glennrp5af765f2010-03-30 11:12:18 +00009157 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009158
glennrp8bb3a022010-12-13 20:40:04 +00009159 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009160 {
glennrp5af765f2010-03-30 11:12:18 +00009161 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009162
glennrp5af765f2010-03-30 11:12:18 +00009163 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9164 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009165 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009166
glennrp8bb3a022010-12-13 20:40:04 +00009167 else
9168 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009169
9170 if (logging != MagickFalse)
9171 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9172 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009173 }
glennrp0fe50b42010-11-16 03:52:51 +00009174
glennrp7c4c9e62011-03-21 20:23:32 +00009175 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009176 {
9177 if (logging != MagickFalse)
9178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009179 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009180
glennrpd6bf1612010-12-17 17:28:54 +00009181 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009182 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009183
glennrpd6bf1612010-12-17 17:28:54 +00009184 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009185 {
glennrp5af765f2010-03-30 11:12:18 +00009186 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009187 image_matte=MagickFalse;
9188 }
glennrp0fe50b42010-11-16 03:52:51 +00009189
glennrpd6bf1612010-12-17 17:28:54 +00009190 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009191 {
glennrp5af765f2010-03-30 11:12:18 +00009192 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009193 image_matte=MagickTrue;
9194 }
glennrp0fe50b42010-11-16 03:52:51 +00009195
glennrp5aa37f62011-01-02 03:07:57 +00009196 if (image_info->type == PaletteType ||
9197 image_info->type == PaletteMatteType)
9198 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9199
glennrp7c4c9e62011-03-21 20:23:32 +00009200 if (mng_info->write_png_colortype == 0 &&
9201 (image_info->type == UndefinedType ||
9202 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009203 {
glennrp5aa37f62011-01-02 03:07:57 +00009204 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009205 {
glennrp5aa37f62011-01-02 03:07:57 +00009206 if (image_matte == MagickFalse)
9207 {
9208 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9209 image_matte=MagickFalse;
9210 }
glennrp0fe50b42010-11-16 03:52:51 +00009211
glennrp0b206f52011-01-07 04:55:32 +00009212 else
glennrp5aa37f62011-01-02 03:07:57 +00009213 {
9214 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9215 image_matte=MagickTrue;
9216 }
9217 }
9218 else
glennrp8bb3a022010-12-13 20:40:04 +00009219 {
glennrp5aa37f62011-01-02 03:07:57 +00009220 if (image_matte == MagickFalse)
9221 {
9222 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9223 image_matte=MagickFalse;
9224 }
glennrp8bb3a022010-12-13 20:40:04 +00009225
glennrp0b206f52011-01-07 04:55:32 +00009226 else
glennrp5aa37f62011-01-02 03:07:57 +00009227 {
9228 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9229 image_matte=MagickTrue;
9230 }
9231 }
glennrp0fe50b42010-11-16 03:52:51 +00009232 }
glennrp5aa37f62011-01-02 03:07:57 +00009233
cristy3ed852e2009-09-05 21:47:34 +00009234 }
glennrp0fe50b42010-11-16 03:52:51 +00009235
cristy3ed852e2009-09-05 21:47:34 +00009236 if (logging != MagickFalse)
9237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009238 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009239
glennrp5af765f2010-03-30 11:12:18 +00009240 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009241 {
9242 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9243 ping_color_type == PNG_COLOR_TYPE_RGB ||
9244 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9245 ping_bit_depth=8;
9246 }
cristy3ed852e2009-09-05 21:47:34 +00009247
glennrpd6bf1612010-12-17 17:28:54 +00009248 old_bit_depth=ping_bit_depth;
9249
glennrp5af765f2010-03-30 11:12:18 +00009250 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009251 {
glennrp8d579662011-02-23 02:05:02 +00009252 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9253 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009254 }
glennrp8640fb52010-11-23 15:48:26 +00009255
glennrp5af765f2010-03-30 11:12:18 +00009256 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009257 {
cristy35ef8242010-06-03 16:24:13 +00009258 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009259 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009260
9261 if (image->colors == 0)
9262 {
glennrp0fe50b42010-11-16 03:52:51 +00009263 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00009264 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00009265 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009266 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009267 }
9268
cristy35ef8242010-06-03 16:24:13 +00009269 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009270 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009271 }
glennrp2b013e42010-11-24 16:55:50 +00009272
glennrpd6bf1612010-12-17 17:28:54 +00009273 if (logging != MagickFalse)
9274 {
9275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9276 " Number of colors: %.20g",(double) image_colors);
9277
9278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9279 " Tentative PNG bit depth: %d",ping_bit_depth);
9280 }
9281
9282 if (ping_bit_depth < (int) mng_info->write_png_depth)
9283 ping_bit_depth = mng_info->write_png_depth;
9284 }
glennrp2cc891a2010-12-24 13:44:32 +00009285
glennrp5af765f2010-03-30 11:12:18 +00009286 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009287
cristy3ed852e2009-09-05 21:47:34 +00009288 if (logging != MagickFalse)
9289 {
9290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009291 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009292
cristy3ed852e2009-09-05 21:47:34 +00009293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009294 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009295
cristy3ed852e2009-09-05 21:47:34 +00009296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009297 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009298
cristy3ed852e2009-09-05 21:47:34 +00009299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009300
glennrp8640fb52010-11-23 15:48:26 +00009301 " image->depth: %.20g",(double) image->depth);
9302
9303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009304 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009305 }
9306
glennrp58e01762011-01-07 15:28:54 +00009307 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009308 {
glennrp4f25bd02011-01-01 18:51:28 +00009309 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009310 {
glennrp7c4c9e62011-03-21 20:23:32 +00009311 if (mng_info->write_png_colortype == 0)
9312 {
9313 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009314
glennrp7c4c9e62011-03-21 20:23:32 +00009315 if (ping_have_color != MagickFalse)
9316 ping_color_type=PNG_COLOR_TYPE_RGBA;
9317 }
glennrp4f25bd02011-01-01 18:51:28 +00009318
9319 /*
9320 * Determine if there is any transparent color.
9321 */
9322 if (number_transparent + number_semitransparent == 0)
9323 {
9324 /*
9325 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9326 */
glennrpa6a06632011-01-19 15:15:34 +00009327
glennrp4f25bd02011-01-01 18:51:28 +00009328 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009329
9330 if (mng_info->write_png_colortype == 0)
9331 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009332 }
9333
9334 else
9335 {
9336 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009337 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009338
9339 mask=0xffff;
9340
9341 if (ping_bit_depth == 8)
9342 mask=0x00ff;
9343
9344 if (ping_bit_depth == 4)
9345 mask=0x000f;
9346
9347 if (ping_bit_depth == 2)
9348 mask=0x0003;
9349
9350 if (ping_bit_depth == 1)
9351 mask=0x0001;
9352
9353 ping_trans_color.red=(png_uint_16)
9354 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9355
9356 ping_trans_color.green=(png_uint_16)
9357 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9358
9359 ping_trans_color.blue=(png_uint_16)
9360 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9361
9362 ping_trans_color.gray=(png_uint_16)
cristy101ab702011-10-13 13:06:32 +00009363 (ScaleQuantumToShort(GetPixelInfoIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009364 image->colormap)) & mask);
9365
9366 ping_trans_color.index=(png_byte) 0;
9367
9368 ping_have_tRNS=MagickTrue;
9369 }
9370
9371 if (ping_have_tRNS != MagickFalse)
9372 {
9373 /*
glennrpfd05d622011-02-25 04:10:33 +00009374 * Determine if there is one and only one transparent color
9375 * and if so if it is fully transparent.
9376 */
9377 if (ping_have_cheap_transparency == MagickFalse)
9378 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009379 }
9380
9381 if (ping_have_tRNS != MagickFalse)
9382 {
glennrp7c4c9e62011-03-21 20:23:32 +00009383 if (mng_info->write_png_colortype == 0)
9384 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009385
9386 if (image_depth == 8)
9387 {
9388 ping_trans_color.red&=0xff;
9389 ping_trans_color.green&=0xff;
9390 ping_trans_color.blue&=0xff;
9391 ping_trans_color.gray&=0xff;
9392 }
9393 }
9394 }
cristy3ed852e2009-09-05 21:47:34 +00009395 else
9396 {
cristy3ed852e2009-09-05 21:47:34 +00009397 if (image_depth == 8)
9398 {
glennrp5af765f2010-03-30 11:12:18 +00009399 ping_trans_color.red&=0xff;
9400 ping_trans_color.green&=0xff;
9401 ping_trans_color.blue&=0xff;
9402 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009403 }
9404 }
9405 }
glennrp8640fb52010-11-23 15:48:26 +00009406
cristy3ed852e2009-09-05 21:47:34 +00009407 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009408
glennrp2e09f552010-11-14 00:38:48 +00009409 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009410 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009411
glennrp39992b42010-11-14 00:03:43 +00009412 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009413 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009414 ping_have_color == MagickFalse &&
9415 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009416 {
cristy35ef8242010-06-03 16:24:13 +00009417 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009418
cristy3ed852e2009-09-05 21:47:34 +00009419 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009420 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009421
glennrp7c4c9e62011-03-21 20:23:32 +00009422 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009423 {
glennrp5af765f2010-03-30 11:12:18 +00009424 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009425
cristy3ed852e2009-09-05 21:47:34 +00009426 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009427 {
9428 if (logging != MagickFalse)
9429 {
9430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9431 " Scaling ping_trans_color (0)");
9432 }
9433 ping_trans_color.gray*=0x0101;
9434 }
cristy3ed852e2009-09-05 21:47:34 +00009435 }
glennrp0fe50b42010-11-16 03:52:51 +00009436
cristy3ed852e2009-09-05 21:47:34 +00009437 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9438 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009439
glennrp136ee3a2011-04-27 15:47:45 +00009440 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009441 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009442 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009443
cristy3ed852e2009-09-05 21:47:34 +00009444 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009445 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009446
cristy3ed852e2009-09-05 21:47:34 +00009447 else
9448 {
glennrp5af765f2010-03-30 11:12:18 +00009449 ping_bit_depth=8;
9450 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009451 {
9452 if(!mng_info->write_png_depth)
9453 {
glennrp5af765f2010-03-30 11:12:18 +00009454 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009455
cristy35ef8242010-06-03 16:24:13 +00009456 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009457 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009458 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009459 }
9460 }
glennrp2b013e42010-11-24 16:55:50 +00009461
glennrp0fe50b42010-11-16 03:52:51 +00009462 else if (ping_color_type ==
9463 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009464 mng_info->IsPalette)
9465 {
cristy3ed852e2009-09-05 21:47:34 +00009466 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009467
cristy3ed852e2009-09-05 21:47:34 +00009468 int
9469 depth_4_ok=MagickTrue,
9470 depth_2_ok=MagickTrue,
9471 depth_1_ok=MagickTrue;
9472
cristybb503372010-05-27 20:51:26 +00009473 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009474 {
9475 unsigned char
9476 intensity;
9477
9478 intensity=ScaleQuantumToChar(image->colormap[i].red);
9479
9480 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9481 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9482 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9483 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009484 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009485 depth_1_ok=MagickFalse;
9486 }
glennrp2b013e42010-11-24 16:55:50 +00009487
cristy3ed852e2009-09-05 21:47:34 +00009488 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009489 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009490
cristy3ed852e2009-09-05 21:47:34 +00009491 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009492 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009493
cristy3ed852e2009-09-05 21:47:34 +00009494 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009495 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009496 }
9497 }
glennrp2b013e42010-11-24 16:55:50 +00009498
glennrp5af765f2010-03-30 11:12:18 +00009499 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009500 }
glennrp0fe50b42010-11-16 03:52:51 +00009501
cristy3ed852e2009-09-05 21:47:34 +00009502 else
glennrp0fe50b42010-11-16 03:52:51 +00009503
cristy3ed852e2009-09-05 21:47:34 +00009504 if (mng_info->IsPalette)
9505 {
glennrp17a14852010-05-10 03:01:59 +00009506 number_colors=image_colors;
9507
cristy3ed852e2009-09-05 21:47:34 +00009508 if (image_depth <= 8)
9509 {
cristy3ed852e2009-09-05 21:47:34 +00009510 /*
9511 Set image palette.
9512 */
glennrp5af765f2010-03-30 11:12:18 +00009513 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009514
glennrp58e01762011-01-07 15:28:54 +00009515 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009516 {
glennrp9c1eb072010-06-06 22:19:15 +00009517 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009518
glennrp3b51f0e2010-11-27 18:14:08 +00009519 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9521 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009522 }
glennrp0fe50b42010-11-16 03:52:51 +00009523
cristy3ed852e2009-09-05 21:47:34 +00009524 else
9525 {
cristybb503372010-05-27 20:51:26 +00009526 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009527 {
9528 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9529 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9530 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9531 }
glennrp0fe50b42010-11-16 03:52:51 +00009532
glennrp3b51f0e2010-11-27 18:14:08 +00009533 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009535 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009536 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009537
glennrp39992b42010-11-14 00:03:43 +00009538 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009539 }
glennrp0fe50b42010-11-16 03:52:51 +00009540
cristy3ed852e2009-09-05 21:47:34 +00009541 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009542 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009543 {
cristybefe4d22010-06-07 01:18:58 +00009544 size_t
9545 one;
9546
glennrp5af765f2010-03-30 11:12:18 +00009547 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009548 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009549
cristy94b11832011-09-08 19:46:03 +00009550 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009551 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009552 }
glennrp0fe50b42010-11-16 03:52:51 +00009553
glennrp5af765f2010-03-30 11:12:18 +00009554 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009555
glennrp58e01762011-01-07 15:28:54 +00009556 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009557 {
glennrp0fe50b42010-11-16 03:52:51 +00009558 /*
glennrpd6bf1612010-12-17 17:28:54 +00009559 * Set up trans_colors array.
9560 */
glennrp0fe50b42010-11-16 03:52:51 +00009561 assert(number_colors <= 256);
9562
glennrpd6bf1612010-12-17 17:28:54 +00009563 ping_num_trans=(unsigned short) (number_transparent +
9564 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009565
9566 if (ping_num_trans == 0)
9567 ping_have_tRNS=MagickFalse;
9568
glennrpd6bf1612010-12-17 17:28:54 +00009569 else
glennrp0fe50b42010-11-16 03:52:51 +00009570 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009571 if (logging != MagickFalse)
9572 {
9573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9574 " Scaling ping_trans_color (1)");
9575 }
glennrpd6bf1612010-12-17 17:28:54 +00009576 ping_have_tRNS=MagickTrue;
9577
9578 for (i=0; i < ping_num_trans; i++)
9579 {
cristy4c08aed2011-07-01 19:47:50 +00009580 ping_trans_alpha[i]= (png_byte)
9581 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009582 }
glennrp0fe50b42010-11-16 03:52:51 +00009583 }
9584 }
cristy3ed852e2009-09-05 21:47:34 +00009585 }
9586 }
glennrp0fe50b42010-11-16 03:52:51 +00009587
cristy3ed852e2009-09-05 21:47:34 +00009588 else
9589 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009590
cristy3ed852e2009-09-05 21:47:34 +00009591 if (image_depth < 8)
9592 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009593
cristy3ed852e2009-09-05 21:47:34 +00009594 if ((save_image_depth == 16) && (image_depth == 8))
9595 {
glennrp4f25bd02011-01-01 18:51:28 +00009596 if (logging != MagickFalse)
9597 {
9598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9599 " Scaling ping_trans_color from (%d,%d,%d)",
9600 (int) ping_trans_color.red,
9601 (int) ping_trans_color.green,
9602 (int) ping_trans_color.blue);
9603 }
9604
glennrp5af765f2010-03-30 11:12:18 +00009605 ping_trans_color.red*=0x0101;
9606 ping_trans_color.green*=0x0101;
9607 ping_trans_color.blue*=0x0101;
9608 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009609
9610 if (logging != MagickFalse)
9611 {
9612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9613 " to (%d,%d,%d)",
9614 (int) ping_trans_color.red,
9615 (int) ping_trans_color.green,
9616 (int) ping_trans_color.blue);
9617 }
cristy3ed852e2009-09-05 21:47:34 +00009618 }
9619 }
9620
cristy4383ec82011-01-05 15:42:32 +00009621 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9622 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009623
cristy3ed852e2009-09-05 21:47:34 +00009624 /*
9625 Adjust background and transparency samples in sub-8-bit grayscale files.
9626 */
glennrp5af765f2010-03-30 11:12:18 +00009627 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009628 PNG_COLOR_TYPE_GRAY)
9629 {
9630 png_uint_16
9631 maxval;
9632
cristy35ef8242010-06-03 16:24:13 +00009633 size_t
9634 one=1;
9635
cristy22ffd972010-06-03 16:51:47 +00009636 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009637
glennrp4f25bd02011-01-01 18:51:28 +00009638 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009639 {
cristy3ed852e2009-09-05 21:47:34 +00009640
glennrpa521b2f2010-10-29 04:11:03 +00009641 ping_background.gray=(png_uint_16)
cristy101ab702011-10-13 13:06:32 +00009642 ((maxval/255.)*((GetPixelInfoIntensity(&image->background_color)))
glennrp847370c2011-07-05 17:37:15 +00009643 +.5);
cristy3ed852e2009-09-05 21:47:34 +00009644
9645 if (logging != MagickFalse)
9646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009647 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9649 " background_color index is %d",
9650 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009651
glennrp991d11d2010-11-12 21:55:28 +00009652 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009653 }
cristy3ed852e2009-09-05 21:47:34 +00009654
glennrp3e3e20f2011-06-09 04:21:43 +00009655 if (logging != MagickFalse)
9656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9657 " Scaling ping_trans_color.gray from %d",
9658 (int)ping_trans_color.gray);
9659
glennrp9be9b1c2011-06-09 12:21:45 +00009660 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009661 ping_trans_color.gray)+.5);
9662
9663 if (logging != MagickFalse)
9664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9665 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009666 }
glennrp17a14852010-05-10 03:01:59 +00009667
glennrp26f37912010-12-23 16:22:42 +00009668 if (ping_exclude_bKGD == MagickFalse)
9669 {
glennrp1273f7b2011-02-24 03:20:30 +00009670 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009671 {
9672 /*
9673 Identify which colormap entry is the background color.
9674 */
9675
glennrp17a14852010-05-10 03:01:59 +00009676 number_colors=image_colors;
9677
glennrpa521b2f2010-10-29 04:11:03 +00009678 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9679 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009680 break;
9681
9682 ping_background.index=(png_byte) i;
9683
glennrp3b51f0e2010-11-27 18:14:08 +00009684 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009685 {
9686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009687 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009688 }
glennrp0fe50b42010-11-16 03:52:51 +00009689
cristy13d07042010-11-21 20:56:18 +00009690 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009691 {
9692 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009693
9694 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009695 {
9696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9697 " background =(%d,%d,%d)",
9698 (int) ping_background.red,
9699 (int) ping_background.green,
9700 (int) ping_background.blue);
9701 }
9702 }
glennrpa521b2f2010-10-29 04:11:03 +00009703
glennrpd6bf1612010-12-17 17:28:54 +00009704 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009705 {
glennrp3b51f0e2010-11-27 18:14:08 +00009706 if (logging != MagickFalse)
9707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9708 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009709 ping_have_bKGD = MagickFalse;
9710 }
glennrp17a14852010-05-10 03:01:59 +00009711 }
glennrp26f37912010-12-23 16:22:42 +00009712 }
glennrp17a14852010-05-10 03:01:59 +00009713
cristy3ed852e2009-09-05 21:47:34 +00009714 if (logging != MagickFalse)
9715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009716 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009717 /*
9718 Initialize compression level and filtering.
9719 */
9720 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009721 {
9722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9723 " Setting up deflate compression");
9724
9725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9726 " Compression buffer size: 32768");
9727 }
9728
cristy3ed852e2009-09-05 21:47:34 +00009729 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009730
cristy3ed852e2009-09-05 21:47:34 +00009731 if (logging != MagickFalse)
9732 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9733 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009734
cristy4054bfb2011-08-29 23:41:39 +00009735 png_set_compression_mem_level(ping, 9);
9736
glennrp10d739e2011-06-29 18:00:52 +00009737 /* Untangle the "-quality" setting:
9738
9739 Undefined is 0; the default is used.
9740 Default is 75
9741
9742 10's digit:
9743
9744 0: Use Z_HUFFMAN_ONLY strategy with the
9745 zlib default compression level
9746
9747 1-9: the zlib compression level
9748
9749 1's digit:
9750
9751 0-4: the PNG filter method
9752
9753 5: libpng adaptive filtering if compression level > 5
9754 libpng filter type "none" if compression level <= 5
9755 or if image is grayscale or palette
9756
9757 6: libpng adaptive filtering
9758
9759 7: "LOCO" filtering (intrapixel differing) if writing
9760 a MNG, othewise "none". Did not work in IM-6.7.0-9
9761 and earlier because of a missing "else".
9762
9763 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009764 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009765
9766 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009767 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009768
9769 Note that using the -quality option, not all combinations of
9770 PNG filter type, zlib compression level, and zlib compression
cristy5e6be1e2011-07-16 01:23:39 +00009771 strategy are possible. This will be addressed soon in a
cristy4054bfb2011-08-29 23:41:39 +00009772 release that accomodates "-define PNG:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +00009773
9774 */
9775
cristy3ed852e2009-09-05 21:47:34 +00009776 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9777 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009778
glennrp18682582011-06-30 18:11:47 +00009779 if (quality <= 9)
9780 {
9781 if (mng_info->write_png_compression_strategy == 0)
9782 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9783 }
9784
9785 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009786 {
9787 int
9788 level;
9789
cristybb503372010-05-27 20:51:26 +00009790 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009791
glennrp18682582011-06-30 18:11:47 +00009792 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009793 }
glennrp0fe50b42010-11-16 03:52:51 +00009794
glennrp18682582011-06-30 18:11:47 +00009795 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009796 {
glennrp18682582011-06-30 18:11:47 +00009797 if ((quality %10) == 8 || (quality %10) == 9)
9798 mng_info->write_png_compression_strategy=Z_RLE;
cristy3ed852e2009-09-05 21:47:34 +00009799 }
glennrp0fe50b42010-11-16 03:52:51 +00009800
glennrp18682582011-06-30 18:11:47 +00009801 if (mng_info->write_png_compression_filter == 0)
9802 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9803
cristy3ed852e2009-09-05 21:47:34 +00009804 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009805 {
glennrp18682582011-06-30 18:11:47 +00009806 if (mng_info->write_png_compression_level)
9807 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9808 " Compression level: %d",
9809 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009810
glennrp18682582011-06-30 18:11:47 +00009811 if (mng_info->write_png_compression_strategy)
9812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9813 " Compression strategy: %d",
9814 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009815
glennrp18682582011-06-30 18:11:47 +00009816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9817 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009818
cristy4054bfb2011-08-29 23:41:39 +00009819 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +00009820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9821 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +00009822 else if (mng_info->write_png_compression_filter == 0 ||
9823 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +00009824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9825 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009826 else
9827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9828 " Base filter method: %d",
9829 (int) mng_info->write_png_compression_filter-1);
9830 }
glennrp2b013e42010-11-24 16:55:50 +00009831
glennrp18682582011-06-30 18:11:47 +00009832 if (mng_info->write_png_compression_level != 0)
9833 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9834
9835 if (mng_info->write_png_compression_filter == 6)
9836 {
9837 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9838 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9839 (quality < 50))
9840 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9841 else
9842 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9843 }
cristy4054bfb2011-08-29 23:41:39 +00009844 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +00009845 mng_info->write_png_compression_filter == 10)
9846 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9847
9848 else if (mng_info->write_png_compression_filter == 8)
9849 {
9850#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9851 if (mng_info->write_mng)
9852 {
9853 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9854 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9855 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9856 }
9857#endif
cristy4054bfb2011-08-29 23:41:39 +00009858 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +00009859 }
9860
9861 else if (mng_info->write_png_compression_filter == 9)
9862 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9863
9864 else if (mng_info->write_png_compression_filter != 0)
9865 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9866 mng_info->write_png_compression_filter-1);
9867
9868 if (mng_info->write_png_compression_strategy != 0)
9869 png_set_compression_strategy(ping,
9870 mng_info->write_png_compression_strategy-1);
9871
cristy0d57eec2011-09-04 22:13:56 +00009872 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9873 if (ping_exclude_sRGB != MagickFalse ||
9874 (image->rendering_intent == UndefinedIntent))
9875 {
9876 if ((ping_exclude_tEXt == MagickFalse ||
9877 ping_exclude_zTXt == MagickFalse) &&
9878 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009879 {
9880 ResetImageProfileIterator(image);
9881 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009882 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009883 profile=GetImageProfile(image,name);
9884
9885 if (profile != (StringInfo *) NULL)
9886 {
glennrp5af765f2010-03-30 11:12:18 +00009887#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009888 if ((LocaleCompare(name,"ICC") == 0) ||
9889 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009890 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009891
9892 if (ping_exclude_iCCP == MagickFalse)
9893 {
cristy9f027d12011-09-21 01:17:17 +00009894 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009895#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009896 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009897#else
9898 (png_const_bytep) GetStringInfoDatum(profile),
9899#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009900 (png_uint_32) GetStringInfoLength(profile));
9901 }
glennrp26f37912010-12-23 16:22:42 +00009902 }
glennrp0fe50b42010-11-16 03:52:51 +00009903
glennrpc8cbc5d2011-01-01 00:12:34 +00009904 else
cristy3ed852e2009-09-05 21:47:34 +00009905#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009906 if (ping_exclude_zCCP == MagickFalse)
9907 {
glennrpcf002022011-01-30 02:38:15 +00009908 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009909 (unsigned char *) name,(unsigned char *) name,
9910 GetStringInfoDatum(profile),
9911 (png_uint_32) GetStringInfoLength(profile));
9912 }
9913 }
glennrp0b206f52011-01-07 04:55:32 +00009914
glennrpc8cbc5d2011-01-01 00:12:34 +00009915 if (logging != MagickFalse)
9916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9917 " Setting up text chunk with %s profile",name);
9918
9919 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009920 }
cristy0d57eec2011-09-04 22:13:56 +00009921 }
cristy3ed852e2009-09-05 21:47:34 +00009922 }
9923
9924#if defined(PNG_WRITE_sRGB_SUPPORTED)
9925 if ((mng_info->have_write_global_srgb == 0) &&
9926 ((image->rendering_intent != UndefinedIntent) ||
9927 (image->colorspace == sRGBColorspace)))
9928 {
glennrp26f37912010-12-23 16:22:42 +00009929 if (ping_exclude_sRGB == MagickFalse)
9930 {
9931 /*
9932 Note image rendering intent.
9933 */
9934 if (logging != MagickFalse)
9935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9936 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009937
glennrp26f37912010-12-23 16:22:42 +00009938 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009939 Magick_RenderingIntent_to_PNG_RenderingIntent(
9940 image->rendering_intent)));
glennrp26f37912010-12-23 16:22:42 +00009941 }
cristy3ed852e2009-09-05 21:47:34 +00009942 }
glennrp26f37912010-12-23 16:22:42 +00009943
glennrp5af765f2010-03-30 11:12:18 +00009944 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009945#endif
9946 {
glennrp2cc891a2010-12-24 13:44:32 +00009947 if (ping_exclude_gAMA == MagickFalse &&
9948 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009949 (image->gamma < .45 || image->gamma > .46)))
9950 {
cristy3ed852e2009-09-05 21:47:34 +00009951 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9952 {
9953 /*
9954 Note image gamma.
9955 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9956 */
9957 if (logging != MagickFalse)
9958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9959 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009960
cristy3ed852e2009-09-05 21:47:34 +00009961 png_set_gAMA(ping,ping_info,image->gamma);
9962 }
glennrp26f37912010-12-23 16:22:42 +00009963 }
glennrp2b013e42010-11-24 16:55:50 +00009964
glennrp26f37912010-12-23 16:22:42 +00009965 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009966 {
glennrp26f37912010-12-23 16:22:42 +00009967 if ((mng_info->have_write_global_chrm == 0) &&
9968 (image->chromaticity.red_primary.x != 0.0))
9969 {
9970 /*
9971 Note image chromaticity.
9972 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9973 */
9974 PrimaryInfo
9975 bp,
9976 gp,
9977 rp,
9978 wp;
cristy3ed852e2009-09-05 21:47:34 +00009979
glennrp26f37912010-12-23 16:22:42 +00009980 wp=image->chromaticity.white_point;
9981 rp=image->chromaticity.red_primary;
9982 gp=image->chromaticity.green_primary;
9983 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009984
glennrp26f37912010-12-23 16:22:42 +00009985 if (logging != MagickFalse)
9986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9987 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009988
glennrp26f37912010-12-23 16:22:42 +00009989 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9990 bp.x,bp.y);
9991 }
9992 }
cristy3ed852e2009-09-05 21:47:34 +00009993 }
glennrpdfd70802010-11-14 01:23:35 +00009994
glennrp5af765f2010-03-30 11:12:18 +00009995 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009996
9997 if (mng_info->write_mng)
9998 png_set_sig_bytes(ping,8);
9999
10000 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
10001
glennrpd6bf1612010-12-17 17:28:54 +000010002 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +000010003 {
10004 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +000010005 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010006 {
glennrp5af765f2010-03-30 11:12:18 +000010007 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +000010008
glennrp5af765f2010-03-30 11:12:18 +000010009 if (ping_bit_depth < 8)
10010 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +000010011 }
glennrp0fe50b42010-11-16 03:52:51 +000010012
cristy3ed852e2009-09-05 21:47:34 +000010013 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +000010014 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +000010015 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +000010016 }
10017
glennrp0e8ea192010-12-24 18:00:33 +000010018 if (ping_need_colortype_warning != MagickFalse ||
10019 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +000010020 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +000010021 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +000010022 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +000010023 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +000010024 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +000010025 {
10026 if (logging != MagickFalse)
10027 {
glennrp0e8ea192010-12-24 18:00:33 +000010028 if (ping_need_colortype_warning != MagickFalse)
10029 {
10030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10031 " Image has transparency but tRNS chunk was excluded");
10032 }
10033
cristy3ed852e2009-09-05 21:47:34 +000010034 if (mng_info->write_png_depth)
10035 {
10036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10037 " Defined PNG:bit-depth=%u, Computed depth=%u",
10038 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +000010039 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +000010040 }
glennrp0e8ea192010-12-24 18:00:33 +000010041
cristy3ed852e2009-09-05 21:47:34 +000010042 if (mng_info->write_png_colortype)
10043 {
10044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10045 " Defined PNG:color-type=%u, Computed color type=%u",
10046 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +000010047 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010048 }
10049 }
glennrp0e8ea192010-12-24 18:00:33 +000010050
glennrp3bd2e412010-08-10 13:34:52 +000010051 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +000010052 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
10053 }
10054
glennrp58e01762011-01-07 15:28:54 +000010055 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010056 {
10057 /* Add an opaque matte channel */
10058 image->matte = MagickTrue;
cristyb6a294d2011-10-03 00:55:17 +000010059 (void) SetImageAlpha(image,OpaqueAlpha);
glennrp0fe50b42010-11-16 03:52:51 +000010060
glennrpb4a13412010-05-05 12:47:19 +000010061 if (logging != MagickFalse)
10062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10063 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +000010064 }
10065
glennrp0e319732011-01-25 21:53:13 +000010066 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +000010067 {
glennrp991d11d2010-11-12 21:55:28 +000010068 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +000010069 {
glennrp991d11d2010-11-12 21:55:28 +000010070 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010071 if (logging != MagickFalse)
10072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10073 " Setting ping_have_tRNS=MagickTrue.");
10074 }
glennrpe9c26dc2010-05-30 01:56:35 +000010075 }
10076
cristy3ed852e2009-09-05 21:47:34 +000010077 if (logging != MagickFalse)
10078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10079 " Writing PNG header chunks");
10080
glennrp5af765f2010-03-30 11:12:18 +000010081 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10082 ping_bit_depth,ping_color_type,
10083 ping_interlace_method,ping_compression_method,
10084 ping_filter_method);
10085
glennrp39992b42010-11-14 00:03:43 +000010086 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10087 {
glennrpf09bded2011-01-08 01:15:59 +000010088 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010089
glennrp3b51f0e2010-11-27 18:14:08 +000010090 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +000010091 {
glennrp8640fb52010-11-23 15:48:26 +000010092 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +000010093 {
glennrpd6bf1612010-12-17 17:28:54 +000010094 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +000010095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010096 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10097 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010098 (int) palette[i].red,
10099 (int) palette[i].green,
10100 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +000010101 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010102 (int) ping_trans_alpha[i]);
10103 else
10104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010105 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010106 (int) i,
10107 (int) palette[i].red,
10108 (int) palette[i].green,
10109 (int) palette[i].blue);
10110 }
glennrp39992b42010-11-14 00:03:43 +000010111 }
glennrp39992b42010-11-14 00:03:43 +000010112 }
10113
glennrp26f37912010-12-23 16:22:42 +000010114 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010115 {
glennrp26f37912010-12-23 16:22:42 +000010116 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010117 {
glennrp26f37912010-12-23 16:22:42 +000010118 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010119 if (logging)
10120 {
10121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10122 " Setting up bKGD chunk");
10123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10124 " background color = (%d,%d,%d)",
10125 (int) ping_background.red,
10126 (int) ping_background.green,
10127 (int) ping_background.blue);
10128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10129 " index = %d, gray=%d",
10130 (int) ping_background.index,
10131 (int) ping_background.gray);
10132 }
10133 }
glennrp26f37912010-12-23 16:22:42 +000010134 }
10135
10136 if (ping_exclude_pHYs == MagickFalse)
10137 {
10138 if (ping_have_pHYs != MagickFalse)
10139 {
10140 png_set_pHYs(ping,ping_info,
10141 ping_pHYs_x_resolution,
10142 ping_pHYs_y_resolution,
10143 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010144
10145 if (logging)
10146 {
10147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10148 " Setting up pHYs chunk");
10149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10150 " x_resolution=%lu",
10151 (unsigned long) ping_pHYs_x_resolution);
10152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10153 " y_resolution=%lu",
10154 (unsigned long) ping_pHYs_y_resolution);
10155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10156 " unit_type=%lu",
10157 (unsigned long) ping_pHYs_unit_type);
10158 }
glennrp26f37912010-12-23 16:22:42 +000010159 }
glennrpdfd70802010-11-14 01:23:35 +000010160 }
10161
10162#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010163 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010164 {
glennrp26f37912010-12-23 16:22:42 +000010165 if (image->page.x || image->page.y)
10166 {
10167 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10168 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010169
glennrp26f37912010-12-23 16:22:42 +000010170 if (logging != MagickFalse)
10171 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10172 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10173 (int) image->page.x, (int) image->page.y);
10174 }
glennrpdfd70802010-11-14 01:23:35 +000010175 }
10176#endif
10177
glennrpda8f3a72011-02-27 23:54:12 +000010178 if (mng_info->need_blob != MagickFalse)
10179 {
10180 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
10181 MagickFalse)
10182 png_error(ping,"WriteBlob Failed");
10183
10184 ping_have_blob=MagickTrue;
10185 }
10186
cristy3ed852e2009-09-05 21:47:34 +000010187 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010188
glennrp39992b42010-11-14 00:03:43 +000010189 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010190 {
glennrp3b51f0e2010-11-27 18:14:08 +000010191 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010192 {
10193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10194 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10195 }
10196
10197 if (ping_color_type == 3)
10198 (void) png_set_tRNS(ping, ping_info,
10199 ping_trans_alpha,
10200 ping_num_trans,
10201 NULL);
10202
10203 else
10204 {
10205 (void) png_set_tRNS(ping, ping_info,
10206 NULL,
10207 0,
10208 &ping_trans_color);
10209
glennrp3b51f0e2010-11-27 18:14:08 +000010210 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010211 {
10212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010213 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010214 (int) ping_trans_color.red,
10215 (int) ping_trans_color.green,
10216 (int) ping_trans_color.blue);
10217 }
10218 }
glennrp991d11d2010-11-12 21:55:28 +000010219 }
10220
cristy3ed852e2009-09-05 21:47:34 +000010221 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010222 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010223
cristy3ed852e2009-09-05 21:47:34 +000010224 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010225
cristy3ed852e2009-09-05 21:47:34 +000010226 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010227 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010228
glennrp26f37912010-12-23 16:22:42 +000010229 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010230 {
glennrp4f25bd02011-01-01 18:51:28 +000010231 if ((image->page.width != 0 && image->page.width != image->columns) ||
10232 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010233 {
10234 unsigned char
10235 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010236
glennrp26f37912010-12-23 16:22:42 +000010237 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10238 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010239 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010240 PNGLong(chunk+4,(png_uint_32) image->page.width);
10241 PNGLong(chunk+8,(png_uint_32) image->page.height);
10242 chunk[12]=0; /* unit = pixels */
10243 (void) WriteBlob(image,13,chunk);
10244 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10245 }
cristy3ed852e2009-09-05 21:47:34 +000010246 }
10247
10248#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010249 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010250#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010251 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010252#undef PNG_HAVE_IDAT
10253#endif
10254
10255 png_set_packing(ping);
10256 /*
10257 Allocate memory.
10258 */
10259 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010260 if (image_depth > 8)
10261 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010262 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010263 {
glennrpb4a13412010-05-05 12:47:19 +000010264 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010265 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010266 break;
glennrp0fe50b42010-11-16 03:52:51 +000010267
glennrpb4a13412010-05-05 12:47:19 +000010268 case PNG_COLOR_TYPE_GRAY_ALPHA:
10269 rowbytes*=2;
10270 break;
glennrp0fe50b42010-11-16 03:52:51 +000010271
glennrpb4a13412010-05-05 12:47:19 +000010272 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010273 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010274 break;
glennrp0fe50b42010-11-16 03:52:51 +000010275
glennrpb4a13412010-05-05 12:47:19 +000010276 default:
10277 break;
cristy3ed852e2009-09-05 21:47:34 +000010278 }
glennrp3b51f0e2010-11-27 18:14:08 +000010279
10280 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010281 {
10282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10283 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010284
glennrpb4a13412010-05-05 12:47:19 +000010285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010286 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010287 }
glennrpcf002022011-01-30 02:38:15 +000010288 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10289 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010290
glennrpcf002022011-01-30 02:38:15 +000010291 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010292 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010293
cristy3ed852e2009-09-05 21:47:34 +000010294 /*
10295 Initialize image scanlines.
10296 */
glennrp5af765f2010-03-30 11:12:18 +000010297 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010298 {
10299 /*
10300 PNG write failed.
10301 */
10302#ifdef PNG_DEBUG
10303 if (image_info->verbose)
10304 (void) printf("PNG write has failed.\n");
10305#endif
10306 png_destroy_write_struct(&ping,&ping_info);
10307 if (quantum_info != (QuantumInfo *) NULL)
10308 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010309 if (ping_pixels != (unsigned char *) NULL)
10310 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010311#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010312 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010313#endif
glennrpda8f3a72011-02-27 23:54:12 +000010314 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010315 (void) CloseBlob(image);
10316 image_info=DestroyImageInfo(image_info);
10317 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010318 return(MagickFalse);
10319 }
cristyed552522009-10-16 14:04:35 +000010320 quantum_info=AcquireQuantumInfo(image_info,image);
10321 if (quantum_info == (QuantumInfo *) NULL)
10322 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010323 quantum_info->format=UndefinedQuantumFormat;
10324 quantum_info->depth=image_depth;
10325 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010326
cristy3ed852e2009-09-05 21:47:34 +000010327 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010328 !mng_info->write_png32) &&
10329 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010330 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010331 image_matte == MagickFalse &&
10332 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010333 {
glennrp8bb3a022010-12-13 20:40:04 +000010334 /* Palette, Bilevel, or Opaque Monochrome */
cristy4c08aed2011-07-01 19:47:50 +000010335 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010336 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010337
cristy3ed852e2009-09-05 21:47:34 +000010338 quantum_info->depth=8;
10339 for (pass=0; pass < num_passes; pass++)
10340 {
10341 /*
10342 Convert PseudoClass image to a PNG monochrome image.
10343 */
cristybb503372010-05-27 20:51:26 +000010344 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010345 {
glennrpd71e86a2011-02-24 01:28:37 +000010346 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10348 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010349
cristy3ed852e2009-09-05 21:47:34 +000010350 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010351
cristy4c08aed2011-07-01 19:47:50 +000010352 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010353 break;
glennrp0fe50b42010-11-16 03:52:51 +000010354
cristy3ed852e2009-09-05 21:47:34 +000010355 if (mng_info->IsPalette)
10356 {
cristy4c08aed2011-07-01 19:47:50 +000010357 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010358 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010359 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10360 mng_info->write_png_depth &&
10361 mng_info->write_png_depth != old_bit_depth)
10362 {
10363 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010364 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010365 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010366 >> (8-old_bit_depth));
10367 }
10368 }
glennrp0fe50b42010-11-16 03:52:51 +000010369
cristy3ed852e2009-09-05 21:47:34 +000010370 else
10371 {
cristy4c08aed2011-07-01 19:47:50 +000010372 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010373 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010374 }
glennrp0fe50b42010-11-16 03:52:51 +000010375
cristy3ed852e2009-09-05 21:47:34 +000010376 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010377 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010378 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010379 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010380
glennrp3b51f0e2010-11-27 18:14:08 +000010381 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10383 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010384
glennrpcf002022011-01-30 02:38:15 +000010385 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010386 }
10387 if (image->previous == (Image *) NULL)
10388 {
10389 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10390 if (status == MagickFalse)
10391 break;
10392 }
10393 }
10394 }
glennrp0fe50b42010-11-16 03:52:51 +000010395
glennrp8bb3a022010-12-13 20:40:04 +000010396 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010397 {
glennrp0fe50b42010-11-16 03:52:51 +000010398 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010399 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010400 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010401 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010402 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010403 {
cristy4c08aed2011-07-01 19:47:50 +000010404 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010405 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010406
glennrp8bb3a022010-12-13 20:40:04 +000010407 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010408 {
glennrp8bb3a022010-12-13 20:40:04 +000010409
cristybb503372010-05-27 20:51:26 +000010410 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010411 {
10412 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010413
cristy4c08aed2011-07-01 19:47:50 +000010414 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010415 break;
glennrp2cc891a2010-12-24 13:44:32 +000010416
glennrp5af765f2010-03-30 11:12:18 +000010417 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010418 {
glennrp8bb3a022010-12-13 20:40:04 +000010419 if (mng_info->IsPalette)
cristy4c08aed2011-07-01 19:47:50 +000010420 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010421 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010422
glennrp8bb3a022010-12-13 20:40:04 +000010423 else
cristy4c08aed2011-07-01 19:47:50 +000010424 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010425 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010426
glennrp3b51f0e2010-11-27 18:14:08 +000010427 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010429 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010430 }
glennrp2cc891a2010-12-24 13:44:32 +000010431
glennrp8bb3a022010-12-13 20:40:04 +000010432 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10433 {
10434 if (logging != MagickFalse && y == 0)
10435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10436 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010437
cristy4c08aed2011-07-01 19:47:50 +000010438 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010439 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010440 }
glennrp2cc891a2010-12-24 13:44:32 +000010441
glennrp3b51f0e2010-11-27 18:14:08 +000010442 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010444 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010445
glennrpcf002022011-01-30 02:38:15 +000010446 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010447 }
glennrp2cc891a2010-12-24 13:44:32 +000010448
glennrp8bb3a022010-12-13 20:40:04 +000010449 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010450 {
glennrp8bb3a022010-12-13 20:40:04 +000010451 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10452 if (status == MagickFalse)
10453 break;
cristy3ed852e2009-09-05 21:47:34 +000010454 }
cristy3ed852e2009-09-05 21:47:34 +000010455 }
10456 }
glennrp8bb3a022010-12-13 20:40:04 +000010457
10458 else
10459 {
cristy4c08aed2011-07-01 19:47:50 +000010460 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010461 *p;
10462
10463 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010464 {
glennrp8bb3a022010-12-13 20:40:04 +000010465 if ((image_depth > 8) || (mng_info->write_png24 ||
10466 mng_info->write_png32 ||
10467 (!mng_info->write_png8 && !mng_info->IsPalette)))
10468 {
10469 for (y=0; y < (ssize_t) image->rows; y++)
10470 {
10471 p=GetVirtualPixels(image,0,y,image->columns,1,
10472 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010473
cristy4c08aed2011-07-01 19:47:50 +000010474 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010475 break;
glennrp2cc891a2010-12-24 13:44:32 +000010476
glennrp8bb3a022010-12-13 20:40:04 +000010477 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10478 {
10479 if (image->storage_class == DirectClass)
cristy4c08aed2011-07-01 19:47:50 +000010480 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010481 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010482
glennrp8bb3a022010-12-13 20:40:04 +000010483 else
cristy4c08aed2011-07-01 19:47:50 +000010484 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010485 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010486 }
glennrp2cc891a2010-12-24 13:44:32 +000010487
glennrp8bb3a022010-12-13 20:40:04 +000010488 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10489 {
cristy4c08aed2011-07-01 19:47:50 +000010490 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010491 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +000010492 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010493
glennrp8bb3a022010-12-13 20:40:04 +000010494 if (logging != MagickFalse && y == 0)
10495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10496 " Writing GRAY_ALPHA PNG pixels (3)");
10497 }
glennrp2cc891a2010-12-24 13:44:32 +000010498
glennrp8bb3a022010-12-13 20:40:04 +000010499 else if (image_matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +000010500 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010501 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010502
glennrp8bb3a022010-12-13 20:40:04 +000010503 else
cristy4c08aed2011-07-01 19:47:50 +000010504 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010505 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010506
glennrp8bb3a022010-12-13 20:40:04 +000010507 if (logging != MagickFalse && y == 0)
10508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10509 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010510
glennrpcf002022011-01-30 02:38:15 +000010511 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010512 }
10513 }
glennrp2cc891a2010-12-24 13:44:32 +000010514
glennrp8bb3a022010-12-13 20:40:04 +000010515 else
10516 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10517 mng_info->write_png32 ||
10518 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10519 {
10520 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10521 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10522 {
10523 if (logging != MagickFalse)
10524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10525 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010526
glennrp8bb3a022010-12-13 20:40:04 +000010527 quantum_info->depth=8;
10528 image_depth=8;
10529 }
glennrp2cc891a2010-12-24 13:44:32 +000010530
glennrp8bb3a022010-12-13 20:40:04 +000010531 for (y=0; y < (ssize_t) image->rows; y++)
10532 {
10533 if (logging != MagickFalse && y == 0)
10534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10535 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010536
glennrp770d1932011-03-06 22:11:17 +000010537 p=GetVirtualPixels(image,0,y,image->columns,1,
10538 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010539
cristy4c08aed2011-07-01 19:47:50 +000010540 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010541 break;
glennrp2cc891a2010-12-24 13:44:32 +000010542
glennrp8bb3a022010-12-13 20:40:04 +000010543 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010544 {
glennrp4bf89732011-03-21 13:48:28 +000010545 quantum_info->depth=image->depth;
10546
cristy4c08aed2011-07-01 19:47:50 +000010547 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010548 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +000010549 }
glennrp2cc891a2010-12-24 13:44:32 +000010550
glennrp8bb3a022010-12-13 20:40:04 +000010551 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10552 {
10553 if (logging != MagickFalse && y == 0)
10554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10555 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010556
cristy4c08aed2011-07-01 19:47:50 +000010557 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010558 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +000010559 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010560 }
glennrp2cc891a2010-12-24 13:44:32 +000010561
glennrp8bb3a022010-12-13 20:40:04 +000010562 else
glennrp8bb3a022010-12-13 20:40:04 +000010563 {
cristy4c08aed2011-07-01 19:47:50 +000010564 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +000010565 quantum_info,IndexQuantum,ping_pixels,&image->exception);
10566
10567 if (logging != MagickFalse && y <= 2)
10568 {
10569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010570 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010571
10572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10573 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10574 (int)ping_pixels[0],(int)ping_pixels[1]);
10575 }
glennrp8bb3a022010-12-13 20:40:04 +000010576 }
glennrpcf002022011-01-30 02:38:15 +000010577 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010578 }
10579 }
glennrp2cc891a2010-12-24 13:44:32 +000010580
glennrp8bb3a022010-12-13 20:40:04 +000010581 if (image->previous == (Image *) NULL)
10582 {
10583 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10584 if (status == MagickFalse)
10585 break;
10586 }
cristy3ed852e2009-09-05 21:47:34 +000010587 }
glennrp8bb3a022010-12-13 20:40:04 +000010588 }
10589 }
10590
cristyb32b90a2009-09-07 21:45:48 +000010591 if (quantum_info != (QuantumInfo *) NULL)
10592 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010593
10594 if (logging != MagickFalse)
10595 {
10596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010597 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010598
cristy3ed852e2009-09-05 21:47:34 +000010599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010600 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010601
cristy3ed852e2009-09-05 21:47:34 +000010602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010603 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010604
cristy3ed852e2009-09-05 21:47:34 +000010605 if (mng_info->write_png_depth)
10606 {
10607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10608 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
10609 }
glennrp0fe50b42010-11-16 03:52:51 +000010610
cristy3ed852e2009-09-05 21:47:34 +000010611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010612 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010613
cristy3ed852e2009-09-05 21:47:34 +000010614 if (mng_info->write_png_colortype)
10615 {
10616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10617 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
10618 }
glennrp0fe50b42010-11-16 03:52:51 +000010619
cristy3ed852e2009-09-05 21:47:34 +000010620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010621 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010622
cristy3ed852e2009-09-05 21:47:34 +000010623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010624 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010625 }
10626 /*
glennrpa0ed0092011-04-18 16:36:29 +000010627 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010628 */
glennrp823b55c2011-03-14 18:46:46 +000010629 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010630 {
glennrp26f37912010-12-23 16:22:42 +000010631 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010632 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010633 while (property != (const char *) NULL)
10634 {
10635 png_textp
10636 text;
glennrp2cc891a2010-12-24 13:44:32 +000010637
glennrp26f37912010-12-23 16:22:42 +000010638 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +000010639
10640 /* Don't write any "png:" properties; those are just for "identify" */
10641 if (LocaleNCompare(property,"png:",4) != 0 &&
10642
10643 /* Suppress density and units if we wrote a pHYs chunk */
10644 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010645 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010646 LocaleCompare(property,"units") != 0) &&
10647
10648 /* Suppress the IM-generated Date:create and Date:modify */
10649 (ping_exclude_date == MagickFalse ||
10650 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010651 {
glennrpc70af4a2011-03-07 00:08:23 +000010652 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010653 {
glennrpc70af4a2011-03-07 00:08:23 +000010654 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10655 text[0].key=(char *) property;
10656 text[0].text=(char *) value;
10657 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010658
glennrpc70af4a2011-03-07 00:08:23 +000010659 if (ping_exclude_tEXt != MagickFalse)
10660 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10661
10662 else if (ping_exclude_zTXt != MagickFalse)
10663 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10664
10665 else
glennrp26f37912010-12-23 16:22:42 +000010666 {
glennrpc70af4a2011-03-07 00:08:23 +000010667 text[0].compression=image_info->compression == NoCompression ||
10668 (image_info->compression == UndefinedCompression &&
10669 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10670 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010671 }
glennrp2cc891a2010-12-24 13:44:32 +000010672
glennrpc70af4a2011-03-07 00:08:23 +000010673 if (logging != MagickFalse)
10674 {
10675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10676 " Setting up text chunk");
10677
10678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10679 " keyword: %s",text[0].key);
10680 }
10681
10682 png_set_text(ping,ping_info,text,1);
10683 png_free(ping,text);
10684 }
glennrp26f37912010-12-23 16:22:42 +000010685 }
10686 property=GetNextImageProperty(image);
10687 }
cristy3ed852e2009-09-05 21:47:34 +000010688 }
10689
10690 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010691 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010692
10693 if (logging != MagickFalse)
10694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10695 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010696
cristy3ed852e2009-09-05 21:47:34 +000010697 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010698
cristy3ed852e2009-09-05 21:47:34 +000010699 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10700 {
10701 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010702 (ping_width != mng_info->page.width) ||
10703 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010704 {
10705 unsigned char
10706 chunk[32];
10707
10708 /*
10709 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10710 */
10711 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10712 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010713 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010714 chunk[4]=4;
10715 chunk[5]=0; /* frame name separator (no name) */
10716 chunk[6]=1; /* flag for changing delay, for next frame only */
10717 chunk[7]=0; /* flag for changing frame timeout */
10718 chunk[8]=1; /* flag for changing frame clipping for next frame */
10719 chunk[9]=0; /* flag for changing frame sync_id */
10720 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10721 chunk[14]=0; /* clipping boundaries delta type */
10722 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10723 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010724 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010725 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10726 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010727 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010728 (void) WriteBlob(image,31,chunk);
10729 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10730 mng_info->old_framing_mode=4;
10731 mng_info->framing_mode=1;
10732 }
glennrp0fe50b42010-11-16 03:52:51 +000010733
cristy3ed852e2009-09-05 21:47:34 +000010734 else
10735 mng_info->framing_mode=3;
10736 }
10737 if (mng_info->write_mng && !mng_info->need_fram &&
10738 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +000010739 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010740 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010741 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010742
cristy3ed852e2009-09-05 21:47:34 +000010743 /*
10744 Free PNG resources.
10745 */
glennrp5af765f2010-03-30 11:12:18 +000010746
cristy3ed852e2009-09-05 21:47:34 +000010747 png_destroy_write_struct(&ping,&ping_info);
10748
glennrpcf002022011-01-30 02:38:15 +000010749 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010750
10751#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010752 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010753#endif
10754
glennrpda8f3a72011-02-27 23:54:12 +000010755 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010756 (void) CloseBlob(image);
10757
10758 image_info=DestroyImageInfo(image_info);
10759 image=DestroyImage(image);
10760
10761 /* Store bit depth actually written */
10762 s[0]=(char) ping_bit_depth;
10763 s[1]='\0';
10764
10765 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
10766
cristy3ed852e2009-09-05 21:47:34 +000010767 if (logging != MagickFalse)
10768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10769 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010770
cristy3ed852e2009-09-05 21:47:34 +000010771 return(MagickTrue);
10772/* End write one PNG image */
10773}
10774
10775/*
10776%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10777% %
10778% %
10779% %
10780% W r i t e P N G I m a g e %
10781% %
10782% %
10783% %
10784%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10785%
10786% WritePNGImage() writes a Portable Network Graphics (PNG) or
10787% Multiple-image Network Graphics (MNG) image file.
10788%
10789% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10790%
10791% The format of the WritePNGImage method is:
10792%
cristy1e178e72011-08-28 19:44:34 +000010793% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10794% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010795%
10796% A description of each parameter follows:
10797%
10798% o image_info: the image info.
10799%
10800% o image: The image.
10801%
cristy1e178e72011-08-28 19:44:34 +000010802% o exception: return any errors or warnings in this structure.
10803%
cristy3ed852e2009-09-05 21:47:34 +000010804% Returns MagickTrue on success, MagickFalse on failure.
10805%
10806% Communicating with the PNG encoder:
10807%
10808% While the datastream written is always in PNG format and normally would
10809% be given the "png" file extension, this method also writes the following
10810% pseudo-formats which are subsets of PNG:
10811%
glennrp5a39f372011-02-25 04:52:16 +000010812% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10813% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010814% is present, the tRNS chunk must only have values 0 and 255
10815% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010816% transparent). If other values are present they will be
10817% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010818% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000010819% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10820% of any resulting fully-transparent pixels is changed to
10821% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000010822%
10823% If you want better quantization or dithering of the colors
10824% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010825% PNG encoder. The pixels contain 8-bit indices even if
10826% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010827% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010828% PNG grayscale type might be slightly more efficient. Please
10829% note that writing to the PNG8 format may result in loss
10830% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010831%
10832% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10833% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010834% one of the colors as transparent. The only loss incurred
10835% is reduction of sample depth to 8. If the image has more
10836% than one transparent color, has semitransparent pixels, or
10837% has an opaque pixel with the same RGB components as the
10838% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010839%
10840% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10841% transparency is permitted, i.e., the alpha sample for
10842% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010843% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010844% The only loss in data is the reduction of the sample depth
10845% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010846%
10847% o -define: For more precise control of the PNG output, you can use the
10848% Image options "png:bit-depth" and "png:color-type". These
10849% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010850% from the application programming interfaces. The options
10851% are case-independent and are converted to lowercase before
10852% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010853%
10854% png:color-type can be 0, 2, 3, 4, or 6.
10855%
10856% When png:color-type is 0 (Grayscale), png:bit-depth can
10857% be 1, 2, 4, 8, or 16.
10858%
10859% When png:color-type is 2 (RGB), png:bit-depth can
10860% be 8 or 16.
10861%
10862% When png:color-type is 3 (Indexed), png:bit-depth can
10863% be 1, 2, 4, or 8. This refers to the number of bits
10864% used to store the index. The color samples always have
10865% bit-depth 8 in indexed PNG files.
10866%
10867% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10868% png:bit-depth can be 8 or 16.
10869%
glennrp5a39f372011-02-25 04:52:16 +000010870% If the image cannot be written without loss with the requested bit-depth
10871% and color-type, a PNG file will not be written, and the encoder will
10872% return MagickFalse.
10873%
cristy3ed852e2009-09-05 21:47:34 +000010874% Since image encoders should not be responsible for the "heavy lifting",
10875% the user should make sure that ImageMagick has already reduced the
10876% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010877% transparency prior to attempting to write the image with depth, color,
glennrp54dc0692011-07-01 19:02:13 +000010878% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010879%
cristy3ed852e2009-09-05 21:47:34 +000010880% Note that another definition, "png:bit-depth-written" exists, but it
10881% is not intended for external use. It is only used internally by the
10882% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10883%
10884% It is possible to request that the PNG encoder write previously-formatted
10885% ancillary chunks in the output PNG file, using the "-profile" commandline
10886% option as shown below or by setting the profile via a programming
10887% interface:
10888%
10889% -profile PNG-chunk-x:<file>
10890%
10891% where x is a location flag and <file> is a file containing the chunk
10892% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010893% This encoder will compute the chunk length and CRC, so those must not
10894% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010895%
10896% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10897% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10898% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010899% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010900%
glennrpbb8a7332010-11-13 15:17:35 +000010901% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010902%
glennrp3241bd02010-12-12 04:36:28 +000010903% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010904%
glennrpd6afd542010-11-19 01:53:05 +000010905% o 32-bit depth is reduced to 16.
10906% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10907% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010908% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010909% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010910% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010911% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10912% this can be done without loss and a larger bit depth N was not
10913% requested via the "-define PNG:bit-depth=N" option.
10914% o If matte channel is present but only one transparent color is
10915% present, RGB+tRNS is written instead of RGBA
10916% o Opaque matte channel is removed (or added, if color-type 4 or 6
10917% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010918%
cristy3ed852e2009-09-05 21:47:34 +000010919%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10920*/
10921static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +000010922 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010923{
10924 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010925 excluding,
10926 logging,
10927 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010928 status;
10929
10930 MngInfo
10931 *mng_info;
10932
10933 const char
10934 *value;
10935
10936 int
glennrp21f0e622011-01-07 16:20:57 +000010937 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010938 source;
10939
cristy3ed852e2009-09-05 21:47:34 +000010940 /*
10941 Open image file.
10942 */
10943 assert(image_info != (const ImageInfo *) NULL);
10944 assert(image_info->signature == MagickSignature);
10945 assert(image != (Image *) NULL);
10946 assert(image->signature == MagickSignature);
10947 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010948 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010949 /*
10950 Allocate a MngInfo structure.
10951 */
10952 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010953 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010954
cristy3ed852e2009-09-05 21:47:34 +000010955 if (mng_info == (MngInfo *) NULL)
10956 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010957
cristy3ed852e2009-09-05 21:47:34 +000010958 /*
10959 Initialize members of the MngInfo structure.
10960 */
10961 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10962 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010963 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010964 have_mng_structure=MagickTrue;
10965
10966 /* See if user has requested a specific PNG subformat */
10967
10968 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10969 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10970 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10971
10972 if (mng_info->write_png8)
10973 {
glennrp9c1eb072010-06-06 22:19:15 +000010974 mng_info->write_png_colortype = /* 3 */ 4;
10975 mng_info->write_png_depth = 8;
10976 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010977 }
10978
10979 if (mng_info->write_png24)
10980 {
glennrp9c1eb072010-06-06 22:19:15 +000010981 mng_info->write_png_colortype = /* 2 */ 3;
10982 mng_info->write_png_depth = 8;
10983 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010984
glennrp9c1eb072010-06-06 22:19:15 +000010985 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000010986 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010987
glennrp9c1eb072010-06-06 22:19:15 +000010988 else
cristy018f07f2011-09-04 21:15:19 +000010989 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010990
glennrp9c1eb072010-06-06 22:19:15 +000010991 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010992 }
10993
10994 if (mng_info->write_png32)
10995 {
glennrp9c1eb072010-06-06 22:19:15 +000010996 mng_info->write_png_colortype = /* 6 */ 7;
10997 mng_info->write_png_depth = 8;
10998 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010999
glennrp9c1eb072010-06-06 22:19:15 +000011000 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011001 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011002
glennrp9c1eb072010-06-06 22:19:15 +000011003 else
cristy018f07f2011-09-04 21:15:19 +000011004 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011005
glennrp9c1eb072010-06-06 22:19:15 +000011006 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000011007 }
11008
11009 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011010
cristy3ed852e2009-09-05 21:47:34 +000011011 if (value != (char *) NULL)
11012 {
11013 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011014 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011015
cristy3ed852e2009-09-05 21:47:34 +000011016 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011017 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011018
cristy3ed852e2009-09-05 21:47:34 +000011019 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011020 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011021
cristy3ed852e2009-09-05 21:47:34 +000011022 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011023 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011024
cristy3ed852e2009-09-05 21:47:34 +000011025 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011026 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011027
glennrpbb8a7332010-11-13 15:17:35 +000011028 else
11029 (void) ThrowMagickException(&image->exception,
11030 GetMagickModule(),CoderWarning,
11031 "ignoring invalid defined png:bit-depth",
11032 "=%s",value);
11033
cristy3ed852e2009-09-05 21:47:34 +000011034 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011036 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011037 }
glennrp0fe50b42010-11-16 03:52:51 +000011038
cristy3ed852e2009-09-05 21:47:34 +000011039 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011040
cristy3ed852e2009-09-05 21:47:34 +000011041 if (value != (char *) NULL)
11042 {
11043 /* We must store colortype+1 because 0 is a valid colortype */
11044 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011045 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011046
cristy3ed852e2009-09-05 21:47:34 +000011047 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011048 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011049
cristy3ed852e2009-09-05 21:47:34 +000011050 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011051 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011052
cristy3ed852e2009-09-05 21:47:34 +000011053 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011054 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011055
cristy3ed852e2009-09-05 21:47:34 +000011056 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011057 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011058
glennrpbb8a7332010-11-13 15:17:35 +000011059 else
11060 (void) ThrowMagickException(&image->exception,
11061 GetMagickModule(),CoderWarning,
11062 "ignoring invalid defined png:color-type",
11063 "=%s",value);
11064
cristy3ed852e2009-09-05 21:47:34 +000011065 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011067 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011068 }
11069
glennrp0e8ea192010-12-24 18:00:33 +000011070 /* Check for chunks to be excluded:
11071 *
glennrp0dff56c2011-01-29 19:10:02 +000011072 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011073 * listed in the "unused_chunks" array, above.
11074 *
11075 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
11076 * define (in the image properties or in the image artifacts)
11077 * or via a mng_info member. For convenience, in addition
11078 * to or instead of a comma-separated list of chunks, the
11079 * "exclude-chunk" string can be simply "all" or "none".
11080 *
11081 * The exclude-chunk define takes priority over the mng_info.
11082 *
11083 * A "PNG:include-chunk" define takes priority over both the
11084 * mng_info and the "PNG:exclude-chunk" define. Like the
11085 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011086 * well as a comma-separated list. Chunks that are unknown to
11087 * ImageMagick are always excluded, regardless of their "copy-safe"
11088 * status according to the PNG specification, and even if they
11089 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000011090 *
11091 * Finally, all chunks listed in the "unused_chunks" array are
11092 * automatically excluded, regardless of the other instructions
11093 * or lack thereof.
11094 *
11095 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11096 * will not be written and the gAMA chunk will only be written if it
11097 * is not between .45 and .46, or approximately (1.0/2.2).
11098 *
11099 * If you exclude tRNS and the image has transparency, the colortype
11100 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11101 *
11102 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011103 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011104 */
11105
glennrp26f37912010-12-23 16:22:42 +000011106 mng_info->ping_exclude_bKGD=MagickFalse;
11107 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011108 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011109 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11110 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011111 mng_info->ping_exclude_iCCP=MagickFalse;
11112 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11113 mng_info->ping_exclude_oFFs=MagickFalse;
11114 mng_info->ping_exclude_pHYs=MagickFalse;
11115 mng_info->ping_exclude_sRGB=MagickFalse;
11116 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011117 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011118 mng_info->ping_exclude_vpAg=MagickFalse;
11119 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11120 mng_info->ping_exclude_zTXt=MagickFalse;
11121
glennrp8d3d6e52011-04-19 04:39:51 +000011122 mng_info->ping_preserve_colormap=MagickFalse;
11123
11124 value=GetImageArtifact(image,"png:preserve-colormap");
11125 if (value == NULL)
11126 value=GetImageOption(image_info,"png:preserve-colormap");
11127 if (value != NULL)
11128 mng_info->ping_preserve_colormap=MagickTrue;
11129
glennrp18682582011-06-30 18:11:47 +000011130 /* Thes compression-level, compression-strategy, and compression-filter
11131 * defines take precedence over values from the -quality option.
11132 */
11133 value=GetImageArtifact(image,"png:compression-level");
11134 if (value == NULL)
11135 value=GetImageOption(image_info,"png:compression-level");
11136 if (value != NULL)
11137 {
glennrp18682582011-06-30 18:11:47 +000011138 /* We have to add 1 to everything because 0 is a valid input,
11139 * and we want to use 0 (the default) to mean undefined.
11140 */
11141 if (LocaleCompare(value,"0") == 0)
11142 mng_info->write_png_compression_level = 1;
11143
11144 if (LocaleCompare(value,"1") == 0)
11145 mng_info->write_png_compression_level = 2;
11146
11147 else if (LocaleCompare(value,"2") == 0)
11148 mng_info->write_png_compression_level = 3;
11149
11150 else if (LocaleCompare(value,"3") == 0)
11151 mng_info->write_png_compression_level = 4;
11152
11153 else if (LocaleCompare(value,"4") == 0)
11154 mng_info->write_png_compression_level = 5;
11155
11156 else if (LocaleCompare(value,"5") == 0)
11157 mng_info->write_png_compression_level = 6;
11158
11159 else if (LocaleCompare(value,"6") == 0)
11160 mng_info->write_png_compression_level = 7;
11161
11162 else if (LocaleCompare(value,"7") == 0)
11163 mng_info->write_png_compression_level = 8;
11164
11165 else if (LocaleCompare(value,"8") == 0)
11166 mng_info->write_png_compression_level = 9;
11167
11168 else if (LocaleCompare(value,"9") == 0)
11169 mng_info->write_png_compression_level = 10;
11170
11171 else
11172 (void) ThrowMagickException(&image->exception,
11173 GetMagickModule(),CoderWarning,
11174 "ignoring invalid defined png:compression-level",
11175 "=%s",value);
11176 }
11177
11178 value=GetImageArtifact(image,"png:compression-strategy");
11179 if (value == NULL)
11180 value=GetImageOption(image_info,"png:compression-strategy");
11181 if (value != NULL)
11182 {
11183
11184 if (LocaleCompare(value,"0") == 0)
11185 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11186
11187 else if (LocaleCompare(value,"1") == 0)
11188 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11189
11190 else if (LocaleCompare(value,"2") == 0)
11191 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11192
11193 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011194#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011195 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011196#else
11197 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11198#endif
glennrp18682582011-06-30 18:11:47 +000011199
11200 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011201#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011202 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011203#else
11204 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11205#endif
glennrp18682582011-06-30 18:11:47 +000011206
11207 else
11208 (void) ThrowMagickException(&image->exception,
11209 GetMagickModule(),CoderWarning,
11210 "ignoring invalid defined png:compression-strategy",
11211 "=%s",value);
11212 }
11213
11214 value=GetImageArtifact(image,"png:compression-filter");
11215 if (value == NULL)
11216 value=GetImageOption(image_info,"png:compression-filter");
11217 if (value != NULL)
11218 {
11219
11220 /* To do: combinations of filters allowed by libpng
11221 * masks 0x08 through 0xf8
11222 *
11223 * Implement this as a comma-separated list of 0,1,2,3,4,5
11224 * where 5 is a special case meaning PNG_ALL_FILTERS.
11225 */
11226
11227 if (LocaleCompare(value,"0") == 0)
11228 mng_info->write_png_compression_filter = 1;
11229
11230 if (LocaleCompare(value,"1") == 0)
11231 mng_info->write_png_compression_filter = 2;
11232
11233 else if (LocaleCompare(value,"2") == 0)
11234 mng_info->write_png_compression_filter = 3;
11235
11236 else if (LocaleCompare(value,"3") == 0)
11237 mng_info->write_png_compression_filter = 4;
11238
11239 else if (LocaleCompare(value,"4") == 0)
11240 mng_info->write_png_compression_filter = 5;
11241
11242 else if (LocaleCompare(value,"5") == 0)
11243 mng_info->write_png_compression_filter = 6;
11244
glennrp18682582011-06-30 18:11:47 +000011245 else
11246 (void) ThrowMagickException(&image->exception,
11247 GetMagickModule(),CoderWarning,
11248 "ignoring invalid defined png:compression-filter",
11249 "=%s",value);
11250 }
11251
glennrp03812ae2010-12-24 01:31:34 +000011252 excluding=MagickFalse;
11253
glennrp5c7cf4e2010-12-24 00:30:00 +000011254 for (source=0; source<1; source++)
11255 {
11256 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011257 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011258 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011259
11260 if (value == NULL)
11261 value=GetImageArtifact(image,"png:exclude-chunks");
11262 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011263 else
glennrpacba0042010-12-24 14:27:26 +000011264 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011265 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011266
glennrpacba0042010-12-24 14:27:26 +000011267 if (value == NULL)
11268 value=GetImageOption(image_info,"png:exclude-chunks");
11269 }
11270
glennrp03812ae2010-12-24 01:31:34 +000011271 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011272 {
glennrp03812ae2010-12-24 01:31:34 +000011273
11274 size_t
11275 last;
11276
11277 excluding=MagickTrue;
11278
11279 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011280 {
11281 if (source == 0)
11282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11283 " png:exclude-chunk=%s found in image artifacts.\n", value);
11284 else
11285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11286 " png:exclude-chunk=%s found in image properties.\n", value);
11287 }
glennrp03812ae2010-12-24 01:31:34 +000011288
11289 last=strlen(value);
11290
11291 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011292 {
glennrp03812ae2010-12-24 01:31:34 +000011293
11294 if (LocaleNCompare(value+i,"all",3) == 0)
11295 {
11296 mng_info->ping_exclude_bKGD=MagickTrue;
11297 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011298 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011299 mng_info->ping_exclude_EXIF=MagickTrue;
11300 mng_info->ping_exclude_gAMA=MagickTrue;
11301 mng_info->ping_exclude_iCCP=MagickTrue;
11302 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11303 mng_info->ping_exclude_oFFs=MagickTrue;
11304 mng_info->ping_exclude_pHYs=MagickTrue;
11305 mng_info->ping_exclude_sRGB=MagickTrue;
11306 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011307 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011308 mng_info->ping_exclude_vpAg=MagickTrue;
11309 mng_info->ping_exclude_zCCP=MagickTrue;
11310 mng_info->ping_exclude_zTXt=MagickTrue;
11311 i--;
11312 }
glennrp2cc891a2010-12-24 13:44:32 +000011313
glennrp03812ae2010-12-24 01:31:34 +000011314 if (LocaleNCompare(value+i,"none",4) == 0)
11315 {
11316 mng_info->ping_exclude_bKGD=MagickFalse;
11317 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011318 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011319 mng_info->ping_exclude_EXIF=MagickFalse;
11320 mng_info->ping_exclude_gAMA=MagickFalse;
11321 mng_info->ping_exclude_iCCP=MagickFalse;
11322 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11323 mng_info->ping_exclude_oFFs=MagickFalse;
11324 mng_info->ping_exclude_pHYs=MagickFalse;
11325 mng_info->ping_exclude_sRGB=MagickFalse;
11326 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011327 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011328 mng_info->ping_exclude_vpAg=MagickFalse;
11329 mng_info->ping_exclude_zCCP=MagickFalse;
11330 mng_info->ping_exclude_zTXt=MagickFalse;
11331 }
glennrp2cc891a2010-12-24 13:44:32 +000011332
glennrp03812ae2010-12-24 01:31:34 +000011333 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11334 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011335
glennrp03812ae2010-12-24 01:31:34 +000011336 if (LocaleNCompare(value+i,"chrm",4) == 0)
11337 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011338
glennrpa0ed0092011-04-18 16:36:29 +000011339 if (LocaleNCompare(value+i,"date",4) == 0)
11340 mng_info->ping_exclude_date=MagickTrue;
11341
glennrp03812ae2010-12-24 01:31:34 +000011342 if (LocaleNCompare(value+i,"exif",4) == 0)
11343 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011344
glennrp03812ae2010-12-24 01:31:34 +000011345 if (LocaleNCompare(value+i,"gama",4) == 0)
11346 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011347
glennrp03812ae2010-12-24 01:31:34 +000011348 if (LocaleNCompare(value+i,"iccp",4) == 0)
11349 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011350
glennrp03812ae2010-12-24 01:31:34 +000011351 /*
11352 if (LocaleNCompare(value+i,"itxt",4) == 0)
11353 mng_info->ping_exclude_iTXt=MagickTrue;
11354 */
glennrp2cc891a2010-12-24 13:44:32 +000011355
glennrp03812ae2010-12-24 01:31:34 +000011356 if (LocaleNCompare(value+i,"gama",4) == 0)
11357 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011358
glennrp03812ae2010-12-24 01:31:34 +000011359 if (LocaleNCompare(value+i,"offs",4) == 0)
11360 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011361
glennrp03812ae2010-12-24 01:31:34 +000011362 if (LocaleNCompare(value+i,"phys",4) == 0)
11363 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011364
glennrpa1e3b7b2010-12-24 16:37:33 +000011365 if (LocaleNCompare(value+i,"srgb",4) == 0)
11366 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011367
glennrp03812ae2010-12-24 01:31:34 +000011368 if (LocaleNCompare(value+i,"text",4) == 0)
11369 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011370
glennrpa1e3b7b2010-12-24 16:37:33 +000011371 if (LocaleNCompare(value+i,"trns",4) == 0)
11372 mng_info->ping_exclude_tRNS=MagickTrue;
11373
glennrp03812ae2010-12-24 01:31:34 +000011374 if (LocaleNCompare(value+i,"vpag",4) == 0)
11375 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011376
glennrp03812ae2010-12-24 01:31:34 +000011377 if (LocaleNCompare(value+i,"zccp",4) == 0)
11378 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011379
glennrp03812ae2010-12-24 01:31:34 +000011380 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11381 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011382
glennrp03812ae2010-12-24 01:31:34 +000011383 }
glennrpce91ed52010-12-23 22:37:49 +000011384 }
glennrp26f37912010-12-23 16:22:42 +000011385 }
11386
glennrp5c7cf4e2010-12-24 00:30:00 +000011387 for (source=0; source<1; source++)
11388 {
11389 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011390 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011391 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011392
11393 if (value == NULL)
11394 value=GetImageArtifact(image,"png:include-chunks");
11395 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011396 else
glennrpacba0042010-12-24 14:27:26 +000011397 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011398 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011399
glennrpacba0042010-12-24 14:27:26 +000011400 if (value == NULL)
11401 value=GetImageOption(image_info,"png:include-chunks");
11402 }
11403
glennrp03812ae2010-12-24 01:31:34 +000011404 if (value != NULL)
11405 {
11406 size_t
11407 last;
glennrp26f37912010-12-23 16:22:42 +000011408
glennrp03812ae2010-12-24 01:31:34 +000011409 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011410
glennrp03812ae2010-12-24 01:31:34 +000011411 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011412 {
11413 if (source == 0)
11414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11415 " png:include-chunk=%s found in image artifacts.\n", value);
11416 else
11417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11418 " png:include-chunk=%s found in image properties.\n", value);
11419 }
glennrp03812ae2010-12-24 01:31:34 +000011420
11421 last=strlen(value);
11422
11423 for (i=0; i<(int) last; i+=5)
11424 {
11425 if (LocaleNCompare(value+i,"all",3) == 0)
11426 {
11427 mng_info->ping_exclude_bKGD=MagickFalse;
11428 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011429 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011430 mng_info->ping_exclude_EXIF=MagickFalse;
11431 mng_info->ping_exclude_gAMA=MagickFalse;
11432 mng_info->ping_exclude_iCCP=MagickFalse;
11433 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11434 mng_info->ping_exclude_oFFs=MagickFalse;
11435 mng_info->ping_exclude_pHYs=MagickFalse;
11436 mng_info->ping_exclude_sRGB=MagickFalse;
11437 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011438 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011439 mng_info->ping_exclude_vpAg=MagickFalse;
11440 mng_info->ping_exclude_zCCP=MagickFalse;
11441 mng_info->ping_exclude_zTXt=MagickFalse;
11442 i--;
11443 }
glennrp2cc891a2010-12-24 13:44:32 +000011444
glennrp03812ae2010-12-24 01:31:34 +000011445 if (LocaleNCompare(value+i,"none",4) == 0)
11446 {
11447 mng_info->ping_exclude_bKGD=MagickTrue;
11448 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011449 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011450 mng_info->ping_exclude_EXIF=MagickTrue;
11451 mng_info->ping_exclude_gAMA=MagickTrue;
11452 mng_info->ping_exclude_iCCP=MagickTrue;
11453 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11454 mng_info->ping_exclude_oFFs=MagickTrue;
11455 mng_info->ping_exclude_pHYs=MagickTrue;
11456 mng_info->ping_exclude_sRGB=MagickTrue;
11457 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011458 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011459 mng_info->ping_exclude_vpAg=MagickTrue;
11460 mng_info->ping_exclude_zCCP=MagickTrue;
11461 mng_info->ping_exclude_zTXt=MagickTrue;
11462 }
glennrp2cc891a2010-12-24 13:44:32 +000011463
glennrp03812ae2010-12-24 01:31:34 +000011464 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11465 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011466
glennrp03812ae2010-12-24 01:31:34 +000011467 if (LocaleNCompare(value+i,"chrm",4) == 0)
11468 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011469
glennrpa0ed0092011-04-18 16:36:29 +000011470 if (LocaleNCompare(value+i,"date",4) == 0)
11471 mng_info->ping_exclude_date=MagickFalse;
11472
glennrp03812ae2010-12-24 01:31:34 +000011473 if (LocaleNCompare(value+i,"exif",4) == 0)
11474 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011475
glennrp03812ae2010-12-24 01:31:34 +000011476 if (LocaleNCompare(value+i,"gama",4) == 0)
11477 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011478
glennrp03812ae2010-12-24 01:31:34 +000011479 if (LocaleNCompare(value+i,"iccp",4) == 0)
11480 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011481
glennrp03812ae2010-12-24 01:31:34 +000011482 /*
11483 if (LocaleNCompare(value+i,"itxt",4) == 0)
11484 mng_info->ping_exclude_iTXt=MagickFalse;
11485 */
glennrp2cc891a2010-12-24 13:44:32 +000011486
glennrp03812ae2010-12-24 01:31:34 +000011487 if (LocaleNCompare(value+i,"gama",4) == 0)
11488 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011489
glennrp03812ae2010-12-24 01:31:34 +000011490 if (LocaleNCompare(value+i,"offs",4) == 0)
11491 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011492
glennrp03812ae2010-12-24 01:31:34 +000011493 if (LocaleNCompare(value+i,"phys",4) == 0)
11494 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011495
glennrpa1e3b7b2010-12-24 16:37:33 +000011496 if (LocaleNCompare(value+i,"srgb",4) == 0)
11497 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011498
glennrp03812ae2010-12-24 01:31:34 +000011499 if (LocaleNCompare(value+i,"text",4) == 0)
11500 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011501
glennrpa1e3b7b2010-12-24 16:37:33 +000011502 if (LocaleNCompare(value+i,"trns",4) == 0)
11503 mng_info->ping_exclude_tRNS=MagickFalse;
11504
glennrp03812ae2010-12-24 01:31:34 +000011505 if (LocaleNCompare(value+i,"vpag",4) == 0)
11506 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011507
glennrp03812ae2010-12-24 01:31:34 +000011508 if (LocaleNCompare(value+i,"zccp",4) == 0)
11509 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011510
glennrp03812ae2010-12-24 01:31:34 +000011511 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11512 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011513
glennrp03812ae2010-12-24 01:31:34 +000011514 }
glennrpce91ed52010-12-23 22:37:49 +000011515 }
glennrp26f37912010-12-23 16:22:42 +000011516 }
11517
glennrp03812ae2010-12-24 01:31:34 +000011518 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011519 {
11520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11521 " Chunks to be excluded from the output PNG:");
11522 if (mng_info->ping_exclude_bKGD != MagickFalse)
11523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11524 " bKGD");
11525 if (mng_info->ping_exclude_cHRM != MagickFalse)
11526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11527 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011528 if (mng_info->ping_exclude_date != MagickFalse)
11529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11530 " date");
glennrp26f37912010-12-23 16:22:42 +000011531 if (mng_info->ping_exclude_EXIF != MagickFalse)
11532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11533 " EXIF");
11534 if (mng_info->ping_exclude_gAMA != MagickFalse)
11535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11536 " gAMA");
11537 if (mng_info->ping_exclude_iCCP != MagickFalse)
11538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11539 " iCCP");
11540/*
11541 if (mng_info->ping_exclude_iTXt != MagickFalse)
11542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11543 " iTXt");
11544*/
11545 if (mng_info->ping_exclude_oFFs != MagickFalse)
11546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11547 " oFFs");
11548 if (mng_info->ping_exclude_pHYs != MagickFalse)
11549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11550 " pHYs");
11551 if (mng_info->ping_exclude_sRGB != MagickFalse)
11552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11553 " sRGB");
11554 if (mng_info->ping_exclude_tEXt != MagickFalse)
11555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11556 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011557 if (mng_info->ping_exclude_tRNS != MagickFalse)
11558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11559 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011560 if (mng_info->ping_exclude_vpAg != MagickFalse)
11561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11562 " vpAg");
11563 if (mng_info->ping_exclude_zCCP != MagickFalse)
11564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11565 " zCCP");
11566 if (mng_info->ping_exclude_zTXt != MagickFalse)
11567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11568 " zTXt");
11569 }
11570
glennrpb9cfe272010-12-21 15:08:06 +000011571 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011572
cristy018f07f2011-09-04 21:15:19 +000011573 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011574
11575 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011576
cristy3ed852e2009-09-05 21:47:34 +000011577 if (logging != MagickFalse)
11578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011579
cristy3ed852e2009-09-05 21:47:34 +000011580 return(status);
11581}
11582
11583#if defined(JNG_SUPPORTED)
11584
11585/* Write one JNG image */
11586static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +000011587 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011588{
11589 Image
11590 *jpeg_image;
11591
11592 ImageInfo
11593 *jpeg_image_info;
11594
11595 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011596 logging,
cristy3ed852e2009-09-05 21:47:34 +000011597 status;
11598
11599 size_t
11600 length;
11601
11602 unsigned char
11603 *blob,
11604 chunk[80],
11605 *p;
11606
11607 unsigned int
11608 jng_alpha_compression_method,
11609 jng_alpha_sample_depth,
11610 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011611 transparent;
11612
cristybb503372010-05-27 20:51:26 +000011613 size_t
cristy3ed852e2009-09-05 21:47:34 +000011614 jng_quality;
11615
11616 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011617 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011618
11619 blob=(unsigned char *) NULL;
11620 jpeg_image=(Image *) NULL;
11621 jpeg_image_info=(ImageInfo *) NULL;
11622
11623 status=MagickTrue;
11624 transparent=image_info->type==GrayscaleMatteType ||
11625 image_info->type==TrueColorMatteType;
11626 jng_color_type=10;
11627 jng_alpha_sample_depth=0;
11628 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11629 jng_alpha_compression_method=0;
11630
11631 if (image->matte != MagickFalse)
11632 {
11633 /* if any pixels are transparent */
11634 transparent=MagickTrue;
11635 if (image_info->compression==JPEGCompression)
11636 jng_alpha_compression_method=8;
11637 }
11638
11639 if (transparent)
11640 {
cristybd5a96c2011-08-21 00:04:26 +000011641 ChannelType
11642 channel_mask;
11643
cristy3ed852e2009-09-05 21:47:34 +000011644 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011645
cristy3ed852e2009-09-05 21:47:34 +000011646 /* Create JPEG blob, image, and image_info */
11647 if (logging != MagickFalse)
11648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +000011649 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011650
cristy3ed852e2009-09-05 21:47:34 +000011651 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011652
cristy3ed852e2009-09-05 21:47:34 +000011653 if (jpeg_image_info == (ImageInfo *) NULL)
11654 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011655
cristy3ed852e2009-09-05 21:47:34 +000011656 if (logging != MagickFalse)
11657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11658 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011659
cristy3ed852e2009-09-05 21:47:34 +000011660 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011661
cristy3ed852e2009-09-05 21:47:34 +000011662 if (jpeg_image == (Image *) NULL)
11663 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011664
cristy3ed852e2009-09-05 21:47:34 +000011665 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristybd5a96c2011-08-21 00:04:26 +000011666 channel_mask=SetPixelChannelMask(jpeg_image,AlphaChannel);
cristy3139dc22011-07-08 00:11:42 +000011667 status=SeparateImage(jpeg_image);
cristybd5a96c2011-08-21 00:04:26 +000011668 (void) SetPixelChannelMap(jpeg_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +000011669 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011670
cristy3ed852e2009-09-05 21:47:34 +000011671 if (jng_quality >= 1000)
11672 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000011673
cristy3ed852e2009-09-05 21:47:34 +000011674 else
11675 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000011676
cristy3ed852e2009-09-05 21:47:34 +000011677 jpeg_image_info->type=GrayscaleType;
cristy018f07f2011-09-04 21:15:19 +000011678 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000011679 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011680 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011681 "%s",jpeg_image->filename);
11682 }
11683
11684 /* To do: check bit depth of PNG alpha channel */
11685
11686 /* Check if image is grayscale. */
11687 if (image_info->type != TrueColorMatteType && image_info->type !=
11688 TrueColorType && ImageIsGray(image))
11689 jng_color_type-=2;
11690
11691 if (transparent)
11692 {
11693 if (jng_alpha_compression_method==0)
11694 {
11695 const char
11696 *value;
11697
cristy4c08aed2011-07-01 19:47:50 +000011698 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011699 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11700 &image->exception);
11701 if (logging != MagickFalse)
11702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11703 " Creating PNG blob.");
11704 length=0;
11705
11706 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11707 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11708 jpeg_image_info->interlace=NoInterlace;
11709
11710 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11711 &image->exception);
11712
11713 /* Retrieve sample depth used */
11714 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
11715 if (value != (char *) NULL)
11716 jng_alpha_sample_depth= (unsigned int) value[0];
11717 }
11718 else
11719 {
cristy4c08aed2011-07-01 19:47:50 +000011720 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011721
11722 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11723 &image->exception);
11724
11725 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11726 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11727 jpeg_image_info->interlace=NoInterlace;
11728 if (logging != MagickFalse)
11729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11730 " Creating blob.");
11731 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000011732 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000011733 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011734
cristy3ed852e2009-09-05 21:47:34 +000011735 if (logging != MagickFalse)
11736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011737 " Successfully read jpeg_image into a blob, length=%.20g.",
11738 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011739
11740 }
11741 /* Destroy JPEG image and image_info */
11742 jpeg_image=DestroyImage(jpeg_image);
11743 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11744 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11745 }
11746
11747 /* Write JHDR chunk */
11748 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11749 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011750 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011751 PNGLong(chunk+4,(png_uint_32) image->columns);
11752 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011753 chunk[12]=jng_color_type;
11754 chunk[13]=8; /* sample depth */
11755 chunk[14]=8; /*jng_image_compression_method */
11756 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11757 chunk[16]=jng_alpha_sample_depth;
11758 chunk[17]=jng_alpha_compression_method;
11759 chunk[18]=0; /*jng_alpha_filter_method */
11760 chunk[19]=0; /*jng_alpha_interlace_method */
11761 (void) WriteBlob(image,20,chunk);
11762 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11763 if (logging != MagickFalse)
11764 {
11765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011766 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011767
cristy3ed852e2009-09-05 21:47:34 +000011768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011769 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011770
cristy3ed852e2009-09-05 21:47:34 +000011771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11772 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011773
cristy3ed852e2009-09-05 21:47:34 +000011774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11775 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011776
cristy3ed852e2009-09-05 21:47:34 +000011777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11778 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011779
cristy3ed852e2009-09-05 21:47:34 +000011780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11781 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011782
cristy3ed852e2009-09-05 21:47:34 +000011783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11784 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011785
cristy3ed852e2009-09-05 21:47:34 +000011786 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11787 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011788
cristy3ed852e2009-09-05 21:47:34 +000011789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11790 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011791
cristy3ed852e2009-09-05 21:47:34 +000011792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11793 " JNG alpha interlace:%5d",0);
11794 }
11795
glennrp0fe50b42010-11-16 03:52:51 +000011796 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011797 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011798
11799 /*
11800 Write leading ancillary chunks
11801 */
11802
11803 if (transparent)
11804 {
11805 /*
11806 Write JNG bKGD chunk
11807 */
11808
11809 unsigned char
11810 blue,
11811 green,
11812 red;
11813
cristybb503372010-05-27 20:51:26 +000011814 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011815 num_bytes;
11816
11817 if (jng_color_type == 8 || jng_color_type == 12)
11818 num_bytes=6L;
11819 else
11820 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011821 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011822 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011823 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011824 red=ScaleQuantumToChar(image->background_color.red);
11825 green=ScaleQuantumToChar(image->background_color.green);
11826 blue=ScaleQuantumToChar(image->background_color.blue);
11827 *(chunk+4)=0;
11828 *(chunk+5)=red;
11829 *(chunk+6)=0;
11830 *(chunk+7)=green;
11831 *(chunk+8)=0;
11832 *(chunk+9)=blue;
11833 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11834 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11835 }
11836
11837 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11838 {
11839 /*
11840 Write JNG sRGB chunk
11841 */
11842 (void) WriteBlobMSBULong(image,1L);
11843 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011844 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011845
cristy3ed852e2009-09-05 21:47:34 +000011846 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011847 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011848 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011849 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011850
cristy3ed852e2009-09-05 21:47:34 +000011851 else
glennrpe610a072010-08-05 17:08:46 +000011852 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011853 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011854 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011855
cristy3ed852e2009-09-05 21:47:34 +000011856 (void) WriteBlob(image,5,chunk);
11857 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11858 }
11859 else
11860 {
11861 if (image->gamma != 0.0)
11862 {
11863 /*
11864 Write JNG gAMA chunk
11865 */
11866 (void) WriteBlobMSBULong(image,4L);
11867 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011868 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011869 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011870 (void) WriteBlob(image,8,chunk);
11871 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11872 }
glennrp0fe50b42010-11-16 03:52:51 +000011873
cristy3ed852e2009-09-05 21:47:34 +000011874 if ((mng_info->equal_chrms == MagickFalse) &&
11875 (image->chromaticity.red_primary.x != 0.0))
11876 {
11877 PrimaryInfo
11878 primary;
11879
11880 /*
11881 Write JNG cHRM chunk
11882 */
11883 (void) WriteBlobMSBULong(image,32L);
11884 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011885 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011886 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011887 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11888 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011889 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011890 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11891 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011892 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011893 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11894 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011895 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011896 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11897 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011898 (void) WriteBlob(image,36,chunk);
11899 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11900 }
11901 }
glennrp0fe50b42010-11-16 03:52:51 +000011902
cristy3ed852e2009-09-05 21:47:34 +000011903 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11904 {
11905 /*
11906 Write JNG pHYs chunk
11907 */
11908 (void) WriteBlobMSBULong(image,9L);
11909 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011910 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011911 if (image->units == PixelsPerInchResolution)
11912 {
cristy35ef8242010-06-03 16:24:13 +000011913 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011914 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011915
cristy35ef8242010-06-03 16:24:13 +000011916 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011917 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011918
cristy3ed852e2009-09-05 21:47:34 +000011919 chunk[12]=1;
11920 }
glennrp0fe50b42010-11-16 03:52:51 +000011921
cristy3ed852e2009-09-05 21:47:34 +000011922 else
11923 {
11924 if (image->units == PixelsPerCentimeterResolution)
11925 {
cristy35ef8242010-06-03 16:24:13 +000011926 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011927 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011928
cristy35ef8242010-06-03 16:24:13 +000011929 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011930 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011931
cristy3ed852e2009-09-05 21:47:34 +000011932 chunk[12]=1;
11933 }
glennrp0fe50b42010-11-16 03:52:51 +000011934
cristy3ed852e2009-09-05 21:47:34 +000011935 else
11936 {
cristy35ef8242010-06-03 16:24:13 +000011937 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11938 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011939 chunk[12]=0;
11940 }
11941 }
11942 (void) WriteBlob(image,13,chunk);
11943 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11944 }
11945
11946 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11947 {
11948 /*
11949 Write JNG oFFs chunk
11950 */
11951 (void) WriteBlobMSBULong(image,9L);
11952 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011953 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011954 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11955 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011956 chunk[12]=0;
11957 (void) WriteBlob(image,13,chunk);
11958 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11959 }
11960 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11961 {
11962 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11963 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011964 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011965 PNGLong(chunk+4,(png_uint_32) image->page.width);
11966 PNGLong(chunk+8,(png_uint_32) image->page.height);
11967 chunk[12]=0; /* unit = pixels */
11968 (void) WriteBlob(image,13,chunk);
11969 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11970 }
11971
11972
11973 if (transparent)
11974 {
11975 if (jng_alpha_compression_method==0)
11976 {
cristybb503372010-05-27 20:51:26 +000011977 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011978 i;
11979
cristybb503372010-05-27 20:51:26 +000011980 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011981 len;
11982
11983 /* Write IDAT chunk header */
11984 if (logging != MagickFalse)
11985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011986 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000011987 length);
cristy3ed852e2009-09-05 21:47:34 +000011988
11989 /* Copy IDAT chunks */
11990 len=0;
11991 p=blob+8;
cristybb503372010-05-27 20:51:26 +000011992 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000011993 {
11994 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11995 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000011996
cristy3ed852e2009-09-05 21:47:34 +000011997 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11998 {
11999 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000012000 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000012001 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000012002 (void) WriteBlob(image,(size_t) len+4,p);
12003 (void) WriteBlobMSBULong(image,
12004 crc32(0,p,(uInt) len+4));
12005 }
glennrp0fe50b42010-11-16 03:52:51 +000012006
cristy3ed852e2009-09-05 21:47:34 +000012007 else
12008 {
12009 if (logging != MagickFalse)
12010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012011 " Skipping %c%c%c%c chunk, length=%.20g.",
12012 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012013 }
12014 p+=(8+len);
12015 }
12016 }
12017 else
12018 {
12019 /* Write JDAA chunk header */
12020 if (logging != MagickFalse)
12021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012022 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012023 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012024 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012025 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012026 /* Write JDAT chunk(s) data */
12027 (void) WriteBlob(image,4,chunk);
12028 (void) WriteBlob(image,length,blob);
12029 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12030 (uInt) length));
12031 }
12032 blob=(unsigned char *) RelinquishMagickMemory(blob);
12033 }
12034
12035 /* Encode image as a JPEG blob */
12036 if (logging != MagickFalse)
12037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12038 " Creating jpeg_image_info.");
12039 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12040 if (jpeg_image_info == (ImageInfo *) NULL)
12041 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12042
12043 if (logging != MagickFalse)
12044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12045 " Creating jpeg_image.");
12046
12047 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12048 if (jpeg_image == (Image *) NULL)
12049 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12050 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12051
12052 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012053 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012054 jpeg_image->filename);
12055
12056 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12057 &image->exception);
12058
12059 if (logging != MagickFalse)
12060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012061 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12062 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012063
12064 if (jng_color_type == 8 || jng_color_type == 12)
12065 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012066
cristy3ed852e2009-09-05 21:47:34 +000012067 jpeg_image_info->quality=jng_quality % 1000;
12068 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12069 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012070
cristy3ed852e2009-09-05 21:47:34 +000012071 if (logging != MagickFalse)
12072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12073 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012074
cristy3ed852e2009-09-05 21:47:34 +000012075 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000012076
cristy3ed852e2009-09-05 21:47:34 +000012077 if (logging != MagickFalse)
12078 {
12079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012080 " Successfully read jpeg_image into a blob, length=%.20g.",
12081 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012082
12083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012084 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012085 }
glennrp0fe50b42010-11-16 03:52:51 +000012086
cristy3ed852e2009-09-05 21:47:34 +000012087 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012088 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012089 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012090 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012091 (void) WriteBlob(image,4,chunk);
12092 (void) WriteBlob(image,length,blob);
12093 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12094
12095 jpeg_image=DestroyImage(jpeg_image);
12096 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12097 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12098 blob=(unsigned char *) RelinquishMagickMemory(blob);
12099
12100 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012101 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012102
12103 /* Write IEND chunk */
12104 (void) WriteBlobMSBULong(image,0L);
12105 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012106 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012107 (void) WriteBlob(image,4,chunk);
12108 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12109
12110 if (logging != MagickFalse)
12111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12112 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012113
cristy3ed852e2009-09-05 21:47:34 +000012114 return(status);
12115}
12116
12117
12118/*
12119%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12120% %
12121% %
12122% %
12123% W r i t e J N G I m a g e %
12124% %
12125% %
12126% %
12127%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12128%
12129% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12130%
12131% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12132%
12133% The format of the WriteJNGImage method is:
12134%
cristy1e178e72011-08-28 19:44:34 +000012135% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12136% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012137%
12138% A description of each parameter follows:
12139%
12140% o image_info: the image info.
12141%
12142% o image: The image.
12143%
cristy1e178e72011-08-28 19:44:34 +000012144% o exception: return any errors or warnings in this structure.
12145%
cristy3ed852e2009-09-05 21:47:34 +000012146%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12147*/
cristy1e178e72011-08-28 19:44:34 +000012148static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12149 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012150{
12151 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012152 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012153 logging,
cristy3ed852e2009-09-05 21:47:34 +000012154 status;
12155
12156 MngInfo
12157 *mng_info;
12158
cristy3ed852e2009-09-05 21:47:34 +000012159 /*
12160 Open image file.
12161 */
12162 assert(image_info != (const ImageInfo *) NULL);
12163 assert(image_info->signature == MagickSignature);
12164 assert(image != (Image *) NULL);
12165 assert(image->signature == MagickSignature);
12166 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012167 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012168 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12169 if (status == MagickFalse)
12170 return(status);
12171
12172 /*
12173 Allocate a MngInfo structure.
12174 */
12175 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012176 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012177 if (mng_info == (MngInfo *) NULL)
12178 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12179 /*
12180 Initialize members of the MngInfo structure.
12181 */
12182 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12183 mng_info->image=image;
12184 have_mng_structure=MagickTrue;
12185
12186 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12187
cristy018f07f2011-09-04 21:15:19 +000012188 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012189 (void) CloseBlob(image);
12190
12191 (void) CatchImageException(image);
12192 MngInfoFreeStruct(mng_info,&have_mng_structure);
12193 if (logging != MagickFalse)
12194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12195 return(status);
12196}
12197#endif
12198
cristy1e178e72011-08-28 19:44:34 +000012199static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12200 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012201{
12202 const char
12203 *option;
12204
12205 Image
12206 *next_image;
12207
12208 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012209 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012210 status;
12211
glennrp03812ae2010-12-24 01:31:34 +000012212 volatile MagickBooleanType
12213 logging;
12214
cristy3ed852e2009-09-05 21:47:34 +000012215 MngInfo
12216 *mng_info;
12217
12218 int
cristy3ed852e2009-09-05 21:47:34 +000012219 image_count,
12220 need_iterations,
12221 need_matte;
12222
12223 volatile int
12224#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12225 defined(PNG_MNG_FEATURES_SUPPORTED)
12226 need_local_plte,
12227#endif
12228 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012229 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012230 use_global_plte;
12231
cristybb503372010-05-27 20:51:26 +000012232 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012233 i;
12234
12235 unsigned char
12236 chunk[800];
12237
12238 volatile unsigned int
12239 write_jng,
12240 write_mng;
12241
cristybb503372010-05-27 20:51:26 +000012242 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012243 scene;
12244
cristybb503372010-05-27 20:51:26 +000012245 size_t
cristy3ed852e2009-09-05 21:47:34 +000012246 final_delay=0,
12247 initial_delay;
12248
glennrpd5045b42010-03-24 12:40:35 +000012249#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012250 if (image_info->verbose)
12251 printf("Your PNG library (libpng-%s) is rather old.\n",
12252 PNG_LIBPNG_VER_STRING);
12253#endif
12254
12255 /*
12256 Open image file.
12257 */
12258 assert(image_info != (const ImageInfo *) NULL);
12259 assert(image_info->signature == MagickSignature);
12260 assert(image != (Image *) NULL);
12261 assert(image->signature == MagickSignature);
12262 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012263 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012264 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12265 if (status == MagickFalse)
12266 return(status);
12267
12268 /*
12269 Allocate a MngInfo structure.
12270 */
12271 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012272 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012273 if (mng_info == (MngInfo *) NULL)
12274 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12275 /*
12276 Initialize members of the MngInfo structure.
12277 */
12278 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12279 mng_info->image=image;
12280 have_mng_structure=MagickTrue;
12281 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12282
12283 /*
12284 * See if user has requested a specific PNG subformat to be used
12285 * for all of the PNGs in the MNG being written, e.g.,
12286 *
12287 * convert *.png png8:animation.mng
12288 *
12289 * To do: check -define png:bit_depth and png:color_type as well,
12290 * or perhaps use mng:bit_depth and mng:color_type instead for
12291 * global settings.
12292 */
12293
12294 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12295 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12296 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12297
12298 write_jng=MagickFalse;
12299 if (image_info->compression == JPEGCompression)
12300 write_jng=MagickTrue;
12301
12302 mng_info->adjoin=image_info->adjoin &&
12303 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12304
cristy3ed852e2009-09-05 21:47:34 +000012305 if (logging != MagickFalse)
12306 {
12307 /* Log some info about the input */
12308 Image
12309 *p;
12310
12311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12312 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012313
cristy3ed852e2009-09-05 21:47:34 +000012314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012315 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012316
cristy3ed852e2009-09-05 21:47:34 +000012317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12318 " Type: %d",image_info->type);
12319
12320 scene=0;
12321 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12322 {
12323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012324 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012325
cristy3ed852e2009-09-05 21:47:34 +000012326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012327 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012328
cristy3ed852e2009-09-05 21:47:34 +000012329 if (p->matte)
12330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12331 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012332
cristy3ed852e2009-09-05 21:47:34 +000012333 else
12334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12335 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012336
cristy3ed852e2009-09-05 21:47:34 +000012337 if (p->storage_class == PseudoClass)
12338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12339 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012340
cristy3ed852e2009-09-05 21:47:34 +000012341 else
12342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12343 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012344
cristy3ed852e2009-09-05 21:47:34 +000012345 if (p->colors)
12346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012347 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012348
cristy3ed852e2009-09-05 21:47:34 +000012349 else
12350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12351 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012352
cristy3ed852e2009-09-05 21:47:34 +000012353 if (mng_info->adjoin == MagickFalse)
12354 break;
12355 }
12356 }
12357
cristy3ed852e2009-09-05 21:47:34 +000012358 use_global_plte=MagickFalse;
12359 all_images_are_gray=MagickFalse;
12360#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12361 need_local_plte=MagickTrue;
12362#endif
12363 need_defi=MagickFalse;
12364 need_matte=MagickFalse;
12365 mng_info->framing_mode=1;
12366 mng_info->old_framing_mode=1;
12367
12368 if (write_mng)
12369 if (image_info->page != (char *) NULL)
12370 {
12371 /*
12372 Determine image bounding box.
12373 */
12374 SetGeometry(image,&mng_info->page);
12375 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12376 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12377 }
12378 if (write_mng)
12379 {
12380 unsigned int
12381 need_geom;
12382
12383 unsigned short
12384 red,
12385 green,
12386 blue;
12387
12388 mng_info->page=image->page;
12389 need_geom=MagickTrue;
12390 if (mng_info->page.width || mng_info->page.height)
12391 need_geom=MagickFalse;
12392 /*
12393 Check all the scenes.
12394 */
12395 initial_delay=image->delay;
12396 need_iterations=MagickFalse;
12397 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12398 mng_info->equal_physs=MagickTrue,
12399 mng_info->equal_gammas=MagickTrue;
12400 mng_info->equal_srgbs=MagickTrue;
12401 mng_info->equal_backgrounds=MagickTrue;
12402 image_count=0;
12403#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12404 defined(PNG_MNG_FEATURES_SUPPORTED)
12405 all_images_are_gray=MagickTrue;
12406 mng_info->equal_palettes=MagickFalse;
12407 need_local_plte=MagickFalse;
12408#endif
12409 for (next_image=image; next_image != (Image *) NULL; )
12410 {
12411 if (need_geom)
12412 {
12413 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12414 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012415
cristy3ed852e2009-09-05 21:47:34 +000012416 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12417 mng_info->page.height=next_image->rows+next_image->page.y;
12418 }
glennrp0fe50b42010-11-16 03:52:51 +000012419
cristy3ed852e2009-09-05 21:47:34 +000012420 if (next_image->page.x || next_image->page.y)
12421 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012422
cristy3ed852e2009-09-05 21:47:34 +000012423 if (next_image->matte)
12424 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012425
cristy3ed852e2009-09-05 21:47:34 +000012426 if ((int) next_image->dispose >= BackgroundDispose)
12427 if (next_image->matte || next_image->page.x || next_image->page.y ||
12428 ((next_image->columns < mng_info->page.width) &&
12429 (next_image->rows < mng_info->page.height)))
12430 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012431
cristy3ed852e2009-09-05 21:47:34 +000012432 if (next_image->iterations)
12433 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012434
cristy3ed852e2009-09-05 21:47:34 +000012435 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012436
cristy3ed852e2009-09-05 21:47:34 +000012437 if (final_delay != initial_delay || final_delay > 1UL*
12438 next_image->ticks_per_second)
12439 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012440
cristy3ed852e2009-09-05 21:47:34 +000012441#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12442 defined(PNG_MNG_FEATURES_SUPPORTED)
12443 /*
12444 check for global palette possibility.
12445 */
12446 if (image->matte != MagickFalse)
12447 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012448
cristy3ed852e2009-09-05 21:47:34 +000012449 if (need_local_plte == 0)
12450 {
12451 if (ImageIsGray(image) == MagickFalse)
12452 all_images_are_gray=MagickFalse;
12453 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12454 if (use_global_plte == 0)
12455 use_global_plte=mng_info->equal_palettes;
12456 need_local_plte=!mng_info->equal_palettes;
12457 }
12458#endif
12459 if (GetNextImageInList(next_image) != (Image *) NULL)
12460 {
12461 if (next_image->background_color.red !=
12462 next_image->next->background_color.red ||
12463 next_image->background_color.green !=
12464 next_image->next->background_color.green ||
12465 next_image->background_color.blue !=
12466 next_image->next->background_color.blue)
12467 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012468
cristy3ed852e2009-09-05 21:47:34 +000012469 if (next_image->gamma != next_image->next->gamma)
12470 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012471
cristy3ed852e2009-09-05 21:47:34 +000012472 if (next_image->rendering_intent !=
12473 next_image->next->rendering_intent)
12474 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012475
cristy3ed852e2009-09-05 21:47:34 +000012476 if ((next_image->units != next_image->next->units) ||
12477 (next_image->x_resolution != next_image->next->x_resolution) ||
12478 (next_image->y_resolution != next_image->next->y_resolution))
12479 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012480
cristy3ed852e2009-09-05 21:47:34 +000012481 if (mng_info->equal_chrms)
12482 {
12483 if (next_image->chromaticity.red_primary.x !=
12484 next_image->next->chromaticity.red_primary.x ||
12485 next_image->chromaticity.red_primary.y !=
12486 next_image->next->chromaticity.red_primary.y ||
12487 next_image->chromaticity.green_primary.x !=
12488 next_image->next->chromaticity.green_primary.x ||
12489 next_image->chromaticity.green_primary.y !=
12490 next_image->next->chromaticity.green_primary.y ||
12491 next_image->chromaticity.blue_primary.x !=
12492 next_image->next->chromaticity.blue_primary.x ||
12493 next_image->chromaticity.blue_primary.y !=
12494 next_image->next->chromaticity.blue_primary.y ||
12495 next_image->chromaticity.white_point.x !=
12496 next_image->next->chromaticity.white_point.x ||
12497 next_image->chromaticity.white_point.y !=
12498 next_image->next->chromaticity.white_point.y)
12499 mng_info->equal_chrms=MagickFalse;
12500 }
12501 }
12502 image_count++;
12503 next_image=GetNextImageInList(next_image);
12504 }
12505 if (image_count < 2)
12506 {
12507 mng_info->equal_backgrounds=MagickFalse;
12508 mng_info->equal_chrms=MagickFalse;
12509 mng_info->equal_gammas=MagickFalse;
12510 mng_info->equal_srgbs=MagickFalse;
12511 mng_info->equal_physs=MagickFalse;
12512 use_global_plte=MagickFalse;
12513#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12514 need_local_plte=MagickTrue;
12515#endif
12516 need_iterations=MagickFalse;
12517 }
glennrp0fe50b42010-11-16 03:52:51 +000012518
cristy3ed852e2009-09-05 21:47:34 +000012519 if (mng_info->need_fram == MagickFalse)
12520 {
12521 /*
12522 Only certain framing rates 100/n are exactly representable without
12523 the FRAM chunk but we'll allow some slop in VLC files
12524 */
12525 if (final_delay == 0)
12526 {
12527 if (need_iterations != MagickFalse)
12528 {
12529 /*
12530 It's probably a GIF with loop; don't run it *too* fast.
12531 */
glennrp02617122010-07-28 13:07:35 +000012532 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012533 {
12534 final_delay=10;
12535 (void) ThrowMagickException(&image->exception,
12536 GetMagickModule(),CoderWarning,
12537 "input has zero delay between all frames; assuming",
12538 " 10 cs `%s'","");
12539 }
cristy3ed852e2009-09-05 21:47:34 +000012540 }
12541 else
12542 mng_info->ticks_per_second=0;
12543 }
12544 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012545 mng_info->ticks_per_second=(png_uint_32)
12546 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012547 if (final_delay > 50)
12548 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012549
cristy3ed852e2009-09-05 21:47:34 +000012550 if (final_delay > 75)
12551 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012552
cristy3ed852e2009-09-05 21:47:34 +000012553 if (final_delay > 125)
12554 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012555
cristy3ed852e2009-09-05 21:47:34 +000012556 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12557 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12558 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12559 1UL*image->ticks_per_second))
12560 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12561 }
glennrp0fe50b42010-11-16 03:52:51 +000012562
cristy3ed852e2009-09-05 21:47:34 +000012563 if (mng_info->need_fram != MagickFalse)
12564 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12565 /*
12566 If pseudocolor, we should also check to see if all the
12567 palettes are identical and write a global PLTE if they are.
12568 ../glennrp Feb 99.
12569 */
12570 /*
12571 Write the MNG version 1.0 signature and MHDR chunk.
12572 */
12573 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12574 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12575 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012576 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012577 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12578 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012579 PNGLong(chunk+12,mng_info->ticks_per_second);
12580 PNGLong(chunk+16,0L); /* layer count=unknown */
12581 PNGLong(chunk+20,0L); /* frame count=unknown */
12582 PNGLong(chunk+24,0L); /* play time=unknown */
12583 if (write_jng)
12584 {
12585 if (need_matte)
12586 {
12587 if (need_defi || mng_info->need_fram || use_global_plte)
12588 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012589
cristy3ed852e2009-09-05 21:47:34 +000012590 else
12591 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12592 }
glennrp0fe50b42010-11-16 03:52:51 +000012593
cristy3ed852e2009-09-05 21:47:34 +000012594 else
12595 {
12596 if (need_defi || mng_info->need_fram || use_global_plte)
12597 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012598
cristy3ed852e2009-09-05 21:47:34 +000012599 else
12600 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12601 }
12602 }
glennrp0fe50b42010-11-16 03:52:51 +000012603
cristy3ed852e2009-09-05 21:47:34 +000012604 else
12605 {
12606 if (need_matte)
12607 {
12608 if (need_defi || mng_info->need_fram || use_global_plte)
12609 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012610
cristy3ed852e2009-09-05 21:47:34 +000012611 else
12612 PNGLong(chunk+28,9L); /* simplicity=VLC */
12613 }
glennrp0fe50b42010-11-16 03:52:51 +000012614
cristy3ed852e2009-09-05 21:47:34 +000012615 else
12616 {
12617 if (need_defi || mng_info->need_fram || use_global_plte)
12618 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012619
cristy3ed852e2009-09-05 21:47:34 +000012620 else
12621 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12622 }
12623 }
12624 (void) WriteBlob(image,32,chunk);
12625 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12626 option=GetImageOption(image_info,"mng:need-cacheoff");
12627 if (option != (const char *) NULL)
12628 {
12629 size_t
12630 length;
12631
12632 /*
12633 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12634 */
12635 PNGType(chunk,mng_nEED);
12636 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012637 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012638 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012639 length+=4;
12640 (void) WriteBlob(image,length,chunk);
12641 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12642 }
12643 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12644 (GetNextImageInList(image) != (Image *) NULL) &&
12645 (image->iterations != 1))
12646 {
12647 /*
12648 Write MNG TERM chunk
12649 */
12650 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12651 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012652 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012653 chunk[4]=3; /* repeat animation */
12654 chunk[5]=0; /* show last frame when done */
12655 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12656 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012657
cristy3ed852e2009-09-05 21:47:34 +000012658 if (image->iterations == 0)
12659 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012660
cristy3ed852e2009-09-05 21:47:34 +000012661 else
12662 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012663
cristy3ed852e2009-09-05 21:47:34 +000012664 if (logging != MagickFalse)
12665 {
12666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012667 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12668 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012669
cristy3ed852e2009-09-05 21:47:34 +000012670 if (image->iterations == 0)
12671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012672 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012673
cristy3ed852e2009-09-05 21:47:34 +000012674 else
12675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012676 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012677 }
12678 (void) WriteBlob(image,14,chunk);
12679 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12680 }
12681 /*
12682 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12683 */
12684 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12685 mng_info->equal_srgbs)
12686 {
12687 /*
12688 Write MNG sRGB chunk
12689 */
12690 (void) WriteBlobMSBULong(image,1L);
12691 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012692 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012693
cristy3ed852e2009-09-05 21:47:34 +000012694 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012695 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012696 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012697 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012698
cristy3ed852e2009-09-05 21:47:34 +000012699 else
glennrpe610a072010-08-05 17:08:46 +000012700 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012701 Magick_RenderingIntent_to_PNG_RenderingIntent(
12702 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012703
cristy3ed852e2009-09-05 21:47:34 +000012704 (void) WriteBlob(image,5,chunk);
12705 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12706 mng_info->have_write_global_srgb=MagickTrue;
12707 }
glennrp0fe50b42010-11-16 03:52:51 +000012708
cristy3ed852e2009-09-05 21:47:34 +000012709 else
12710 {
12711 if (image->gamma && mng_info->equal_gammas)
12712 {
12713 /*
12714 Write MNG gAMA chunk
12715 */
12716 (void) WriteBlobMSBULong(image,4L);
12717 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012718 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012719 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012720 (void) WriteBlob(image,8,chunk);
12721 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12722 mng_info->have_write_global_gama=MagickTrue;
12723 }
12724 if (mng_info->equal_chrms)
12725 {
12726 PrimaryInfo
12727 primary;
12728
12729 /*
12730 Write MNG cHRM chunk
12731 */
12732 (void) WriteBlobMSBULong(image,32L);
12733 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012734 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012735 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012736 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12737 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012738 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012739 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12740 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012741 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012742 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12743 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012744 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012745 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12746 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012747 (void) WriteBlob(image,36,chunk);
12748 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12749 mng_info->have_write_global_chrm=MagickTrue;
12750 }
12751 }
12752 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
12753 {
12754 /*
12755 Write MNG pHYs chunk
12756 */
12757 (void) WriteBlobMSBULong(image,9L);
12758 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012759 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012760
cristy3ed852e2009-09-05 21:47:34 +000012761 if (image->units == PixelsPerInchResolution)
12762 {
cristy35ef8242010-06-03 16:24:13 +000012763 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012764 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012765
cristy35ef8242010-06-03 16:24:13 +000012766 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012767 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012768
cristy3ed852e2009-09-05 21:47:34 +000012769 chunk[12]=1;
12770 }
glennrp0fe50b42010-11-16 03:52:51 +000012771
cristy3ed852e2009-09-05 21:47:34 +000012772 else
12773 {
12774 if (image->units == PixelsPerCentimeterResolution)
12775 {
cristy35ef8242010-06-03 16:24:13 +000012776 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012777 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012778
cristy35ef8242010-06-03 16:24:13 +000012779 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012780 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012781
cristy3ed852e2009-09-05 21:47:34 +000012782 chunk[12]=1;
12783 }
glennrp0fe50b42010-11-16 03:52:51 +000012784
cristy3ed852e2009-09-05 21:47:34 +000012785 else
12786 {
cristy35ef8242010-06-03 16:24:13 +000012787 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
12788 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012789 chunk[12]=0;
12790 }
12791 }
12792 (void) WriteBlob(image,13,chunk);
12793 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12794 }
12795 /*
12796 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12797 or does not cover the entire frame.
12798 */
12799 if (write_mng && (image->matte || image->page.x > 0 ||
12800 image->page.y > 0 || (image->page.width &&
12801 (image->page.width+image->page.x < mng_info->page.width))
12802 || (image->page.height && (image->page.height+image->page.y
12803 < mng_info->page.height))))
12804 {
12805 (void) WriteBlobMSBULong(image,6L);
12806 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012807 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012808 red=ScaleQuantumToShort(image->background_color.red);
12809 green=ScaleQuantumToShort(image->background_color.green);
12810 blue=ScaleQuantumToShort(image->background_color.blue);
12811 PNGShort(chunk+4,red);
12812 PNGShort(chunk+6,green);
12813 PNGShort(chunk+8,blue);
12814 (void) WriteBlob(image,10,chunk);
12815 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12816 if (mng_info->equal_backgrounds)
12817 {
12818 (void) WriteBlobMSBULong(image,6L);
12819 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012820 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012821 (void) WriteBlob(image,10,chunk);
12822 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12823 }
12824 }
12825
12826#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12827 if ((need_local_plte == MagickFalse) &&
12828 (image->storage_class == PseudoClass) &&
12829 (all_images_are_gray == MagickFalse))
12830 {
cristybb503372010-05-27 20:51:26 +000012831 size_t
cristy3ed852e2009-09-05 21:47:34 +000012832 data_length;
12833
12834 /*
12835 Write MNG PLTE chunk
12836 */
12837 data_length=3*image->colors;
12838 (void) WriteBlobMSBULong(image,data_length);
12839 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012840 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012841
cristybb503372010-05-27 20:51:26 +000012842 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012843 {
cristy5f07f702011-09-26 17:29:10 +000012844 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12845 image->colormap[i].red) & 0xff);
12846 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12847 image->colormap[i].green) & 0xff);
12848 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12849 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000012850 }
glennrp0fe50b42010-11-16 03:52:51 +000012851
cristy3ed852e2009-09-05 21:47:34 +000012852 (void) WriteBlob(image,data_length+4,chunk);
12853 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12854 mng_info->have_write_global_plte=MagickTrue;
12855 }
12856#endif
12857 }
12858 scene=0;
12859 mng_info->delay=0;
12860#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12861 defined(PNG_MNG_FEATURES_SUPPORTED)
12862 mng_info->equal_palettes=MagickFalse;
12863#endif
12864 do
12865 {
12866 if (mng_info->adjoin)
12867 {
12868#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12869 defined(PNG_MNG_FEATURES_SUPPORTED)
12870 /*
12871 If we aren't using a global palette for the entire MNG, check to
12872 see if we can use one for two or more consecutive images.
12873 */
12874 if (need_local_plte && use_global_plte && !all_images_are_gray)
12875 {
12876 if (mng_info->IsPalette)
12877 {
12878 /*
12879 When equal_palettes is true, this image has the same palette
12880 as the previous PseudoClass image
12881 */
12882 mng_info->have_write_global_plte=mng_info->equal_palettes;
12883 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12884 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12885 {
12886 /*
12887 Write MNG PLTE chunk
12888 */
cristybb503372010-05-27 20:51:26 +000012889 size_t
cristy3ed852e2009-09-05 21:47:34 +000012890 data_length;
12891
12892 data_length=3*image->colors;
12893 (void) WriteBlobMSBULong(image,data_length);
12894 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012895 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012896
cristybb503372010-05-27 20:51:26 +000012897 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012898 {
12899 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12900 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12901 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12902 }
glennrp0fe50b42010-11-16 03:52:51 +000012903
cristy3ed852e2009-09-05 21:47:34 +000012904 (void) WriteBlob(image,data_length+4,chunk);
12905 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12906 (uInt) (data_length+4)));
12907 mng_info->have_write_global_plte=MagickTrue;
12908 }
12909 }
12910 else
12911 mng_info->have_write_global_plte=MagickFalse;
12912 }
12913#endif
12914 if (need_defi)
12915 {
cristybb503372010-05-27 20:51:26 +000012916 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012917 previous_x,
12918 previous_y;
12919
12920 if (scene)
12921 {
12922 previous_x=mng_info->page.x;
12923 previous_y=mng_info->page.y;
12924 }
12925 else
12926 {
12927 previous_x=0;
12928 previous_y=0;
12929 }
12930 mng_info->page=image->page;
12931 if ((mng_info->page.x != previous_x) ||
12932 (mng_info->page.y != previous_y))
12933 {
12934 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12935 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012936 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012937 chunk[4]=0; /* object 0 MSB */
12938 chunk[5]=0; /* object 0 LSB */
12939 chunk[6]=0; /* visible */
12940 chunk[7]=0; /* abstract */
12941 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12942 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12943 (void) WriteBlob(image,16,chunk);
12944 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12945 }
12946 }
12947 }
12948
12949 mng_info->write_mng=write_mng;
12950
12951 if ((int) image->dispose >= 3)
12952 mng_info->framing_mode=3;
12953
12954 if (mng_info->need_fram && mng_info->adjoin &&
12955 ((image->delay != mng_info->delay) ||
12956 (mng_info->framing_mode != mng_info->old_framing_mode)))
12957 {
12958 if (image->delay == mng_info->delay)
12959 {
12960 /*
12961 Write a MNG FRAM chunk with the new framing mode.
12962 */
12963 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12964 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012965 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012966 chunk[4]=(unsigned char) mng_info->framing_mode;
12967 (void) WriteBlob(image,5,chunk);
12968 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12969 }
12970 else
12971 {
12972 /*
12973 Write a MNG FRAM chunk with the delay.
12974 */
12975 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12976 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012977 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012978 chunk[4]=(unsigned char) mng_info->framing_mode;
12979 chunk[5]=0; /* frame name separator (no name) */
12980 chunk[6]=2; /* flag for changing default delay */
12981 chunk[7]=0; /* flag for changing frame timeout */
12982 chunk[8]=0; /* flag for changing frame clipping */
12983 chunk[9]=0; /* flag for changing frame sync_id */
12984 PNGLong(chunk+10,(png_uint_32)
12985 ((mng_info->ticks_per_second*
12986 image->delay)/MagickMax(image->ticks_per_second,1)));
12987 (void) WriteBlob(image,14,chunk);
12988 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000012989 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000012990 }
12991 mng_info->old_framing_mode=mng_info->framing_mode;
12992 }
12993
12994#if defined(JNG_SUPPORTED)
12995 if (image_info->compression == JPEGCompression)
12996 {
12997 ImageInfo
12998 *write_info;
12999
13000 if (logging != MagickFalse)
13001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13002 " Writing JNG object.");
13003 /* To do: specify the desired alpha compression method. */
13004 write_info=CloneImageInfo(image_info);
13005 write_info->compression=UndefinedCompression;
cristy018f07f2011-09-04 21:15:19 +000013006 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013007 write_info=DestroyImageInfo(write_info);
13008 }
13009 else
13010#endif
13011 {
13012 if (logging != MagickFalse)
13013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13014 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013015
glennrpb9cfe272010-12-21 15:08:06 +000013016 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013017 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013018
13019 /* We don't want any ancillary chunks written */
13020 mng_info->ping_exclude_bKGD=MagickTrue;
13021 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013022 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013023 mng_info->ping_exclude_EXIF=MagickTrue;
13024 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013025 mng_info->ping_exclude_iCCP=MagickTrue;
13026 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13027 mng_info->ping_exclude_oFFs=MagickTrue;
13028 mng_info->ping_exclude_pHYs=MagickTrue;
13029 mng_info->ping_exclude_sRGB=MagickTrue;
13030 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013031 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013032 mng_info->ping_exclude_vpAg=MagickTrue;
13033 mng_info->ping_exclude_zCCP=MagickTrue;
13034 mng_info->ping_exclude_zTXt=MagickTrue;
13035
cristy018f07f2011-09-04 21:15:19 +000013036 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013037 }
13038
13039 if (status == MagickFalse)
13040 {
13041 MngInfoFreeStruct(mng_info,&have_mng_structure);
13042 (void) CloseBlob(image);
13043 return(MagickFalse);
13044 }
13045 (void) CatchImageException(image);
13046 if (GetNextImageInList(image) == (Image *) NULL)
13047 break;
13048 image=SyncNextImageInList(image);
13049 status=SetImageProgress(image,SaveImagesTag,scene++,
13050 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013051
cristy3ed852e2009-09-05 21:47:34 +000013052 if (status == MagickFalse)
13053 break;
glennrp0fe50b42010-11-16 03:52:51 +000013054
cristy3ed852e2009-09-05 21:47:34 +000013055 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013056
cristy3ed852e2009-09-05 21:47:34 +000013057 if (write_mng)
13058 {
13059 while (GetPreviousImageInList(image) != (Image *) NULL)
13060 image=GetPreviousImageInList(image);
13061 /*
13062 Write the MEND chunk.
13063 */
13064 (void) WriteBlobMSBULong(image,0x00000000L);
13065 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013066 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013067 (void) WriteBlob(image,4,chunk);
13068 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13069 }
13070 /*
13071 Relinquish resources.
13072 */
13073 (void) CloseBlob(image);
13074 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013075
cristy3ed852e2009-09-05 21:47:34 +000013076 if (logging != MagickFalse)
13077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013078
cristy3ed852e2009-09-05 21:47:34 +000013079 return(MagickTrue);
13080}
glennrpd5045b42010-03-24 12:40:35 +000013081#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013082
cristy3ed852e2009-09-05 21:47:34 +000013083static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13084{
13085 image=image;
13086 printf("Your PNG library is too old: You have libpng-%s\n",
13087 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013088
cristy3ed852e2009-09-05 21:47:34 +000013089 ThrowBinaryException(CoderError,"PNG library is too old",
13090 image_info->filename);
13091}
glennrp39992b42010-11-14 00:03:43 +000013092
cristy3ed852e2009-09-05 21:47:34 +000013093static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13094{
13095 return(WritePNGImage(image_info,image));
13096}
glennrpd5045b42010-03-24 12:40:35 +000013097#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013098#endif