blob: a16e8904b6d82ba54b14957ebbdbd603e8985f77 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP N N GGGG %
7% P P NN N G %
8% PPPP N N N G GG %
9% P N NN G G %
10% P N N GGG %
11% %
12% %
13% Read/Write Portable Network Graphics Image Format %
14% %
15% Software Design %
16% John Cristy %
17% Glenn Randers-Pehrson %
18% November 1997 %
19% %
20% %
cristy7e41fe82010-12-04 23:12:08 +000021% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000022% dedicated to making software imaging solutions freely available. %
23% %
24% You may not use this file except in compliance with the License. You may %
25% obtain a copy of the License at %
26% %
27% http://www.imagemagick.org/script/license.php %
28% %
29% Unless required by applicable law or agreed to in writing, software %
30% distributed under the License is distributed on an "AS IS" BASIS, %
31% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32% See the License for the specific language governing permissions and %
33% limitations under the License. %
34% %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38*/
39
40
41/*
42 Include declarations.
43*/
cristy4c08aed2011-07-01 19:47:50 +000044#include "MagickCore/studio.h"
45#include "MagickCore/artifact.h"
46#include "MagickCore/attribute.h"
47#include "MagickCore/blob.h"
48#include "MagickCore/blob-private.h"
49#include "MagickCore/cache.h"
50#include "MagickCore/color.h"
51#include "MagickCore/color-private.h"
52#include "MagickCore/colormap.h"
53#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000054#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000055#include "MagickCore/constitute.h"
56#include "MagickCore/enhance.h"
57#include "MagickCore/exception.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/geometry.h"
60#include "MagickCore/histogram.h"
61#include "MagickCore/image.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/layer.h"
64#include "MagickCore/list.h"
65#include "MagickCore/log.h"
66#include "MagickCore/MagickCore.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/module.h"
69#include "MagickCore/monitor.h"
70#include "MagickCore/monitor-private.h"
71#include "MagickCore/option.h"
72#include "MagickCore/pixel.h"
73#include "MagickCore/pixel-accessor.h"
74#include "MagickCore/profile.h"
75#include "MagickCore/property.h"
76#include "MagickCore/quantum-private.h"
77#include "MagickCore/resource_.h"
78#include "MagickCore/semaphore.h"
79#include "MagickCore/quantum-private.h"
80#include "MagickCore/static.h"
81#include "MagickCore/statistic.h"
82#include "MagickCore/string_.h"
83#include "MagickCore/string-private.h"
84#include "MagickCore/transform.h"
85#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000086#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp286a6352009-11-09 02:58:50 +000087
glennrp7ef138c2009-11-10 13:50:20 +000088/* Suppress libpng pedantic warnings that were added in
89 * libpng-1.2.41 and libpng-1.4.0. If you are working on
glennrpfaa852b2010-03-30 12:17:00 +000090 * migration to libpng-1.5, remove these defines and then
glennrp7ef138c2009-11-10 13:50:20 +000091 * fix any code that generates warnings.
92 */
glennrp991e92a2010-01-28 03:09:00 +000093/* #define PNG_DEPRECATED Use of this function is deprecated */
glennrpfaa852b2010-03-30 12:17:00 +000094/* #define PNG_USE_RESULT The result of this function must be checked */
95/* #define PNG_NORETURN This function does not return */
96/* #define PNG_ALLOCATED The result of the function is new memory */
glennrp8371ecc2011-02-19 19:15:38 +000097/* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
glennrp5b927342011-04-14 14:31:48 +000098
99/* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
cristy75cfe702011-04-14 14:27:17 +0000100#define PNG_PTR_NORETURN
glennrp286a6352009-11-09 02:58:50 +0000101
cristy3ed852e2009-09-05 21:47:34 +0000102#include "png.h"
103#include "zlib.h"
104
105/* ImageMagick differences */
106#define first_scene scene
107
glennrpd5045b42010-03-24 12:40:35 +0000108#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000109/*
110 Optional declarations. Define or undefine them as you like.
111*/
112/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
113
114/*
115 Features under construction. Define these to work on them.
116*/
117#undef MNG_OBJECT_BUFFERS
118#undef MNG_BASI_SUPPORTED
119#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
120#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
cristy3ed852e2009-09-05 21:47:34 +0000121#if defined(MAGICKCORE_JPEG_DELEGATE)
122# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
123#endif
124#if !defined(RGBColorMatchExact)
125#define IsPNGColorEqual(color,target) \
glennrp8e045c82011-04-27 16:40:27 +0000126 (((color).red == (target).red) && \
127 ((color).green == (target).green) && \
128 ((color).blue == (target).blue))
cristy3ed852e2009-09-05 21:47:34 +0000129#endif
130
glennrp8e58efd2011-05-20 12:16:29 +0000131/* Macros for left-bit-replication to ensure that pixels
132 * and PixelPackets all have the image->depth, and for use
133 * in PNG8 quantization.
134 */
135
136
137/* LBR01: Replicate top bit */
138
glennrp05001c32011-08-06 13:04:16 +0000139#define LBR01PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000140 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
141 0 : QuantumRange);
142
glennrp91d99252011-06-25 14:30:13 +0000143#define LBR01PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000144 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
145 0 : QuantumRange);
146
glennrp91d99252011-06-25 14:30:13 +0000147#define LBR01PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000148 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
149 0 : QuantumRange);
150
cristy4c08aed2011-07-01 19:47:50 +0000151#define LBR01PacketAlpha(pixelpacket) \
152 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000153 0 : QuantumRange);
154
glennrp91d99252011-06-25 14:30:13 +0000155#define LBR01PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000156 { \
glennrp05001c32011-08-06 13:04:16 +0000157 LBR01PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000158 LBR01PacketGreen((pixelpacket)); \
159 LBR01PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000160 }
glennrp8e58efd2011-05-20 12:16:29 +0000161
glennrp91d99252011-06-25 14:30:13 +0000162#define LBR01PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000163 { \
glennrp91d99252011-06-25 14:30:13 +0000164 LBR01PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000165 LBR01PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000166 }
glennrp8e58efd2011-05-20 12:16:29 +0000167
cristyef618312011-06-25 12:26:44 +0000168#define LBR01PixelRed(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000169 (ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000170 0 : QuantumRange);
171
glennrp54cf7972011-08-06 14:28:09 +0000172#define LBR01PixelGreen(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000173 (ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000174 0 : QuantumRange);
175
glennrp54cf7972011-08-06 14:28:09 +0000176#define LBR01PixelBlue(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000177 (ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000178 0 : QuantumRange);
179
glennrp54cf7972011-08-06 14:28:09 +0000180#define LBR01PixelAlpha(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000181 (ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000182 0 : QuantumRange);
183
glennrp54cf7972011-08-06 14:28:09 +0000184#define LBR01PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000185 { \
cristyef618312011-06-25 12:26:44 +0000186 LBR01PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000187 LBR01PixelGreen((pixel)); \
188 LBR01PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000189 }
glennrp8e58efd2011-05-20 12:16:29 +0000190
glennrp54cf7972011-08-06 14:28:09 +0000191#define LBR01PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000192 { \
glennrp54cf7972011-08-06 14:28:09 +0000193 LBR01PixelRGB((pixel)); \
194 LBR01PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000195 }
glennrp8e58efd2011-05-20 12:16:29 +0000196
197/* LBR02: Replicate top 2 bits */
198
glennrp05001c32011-08-06 13:04:16 +0000199#define LBR02PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000200 { \
201 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
202 (pixelpacket).red=ScaleCharToQuantum( \
203 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
204 }
glennrp91d99252011-06-25 14:30:13 +0000205#define LBR02PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000206 { \
207 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
208 (pixelpacket).green=ScaleCharToQuantum( \
209 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
210 }
glennrp91d99252011-06-25 14:30:13 +0000211#define LBR02PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000212 { \
213 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
214 (pixelpacket).blue=ScaleCharToQuantum( \
215 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
216 }
cristy4c08aed2011-07-01 19:47:50 +0000217#define LBR02PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000218 { \
cristy4c08aed2011-07-01 19:47:50 +0000219 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
220 (pixelpacket).alpha=ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000221 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
222 }
223
glennrp91d99252011-06-25 14:30:13 +0000224#define LBR02PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000225 { \
glennrp05001c32011-08-06 13:04:16 +0000226 LBR02PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000227 LBR02PacketGreen((pixelpacket)); \
228 LBR02PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000229 }
glennrp8e58efd2011-05-20 12:16:29 +0000230
glennrp91d99252011-06-25 14:30:13 +0000231#define LBR02PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000232 { \
glennrp91d99252011-06-25 14:30:13 +0000233 LBR02PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000234 LBR02PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000235 }
glennrp8e58efd2011-05-20 12:16:29 +0000236
cristyef618312011-06-25 12:26:44 +0000237#define LBR02PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000238 { \
cristy4c08aed2011-07-01 19:47:50 +0000239 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000240 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000241 SetPixelRed(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000242 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
243 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000244 }
glennrp54cf7972011-08-06 14:28:09 +0000245#define LBR02PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000246 { \
cristy4c08aed2011-07-01 19:47:50 +0000247 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000248 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000249 SetPixelGreen(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000250 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
251 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000252 }
glennrp54cf7972011-08-06 14:28:09 +0000253#define LBR02PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000254 { \
255 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000256 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
257 SetPixelBlue(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000258 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
259 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000260 }
glennrp54cf7972011-08-06 14:28:09 +0000261#define LBR02PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000262 { \
263 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000264 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
265 SetPixelAlpha(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000266 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
267 (pixel) ); \
glennrp8e58efd2011-05-20 12:16:29 +0000268 }
269
glennrp54cf7972011-08-06 14:28:09 +0000270#define LBR02PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000271 { \
cristyef618312011-06-25 12:26:44 +0000272 LBR02PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000273 LBR02PixelGreen((pixel)); \
274 LBR02PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000275 }
glennrp8e58efd2011-05-20 12:16:29 +0000276
glennrp54cf7972011-08-06 14:28:09 +0000277#define LBR02PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000278 { \
glennrp54cf7972011-08-06 14:28:09 +0000279 LBR02PixelRGB((pixel)); \
280 LBR02PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000281 }
glennrp8e58efd2011-05-20 12:16:29 +0000282
283/* LBR03: Replicate top 3 bits (only used with opaque pixels during
284 PNG8 quantization) */
285
glennrp05001c32011-08-06 13:04:16 +0000286#define LBR03PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000287 { \
288 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
289 (pixelpacket).red=ScaleCharToQuantum( \
290 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
291 }
glennrp91d99252011-06-25 14:30:13 +0000292#define LBR03PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000293 { \
294 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
295 (pixelpacket).green=ScaleCharToQuantum( \
296 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
297 }
glennrp91d99252011-06-25 14:30:13 +0000298#define LBR03PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000299 { \
300 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
301 (pixelpacket).blue=ScaleCharToQuantum( \
302 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
303 }
304
glennrp91d99252011-06-25 14:30:13 +0000305#define LBR03PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000306 { \
glennrp05001c32011-08-06 13:04:16 +0000307 LBR03PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000308 LBR03PacketGreen((pixelpacket)); \
309 LBR03PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000310 }
glennrp8e58efd2011-05-20 12:16:29 +0000311
cristyef618312011-06-25 12:26:44 +0000312#define LBR03PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000313 { \
cristy4c08aed2011-07-01 19:47:50 +0000314 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000315 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000316 SetPixelRed(image, ScaleCharToQuantum( \
317 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000318 }
cristy4c08aed2011-07-01 19:47:50 +0000319#define LBR03Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000320 { \
cristy4c08aed2011-07-01 19:47:50 +0000321 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000322 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000323 SetPixelGreen(image, ScaleCharToQuantum( \
324 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000325 }
cristy4c08aed2011-07-01 19:47:50 +0000326#define LBR03Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000327 { \
cristy4c08aed2011-07-01 19:47:50 +0000328 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000329 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000330 SetPixelBlue(image, ScaleCharToQuantum( \
331 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000332 }
333
cristy4c08aed2011-07-01 19:47:50 +0000334#define LBR03RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000335 { \
cristyef618312011-06-25 12:26:44 +0000336 LBR03PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000337 LBR03Green((pixel)); \
338 LBR03Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000339 }
glennrp8e58efd2011-05-20 12:16:29 +0000340
341/* LBR04: Replicate top 4 bits */
342
glennrp05001c32011-08-06 13:04:16 +0000343#define LBR04PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000344 { \
345 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
346 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
347 }
glennrp91d99252011-06-25 14:30:13 +0000348#define LBR04PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000349 { \
350 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
351 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
352 }
glennrp91d99252011-06-25 14:30:13 +0000353#define LBR04PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000354 { \
355 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
356 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
357 }
cristy4c08aed2011-07-01 19:47:50 +0000358#define LBR04PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000359 { \
cristy4c08aed2011-07-01 19:47:50 +0000360 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
361 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
glennrp8e58efd2011-05-20 12:16:29 +0000362 }
363
glennrp91d99252011-06-25 14:30:13 +0000364#define LBR04PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000365 { \
glennrp05001c32011-08-06 13:04:16 +0000366 LBR04PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000367 LBR04PacketGreen((pixelpacket)); \
368 LBR04PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000369 }
glennrp8e58efd2011-05-20 12:16:29 +0000370
glennrp91d99252011-06-25 14:30:13 +0000371#define LBR04PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000372 { \
glennrp91d99252011-06-25 14:30:13 +0000373 LBR04PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000374 LBR04PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000375 }
glennrp8e58efd2011-05-20 12:16:29 +0000376
cristyef618312011-06-25 12:26:44 +0000377#define LBR04PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000378 { \
cristy4c08aed2011-07-01 19:47:50 +0000379 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000380 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000381 SetPixelRed(image,\
382 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000383 }
glennrp54cf7972011-08-06 14:28:09 +0000384#define LBR04PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000385 { \
cristy4c08aed2011-07-01 19:47:50 +0000386 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000387 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000388 SetPixelGreen(image,\
389 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000390 }
glennrp54cf7972011-08-06 14:28:09 +0000391#define LBR04PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000392 { \
393 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000394 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
395 SetPixelBlue(image,\
396 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000397 }
glennrp54cf7972011-08-06 14:28:09 +0000398#define LBR04PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000399 { \
400 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000401 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
402 SetPixelAlpha(image,\
403 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000404 }
405
glennrp54cf7972011-08-06 14:28:09 +0000406#define LBR04PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000407 { \
cristyef618312011-06-25 12:26:44 +0000408 LBR04PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000409 LBR04PixelGreen((pixel)); \
410 LBR04PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000411 }
glennrp8e58efd2011-05-20 12:16:29 +0000412
glennrp54cf7972011-08-06 14:28:09 +0000413#define LBR04PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000414 { \
glennrp54cf7972011-08-06 14:28:09 +0000415 LBR04PixelRGB((pixel)); \
416 LBR04PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000417 }
glennrp8e58efd2011-05-20 12:16:29 +0000418
419
420/* LBR08: Replicate top 8 bits */
421
glennrp05001c32011-08-06 13:04:16 +0000422#define LBR08PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000423 { \
424 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
425 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
426 }
glennrp91d99252011-06-25 14:30:13 +0000427#define LBR08PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000428 { \
429 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
430 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
431 }
glennrp91d99252011-06-25 14:30:13 +0000432#define LBR08PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000433 { \
434 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
435 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
436 }
cristy4c08aed2011-07-01 19:47:50 +0000437#define LBR08PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000438 { \
cristy4c08aed2011-07-01 19:47:50 +0000439 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
440 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000441 }
442
glennrp91d99252011-06-25 14:30:13 +0000443#define LBR08PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000444 { \
glennrp05001c32011-08-06 13:04:16 +0000445 LBR08PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000446 LBR08PacketGreen((pixelpacket)); \
447 LBR08PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000448 }
glennrp8e58efd2011-05-20 12:16:29 +0000449
glennrp91d99252011-06-25 14:30:13 +0000450#define LBR08PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000451 { \
glennrp91d99252011-06-25 14:30:13 +0000452 LBR08PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000453 LBR08PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000454 }
glennrp8e58efd2011-05-20 12:16:29 +0000455
cristyef618312011-06-25 12:26:44 +0000456#define LBR08PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000457 { \
458 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000459 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
460 SetPixelRed(image,\
461 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000462 }
glennrp54cf7972011-08-06 14:28:09 +0000463#define LBR08PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000464 { \
465 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000466 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
467 SetPixelGreen(image,\
468 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000469 }
glennrp54cf7972011-08-06 14:28:09 +0000470#define LBR08PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000471 { \
472 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000473 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
474 SetPixelBlue(image,\
475 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000476 }
glennrp54cf7972011-08-06 14:28:09 +0000477#define LBR08PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000478 { \
479 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000480 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
481 SetPixelAlpha(image,\
482 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000483 }
484
glennrp54cf7972011-08-06 14:28:09 +0000485#define LBR08PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000486 { \
cristyef618312011-06-25 12:26:44 +0000487 LBR08PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000488 LBR08PixelGreen((pixel)); \
489 LBR08PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000490 }
glennrp8e58efd2011-05-20 12:16:29 +0000491
glennrp54cf7972011-08-06 14:28:09 +0000492#define LBR08PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000493 { \
glennrp54cf7972011-08-06 14:28:09 +0000494 LBR08PixelRGB((pixel)); \
495 LBR08PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000496 }
glennrp8e58efd2011-05-20 12:16:29 +0000497
498
499/* LBR16: Replicate top 16 bits */
500
glennrp05001c32011-08-06 13:04:16 +0000501#define LBR16PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000502 { \
503 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
504 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
505 }
glennrp91d99252011-06-25 14:30:13 +0000506#define LBR16PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000507 { \
508 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
509 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
510 }
glennrp91d99252011-06-25 14:30:13 +0000511#define LBR16PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000512 { \
513 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
514 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
515 }
cristy4c08aed2011-07-01 19:47:50 +0000516#define LBR16PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000517 { \
cristy4c08aed2011-07-01 19:47:50 +0000518 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
519 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000520 }
521
glennrp91d99252011-06-25 14:30:13 +0000522#define LBR16PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000523 { \
glennrp05001c32011-08-06 13:04:16 +0000524 LBR16PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000525 LBR16PacketGreen((pixelpacket)); \
526 LBR16PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000527 }
glennrp8e58efd2011-05-20 12:16:29 +0000528
glennrp91d99252011-06-25 14:30:13 +0000529#define LBR16PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000530 { \
glennrp91d99252011-06-25 14:30:13 +0000531 LBR16PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000532 LBR16PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000533 }
glennrp8e58efd2011-05-20 12:16:29 +0000534
cristyef618312011-06-25 12:26:44 +0000535#define LBR16PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000536 { \
537 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000538 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
539 SetPixelRed(image,\
540 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000541 }
glennrp54cf7972011-08-06 14:28:09 +0000542#define LBR16PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000543 { \
544 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000545 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
546 SetPixelGreen(image,\
547 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000548 }
glennrp54cf7972011-08-06 14:28:09 +0000549#define LBR16PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000550 { \
551 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000552 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
553 SetPixelBlue(image,\
554 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000555 }
glennrp54cf7972011-08-06 14:28:09 +0000556#define LBR16PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000557 { \
558 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000559 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
560 SetPixelAlpha(image,\
561 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000562 }
563
glennrp54cf7972011-08-06 14:28:09 +0000564#define LBR16PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000565 { \
cristyef618312011-06-25 12:26:44 +0000566 LBR16PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000567 LBR16PixelGreen((pixel)); \
568 LBR16PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000569 }
glennrp8e58efd2011-05-20 12:16:29 +0000570
glennrp54cf7972011-08-06 14:28:09 +0000571#define LBR16PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000572 { \
glennrp54cf7972011-08-06 14:28:09 +0000573 LBR16PixelRGB((pixel)); \
574 LBR16PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000575 }
glennrp8e58efd2011-05-20 12:16:29 +0000576
cristy3ed852e2009-09-05 21:47:34 +0000577/*
578 Establish thread safety.
579 setjmp/longjmp is claimed to be safe on these platforms:
580 setjmp/longjmp is alleged to be unsafe on these platforms:
581*/
582#ifndef SETJMP_IS_THREAD_SAFE
583#define PNG_SETJMP_NOT_THREAD_SAFE
584#endif
585
586#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
587static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000588 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000589#endif
590
591/*
592 This temporary until I set up malloc'ed object attributes array.
593 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
594 waste more memory.
595*/
596#define MNG_MAX_OBJECTS 256
597
598/*
599 If this not defined, spec is interpreted strictly. If it is
600 defined, an attempt will be made to recover from some errors,
601 including
602 o global PLTE too short
603*/
604#undef MNG_LOOSE
605
606/*
607 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
608 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
609 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
610 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
611 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
612 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
613 will be enabled by default in libpng-1.2.0.
614*/
cristy3ed852e2009-09-05 21:47:34 +0000615#ifdef PNG_MNG_FEATURES_SUPPORTED
616# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
617# define PNG_READ_EMPTY_PLTE_SUPPORTED
618# endif
619# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
620# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
621# endif
622#endif
623
624/*
cristybb503372010-05-27 20:51:26 +0000625 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000626 This macro is only defined in libpng-1.0.3 and later.
627 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
628*/
629#ifndef PNG_UINT_31_MAX
630#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
631#endif
632
633/*
634 Constant strings for known chunk types. If you need to add a chunk,
635 add a string holding the name here. To make the code more
636 portable, we use ASCII numbers like this, not characters.
637*/
638
639static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
640static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
641static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
642static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
643static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
644static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
645static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
646static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
647static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
648static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
649static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
650static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
651static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
652static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
653static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
654static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
655static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
656static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
657static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
658static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
659static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
660static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
661static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
662static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
663static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
664static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
665static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
666static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
667static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
668static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
669static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
670static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
671static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
672static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
673
674#if defined(JNG_SUPPORTED)
675static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
676static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
677static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
678static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
679static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
680static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
681#endif
682
683/*
684Other known chunks that are not yet supported by ImageMagick:
685static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
686static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
687static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
688static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
689static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
690static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
691static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
692static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
693*/
694
695typedef struct _MngBox
696{
cristy8182b072010-05-30 20:10:53 +0000697 long
cristy3ed852e2009-09-05 21:47:34 +0000698 left,
699 right,
700 top,
701 bottom;
702} MngBox;
703
704typedef struct _MngPair
705{
cristy8182b072010-05-30 20:10:53 +0000706 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000707 a,
708 b;
709} MngPair;
710
711#ifdef MNG_OBJECT_BUFFERS
712typedef struct _MngBuffer
713{
714
cristybb503372010-05-27 20:51:26 +0000715 size_t
cristy3ed852e2009-09-05 21:47:34 +0000716 height,
717 width;
718
719 Image
720 *image;
721
722 png_color
723 plte[256];
724
725 int
726 reference_count;
727
728 unsigned char
729 alpha_sample_depth,
730 compression_method,
731 color_type,
732 concrete,
733 filter_method,
734 frozen,
735 image_type,
736 interlace_method,
737 pixel_sample_depth,
738 plte_length,
739 sample_depth,
740 viewable;
741} MngBuffer;
742#endif
743
744typedef struct _MngInfo
745{
746
747#ifdef MNG_OBJECT_BUFFERS
748 MngBuffer
749 *ob[MNG_MAX_OBJECTS];
750#endif
751
752 Image *
753 image;
754
755 RectangleInfo
756 page;
757
758 int
759 adjoin,
760#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
761 bytes_in_read_buffer,
762 found_empty_plte,
763#endif
764 equal_backgrounds,
765 equal_chrms,
766 equal_gammas,
767#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
768 defined(PNG_MNG_FEATURES_SUPPORTED)
769 equal_palettes,
770#endif
771 equal_physs,
772 equal_srgbs,
773 framing_mode,
774 have_global_bkgd,
775 have_global_chrm,
776 have_global_gama,
777 have_global_phys,
778 have_global_sbit,
779 have_global_srgb,
780 have_saved_bkgd_index,
781 have_write_global_chrm,
782 have_write_global_gama,
783 have_write_global_plte,
784 have_write_global_srgb,
785 need_fram,
786 object_id,
787 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000788 saved_bkgd_index;
789
790 int
791 new_number_colors;
792
cristybb503372010-05-27 20:51:26 +0000793 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000794 image_found,
795 loop_count[256],
796 loop_iteration[256],
797 scenes_found,
798 x_off[MNG_MAX_OBJECTS],
799 y_off[MNG_MAX_OBJECTS];
800
801 MngBox
802 clip,
803 frame,
804 image_box,
805 object_clip[MNG_MAX_OBJECTS];
806
807 unsigned char
808 /* These flags could be combined into one byte */
809 exists[MNG_MAX_OBJECTS],
810 frozen[MNG_MAX_OBJECTS],
811 loop_active[256],
812 invisible[MNG_MAX_OBJECTS],
813 viewable[MNG_MAX_OBJECTS];
814
815 MagickOffsetType
816 loop_jump[256];
817
818 png_colorp
819 global_plte;
820
821 png_color_8
822 global_sbit;
823
824 png_byte
825#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
826 read_buffer[8],
827#endif
828 global_trns[256];
829
830 float
831 global_gamma;
832
833 ChromaticityInfo
834 global_chrm;
835
836 RenderingIntent
837 global_srgb_intent;
838
cristy35ef8242010-06-03 16:24:13 +0000839 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000840 delay,
841 global_plte_length,
842 global_trns_length,
843 global_x_pixels_per_unit,
844 global_y_pixels_per_unit,
845 mng_width,
846 mng_height,
847 ticks_per_second;
848
glennrpb9cfe272010-12-21 15:08:06 +0000849 MagickBooleanType
850 need_blob;
851
cristy3ed852e2009-09-05 21:47:34 +0000852 unsigned int
853 IsPalette,
854 global_phys_unit_type,
855 basi_warning,
856 clon_warning,
857 dhdr_warning,
858 jhdr_warning,
859 magn_warning,
860 past_warning,
861 phyg_warning,
862 phys_warning,
863 sbit_warning,
864 show_warning,
865 mng_type,
866 write_mng,
867 write_png_colortype,
868 write_png_depth,
glennrp18682582011-06-30 18:11:47 +0000869 write_png_compression_level,
870 write_png_compression_strategy,
871 write_png_compression_filter,
cristy3ed852e2009-09-05 21:47:34 +0000872 write_png8,
873 write_png24,
874 write_png32;
875
876#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000877 size_t
cristy3ed852e2009-09-05 21:47:34 +0000878 basi_width,
879 basi_height;
880
881 unsigned int
882 basi_depth,
883 basi_color_type,
884 basi_compression_method,
885 basi_filter_type,
886 basi_interlace_method,
887 basi_red,
888 basi_green,
889 basi_blue,
890 basi_alpha,
891 basi_viewable;
892#endif
893
894 png_uint_16
895 magn_first,
896 magn_last,
897 magn_mb,
898 magn_ml,
899 magn_mr,
900 magn_mt,
901 magn_mx,
902 magn_my,
903 magn_methx,
904 magn_methy;
905
906 PixelPacket
907 mng_global_bkgd;
908
glennrp26f37912010-12-23 16:22:42 +0000909 /* Added at version 6.6.6-7 */
910 MagickBooleanType
911 ping_exclude_bKGD,
912 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000913 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000914 ping_exclude_EXIF,
915 ping_exclude_gAMA,
916 ping_exclude_iCCP,
917 /* ping_exclude_iTXt, */
918 ping_exclude_oFFs,
919 ping_exclude_pHYs,
920 ping_exclude_sRGB,
921 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000922 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000923 ping_exclude_vpAg,
924 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000925 ping_exclude_zTXt,
926 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000927
cristy3ed852e2009-09-05 21:47:34 +0000928} MngInfo;
929#endif /* VER */
930
931/*
932 Forward declarations.
933*/
934static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000935 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000936
cristy3ed852e2009-09-05 21:47:34 +0000937static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000938 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000939
cristy3ed852e2009-09-05 21:47:34 +0000940#if defined(JNG_SUPPORTED)
941static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000942 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000943#endif
944
glennrp0c3e06b2010-11-19 13:45:02 +0000945#if PNG_LIBPNG_VER > 10011
946
glennrpfd05d622011-02-25 04:10:33 +0000947
glennrp0c3e06b2010-11-19 13:45:02 +0000948#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
949static MagickBooleanType
glennrpfd05d622011-02-25 04:10:33 +0000950LosslessReduceDepthOK(Image *image)
glennrp0c3e06b2010-11-19 13:45:02 +0000951{
glennrp67b9c1a2011-04-22 18:47:36 +0000952 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
953 *
954 * This is true if the high byte and the next highest byte of
955 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000956 * are equal to each other. We check this by seeing if the samples
957 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000958 *
959 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000960 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000961 */
962
glennrp3faa9a32011-04-23 14:00:25 +0000963#define QuantumToCharToQuantumEqQuantum(quantum) \
964 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
965
glennrp0c3e06b2010-11-19 13:45:02 +0000966 MagickBooleanType
967 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000968
glennrp03e11f62011-04-22 13:30:16 +0000969 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000970 {
971
cristy4c08aed2011-07-01 19:47:50 +0000972 const Quantum
glennrp0c3e06b2010-11-19 13:45:02 +0000973 *p;
974
975 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000976 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
977 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
978 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
979 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000980
981 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
982 {
983 int indx;
984
985 for (indx=0; indx < (ssize_t) image->colors; indx++)
986 {
glennrp3faa9a32011-04-23 14:00:25 +0000987 ok_to_reduce=(
988 QuantumToCharToQuantumEqQuantum(
989 image->colormap[indx].red) &&
990 QuantumToCharToQuantumEqQuantum(
991 image->colormap[indx].green) &&
992 QuantumToCharToQuantumEqQuantum(
993 image->colormap[indx].blue)) ?
994 MagickTrue : MagickFalse;
995
glennrp0c3e06b2010-11-19 13:45:02 +0000996 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000997 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000998 }
999 }
1000
1001 if ((ok_to_reduce != MagickFalse) &&
1002 (image->storage_class != PseudoClass))
1003 {
1004 ssize_t
1005 y;
1006
1007 register ssize_t
1008 x;
1009
1010 for (y=0; y < (ssize_t) image->rows; y++)
1011 {
1012 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1013
cristy4c08aed2011-07-01 19:47:50 +00001014 if (p == (const Quantum *) NULL)
glennrp0c3e06b2010-11-19 13:45:02 +00001015 {
1016 ok_to_reduce = MagickFalse;
1017 break;
1018 }
1019
1020 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1021 {
glennrp3faa9a32011-04-23 14:00:25 +00001022 ok_to_reduce=
cristy4c08aed2011-07-01 19:47:50 +00001023 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1024 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1025 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
glennrp3faa9a32011-04-23 14:00:25 +00001026 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001027
1028 if (ok_to_reduce == MagickFalse)
1029 break;
1030
cristyed231572011-07-14 02:18:59 +00001031 p+=GetPixelChannels(image);
glennrp0c3e06b2010-11-19 13:45:02 +00001032 }
glennrp8640fb52010-11-23 15:48:26 +00001033 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +00001034 break;
1035 }
1036 }
1037
1038 if (ok_to_reduce != MagickFalse)
1039 {
glennrp0c3e06b2010-11-19 13:45:02 +00001040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001041 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +00001042 }
glennrpa6a06632011-01-19 15:15:34 +00001043 else
1044 {
1045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001046 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +00001047 }
glennrp0c3e06b2010-11-19 13:45:02 +00001048 }
1049
1050 return ok_to_reduce;
1051}
1052#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1053
glennrpe610a072010-08-05 17:08:46 +00001054static int
glennrpcf002022011-01-30 02:38:15 +00001055Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +00001056{
glennrpe610a072010-08-05 17:08:46 +00001057 switch (intent)
1058 {
1059 case PerceptualIntent:
1060 return 0;
glennrp0fe50b42010-11-16 03:52:51 +00001061
glennrpe610a072010-08-05 17:08:46 +00001062 case RelativeIntent:
1063 return 1;
glennrp0fe50b42010-11-16 03:52:51 +00001064
glennrpe610a072010-08-05 17:08:46 +00001065 case SaturationIntent:
1066 return 2;
glennrp0fe50b42010-11-16 03:52:51 +00001067
glennrpe610a072010-08-05 17:08:46 +00001068 case AbsoluteIntent:
1069 return 3;
glennrp0fe50b42010-11-16 03:52:51 +00001070
glennrpe610a072010-08-05 17:08:46 +00001071 default:
1072 return -1;
1073 }
1074}
1075
1076static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +00001077Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +00001078{
glennrpcf002022011-01-30 02:38:15 +00001079 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001080 {
1081 case 0:
1082 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001083
glennrpe610a072010-08-05 17:08:46 +00001084 case 1:
1085 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001086
glennrpe610a072010-08-05 17:08:46 +00001087 case 2:
1088 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001089
glennrpe610a072010-08-05 17:08:46 +00001090 case 3:
1091 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001092
glennrpe610a072010-08-05 17:08:46 +00001093 default:
1094 return UndefinedIntent;
1095 }
1096}
1097
cristybb503372010-05-27 20:51:26 +00001098static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001099{
1100 if (x > y)
1101 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001102
cristy3ed852e2009-09-05 21:47:34 +00001103 return(y);
1104}
glennrp0c3e06b2010-11-19 13:45:02 +00001105
cristybb503372010-05-27 20:51:26 +00001106static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001107{
1108 if (x < y)
1109 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001110
cristy3ed852e2009-09-05 21:47:34 +00001111 return(y);
1112}
glennrp0c3e06b2010-11-19 13:45:02 +00001113
cristy3ed852e2009-09-05 21:47:34 +00001114
1115/*
1116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1117% %
1118% %
1119% %
1120% I m a g e I s G r a y %
1121% %
1122% %
1123% %
1124%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1125% %
cristy4c08aed2011-07-01 19:47:50 +00001126% Like IsImageGray except does not change DirectClass to PseudoClass %
cristy3ed852e2009-09-05 21:47:34 +00001127% %
1128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1129*/
1130static MagickBooleanType ImageIsGray(Image *image)
1131{
cristy4c08aed2011-07-01 19:47:50 +00001132 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001133 *p;
1134
cristybb503372010-05-27 20:51:26 +00001135 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001136 i,
1137 x,
1138 y;
1139
1140 assert(image != (Image *) NULL);
1141 assert(image->signature == MagickSignature);
1142 if (image->debug != MagickFalse)
1143 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1144
1145 if (image->storage_class == PseudoClass)
1146 {
cristybb503372010-05-27 20:51:26 +00001147 for (i=0; i < (ssize_t) image->colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00001148 if (IsPixelPacketGray(image->colormap+i) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001149 return(MagickFalse);
1150 return(MagickTrue);
1151 }
cristybb503372010-05-27 20:51:26 +00001152 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001153 {
1154 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00001155 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001156 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +00001157 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001158 {
cristy4c08aed2011-07-01 19:47:50 +00001159 if (IsPixelGray(image,p) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001160 return(MagickFalse);
cristyed231572011-07-14 02:18:59 +00001161 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001162 }
1163 }
1164 return(MagickTrue);
1165}
glennrpd5045b42010-03-24 12:40:35 +00001166#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001167#endif /* MAGICKCORE_PNG_DELEGATE */
1168
1169/*
1170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1171% %
1172% %
1173% %
1174% I s M N G %
1175% %
1176% %
1177% %
1178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1179%
1180% IsMNG() returns MagickTrue if the image format type, identified by the
1181% magick string, is MNG.
1182%
1183% The format of the IsMNG method is:
1184%
1185% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1186%
1187% A description of each parameter follows:
1188%
1189% o magick: compare image format pattern against these bytes.
1190%
1191% o length: Specifies the length of the magick string.
1192%
1193%
1194*/
1195static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1196{
1197 if (length < 8)
1198 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001199
cristy3ed852e2009-09-05 21:47:34 +00001200 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1201 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001202
cristy3ed852e2009-09-05 21:47:34 +00001203 return(MagickFalse);
1204}
1205
1206/*
1207%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1208% %
1209% %
1210% %
1211% I s J N G %
1212% %
1213% %
1214% %
1215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1216%
1217% IsJNG() returns MagickTrue if the image format type, identified by the
1218% magick string, is JNG.
1219%
1220% The format of the IsJNG method is:
1221%
1222% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1223%
1224% A description of each parameter follows:
1225%
1226% o magick: compare image format pattern against these bytes.
1227%
1228% o length: Specifies the length of the magick string.
1229%
1230%
1231*/
1232static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1233{
1234 if (length < 8)
1235 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001236
cristy3ed852e2009-09-05 21:47:34 +00001237 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1238 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001239
cristy3ed852e2009-09-05 21:47:34 +00001240 return(MagickFalse);
1241}
1242
1243/*
1244%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245% %
1246% %
1247% %
1248% I s P N G %
1249% %
1250% %
1251% %
1252%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1253%
1254% IsPNG() returns MagickTrue if the image format type, identified by the
1255% magick string, is PNG.
1256%
1257% The format of the IsPNG method is:
1258%
1259% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1260%
1261% A description of each parameter follows:
1262%
1263% o magick: compare image format pattern against these bytes.
1264%
1265% o length: Specifies the length of the magick string.
1266%
1267*/
1268static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1269{
1270 if (length < 8)
1271 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001272
cristy3ed852e2009-09-05 21:47:34 +00001273 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1274 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001275
cristy3ed852e2009-09-05 21:47:34 +00001276 return(MagickFalse);
1277}
1278
1279#if defined(MAGICKCORE_PNG_DELEGATE)
1280#if defined(__cplusplus) || defined(c_plusplus)
1281extern "C" {
1282#endif
1283
glennrpd5045b42010-03-24 12:40:35 +00001284#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001285static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001286{
1287 unsigned char
1288 buffer[4];
1289
1290 assert(image != (Image *) NULL);
1291 assert(image->signature == MagickSignature);
1292 buffer[0]=(unsigned char) (value >> 24);
1293 buffer[1]=(unsigned char) (value >> 16);
1294 buffer[2]=(unsigned char) (value >> 8);
1295 buffer[3]=(unsigned char) value;
1296 return((size_t) WriteBlob(image,4,buffer));
1297}
1298
1299static void PNGLong(png_bytep p,png_uint_32 value)
1300{
1301 *p++=(png_byte) ((value >> 24) & 0xff);
1302 *p++=(png_byte) ((value >> 16) & 0xff);
1303 *p++=(png_byte) ((value >> 8) & 0xff);
1304 *p++=(png_byte) (value & 0xff);
1305}
1306
glennrpa521b2f2010-10-29 04:11:03 +00001307#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001308static void PNGsLong(png_bytep p,png_int_32 value)
1309{
1310 *p++=(png_byte) ((value >> 24) & 0xff);
1311 *p++=(png_byte) ((value >> 16) & 0xff);
1312 *p++=(png_byte) ((value >> 8) & 0xff);
1313 *p++=(png_byte) (value & 0xff);
1314}
glennrpa521b2f2010-10-29 04:11:03 +00001315#endif
cristy3ed852e2009-09-05 21:47:34 +00001316
1317static void PNGShort(png_bytep p,png_uint_16 value)
1318{
1319 *p++=(png_byte) ((value >> 8) & 0xff);
1320 *p++=(png_byte) (value & 0xff);
1321}
1322
1323static void PNGType(png_bytep p,png_bytep type)
1324{
1325 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1326}
1327
glennrp03812ae2010-12-24 01:31:34 +00001328static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1329 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001330{
1331 if (logging != MagickFalse)
1332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001333 " Writing %c%c%c%c chunk, length: %.20g",
1334 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001335}
glennrpd5045b42010-03-24 12:40:35 +00001336#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001337
1338#if defined(__cplusplus) || defined(c_plusplus)
1339}
1340#endif
1341
glennrpd5045b42010-03-24 12:40:35 +00001342#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001343/*
1344%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1345% %
1346% %
1347% %
1348% R e a d P N G I m a g e %
1349% %
1350% %
1351% %
1352%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1353%
1354% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1355% Multiple-image Network Graphics (MNG) image file and returns it. It
1356% allocates the memory necessary for the new Image structure and returns a
1357% pointer to the new image or set of images.
1358%
1359% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1360%
1361% The format of the ReadPNGImage method is:
1362%
1363% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1364%
1365% A description of each parameter follows:
1366%
1367% o image_info: the image info.
1368%
1369% o exception: return any errors or warnings in this structure.
1370%
1371% To do, more or less in chronological order (as of version 5.5.2,
1372% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1373%
1374% Get 16-bit cheap transparency working.
1375%
1376% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1377%
1378% Preserve all unknown and not-yet-handled known chunks found in input
1379% PNG file and copy them into output PNG files according to the PNG
1380% copying rules.
1381%
1382% (At this point, PNG encoding should be in full MNG compliance)
1383%
1384% Provide options for choice of background to use when the MNG BACK
1385% chunk is not present or is not mandatory (i.e., leave transparent,
1386% user specified, MNG BACK, PNG bKGD)
1387%
1388% Implement LOOP/ENDL [done, but could do discretionary loops more
1389% efficiently by linking in the duplicate frames.].
1390%
1391% Decode and act on the MHDR simplicity profile (offer option to reject
1392% files or attempt to process them anyway when the profile isn't LC or VLC).
1393%
1394% Upgrade to full MNG without Delta-PNG.
1395%
1396% o BACK [done a while ago except for background image ID]
1397% o MOVE [done 15 May 1999]
1398% o CLIP [done 15 May 1999]
1399% o DISC [done 19 May 1999]
1400% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1401% o SEEK [partially done 19 May 1999 (discard function only)]
1402% o SHOW
1403% o PAST
1404% o BASI
1405% o MNG-level tEXt/iTXt/zTXt
1406% o pHYg
1407% o pHYs
1408% o sBIT
1409% o bKGD
1410% o iTXt (wait for libpng implementation).
1411%
1412% Use the scene signature to discover when an identical scene is
1413% being reused, and just point to the original image->exception instead
1414% of storing another set of pixels. This not specific to MNG
1415% but could be applied generally.
1416%
1417% Upgrade to full MNG with Delta-PNG.
1418%
1419% JNG tEXt/iTXt/zTXt
1420%
1421% We will not attempt to read files containing the CgBI chunk.
1422% They are really Xcode files meant for display on the iPhone.
1423% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001424% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001425% since irretrievable loss of color data has occurred due to the
1426% use of premultiplied alpha.
1427*/
1428
1429#if defined(__cplusplus) || defined(c_plusplus)
1430extern "C" {
1431#endif
1432
1433/*
1434 This the function that does the actual reading of data. It is
1435 the same as the one supplied in libpng, except that it receives the
1436 datastream from the ReadBlob() function instead of standard input.
1437*/
1438static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1439{
1440 Image
1441 *image;
1442
1443 image=(Image *) png_get_io_ptr(png_ptr);
1444 if (length)
1445 {
1446 png_size_t
1447 check;
1448
1449 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1450 if (check != length)
1451 {
1452 char
1453 msg[MaxTextExtent];
1454
cristy3b6fd2e2011-05-20 12:53:50 +00001455 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001456 "Expected %.20g bytes; found %.20g bytes",(double) length,
1457 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001458 png_warning(png_ptr,msg);
1459 png_error(png_ptr,"Read Exception");
1460 }
1461 }
1462}
1463
1464#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1465 !defined(PNG_MNG_FEATURES_SUPPORTED)
1466/* We use mng_get_data() instead of png_get_data() if we have a libpng
1467 * older than libpng-1.0.3a, which was the first to allow the empty
1468 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1469 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1470 * encountered after an empty PLTE, so we have to look ahead for bKGD
1471 * chunks and remove them from the datastream that is passed to libpng,
1472 * and store their contents for later use.
1473 */
1474static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1475{
1476 MngInfo
1477 *mng_info;
1478
1479 Image
1480 *image;
1481
1482 png_size_t
1483 check;
1484
cristybb503372010-05-27 20:51:26 +00001485 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001486 i;
1487
1488 i=0;
1489 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1490 image=(Image *) mng_info->image;
1491 while (mng_info->bytes_in_read_buffer && length)
1492 {
1493 data[i]=mng_info->read_buffer[i];
1494 mng_info->bytes_in_read_buffer--;
1495 length--;
1496 i++;
1497 }
1498 if (length)
1499 {
1500 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001501
cristy3ed852e2009-09-05 21:47:34 +00001502 if (check != length)
1503 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001504
cristy3ed852e2009-09-05 21:47:34 +00001505 if (length == 4)
1506 {
1507 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1508 (data[3] == 0))
1509 {
1510 check=(png_size_t) ReadBlob(image,(size_t) length,
1511 (char *) mng_info->read_buffer);
1512 mng_info->read_buffer[4]=0;
1513 mng_info->bytes_in_read_buffer=4;
1514 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1515 mng_info->found_empty_plte=MagickTrue;
1516 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1517 {
1518 mng_info->found_empty_plte=MagickFalse;
1519 mng_info->have_saved_bkgd_index=MagickFalse;
1520 }
1521 }
glennrp0fe50b42010-11-16 03:52:51 +00001522
cristy3ed852e2009-09-05 21:47:34 +00001523 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1524 (data[3] == 1))
1525 {
1526 check=(png_size_t) ReadBlob(image,(size_t) length,
1527 (char *) mng_info->read_buffer);
1528 mng_info->read_buffer[4]=0;
1529 mng_info->bytes_in_read_buffer=4;
1530 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1531 if (mng_info->found_empty_plte)
1532 {
1533 /*
1534 Skip the bKGD data byte and CRC.
1535 */
1536 check=(png_size_t)
1537 ReadBlob(image,5,(char *) mng_info->read_buffer);
1538 check=(png_size_t) ReadBlob(image,(size_t) length,
1539 (char *) mng_info->read_buffer);
1540 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1541 mng_info->have_saved_bkgd_index=MagickTrue;
1542 mng_info->bytes_in_read_buffer=0;
1543 }
1544 }
1545 }
1546 }
1547}
1548#endif
1549
1550static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1551{
1552 Image
1553 *image;
1554
1555 image=(Image *) png_get_io_ptr(png_ptr);
1556 if (length)
1557 {
1558 png_size_t
1559 check;
1560
cristybb503372010-05-27 20:51:26 +00001561 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001562
cristy3ed852e2009-09-05 21:47:34 +00001563 if (check != length)
1564 png_error(png_ptr,"WriteBlob Failed");
1565 }
1566}
1567
1568static void png_flush_data(png_structp png_ptr)
1569{
1570 (void) png_ptr;
1571}
1572
1573#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1574static int PalettesAreEqual(Image *a,Image *b)
1575{
cristybb503372010-05-27 20:51:26 +00001576 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001577 i;
1578
1579 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1580 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001581
cristy3ed852e2009-09-05 21:47:34 +00001582 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1583 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001584
cristy3ed852e2009-09-05 21:47:34 +00001585 if (a->colors != b->colors)
1586 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001587
cristybb503372010-05-27 20:51:26 +00001588 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001589 {
1590 if ((a->colormap[i].red != b->colormap[i].red) ||
1591 (a->colormap[i].green != b->colormap[i].green) ||
1592 (a->colormap[i].blue != b->colormap[i].blue))
1593 return((int) MagickFalse);
1594 }
glennrp0fe50b42010-11-16 03:52:51 +00001595
cristy3ed852e2009-09-05 21:47:34 +00001596 return((int) MagickTrue);
1597}
1598#endif
1599
1600static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1601{
1602 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1603 mng_info->exists[i] && !mng_info->frozen[i])
1604 {
1605#ifdef MNG_OBJECT_BUFFERS
1606 if (mng_info->ob[i] != (MngBuffer *) NULL)
1607 {
1608 if (mng_info->ob[i]->reference_count > 0)
1609 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001610
cristy3ed852e2009-09-05 21:47:34 +00001611 if (mng_info->ob[i]->reference_count == 0)
1612 {
1613 if (mng_info->ob[i]->image != (Image *) NULL)
1614 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001615
cristy3ed852e2009-09-05 21:47:34 +00001616 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1617 }
1618 }
1619 mng_info->ob[i]=(MngBuffer *) NULL;
1620#endif
1621 mng_info->exists[i]=MagickFalse;
1622 mng_info->invisible[i]=MagickFalse;
1623 mng_info->viewable[i]=MagickFalse;
1624 mng_info->frozen[i]=MagickFalse;
1625 mng_info->x_off[i]=0;
1626 mng_info->y_off[i]=0;
1627 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001628 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001629 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001630 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001631 }
1632}
1633
glennrp21f0e622011-01-07 16:20:57 +00001634static void MngInfoFreeStruct(MngInfo *mng_info,
1635 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001636{
glennrp21f0e622011-01-07 16:20:57 +00001637 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001638 {
cristybb503372010-05-27 20:51:26 +00001639 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001640 i;
1641
1642 for (i=1; i < MNG_MAX_OBJECTS; i++)
1643 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001644
cristy3ed852e2009-09-05 21:47:34 +00001645 if (mng_info->global_plte != (png_colorp) NULL)
1646 mng_info->global_plte=(png_colorp)
1647 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001648
cristy3ed852e2009-09-05 21:47:34 +00001649 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1650 *have_mng_structure=MagickFalse;
1651 }
1652}
1653
1654static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1655{
1656 MngBox
1657 box;
1658
1659 box=box1;
1660 if (box.left < box2.left)
1661 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001662
cristy3ed852e2009-09-05 21:47:34 +00001663 if (box.top < box2.top)
1664 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001665
cristy3ed852e2009-09-05 21:47:34 +00001666 if (box.right > box2.right)
1667 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001668
cristy3ed852e2009-09-05 21:47:34 +00001669 if (box.bottom > box2.bottom)
1670 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001671
cristy3ed852e2009-09-05 21:47:34 +00001672 return box;
1673}
1674
1675static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1676{
1677 MngBox
1678 box;
1679
1680 /*
1681 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1682 */
cristybb503372010-05-27 20:51:26 +00001683 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1684 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1685 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1686 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001687 if (delta_type != 0)
1688 {
1689 box.left+=previous_box.left;
1690 box.right+=previous_box.right;
1691 box.top+=previous_box.top;
1692 box.bottom+=previous_box.bottom;
1693 }
glennrp0fe50b42010-11-16 03:52:51 +00001694
cristy3ed852e2009-09-05 21:47:34 +00001695 return(box);
1696}
1697
1698static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1699 unsigned char *p)
1700{
1701 MngPair
1702 pair;
1703 /*
cristybb503372010-05-27 20:51:26 +00001704 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001705 */
cristy8182b072010-05-30 20:10:53 +00001706 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1707 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001708
cristy3ed852e2009-09-05 21:47:34 +00001709 if (delta_type != 0)
1710 {
1711 pair.a+=previous_pair.a;
1712 pair.b+=previous_pair.b;
1713 }
glennrp0fe50b42010-11-16 03:52:51 +00001714
cristy3ed852e2009-09-05 21:47:34 +00001715 return(pair);
1716}
1717
cristy8182b072010-05-30 20:10:53 +00001718static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001719{
cristy8182b072010-05-30 20:10:53 +00001720 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001721}
1722
glennrpcf002022011-01-30 02:38:15 +00001723static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001724{
1725 Image
1726 *image;
1727
1728 image=(Image *) png_get_error_ptr(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001729
cristy3ed852e2009-09-05 21:47:34 +00001730 if (image->debug != MagickFalse)
1731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1732 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001733
cristy3ed852e2009-09-05 21:47:34 +00001734 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1735 message,"`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001736
glennrpe4017e32011-01-08 17:16:09 +00001737#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001738 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1739 * are building with libpng-1.4.x and can be ignored.
1740 */
cristy3ed852e2009-09-05 21:47:34 +00001741 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001742#else
1743 png_longjmp(ping,1);
1744#endif
cristy3ed852e2009-09-05 21:47:34 +00001745}
1746
glennrpcf002022011-01-30 02:38:15 +00001747static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001748{
1749 Image
1750 *image;
1751
1752 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1753 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001754
cristy3ed852e2009-09-05 21:47:34 +00001755 image=(Image *) png_get_error_ptr(ping);
1756 if (image->debug != MagickFalse)
1757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristycc23b9a2010-05-09 22:37:43 +00001758 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001759
cristy3ed852e2009-09-05 21:47:34 +00001760 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1761 message,"`%s'",image->filename);
1762}
1763
1764#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001765static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001766{
1767#if (PNG_LIBPNG_VER < 10011)
1768 png_voidp
1769 ret;
1770
1771 png_ptr=png_ptr;
1772 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001773
cristy3ed852e2009-09-05 21:47:34 +00001774 if (ret == NULL)
1775 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001776
cristy3ed852e2009-09-05 21:47:34 +00001777 return(ret);
1778#else
1779 png_ptr=png_ptr;
1780 return((png_voidp) AcquireMagickMemory((size_t) size));
1781#endif
1782}
1783
1784/*
1785 Free a pointer. It is removed from the list at the same time.
1786*/
glennrpcf002022011-01-30 02:38:15 +00001787static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001788{
1789 png_ptr=png_ptr;
1790 ptr=RelinquishMagickMemory(ptr);
1791 return((png_free_ptr) NULL);
1792}
1793#endif
1794
1795#if defined(__cplusplus) || defined(c_plusplus)
1796}
1797#endif
1798
1799static int
glennrpcf002022011-01-30 02:38:15 +00001800Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristy3ed852e2009-09-05 21:47:34 +00001801 png_textp text,int ii)
1802{
cristybb503372010-05-27 20:51:26 +00001803 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001804 i;
1805
1806 register unsigned char
1807 *dp;
1808
1809 register png_charp
1810 sp;
1811
1812 png_uint_32
1813 length,
1814 nibbles;
1815
1816 StringInfo
1817 *profile;
1818
glennrp0c3e06b2010-11-19 13:45:02 +00001819 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001820 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1821 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1822 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1823 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1824 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1825 13,14,15};
1826
1827 sp=text[ii].text+1;
1828 /* look for newline */
1829 while (*sp != '\n')
1830 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001831
cristy3ed852e2009-09-05 21:47:34 +00001832 /* look for length */
1833 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1834 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001835
cristyf2f27272009-12-17 14:48:46 +00001836 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001837
glennrp97f90e22011-02-22 05:47:58 +00001838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1839 " length: %lu",(unsigned long) length);
1840
cristy3ed852e2009-09-05 21:47:34 +00001841 while (*sp != ' ' && *sp != '\n')
1842 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001843
cristy3ed852e2009-09-05 21:47:34 +00001844 /* allocate space */
1845 if (length == 0)
1846 {
1847 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1848 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1849 return(MagickFalse);
1850 }
glennrp0fe50b42010-11-16 03:52:51 +00001851
cristy8723e4b2011-09-01 13:11:19 +00001852 profile=BlobToStringInfo((const void *) NULL,length);
glennrp0fe50b42010-11-16 03:52:51 +00001853
cristy3ed852e2009-09-05 21:47:34 +00001854 if (profile == (StringInfo *) NULL)
1855 {
1856 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1857 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1858 "unable to copy profile");
1859 return(MagickFalse);
1860 }
glennrp0fe50b42010-11-16 03:52:51 +00001861
cristy3ed852e2009-09-05 21:47:34 +00001862 /* copy profile, skipping white space and column 1 "=" signs */
1863 dp=GetStringInfoDatum(profile);
1864 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001865
cristybb503372010-05-27 20:51:26 +00001866 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001867 {
1868 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1869 {
1870 if (*sp == '\0')
1871 {
1872 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1873 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1874 profile=DestroyStringInfo(profile);
1875 return(MagickFalse);
1876 }
1877 sp++;
1878 }
glennrp0fe50b42010-11-16 03:52:51 +00001879
cristy3ed852e2009-09-05 21:47:34 +00001880 if (i%2 == 0)
1881 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001882
cristy3ed852e2009-09-05 21:47:34 +00001883 else
1884 (*dp++)+=unhex[(int) *sp++];
1885 }
1886 /*
1887 We have already read "Raw profile type.
1888 */
1889 (void) SetImageProfile(image,&text[ii].key[17],profile);
1890 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001891
cristy3ed852e2009-09-05 21:47:34 +00001892 if (image_info->verbose)
1893 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001894
cristy3ed852e2009-09-05 21:47:34 +00001895 return MagickTrue;
1896}
1897
1898#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1899static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1900{
1901 Image
1902 *image;
1903
1904
1905 /* The unknown chunk structure contains the chunk data:
1906 png_byte name[5];
1907 png_byte *data;
1908 png_size_t size;
1909
1910 Note that libpng has already taken care of the CRC handling.
1911 */
1912
1913
1914 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1915 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1916 return(0); /* Did not recognize */
1917
1918 /* recognized vpAg */
1919
1920 if (chunk->size != 9)
1921 return(-1); /* Error return */
1922
1923 if (chunk->data[8] != 0)
1924 return(0); /* ImageMagick requires pixel units */
1925
1926 image=(Image *) png_get_user_chunk_ptr(ping);
1927
cristybb503372010-05-27 20:51:26 +00001928 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001929 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001930
cristybb503372010-05-27 20:51:26 +00001931 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001932 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1933
1934 /* Return one of the following: */
1935 /* return(-n); chunk had an error */
1936 /* return(0); did not recognize */
1937 /* return(n); success */
1938
1939 return(1);
1940
1941}
1942#endif
1943
1944/*
1945%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1946% %
1947% %
1948% %
1949% R e a d O n e P N G I m a g e %
1950% %
1951% %
1952% %
1953%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1954%
1955% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1956% (minus the 8-byte signature) and returns it. It allocates the memory
1957% necessary for the new Image structure and returns a pointer to the new
1958% image.
1959%
1960% The format of the ReadOnePNGImage method is:
1961%
1962% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1963% ExceptionInfo *exception)
1964%
1965% A description of each parameter follows:
1966%
1967% o mng_info: Specifies a pointer to a MngInfo structure.
1968%
1969% o image_info: the image info.
1970%
1971% o exception: return any errors or warnings in this structure.
1972%
1973*/
1974static Image *ReadOnePNGImage(MngInfo *mng_info,
1975 const ImageInfo *image_info, ExceptionInfo *exception)
1976{
1977 /* Read one PNG image */
1978
glennrpcc95c3f2011-04-18 16:46:48 +00001979 /* To do: Read the tIME chunk into the date:modify property */
1980 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1981
cristy3ed852e2009-09-05 21:47:34 +00001982 Image
1983 *image;
1984
1985 int
glennrp4eb39312011-03-30 21:34:55 +00001986 intent,
glennrpcb395ac2011-03-30 19:50:23 +00001987 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00001988 num_text,
glennrp4eb39312011-03-30 21:34:55 +00001989 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00001990 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00001991 pass,
1992 ping_bit_depth,
1993 ping_color_type,
1994 ping_interlace_method,
1995 ping_compression_method,
1996 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00001997 ping_num_trans,
1998 unit_type;
1999
2000 double
2001 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002002
glennrpa6a06632011-01-19 15:15:34 +00002003 LongPixelPacket
2004 transparent_color;
2005
cristy3ed852e2009-09-05 21:47:34 +00002006 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002007 logging,
cristy3ed852e2009-09-05 21:47:34 +00002008 status;
2009
glennrpfaa852b2010-03-30 12:17:00 +00002010 png_bytep
2011 ping_trans_alpha;
2012
2013 png_color_16p
2014 ping_background,
2015 ping_trans_color;
2016
cristy3ed852e2009-09-05 21:47:34 +00002017 png_info
2018 *end_info,
2019 *ping_info;
2020
2021 png_struct
2022 *ping;
2023
2024 png_textp
2025 text;
2026
glennrpfaa852b2010-03-30 12:17:00 +00002027 png_uint_32
2028 ping_height,
2029 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002030 ping_rowbytes,
2031 x_resolution,
2032 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002033
cristy3ed852e2009-09-05 21:47:34 +00002034 QuantumInfo
2035 *quantum_info;
2036
2037 unsigned char
glennrpcf002022011-01-30 02:38:15 +00002038 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002039
cristybb503372010-05-27 20:51:26 +00002040 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002041 y;
2042
2043 register unsigned char
2044 *p;
2045
cristybb503372010-05-27 20:51:26 +00002046 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002047 i,
2048 x;
2049
cristy4c08aed2011-07-01 19:47:50 +00002050 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002051 *q;
2052
2053 size_t
glennrp39992b42010-11-14 00:03:43 +00002054 length,
cristy3ed852e2009-09-05 21:47:34 +00002055 row_offset;
2056
cristyeb3b22a2011-03-31 20:16:11 +00002057 ssize_t
2058 j;
2059
cristy3ed852e2009-09-05 21:47:34 +00002060#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2061 png_byte unused_chunks[]=
2062 {
2063 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2064 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2065 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2066 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2067 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2068 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2069 };
2070#endif
2071
2072 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002073 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002074
2075#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002076 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002077#endif
2078
glennrp25c1e2b2010-03-25 01:39:56 +00002079#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002080 if (image_info->verbose)
2081 printf("Your PNG library (libpng-%s) is rather old.\n",
2082 PNG_LIBPNG_VER_STRING);
2083#endif
2084
glennrp61b4c952009-11-10 20:40:41 +00002085#if (PNG_LIBPNG_VER >= 10400)
2086# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2087 if (image_info->verbose)
2088 {
2089 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2090 PNG_LIBPNG_VER_STRING);
2091 printf("Please update it.\n");
2092 }
2093# endif
2094#endif
2095
2096
cristyed552522009-10-16 14:04:35 +00002097 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002098 image=mng_info->image;
2099
glennrpa6a06632011-01-19 15:15:34 +00002100 if (logging != MagickFalse)
2101 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2102 " image->matte=%d",(int) image->matte);
2103
glennrp0e319732011-01-25 21:53:13 +00002104 /* Set to an out-of-range color unless tRNS chunk is present */
2105 transparent_color.red=65537;
2106 transparent_color.green=65537;
2107 transparent_color.blue=65537;
cristy4c08aed2011-07-01 19:47:50 +00002108 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002109
glennrpcb395ac2011-03-30 19:50:23 +00002110 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002111 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002112 num_raw_profiles = 0;
2113
cristy3ed852e2009-09-05 21:47:34 +00002114 /*
2115 Allocate the PNG structures
2116 */
2117#ifdef PNG_USER_MEM_SUPPORTED
2118 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
glennrpcf002022011-01-30 02:38:15 +00002119 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2120 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002121#else
2122 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00002123 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002124#endif
2125 if (ping == (png_struct *) NULL)
2126 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002127
cristy3ed852e2009-09-05 21:47:34 +00002128 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002129
cristy3ed852e2009-09-05 21:47:34 +00002130 if (ping_info == (png_info *) NULL)
2131 {
2132 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2133 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2134 }
glennrp0fe50b42010-11-16 03:52:51 +00002135
cristy3ed852e2009-09-05 21:47:34 +00002136 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002137
cristy3ed852e2009-09-05 21:47:34 +00002138 if (end_info == (png_info *) NULL)
2139 {
2140 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2141 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2142 }
glennrp0fe50b42010-11-16 03:52:51 +00002143
glennrpcf002022011-01-30 02:38:15 +00002144 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002145
glennrpfaa852b2010-03-30 12:17:00 +00002146 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002147 {
2148 /*
2149 PNG image is corrupt.
2150 */
2151 png_destroy_read_struct(&ping,&ping_info,&end_info);
2152#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002153 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002154#endif
2155 if (logging != MagickFalse)
2156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2157 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002158
cristy3ed852e2009-09-05 21:47:34 +00002159 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002160 {
2161 InheritException(exception,&image->exception);
2162 image->columns=0;
2163 }
glennrp0fe50b42010-11-16 03:52:51 +00002164
cristy3ed852e2009-09-05 21:47:34 +00002165 return(GetFirstImageInList(image));
2166 }
2167 /*
2168 Prepare PNG for reading.
2169 */
glennrpfaa852b2010-03-30 12:17:00 +00002170
cristy3ed852e2009-09-05 21:47:34 +00002171 mng_info->image_found++;
2172 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002173
cristy3ed852e2009-09-05 21:47:34 +00002174 if (LocaleCompare(image_info->magick,"MNG") == 0)
2175 {
2176#if defined(PNG_MNG_FEATURES_SUPPORTED)
2177 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2178 png_set_read_fn(ping,image,png_get_data);
2179#else
2180#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2181 png_permit_empty_plte(ping,MagickTrue);
2182 png_set_read_fn(ping,image,png_get_data);
2183#else
2184 mng_info->image=image;
2185 mng_info->bytes_in_read_buffer=0;
2186 mng_info->found_empty_plte=MagickFalse;
2187 mng_info->have_saved_bkgd_index=MagickFalse;
2188 png_set_read_fn(ping,mng_info,mng_get_data);
2189#endif
2190#endif
2191 }
glennrp0fe50b42010-11-16 03:52:51 +00002192
cristy3ed852e2009-09-05 21:47:34 +00002193 else
2194 png_set_read_fn(ping,image,png_get_data);
2195
2196#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2197 /* Ignore unused chunks and all unknown chunks except for vpAg */
2198 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2199 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2200 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2201 (int)sizeof(unused_chunks)/5);
2202 /* Callback for other unknown chunks */
2203 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2204#endif
2205
glennrp991e92a2010-01-28 03:09:00 +00002206#if (PNG_LIBPNG_VER < 10400)
2207# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2208 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002209 /* Disable thread-unsafe features of pnggccrd */
2210 if (png_access_version_number() >= 10200)
2211 {
2212 png_uint_32 mmx_disable_mask=0;
2213 png_uint_32 asm_flags;
2214
2215 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2216 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2217 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2218 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2219 asm_flags=png_get_asm_flags(ping);
2220 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2221 }
glennrp991e92a2010-01-28 03:09:00 +00002222# endif
cristy3ed852e2009-09-05 21:47:34 +00002223#endif
2224
2225 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002226
2227 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2228 &ping_bit_depth,&ping_color_type,
2229 &ping_interlace_method,&ping_compression_method,
2230 &ping_filter_method);
2231
2232 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2233 &ping_trans_color);
2234
2235 (void) png_get_bKGD(ping, ping_info, &ping_background);
2236
2237 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002238 {
glennrpfaa852b2010-03-30 12:17:00 +00002239 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2240 {
2241 png_set_packing(ping);
2242 ping_bit_depth = 8;
2243 }
cristy3ed852e2009-09-05 21:47:34 +00002244 }
glennrpfaa852b2010-03-30 12:17:00 +00002245
2246 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002247 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002248 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00002249 if (logging != MagickFalse)
2250 {
2251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002252 " PNG width: %.20g, height: %.20g",
2253 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002254
cristy3ed852e2009-09-05 21:47:34 +00002255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2256 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002257 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002258
cristy3ed852e2009-09-05 21:47:34 +00002259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2260 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002261 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002262
cristy3ed852e2009-09-05 21:47:34 +00002263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2264 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002265 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002266 }
2267
glennrpfaa852b2010-03-30 12:17:00 +00002268#ifdef PNG_READ_iCCP_SUPPORTED
2269 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002270 {
2271 int
2272 compression;
2273
glennrpe4017e32011-01-08 17:16:09 +00002274#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002275 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002276 info;
2277#else
2278 png_bytep
2279 info;
2280#endif
2281
2282 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002283 name;
2284
2285 png_uint_32
2286 profile_length;
2287
2288 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2289 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002290
cristy3ed852e2009-09-05 21:47:34 +00002291 if (profile_length != 0)
2292 {
2293 StringInfo
2294 *profile;
2295
2296 if (logging != MagickFalse)
2297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2298 " Reading PNG iCCP chunk.");
cristye8f8f382011-09-01 13:32:37 +00002299 profile=BlobToStringInfo(info,profile_length);
2300 if (profile == (StringInfo *) NULL)
2301 {
2302 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2303 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2304 "unable to copy profile");
cristyad5b0852011-09-08 02:01:21 +00002305 return((Image *) NULL);
cristye8f8f382011-09-01 13:32:37 +00002306 }
cristy3ed852e2009-09-05 21:47:34 +00002307 SetStringInfoDatum(profile,(const unsigned char *) info);
2308 (void) SetImageProfile(image,"icc",profile);
2309 profile=DestroyStringInfo(profile);
2310 }
2311 }
2312#endif
2313#if defined(PNG_READ_sRGB_SUPPORTED)
2314 {
cristy3ed852e2009-09-05 21:47:34 +00002315 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00002316 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2317 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00002318
cristy3ed852e2009-09-05 21:47:34 +00002319 if (png_get_sRGB(ping,ping_info,&intent))
2320 {
glennrpcf002022011-01-30 02:38:15 +00002321 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2322 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002323
cristy3ed852e2009-09-05 21:47:34 +00002324 if (logging != MagickFalse)
2325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002326 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002327 }
2328 }
2329#endif
2330 {
glennrpfaa852b2010-03-30 12:17:00 +00002331 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2332 if (mng_info->have_global_gama)
2333 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002334
cristy3ed852e2009-09-05 21:47:34 +00002335 if (png_get_gAMA(ping,ping_info,&file_gamma))
2336 {
2337 image->gamma=(float) file_gamma;
2338 if (logging != MagickFalse)
2339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2340 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2341 }
2342 }
glennrpfaa852b2010-03-30 12:17:00 +00002343 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2344 {
2345 if (mng_info->have_global_chrm != MagickFalse)
2346 {
2347 (void) png_set_cHRM(ping,ping_info,
2348 mng_info->global_chrm.white_point.x,
2349 mng_info->global_chrm.white_point.y,
2350 mng_info->global_chrm.red_primary.x,
2351 mng_info->global_chrm.red_primary.y,
2352 mng_info->global_chrm.green_primary.x,
2353 mng_info->global_chrm.green_primary.y,
2354 mng_info->global_chrm.blue_primary.x,
2355 mng_info->global_chrm.blue_primary.y);
2356 }
2357 }
glennrp0fe50b42010-11-16 03:52:51 +00002358
glennrpfaa852b2010-03-30 12:17:00 +00002359 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002360 {
2361 (void) png_get_cHRM(ping,ping_info,
2362 &image->chromaticity.white_point.x,
2363 &image->chromaticity.white_point.y,
2364 &image->chromaticity.red_primary.x,
2365 &image->chromaticity.red_primary.y,
2366 &image->chromaticity.green_primary.x,
2367 &image->chromaticity.green_primary.y,
2368 &image->chromaticity.blue_primary.x,
2369 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002370
cristy3ed852e2009-09-05 21:47:34 +00002371 if (logging != MagickFalse)
2372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2373 " Reading PNG cHRM chunk.");
2374 }
glennrp0fe50b42010-11-16 03:52:51 +00002375
glennrpe610a072010-08-05 17:08:46 +00002376 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002377 {
glennrpe610a072010-08-05 17:08:46 +00002378 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002379 Magick_RenderingIntent_to_PNG_RenderingIntent
2380 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00002381 png_set_gAMA(ping,ping_info,0.45455f);
2382 png_set_cHRM(ping,ping_info,
2383 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2384 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002385 }
cristy3ed852e2009-09-05 21:47:34 +00002386#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002387 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002388 {
cristy905ef802011-02-23 00:29:18 +00002389 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2390 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002391
cristy3ed852e2009-09-05 21:47:34 +00002392 if (logging != MagickFalse)
2393 if (image->page.x || image->page.y)
2394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002395 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2396 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002397 }
2398#endif
2399#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002400 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2401 {
2402 if (mng_info->have_global_phys)
2403 {
2404 png_set_pHYs(ping,ping_info,
2405 mng_info->global_x_pixels_per_unit,
2406 mng_info->global_y_pixels_per_unit,
2407 mng_info->global_phys_unit_type);
2408 }
2409 }
2410
2411 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002412 {
cristy3ed852e2009-09-05 21:47:34 +00002413 /*
2414 Set image resolution.
2415 */
2416 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002417 &unit_type);
2418 image->x_resolution=(double) x_resolution;
2419 image->y_resolution=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002420
cristy3ed852e2009-09-05 21:47:34 +00002421 if (unit_type == PNG_RESOLUTION_METER)
2422 {
2423 image->units=PixelsPerCentimeterResolution;
2424 image->x_resolution=(double) x_resolution/100.0;
2425 image->y_resolution=(double) y_resolution/100.0;
2426 }
glennrp0fe50b42010-11-16 03:52:51 +00002427
cristy3ed852e2009-09-05 21:47:34 +00002428 if (logging != MagickFalse)
2429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002430 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2431 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002432 }
cristy3ed852e2009-09-05 21:47:34 +00002433#endif
glennrp823b55c2011-03-14 18:46:46 +00002434
glennrpfaa852b2010-03-30 12:17:00 +00002435 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002436 {
2437 int
2438 number_colors;
2439
2440 png_colorp
2441 palette;
2442
2443 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002444
cristy3ed852e2009-09-05 21:47:34 +00002445 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002446 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002447 {
2448 if (mng_info->global_plte_length)
2449 {
2450 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2451 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002452
glennrpfaa852b2010-03-30 12:17:00 +00002453 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002454 if (mng_info->global_trns_length)
2455 {
2456 if (mng_info->global_trns_length >
2457 mng_info->global_plte_length)
2458 (void) ThrowMagickException(&image->exception,
2459 GetMagickModule(),CoderError,
2460 "global tRNS has more entries than global PLTE",
2461 "`%s'",image_info->filename);
2462 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2463 (int) mng_info->global_trns_length,NULL);
2464 }
glennrpbfd9e612011-04-22 14:02:20 +00002465#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002466 if (
2467#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2468 mng_info->have_saved_bkgd_index ||
2469#endif
glennrpfaa852b2010-03-30 12:17:00 +00002470 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002471 {
2472 png_color_16
2473 background;
2474
2475#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2476 if (mng_info->have_saved_bkgd_index)
2477 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002478#endif
glennrpfaa852b2010-03-30 12:17:00 +00002479 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2480 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002481
cristy3ed852e2009-09-05 21:47:34 +00002482 background.red=(png_uint_16)
2483 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002484
cristy3ed852e2009-09-05 21:47:34 +00002485 background.green=(png_uint_16)
2486 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002487
cristy3ed852e2009-09-05 21:47:34 +00002488 background.blue=(png_uint_16)
2489 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002490
glennrpc6c391a2011-04-27 02:23:56 +00002491 background.gray=(png_uint_16)
2492 mng_info->global_plte[background.index].green;
2493
cristy3ed852e2009-09-05 21:47:34 +00002494 png_set_bKGD(ping,ping_info,&background);
2495 }
2496#endif
2497 }
2498 else
2499 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2500 CoderError,"No global PLTE in file","`%s'",
2501 image_info->filename);
2502 }
2503 }
2504
glennrpbfd9e612011-04-22 14:02:20 +00002505#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002506 if (mng_info->have_global_bkgd &&
2507 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002508 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002509
glennrpfaa852b2010-03-30 12:17:00 +00002510 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002511 {
glennrpbfd9e612011-04-22 14:02:20 +00002512 unsigned int
2513 bkgd_scale;
2514
cristy3ed852e2009-09-05 21:47:34 +00002515 /*
2516 Set image background color.
2517 */
2518 if (logging != MagickFalse)
2519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2520 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002521
glennrpbfd9e612011-04-22 14:02:20 +00002522 /* Scale background components to 16-bit, then scale
2523 * to quantum depth
2524 */
2525 if (logging != MagickFalse)
2526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2527 " raw ping_background=(%d,%d,%d).",ping_background->red,
2528 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002529
glennrpbfd9e612011-04-22 14:02:20 +00002530 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002531
glennrpbfd9e612011-04-22 14:02:20 +00002532 if (ping_bit_depth == 1)
2533 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002534
glennrpbfd9e612011-04-22 14:02:20 +00002535 else if (ping_bit_depth == 2)
2536 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002537
glennrpbfd9e612011-04-22 14:02:20 +00002538 else if (ping_bit_depth == 4)
2539 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002540
glennrpbfd9e612011-04-22 14:02:20 +00002541 if (ping_bit_depth <= 8)
2542 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002543
glennrpbfd9e612011-04-22 14:02:20 +00002544 ping_background->red *= bkgd_scale;
2545 ping_background->green *= bkgd_scale;
2546 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002547
glennrpbfd9e612011-04-22 14:02:20 +00002548 if (logging != MagickFalse)
2549 {
glennrp2cbb4482010-06-02 04:37:24 +00002550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2551 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002552
glennrp2cbb4482010-06-02 04:37:24 +00002553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2554 " ping_background=(%d,%d,%d).",ping_background->red,
2555 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002556 }
glennrp2cbb4482010-06-02 04:37:24 +00002557
glennrpbfd9e612011-04-22 14:02:20 +00002558 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002559 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002560
glennrpbfd9e612011-04-22 14:02:20 +00002561 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002562 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002563
glennrpbfd9e612011-04-22 14:02:20 +00002564 image->background_color.blue=
2565 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002566
cristy4c08aed2011-07-01 19:47:50 +00002567 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002568
glennrpbfd9e612011-04-22 14:02:20 +00002569 if (logging != MagickFalse)
2570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2571 " image->background_color=(%.20g,%.20g,%.20g).",
2572 (double) image->background_color.red,
2573 (double) image->background_color.green,
2574 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002575 }
glennrpbfd9e612011-04-22 14:02:20 +00002576#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002577
glennrpfaa852b2010-03-30 12:17:00 +00002578 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002579 {
2580 /*
glennrpa6a06632011-01-19 15:15:34 +00002581 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002582 */
2583 int
2584 max_sample;
2585
cristy35ef8242010-06-03 16:24:13 +00002586 size_t
2587 one=1;
2588
cristy3ed852e2009-09-05 21:47:34 +00002589 if (logging != MagickFalse)
2590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2591 " Reading PNG tRNS chunk.");
2592
cristyf9cca6a2010-06-04 23:49:28 +00002593 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002594
glennrpfaa852b2010-03-30 12:17:00 +00002595 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2596 (int)ping_trans_color->gray > max_sample) ||
2597 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2598 ((int)ping_trans_color->red > max_sample ||
2599 (int)ping_trans_color->green > max_sample ||
2600 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002601 {
2602 if (logging != MagickFalse)
2603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2604 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002605 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002606 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002607 image->matte=MagickFalse;
2608 }
2609 else
2610 {
glennrpa6a06632011-01-19 15:15:34 +00002611 int
2612 scale_to_short;
2613
2614 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2615
2616 /* Scale transparent_color to short */
2617 transparent_color.red= scale_to_short*ping_trans_color->red;
2618 transparent_color.green= scale_to_short*ping_trans_color->green;
2619 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy4c08aed2011-07-01 19:47:50 +00002620 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002621
glennrpfaa852b2010-03-30 12:17:00 +00002622 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002623 {
glennrp0f111982010-07-07 20:18:33 +00002624 if (logging != MagickFalse)
2625 {
2626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2627 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002628
glennrp0f111982010-07-07 20:18:33 +00002629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00002630 " scaled graylevel is %d.",transparent_color.alpha);
glennrp0f111982010-07-07 20:18:33 +00002631 }
cristy4c08aed2011-07-01 19:47:50 +00002632 transparent_color.red=transparent_color.alpha;
2633 transparent_color.green=transparent_color.alpha;
2634 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002635 }
2636 }
2637 }
2638#if defined(PNG_READ_sBIT_SUPPORTED)
2639 if (mng_info->have_global_sbit)
2640 {
glennrpfaa852b2010-03-30 12:17:00 +00002641 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002642 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2643 }
2644#endif
2645 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002646
cristy3ed852e2009-09-05 21:47:34 +00002647 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002648
2649 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2650
cristy3ed852e2009-09-05 21:47:34 +00002651 /*
2652 Initialize image structure.
2653 */
2654 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002655 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002656 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002657 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002658 if (mng_info->mng_type == 0)
2659 {
glennrpfaa852b2010-03-30 12:17:00 +00002660 mng_info->mng_width=ping_width;
2661 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002662 mng_info->frame=mng_info->image_box;
2663 mng_info->clip=mng_info->image_box;
2664 }
glennrp0fe50b42010-11-16 03:52:51 +00002665
cristy3ed852e2009-09-05 21:47:34 +00002666 else
2667 {
2668 image->page.y=mng_info->y_off[mng_info->object_id];
2669 }
glennrp0fe50b42010-11-16 03:52:51 +00002670
cristy3ed852e2009-09-05 21:47:34 +00002671 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002672 image->columns=ping_width;
2673 image->rows=ping_height;
2674 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002675 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002676 {
cristybefe4d22010-06-07 01:18:58 +00002677 size_t
2678 one;
2679
cristy3ed852e2009-09-05 21:47:34 +00002680 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002681 one=1;
2682 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002683#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2684 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002685 image->colors=256;
2686#else
2687 if (image->colors > 65536L)
2688 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002689#endif
glennrpfaa852b2010-03-30 12:17:00 +00002690 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002691 {
2692 int
2693 number_colors;
2694
2695 png_colorp
2696 palette;
2697
2698 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002699 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002700
cristy3ed852e2009-09-05 21:47:34 +00002701 if (logging != MagickFalse)
2702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2703 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2704 }
2705 }
2706
2707 if (image->storage_class == PseudoClass)
2708 {
2709 /*
2710 Initialize image colormap.
2711 */
cristy018f07f2011-09-04 21:15:19 +00002712 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002713 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002714
glennrpfaa852b2010-03-30 12:17:00 +00002715 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002716 {
2717 int
2718 number_colors;
2719
2720 png_colorp
2721 palette;
2722
2723 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002724
glennrp6af6cf12011-04-22 13:05:16 +00002725 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002726 {
2727 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2728 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2729 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2730 }
glennrp6af6cf12011-04-22 13:05:16 +00002731
glennrp67b9c1a2011-04-22 18:47:36 +00002732 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002733 {
2734 image->colormap[i].red=0;
2735 image->colormap[i].green=0;
2736 image->colormap[i].blue=0;
2737 }
cristy3ed852e2009-09-05 21:47:34 +00002738 }
glennrp0fe50b42010-11-16 03:52:51 +00002739
cristy3ed852e2009-09-05 21:47:34 +00002740 else
2741 {
cristybb503372010-05-27 20:51:26 +00002742 size_t
cristy3ed852e2009-09-05 21:47:34 +00002743 scale;
2744
glennrpfaa852b2010-03-30 12:17:00 +00002745 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002746
cristy3ed852e2009-09-05 21:47:34 +00002747 if (scale < 1)
2748 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002749
cristybb503372010-05-27 20:51:26 +00002750 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002751 {
2752 image->colormap[i].red=(Quantum) (i*scale);
2753 image->colormap[i].green=(Quantum) (i*scale);
2754 image->colormap[i].blue=(Quantum) (i*scale);
2755 }
2756 }
2757 }
glennrp147bc912011-03-30 18:47:21 +00002758
glennrpcb395ac2011-03-30 19:50:23 +00002759 /* Set some properties for reporting by "identify" */
2760 {
glennrp147bc912011-03-30 18:47:21 +00002761 char
2762 msg[MaxTextExtent];
2763
2764 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2765 ping_interlace_method in value */
2766
cristy3b6fd2e2011-05-20 12:53:50 +00002767 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002768 "%d, %d",(int) ping_width, (int) ping_height);
2769 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
glennrp147bc912011-03-30 18:47:21 +00002770
cristy3b6fd2e2011-05-20 12:53:50 +00002771 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
glennrp147bc912011-03-30 18:47:21 +00002772 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2773
cristy3b6fd2e2011-05-20 12:53:50 +00002774 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
glennrp147bc912011-03-30 18:47:21 +00002775 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2776
cristy3b6fd2e2011-05-20 12:53:50 +00002777 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002778 (int) ping_interlace_method);
2779 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
glennrpcb395ac2011-03-30 19:50:23 +00002780 }
glennrp147bc912011-03-30 18:47:21 +00002781
cristy3ed852e2009-09-05 21:47:34 +00002782 /*
2783 Read image scanlines.
2784 */
2785 if (image->delay != 0)
2786 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002787
glennrp0ca69b12010-07-26 01:57:52 +00002788 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002789 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2790 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002791 {
2792 if (logging != MagickFalse)
2793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002794 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002795 mng_info->scenes_found-1);
2796 png_destroy_read_struct(&ping,&ping_info,&end_info);
2797#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002798 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002799#endif
2800 if (logging != MagickFalse)
2801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2802 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002803
cristy3ed852e2009-09-05 21:47:34 +00002804 return(image);
2805 }
glennrp0fe50b42010-11-16 03:52:51 +00002806
cristy3ed852e2009-09-05 21:47:34 +00002807 if (logging != MagickFalse)
2808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2809 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002810
cristy3ed852e2009-09-05 21:47:34 +00002811 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002812 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2813 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002814
cristy3ed852e2009-09-05 21:47:34 +00002815 else
glennrpcf002022011-01-30 02:38:15 +00002816 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2817 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002818
glennrpcf002022011-01-30 02:38:15 +00002819 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002820 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002821
cristy3ed852e2009-09-05 21:47:34 +00002822 if (logging != MagickFalse)
2823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2824 " Converting PNG pixels to pixel packets");
2825 /*
2826 Convert PNG pixels to pixel packets.
2827 */
glennrpfaa852b2010-03-30 12:17:00 +00002828 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002829 {
2830 /*
2831 PNG image is corrupt.
2832 */
2833 png_destroy_read_struct(&ping,&ping_info,&end_info);
2834#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002835 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002836#endif
2837 if (quantum_info != (QuantumInfo *) NULL)
2838 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002839
glennrpcf002022011-01-30 02:38:15 +00002840 if (ping_pixels != (unsigned char *) NULL)
2841 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002842
cristy3ed852e2009-09-05 21:47:34 +00002843 if (logging != MagickFalse)
2844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2845 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002846
cristy3ed852e2009-09-05 21:47:34 +00002847 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002848 {
2849 InheritException(exception,&image->exception);
2850 image->columns=0;
2851 }
glennrp0fe50b42010-11-16 03:52:51 +00002852
cristy3ed852e2009-09-05 21:47:34 +00002853 return(GetFirstImageInList(image));
2854 }
glennrp0fe50b42010-11-16 03:52:51 +00002855
cristyed552522009-10-16 14:04:35 +00002856 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002857
cristyed552522009-10-16 14:04:35 +00002858 if (quantum_info == (QuantumInfo *) NULL)
2859 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002860
glennrpc8cbc5d2011-01-01 00:12:34 +00002861 {
2862
2863 MagickBooleanType
2864 found_transparent_pixel;
2865
2866 found_transparent_pixel=MagickFalse;
2867
cristy3ed852e2009-09-05 21:47:34 +00002868 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002869 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002870 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002871 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002872 /*
2873 Convert image to DirectClass pixel packets.
2874 */
glennrp67b9c1a2011-04-22 18:47:36 +00002875#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2876 int
2877 depth;
2878
2879 depth=(ssize_t) ping_bit_depth;
2880#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002881 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2882 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2883 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2884 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002885
glennrpc8cbc5d2011-01-01 00:12:34 +00002886 for (y=0; y < (ssize_t) image->rows; y++)
2887 {
2888 if (num_passes > 1)
2889 row_offset=ping_rowbytes*y;
2890
2891 else
2892 row_offset=0;
2893
glennrpcf002022011-01-30 02:38:15 +00002894 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002895 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2896
cristyacd2ed22011-08-30 01:44:23 +00002897 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00002898 break;
2899
glennrpc8cbc5d2011-01-01 00:12:34 +00002900 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2901 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002902 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002903
2904 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2905 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002906 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002907
2908 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2909 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002910 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002911
2912 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2913 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002914 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002915
2916 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2917 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002918 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002919
glennrpc8cbc5d2011-01-01 00:12:34 +00002920 if (found_transparent_pixel == MagickFalse)
2921 {
2922 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002923 if (y== 0 && logging != MagickFalse)
2924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2925 " Looking for cheap transparent pixel");
2926
glennrpc8cbc5d2011-01-01 00:12:34 +00002927 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2928 {
glennrp5aa37f62011-01-02 03:07:57 +00002929 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2930 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy4c08aed2011-07-01 19:47:50 +00002931 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00002932 {
glennrpa6a06632011-01-19 15:15:34 +00002933 if (logging != MagickFalse)
2934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2935 " ...got one.");
2936
glennrpc8cbc5d2011-01-01 00:12:34 +00002937 found_transparent_pixel = MagickTrue;
2938 break;
2939 }
glennrp4f25bd02011-01-01 18:51:28 +00002940 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2941 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp847370c2011-07-05 17:37:15 +00002942 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2943 transparent_color.red &&
2944 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2945 transparent_color.green &&
2946 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2947 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002948 {
glennrpa6a06632011-01-19 15:15:34 +00002949 if (logging != MagickFalse)
2950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2951 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002952 found_transparent_pixel = MagickTrue;
2953 break;
2954 }
cristyed231572011-07-14 02:18:59 +00002955 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00002956 }
2957 }
2958
2959 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2960 {
2961 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2962 image->rows);
2963
2964 if (status == MagickFalse)
2965 break;
2966 }
2967 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2968 break;
2969 }
2970
2971 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2972 {
2973 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00002974 if (status == MagickFalse)
2975 break;
2976 }
cristy3ed852e2009-09-05 21:47:34 +00002977 }
cristy3ed852e2009-09-05 21:47:34 +00002978 }
glennrp0fe50b42010-11-16 03:52:51 +00002979
cristy3ed852e2009-09-05 21:47:34 +00002980 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00002981
cristy3ed852e2009-09-05 21:47:34 +00002982 for (pass=0; pass < num_passes; pass++)
2983 {
2984 Quantum
2985 *quantum_scanline;
2986
2987 register Quantum
2988 *r;
2989
2990 /*
2991 Convert grayscale image to PseudoClass pixel packets.
2992 */
glennrpc17d96f2011-06-27 01:20:11 +00002993 if (logging != MagickFalse)
2994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2995 " Converting grayscale pixels to pixel packets");
cristy4c08aed2011-07-01 19:47:50 +00002996
glennrpfaa852b2010-03-30 12:17:00 +00002997 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00002998 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002999
cristy3ed852e2009-09-05 21:47:34 +00003000 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3001 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003002
cristy3ed852e2009-09-05 21:47:34 +00003003 if (quantum_scanline == (Quantum *) NULL)
3004 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003005
cristybb503372010-05-27 20:51:26 +00003006 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003007 {
3008 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003009 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003010
cristy3ed852e2009-09-05 21:47:34 +00003011 else
3012 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003013
glennrpcf002022011-01-30 02:38:15 +00003014 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00003015 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003016
cristyacd2ed22011-08-30 01:44:23 +00003017 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003018 break;
glennrp0fe50b42010-11-16 03:52:51 +00003019
glennrpcf002022011-01-30 02:38:15 +00003020 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003021 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003022
glennrpfaa852b2010-03-30 12:17:00 +00003023 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003024 {
3025 case 1:
3026 {
cristybb503372010-05-27 20:51:26 +00003027 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003028 bit;
3029
cristybb503372010-05-27 20:51:26 +00003030 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003031 {
3032 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003033 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003034 p++;
3035 }
glennrp0fe50b42010-11-16 03:52:51 +00003036
cristy3ed852e2009-09-05 21:47:34 +00003037 if ((image->columns % 8) != 0)
3038 {
cristybb503372010-05-27 20:51:26 +00003039 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003040 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003041 }
glennrp0fe50b42010-11-16 03:52:51 +00003042
cristy3ed852e2009-09-05 21:47:34 +00003043 break;
3044 }
glennrp47b9dd52010-11-24 18:12:06 +00003045
cristy3ed852e2009-09-05 21:47:34 +00003046 case 2:
3047 {
cristybb503372010-05-27 20:51:26 +00003048 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003049 {
glennrpa18d5bc2011-04-23 14:51:34 +00003050 *r++=(*p >> 6) & 0x03;
3051 *r++=(*p >> 4) & 0x03;
3052 *r++=(*p >> 2) & 0x03;
3053 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003054 }
glennrp0fe50b42010-11-16 03:52:51 +00003055
cristy3ed852e2009-09-05 21:47:34 +00003056 if ((image->columns % 4) != 0)
3057 {
cristybb503372010-05-27 20:51:26 +00003058 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003059 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003060 }
glennrp0fe50b42010-11-16 03:52:51 +00003061
cristy3ed852e2009-09-05 21:47:34 +00003062 break;
3063 }
glennrp47b9dd52010-11-24 18:12:06 +00003064
cristy3ed852e2009-09-05 21:47:34 +00003065 case 4:
3066 {
cristybb503372010-05-27 20:51:26 +00003067 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003068 {
glennrpa18d5bc2011-04-23 14:51:34 +00003069 *r++=(*p >> 4) & 0x0f;
3070 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003071 }
glennrp0fe50b42010-11-16 03:52:51 +00003072
cristy3ed852e2009-09-05 21:47:34 +00003073 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003074 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003075
cristy3ed852e2009-09-05 21:47:34 +00003076 break;
3077 }
glennrp47b9dd52010-11-24 18:12:06 +00003078
cristy3ed852e2009-09-05 21:47:34 +00003079 case 8:
3080 {
glennrpfaa852b2010-03-30 12:17:00 +00003081 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003082 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003083 {
glennrpa18d5bc2011-04-23 14:51:34 +00003084 *r++=*p++;
cristy4c08aed2011-07-01 19:47:50 +00003085 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3086 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003087 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003088 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003089 }
glennrp0fe50b42010-11-16 03:52:51 +00003090
cristy3ed852e2009-09-05 21:47:34 +00003091 else
cristybb503372010-05-27 20:51:26 +00003092 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003093 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003094
cristy3ed852e2009-09-05 21:47:34 +00003095 break;
3096 }
glennrp47b9dd52010-11-24 18:12:06 +00003097
cristy3ed852e2009-09-05 21:47:34 +00003098 case 16:
3099 {
cristybb503372010-05-27 20:51:26 +00003100 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003101 {
glennrpc17d96f2011-06-27 01:20:11 +00003102#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003103 size_t
3104 quantum;
3105
3106 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003107 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003108
3109 else
glennrpc17d96f2011-06-27 01:20:11 +00003110 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003111
glennrp58f77c72011-04-23 14:09:09 +00003112 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003113 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003114 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003115
3116 if (ping_color_type == 4)
3117 {
glennrpc17d96f2011-06-27 01:20:11 +00003118 if (image->colors > 256)
3119 quantum=((*p++) << 8);
3120 else
3121 quantum=0;
3122
glennrp58f77c72011-04-23 14:09:09 +00003123 quantum|=(*p++);
cristy4c08aed2011-07-01 19:47:50 +00003124 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3125 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003126 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003127 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003128 }
glennrp58f77c72011-04-23 14:09:09 +00003129
3130#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3131 *r++=(*p++);
3132 p++; /* strip low byte */
3133
3134 if (ping_color_type == 4)
3135 {
cristy4c08aed2011-07-01 19:47:50 +00003136 SetPixelAlpha(image,*p++,q);
3137 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003138 found_transparent_pixel = MagickTrue;
3139 p++;
cristyed231572011-07-14 02:18:59 +00003140 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003141 }
cristy3ed852e2009-09-05 21:47:34 +00003142#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003143 }
glennrp47b9dd52010-11-24 18:12:06 +00003144
cristy3ed852e2009-09-05 21:47:34 +00003145 break;
3146 }
glennrp47b9dd52010-11-24 18:12:06 +00003147
cristy3ed852e2009-09-05 21:47:34 +00003148 default:
3149 break;
3150 }
glennrp3faa9a32011-04-23 14:00:25 +00003151
cristy3ed852e2009-09-05 21:47:34 +00003152 /*
3153 Transfer image scanline.
3154 */
3155 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003156
cristy4c08aed2011-07-01 19:47:50 +00003157 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3158
cristyacd2ed22011-08-30 01:44:23 +00003159 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00003160 break;
cristybb503372010-05-27 20:51:26 +00003161 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00003162 {
3163 SetPixelIndex(image,*r++,q);
cristyed231572011-07-14 02:18:59 +00003164 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00003165 }
glennrp0fe50b42010-11-16 03:52:51 +00003166
cristy3ed852e2009-09-05 21:47:34 +00003167 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3168 break;
glennrp0fe50b42010-11-16 03:52:51 +00003169
cristy7a287bf2010-02-14 02:18:09 +00003170 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3171 {
cristycee97112010-05-28 00:44:52 +00003172 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003173 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003174
cristy7a287bf2010-02-14 02:18:09 +00003175 if (status == MagickFalse)
3176 break;
3177 }
cristy3ed852e2009-09-05 21:47:34 +00003178 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003179
cristy7a287bf2010-02-14 02:18:09 +00003180 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003181 {
3182 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003183
cristy3ed852e2009-09-05 21:47:34 +00003184 if (status == MagickFalse)
3185 break;
3186 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003187
cristy3ed852e2009-09-05 21:47:34 +00003188 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3189 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003190
3191 image->matte=found_transparent_pixel;
3192
3193 if (logging != MagickFalse)
3194 {
3195 if (found_transparent_pixel != MagickFalse)
3196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3197 " Found transparent pixel");
3198 else
glennrp5aa37f62011-01-02 03:07:57 +00003199 {
3200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3201 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003202
glennrp5aa37f62011-01-02 03:07:57 +00003203 ping_color_type&=0x03;
3204 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003205 }
3206 }
3207
cristyb32b90a2009-09-07 21:45:48 +00003208 if (quantum_info != (QuantumInfo *) NULL)
3209 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00003210
cristy5c6f7892010-05-05 22:53:29 +00003211 if (image->storage_class == PseudoClass)
3212 {
cristyaeb2cbc2010-05-07 13:28:58 +00003213 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003214 matte;
3215
3216 matte=image->matte;
3217 image->matte=MagickFalse;
3218 (void) SyncImage(image);
cristyaeb2cbc2010-05-07 13:28:58 +00003219 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003220 }
glennrp47b9dd52010-11-24 18:12:06 +00003221
glennrp4eb39312011-03-30 21:34:55 +00003222 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003223
3224 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003225 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003226 {
3227 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003228 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003229 image->colors=2;
3230 (void) SetImageBackgroundColor(image);
3231#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003232 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003233#endif
3234 if (logging != MagickFalse)
3235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3236 " exit ReadOnePNGImage() early.");
3237 return(image);
3238 }
glennrp47b9dd52010-11-24 18:12:06 +00003239
glennrpfaa852b2010-03-30 12:17:00 +00003240 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003241 {
3242 ClassType
3243 storage_class;
3244
3245 /*
3246 Image has a transparent background.
3247 */
3248 storage_class=image->storage_class;
3249 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003250
glennrp3c218112010-11-27 15:31:26 +00003251/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003252
glennrp0fe50b42010-11-16 03:52:51 +00003253 if (storage_class == PseudoClass)
3254 {
3255 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003256 {
glennrp0fe50b42010-11-16 03:52:51 +00003257 for (x=0; x < ping_num_trans; x++)
3258 {
cristy4c08aed2011-07-01 19:47:50 +00003259 image->colormap[x].alpha =
3260 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003261 }
glennrpc11cf6a2010-03-20 16:46:19 +00003262 }
glennrp47b9dd52010-11-24 18:12:06 +00003263
glennrp0fe50b42010-11-16 03:52:51 +00003264 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3265 {
3266 for (x=0; x < (int) image->colors; x++)
3267 {
3268 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy4c08aed2011-07-01 19:47:50 +00003269 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003270 {
cristy4c08aed2011-07-01 19:47:50 +00003271 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003272 }
3273 }
3274 }
3275 (void) SyncImage(image);
3276 }
glennrp47b9dd52010-11-24 18:12:06 +00003277
glennrpa6a06632011-01-19 15:15:34 +00003278#if 1 /* Should have already been done above, but glennrp problem P10
3279 * needs this.
3280 */
glennrp0fe50b42010-11-16 03:52:51 +00003281 else
3282 {
3283 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003284 {
glennrp0fe50b42010-11-16 03:52:51 +00003285 image->storage_class=storage_class;
3286 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3287
cristyacd2ed22011-08-30 01:44:23 +00003288 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003289 break;
3290
glennrp0fe50b42010-11-16 03:52:51 +00003291
glennrpa6a06632011-01-19 15:15:34 +00003292 /* Caution: on a Q8 build, this does not distinguish between
3293 * 16-bit colors that differ only in the low byte
3294 */
glennrp0fe50b42010-11-16 03:52:51 +00003295 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3296 {
glennrp847370c2011-07-05 17:37:15 +00003297 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3298 transparent_color.red &&
3299 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3300 transparent_color.green &&
3301 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3302 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003303 {
cristy4c08aed2011-07-01 19:47:50 +00003304 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003305 }
glennrp0fe50b42010-11-16 03:52:51 +00003306
glennrp67b9c1a2011-04-22 18:47:36 +00003307#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003308 else
glennrp4f25bd02011-01-01 18:51:28 +00003309 {
cristy4c08aed2011-07-01 19:47:50 +00003310 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003311 }
glennrpa6a06632011-01-19 15:15:34 +00003312#endif
glennrp0fe50b42010-11-16 03:52:51 +00003313
cristyed231572011-07-14 02:18:59 +00003314 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003315 }
3316
3317 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3318 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003319 }
glennrp0fe50b42010-11-16 03:52:51 +00003320 }
glennrpa6a06632011-01-19 15:15:34 +00003321#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003322
cristy3ed852e2009-09-05 21:47:34 +00003323 image->storage_class=DirectClass;
3324 }
glennrp3c218112010-11-27 15:31:26 +00003325
cristyb40fc462010-08-08 00:49:49 +00003326 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3327 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3328 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00003329
cristyeb3b22a2011-03-31 20:16:11 +00003330 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003331 {
3332 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003333 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3334 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003335 else
glennrpa0ed0092011-04-18 16:36:29 +00003336 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3337 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003338
glennrp4eb39312011-03-30 21:34:55 +00003339 if (status != MagickFalse)
3340 for (i=0; i < (ssize_t) num_text; i++)
3341 {
3342 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003343
glennrp4eb39312011-03-30 21:34:55 +00003344 if (logging != MagickFalse)
3345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3346 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003347
glennrp4eb39312011-03-30 21:34:55 +00003348 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003349 {
glennrp4eb39312011-03-30 21:34:55 +00003350 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
3351 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003352 }
glennrp0fe50b42010-11-16 03:52:51 +00003353
glennrp4eb39312011-03-30 21:34:55 +00003354 else
3355 {
3356 char
3357 *value;
3358
3359 length=text[i].text_length;
3360 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3361 sizeof(*value));
3362 if (value == (char *) NULL)
3363 {
3364 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3365 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3366 image->filename);
3367 break;
3368 }
3369 *value='\0';
3370 (void) ConcatenateMagickString(value,text[i].text,length+2);
3371
3372 /* Don't save "density" or "units" property if we have a pHYs
3373 * chunk
3374 */
3375 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3376 (LocaleCompare(text[i].key,"density") != 0 &&
3377 LocaleCompare(text[i].key,"units") != 0))
3378 (void) SetImageProperty(image,text[i].key,value);
3379
3380 if (logging != MagickFalse)
3381 {
3382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3383 " length: %lu",(unsigned long) length);
3384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3385 " Keyword: %s",text[i].key);
3386 }
3387
3388 value=DestroyString(value);
3389 }
3390 }
3391 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003392 }
glennrp3c218112010-11-27 15:31:26 +00003393
cristy3ed852e2009-09-05 21:47:34 +00003394#ifdef MNG_OBJECT_BUFFERS
3395 /*
3396 Store the object if necessary.
3397 */
3398 if (object_id && !mng_info->frozen[object_id])
3399 {
3400 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3401 {
3402 /*
3403 create a new object buffer.
3404 */
3405 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003406 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003407
cristy3ed852e2009-09-05 21:47:34 +00003408 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3409 {
3410 mng_info->ob[object_id]->image=(Image *) NULL;
3411 mng_info->ob[object_id]->reference_count=1;
3412 }
3413 }
glennrp47b9dd52010-11-24 18:12:06 +00003414
cristy3ed852e2009-09-05 21:47:34 +00003415 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3416 mng_info->ob[object_id]->frozen)
3417 {
3418 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3419 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3420 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3421 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003422
cristy3ed852e2009-09-05 21:47:34 +00003423 if (mng_info->ob[object_id]->frozen)
3424 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3425 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3426 "`%s'",image->filename);
3427 }
glennrp0fe50b42010-11-16 03:52:51 +00003428
cristy3ed852e2009-09-05 21:47:34 +00003429 else
3430 {
cristy3ed852e2009-09-05 21:47:34 +00003431
3432 if (mng_info->ob[object_id]->image != (Image *) NULL)
3433 mng_info->ob[object_id]->image=DestroyImage
3434 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003435
cristy3ed852e2009-09-05 21:47:34 +00003436 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3437 &image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00003438
cristy3ed852e2009-09-05 21:47:34 +00003439 if (mng_info->ob[object_id]->image != (Image *) NULL)
3440 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003441
cristy3ed852e2009-09-05 21:47:34 +00003442 else
3443 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3444 ResourceLimitError,"Cloning image for object buffer failed",
3445 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003446
glennrpfaa852b2010-03-30 12:17:00 +00003447 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003448 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003449
glennrpfaa852b2010-03-30 12:17:00 +00003450 mng_info->ob[object_id]->width=ping_width;
3451 mng_info->ob[object_id]->height=ping_height;
3452 mng_info->ob[object_id]->color_type=ping_color_type;
3453 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3454 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3455 mng_info->ob[object_id]->compression_method=
3456 ping_compression_method;
3457 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003458
glennrpfaa852b2010-03-30 12:17:00 +00003459 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003460 {
3461 int
3462 number_colors;
3463
3464 png_colorp
3465 plte;
3466
3467 /*
3468 Copy the PLTE to the object buffer.
3469 */
3470 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3471 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003472
cristy3ed852e2009-09-05 21:47:34 +00003473 for (i=0; i < number_colors; i++)
3474 {
3475 mng_info->ob[object_id]->plte[i]=plte[i];
3476 }
3477 }
glennrp47b9dd52010-11-24 18:12:06 +00003478
cristy3ed852e2009-09-05 21:47:34 +00003479 else
3480 mng_info->ob[object_id]->plte_length=0;
3481 }
3482 }
3483#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003484
3485 /* Set image->matte to MagickTrue if the input colortype supports
3486 * alpha or if a valid tRNS chunk is present, no matter whether there
3487 * is actual transparency present.
3488 */
3489 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3490 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3491 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3492 MagickTrue : MagickFalse;
3493
glennrpcb395ac2011-03-30 19:50:23 +00003494 /* Set more properties for identify to retrieve */
3495 {
3496 char
3497 msg[MaxTextExtent];
3498
glennrp4eb39312011-03-30 21:34:55 +00003499 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003500 {
3501 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003502 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003503 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrpcb395ac2011-03-30 19:50:23 +00003504 (void) SetImageProperty(image,"PNG:text ",msg);
3505 }
3506
3507 if (num_raw_profiles != 0)
3508 {
cristy3b6fd2e2011-05-20 12:53:50 +00003509 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003510 "%d were found", num_raw_profiles);
3511 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3512 }
3513
glennrpcb395ac2011-03-30 19:50:23 +00003514 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003515 {
cristy3b6fd2e2011-05-20 12:53:50 +00003516 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003517 "chunk was found (see Chromaticity, above)");
3518 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3519 }
glennrpcb395ac2011-03-30 19:50:23 +00003520
3521 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003522 {
cristy3b6fd2e2011-05-20 12:53:50 +00003523 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003524 "chunk was found (see Background color, above)");
3525 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3526 }
3527
cristy3b6fd2e2011-05-20 12:53:50 +00003528 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003529 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003530
3531 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3532 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3533
glennrpcb395ac2011-03-30 19:50:23 +00003534 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3535 (void) SetImageProperty(image,"PNG:tRNS ",msg);
glennrp4eb39312011-03-30 21:34:55 +00003536
3537#if defined(PNG_sRGB_SUPPORTED)
3538 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3539 {
cristy3b6fd2e2011-05-20 12:53:50 +00003540 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003541 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003542 (int) intent);
3543 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3544 }
3545#endif
3546
3547 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3548 {
cristy3b6fd2e2011-05-20 12:53:50 +00003549 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003550 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003551 file_gamma);
3552 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3553 }
3554
3555#if defined(PNG_pHYs_SUPPORTED)
3556 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3557 {
cristy3b6fd2e2011-05-20 12:53:50 +00003558 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003559 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003560 (double) x_resolution,(double) y_resolution, unit_type);
3561 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3562 }
3563#endif
3564
3565#if defined(PNG_oFFs_SUPPORTED)
3566 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3567 {
cristy3b6fd2e2011-05-20 12:53:50 +00003568 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003569 (double) image->page.x,(double) image->page.y);
3570 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3571 }
3572#endif
3573
glennrp07523c72011-03-31 18:12:10 +00003574 if ((image->page.width != 0 && image->page.width != image->columns) ||
3575 (image->page.height != 0 && image->page.height != image->rows))
3576 {
cristy3b6fd2e2011-05-20 12:53:50 +00003577 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003578 "width=%.20g, height=%.20g",
3579 (double) image->page.width,(double) image->page.height);
3580 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3581 }
glennrpcb395ac2011-03-30 19:50:23 +00003582 }
3583
cristy3ed852e2009-09-05 21:47:34 +00003584 /*
3585 Relinquish resources.
3586 */
3587 png_destroy_read_struct(&ping,&ping_info,&end_info);
3588
glennrpcf002022011-01-30 02:38:15 +00003589 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003590#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003591 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003592#endif
3593
3594 if (logging != MagickFalse)
3595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3596 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003597
cristy3ed852e2009-09-05 21:47:34 +00003598 return(image);
3599
3600/* end of reading one PNG image */
3601}
3602
3603static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3604{
3605 Image
3606 *image,
3607 *previous;
3608
3609 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003610 have_mng_structure,
3611 logging,
cristy3ed852e2009-09-05 21:47:34 +00003612 status;
3613
3614 MngInfo
3615 *mng_info;
3616
3617 char
3618 magic_number[MaxTextExtent];
3619
cristy3ed852e2009-09-05 21:47:34 +00003620 ssize_t
3621 count;
3622
3623 /*
3624 Open image file.
3625 */
3626 assert(image_info != (const ImageInfo *) NULL);
3627 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003628
cristy3ed852e2009-09-05 21:47:34 +00003629 if (image_info->debug != MagickFalse)
3630 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3631 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003632
cristy3ed852e2009-09-05 21:47:34 +00003633 assert(exception != (ExceptionInfo *) NULL);
3634 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003635 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003636 image=AcquireImage(image_info);
3637 mng_info=(MngInfo *) NULL;
3638 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003639
cristy3ed852e2009-09-05 21:47:34 +00003640 if (status == MagickFalse)
3641 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003642
cristy3ed852e2009-09-05 21:47:34 +00003643 /*
3644 Verify PNG signature.
3645 */
3646 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003647
glennrpdde35db2011-02-21 12:06:32 +00003648 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003649 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003650
cristy3ed852e2009-09-05 21:47:34 +00003651 /*
3652 Allocate a MngInfo structure.
3653 */
3654 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003655 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003656
cristy3ed852e2009-09-05 21:47:34 +00003657 if (mng_info == (MngInfo *) NULL)
3658 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003659
cristy3ed852e2009-09-05 21:47:34 +00003660 /*
3661 Initialize members of the MngInfo structure.
3662 */
3663 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3664 mng_info->image=image;
3665 have_mng_structure=MagickTrue;
3666
3667 previous=image;
3668 image=ReadOnePNGImage(mng_info,image_info,exception);
3669 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003670
cristy3ed852e2009-09-05 21:47:34 +00003671 if (image == (Image *) NULL)
3672 {
3673 if (previous != (Image *) NULL)
3674 {
3675 if (previous->signature != MagickSignature)
3676 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003677
cristy3ed852e2009-09-05 21:47:34 +00003678 (void) CloseBlob(previous);
3679 (void) DestroyImageList(previous);
3680 }
glennrp0fe50b42010-11-16 03:52:51 +00003681
cristy3ed852e2009-09-05 21:47:34 +00003682 if (logging != MagickFalse)
3683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3684 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003685
cristy3ed852e2009-09-05 21:47:34 +00003686 return((Image *) NULL);
3687 }
glennrp47b9dd52010-11-24 18:12:06 +00003688
cristy3ed852e2009-09-05 21:47:34 +00003689 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003690
cristy3ed852e2009-09-05 21:47:34 +00003691 if ((image->columns == 0) || (image->rows == 0))
3692 {
3693 if (logging != MagickFalse)
3694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3695 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003696
cristy3ed852e2009-09-05 21:47:34 +00003697 ThrowReaderException(CorruptImageError,"CorruptImage");
3698 }
glennrp47b9dd52010-11-24 18:12:06 +00003699
cristy3ed852e2009-09-05 21:47:34 +00003700 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3701 {
cristy018f07f2011-09-04 21:15:19 +00003702 (void) SetImageType(image,TrueColorType,exception);
cristy3ed852e2009-09-05 21:47:34 +00003703 image->matte=MagickFalse;
3704 }
glennrp0fe50b42010-11-16 03:52:51 +00003705
cristy3ed852e2009-09-05 21:47:34 +00003706 if (LocaleCompare(image_info->magick,"PNG32") == 0)
cristy018f07f2011-09-04 21:15:19 +00003707 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003708
cristy3ed852e2009-09-05 21:47:34 +00003709 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3711 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3712 (double) image->page.width,(double) image->page.height,
3713 (double) image->page.x,(double) image->page.y);
3714
3715 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003717
cristy3ed852e2009-09-05 21:47:34 +00003718 return(image);
3719}
3720
3721
3722
3723#if defined(JNG_SUPPORTED)
3724/*
3725%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3726% %
3727% %
3728% %
3729% R e a d O n e J N G I m a g e %
3730% %
3731% %
3732% %
3733%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3734%
3735% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3736% (minus the 8-byte signature) and returns it. It allocates the memory
3737% necessary for the new Image structure and returns a pointer to the new
3738% image.
3739%
3740% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3741%
3742% The format of the ReadOneJNGImage method is:
3743%
3744% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3745% ExceptionInfo *exception)
3746%
3747% A description of each parameter follows:
3748%
3749% o mng_info: Specifies a pointer to a MngInfo structure.
3750%
3751% o image_info: the image info.
3752%
3753% o exception: return any errors or warnings in this structure.
3754%
3755*/
3756static Image *ReadOneJNGImage(MngInfo *mng_info,
3757 const ImageInfo *image_info, ExceptionInfo *exception)
3758{
3759 Image
3760 *alpha_image,
3761 *color_image,
3762 *image,
3763 *jng_image;
3764
3765 ImageInfo
3766 *alpha_image_info,
3767 *color_image_info;
3768
cristy4383ec82011-01-05 15:42:32 +00003769 MagickBooleanType
3770 logging;
3771
cristybb503372010-05-27 20:51:26 +00003772 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003773 y;
3774
3775 MagickBooleanType
3776 status;
3777
3778 png_uint_32
3779 jng_height,
3780 jng_width;
3781
3782 png_byte
3783 jng_color_type,
3784 jng_image_sample_depth,
3785 jng_image_compression_method,
3786 jng_image_interlace_method,
3787 jng_alpha_sample_depth,
3788 jng_alpha_compression_method,
3789 jng_alpha_filter_method,
3790 jng_alpha_interlace_method;
3791
cristy4c08aed2011-07-01 19:47:50 +00003792 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003793 *s;
3794
cristybb503372010-05-27 20:51:26 +00003795 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003796 i,
3797 x;
3798
cristy4c08aed2011-07-01 19:47:50 +00003799 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003800 *q;
3801
3802 register unsigned char
3803 *p;
3804
3805 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003806 read_JSEP,
3807 reading_idat,
3808 skip_to_iend;
3809
cristybb503372010-05-27 20:51:26 +00003810 size_t
cristy3ed852e2009-09-05 21:47:34 +00003811 length;
3812
3813 jng_alpha_compression_method=0;
3814 jng_alpha_sample_depth=8;
3815 jng_color_type=0;
3816 jng_height=0;
3817 jng_width=0;
3818 alpha_image=(Image *) NULL;
3819 color_image=(Image *) NULL;
3820 alpha_image_info=(ImageInfo *) NULL;
3821 color_image_info=(ImageInfo *) NULL;
3822
3823 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003824 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003825
3826 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003827
cristy4c08aed2011-07-01 19:47:50 +00003828 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003829 {
3830 /*
3831 Allocate next image structure.
3832 */
3833 if (logging != MagickFalse)
3834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3835 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003836
cristy3ed852e2009-09-05 21:47:34 +00003837 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00003838
cristy3ed852e2009-09-05 21:47:34 +00003839 if (GetNextImageInList(image) == (Image *) NULL)
3840 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003841
cristy3ed852e2009-09-05 21:47:34 +00003842 image=SyncNextImageInList(image);
3843 }
3844 mng_info->image=image;
3845
3846 /*
3847 Signature bytes have already been read.
3848 */
3849
3850 read_JSEP=MagickFalse;
3851 reading_idat=MagickFalse;
3852 skip_to_iend=MagickFalse;
3853 for (;;)
3854 {
3855 char
3856 type[MaxTextExtent];
3857
3858 unsigned char
3859 *chunk;
3860
3861 unsigned int
3862 count;
3863
3864 /*
3865 Read a new JNG chunk.
3866 */
3867 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3868 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003869
cristy3ed852e2009-09-05 21:47:34 +00003870 if (status == MagickFalse)
3871 break;
glennrp0fe50b42010-11-16 03:52:51 +00003872
cristy3ed852e2009-09-05 21:47:34 +00003873 type[0]='\0';
3874 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3875 length=ReadBlobMSBLong(image);
3876 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3877
3878 if (logging != MagickFalse)
3879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003880 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3881 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003882
3883 if (length > PNG_UINT_31_MAX || count == 0)
3884 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003885
cristy3ed852e2009-09-05 21:47:34 +00003886 p=NULL;
3887 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003888
cristy3ed852e2009-09-05 21:47:34 +00003889 if (length)
3890 {
3891 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003892
cristy3ed852e2009-09-05 21:47:34 +00003893 if (chunk == (unsigned char *) NULL)
3894 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003895
cristybb503372010-05-27 20:51:26 +00003896 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003897 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003898
cristy3ed852e2009-09-05 21:47:34 +00003899 p=chunk;
3900 }
glennrp47b9dd52010-11-24 18:12:06 +00003901
cristy3ed852e2009-09-05 21:47:34 +00003902 (void) ReadBlobMSBLong(image); /* read crc word */
3903
3904 if (skip_to_iend)
3905 {
3906 if (length)
3907 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003908
cristy3ed852e2009-09-05 21:47:34 +00003909 continue;
3910 }
3911
3912 if (memcmp(type,mng_JHDR,4) == 0)
3913 {
3914 if (length == 16)
3915 {
cristybb503372010-05-27 20:51:26 +00003916 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003917 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003918 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003919 (p[6] << 8) | p[7]);
3920 jng_color_type=p[8];
3921 jng_image_sample_depth=p[9];
3922 jng_image_compression_method=p[10];
3923 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003924
cristy3ed852e2009-09-05 21:47:34 +00003925 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3926 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003927
cristy3ed852e2009-09-05 21:47:34 +00003928 jng_alpha_sample_depth=p[12];
3929 jng_alpha_compression_method=p[13];
3930 jng_alpha_filter_method=p[14];
3931 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003932
cristy3ed852e2009-09-05 21:47:34 +00003933 if (logging != MagickFalse)
3934 {
3935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003936 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003937
cristy3ed852e2009-09-05 21:47:34 +00003938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003939 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003940
cristy3ed852e2009-09-05 21:47:34 +00003941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3942 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003943
cristy3ed852e2009-09-05 21:47:34 +00003944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3945 " jng_image_sample_depth: %3d",
3946 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003947
cristy3ed852e2009-09-05 21:47:34 +00003948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3949 " jng_image_compression_method:%3d",
3950 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003951
cristy3ed852e2009-09-05 21:47:34 +00003952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3953 " jng_image_interlace_method: %3d",
3954 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003955
cristy3ed852e2009-09-05 21:47:34 +00003956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3957 " jng_alpha_sample_depth: %3d",
3958 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003959
cristy3ed852e2009-09-05 21:47:34 +00003960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3961 " jng_alpha_compression_method:%3d",
3962 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003963
cristy3ed852e2009-09-05 21:47:34 +00003964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3965 " jng_alpha_filter_method: %3d",
3966 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00003967
cristy3ed852e2009-09-05 21:47:34 +00003968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3969 " jng_alpha_interlace_method: %3d",
3970 jng_alpha_interlace_method);
3971 }
3972 }
glennrp47b9dd52010-11-24 18:12:06 +00003973
cristy3ed852e2009-09-05 21:47:34 +00003974 if (length)
3975 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003976
cristy3ed852e2009-09-05 21:47:34 +00003977 continue;
3978 }
3979
3980
3981 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3982 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3983 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3984 {
3985 /*
3986 o create color_image
3987 o open color_blob, attached to color_image
3988 o if (color type has alpha)
3989 open alpha_blob, attached to alpha_image
3990 */
3991
cristy73bd4a52010-10-05 11:24:23 +00003992 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003993
cristy3ed852e2009-09-05 21:47:34 +00003994 if (color_image_info == (ImageInfo *) NULL)
3995 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003996
cristy3ed852e2009-09-05 21:47:34 +00003997 GetImageInfo(color_image_info);
3998 color_image=AcquireImage(color_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003999
cristy3ed852e2009-09-05 21:47:34 +00004000 if (color_image == (Image *) NULL)
4001 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4002
4003 if (logging != MagickFalse)
4004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4005 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004006
cristy3ed852e2009-09-05 21:47:34 +00004007 (void) AcquireUniqueFilename(color_image->filename);
4008 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4009 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004010
cristy3ed852e2009-09-05 21:47:34 +00004011 if (status == MagickFalse)
4012 return((Image *) NULL);
4013
4014 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4015 {
4016 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004017 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004018
cristy3ed852e2009-09-05 21:47:34 +00004019 if (alpha_image_info == (ImageInfo *) NULL)
4020 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004021
cristy3ed852e2009-09-05 21:47:34 +00004022 GetImageInfo(alpha_image_info);
4023 alpha_image=AcquireImage(alpha_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00004024
cristy3ed852e2009-09-05 21:47:34 +00004025 if (alpha_image == (Image *) NULL)
4026 {
4027 alpha_image=DestroyImage(alpha_image);
4028 ThrowReaderException(ResourceLimitError,
4029 "MemoryAllocationFailed");
4030 }
glennrp0fe50b42010-11-16 03:52:51 +00004031
cristy3ed852e2009-09-05 21:47:34 +00004032 if (logging != MagickFalse)
4033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4034 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004035
cristy3ed852e2009-09-05 21:47:34 +00004036 (void) AcquireUniqueFilename(alpha_image->filename);
4037 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4038 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004039
cristy3ed852e2009-09-05 21:47:34 +00004040 if (status == MagickFalse)
4041 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004042
cristy3ed852e2009-09-05 21:47:34 +00004043 if (jng_alpha_compression_method == 0)
4044 {
4045 unsigned char
4046 data[18];
4047
4048 if (logging != MagickFalse)
4049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4050 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004051
cristy3ed852e2009-09-05 21:47:34 +00004052 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4053 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004054
cristy3ed852e2009-09-05 21:47:34 +00004055 (void) WriteBlobMSBULong(alpha_image,13L);
4056 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004057 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004058 PNGLong(data+4,jng_width);
4059 PNGLong(data+8,jng_height);
4060 data[12]=jng_alpha_sample_depth;
4061 data[13]=0; /* color_type gray */
4062 data[14]=0; /* compression method 0 */
4063 data[15]=0; /* filter_method 0 */
4064 data[16]=0; /* interlace_method 0 */
4065 (void) WriteBlob(alpha_image,17,data);
4066 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4067 }
4068 }
4069 reading_idat=MagickTrue;
4070 }
4071
4072 if (memcmp(type,mng_JDAT,4) == 0)
4073 {
glennrp47b9dd52010-11-24 18:12:06 +00004074 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004075
4076 if (logging != MagickFalse)
4077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4078 " Copying JDAT chunk data to color_blob.");
4079
4080 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004081
cristy3ed852e2009-09-05 21:47:34 +00004082 if (length)
4083 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004084
cristy3ed852e2009-09-05 21:47:34 +00004085 continue;
4086 }
4087
4088 if (memcmp(type,mng_IDAT,4) == 0)
4089 {
4090 png_byte
4091 data[5];
4092
glennrp47b9dd52010-11-24 18:12:06 +00004093 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004094
4095 if (image_info->ping == MagickFalse)
4096 {
4097 if (logging != MagickFalse)
4098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4099 " Copying IDAT chunk data to alpha_blob.");
4100
cristybb503372010-05-27 20:51:26 +00004101 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004102 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004103 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004104 (void) WriteBlob(alpha_image,4,data);
4105 (void) WriteBlob(alpha_image,length,chunk);
4106 (void) WriteBlobMSBULong(alpha_image,
4107 crc32(crc32(0,data,4),chunk,(uInt) length));
4108 }
glennrp0fe50b42010-11-16 03:52:51 +00004109
cristy3ed852e2009-09-05 21:47:34 +00004110 if (length)
4111 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004112
cristy3ed852e2009-09-05 21:47:34 +00004113 continue;
4114 }
4115
4116 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4117 {
glennrp47b9dd52010-11-24 18:12:06 +00004118 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004119
4120 if (image_info->ping == MagickFalse)
4121 {
4122 if (logging != MagickFalse)
4123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4124 " Copying JDAA chunk data to alpha_blob.");
4125
4126 (void) WriteBlob(alpha_image,length,chunk);
4127 }
glennrp0fe50b42010-11-16 03:52:51 +00004128
cristy3ed852e2009-09-05 21:47:34 +00004129 if (length)
4130 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004131
cristy3ed852e2009-09-05 21:47:34 +00004132 continue;
4133 }
4134
4135 if (memcmp(type,mng_JSEP,4) == 0)
4136 {
4137 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004138
cristy3ed852e2009-09-05 21:47:34 +00004139 if (length)
4140 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004141
cristy3ed852e2009-09-05 21:47:34 +00004142 continue;
4143 }
4144
4145 if (memcmp(type,mng_bKGD,4) == 0)
4146 {
4147 if (length == 2)
4148 {
4149 image->background_color.red=ScaleCharToQuantum(p[1]);
4150 image->background_color.green=image->background_color.red;
4151 image->background_color.blue=image->background_color.red;
4152 }
glennrp0fe50b42010-11-16 03:52:51 +00004153
cristy3ed852e2009-09-05 21:47:34 +00004154 if (length == 6)
4155 {
4156 image->background_color.red=ScaleCharToQuantum(p[1]);
4157 image->background_color.green=ScaleCharToQuantum(p[3]);
4158 image->background_color.blue=ScaleCharToQuantum(p[5]);
4159 }
glennrp0fe50b42010-11-16 03:52:51 +00004160
cristy3ed852e2009-09-05 21:47:34 +00004161 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4162 continue;
4163 }
4164
4165 if (memcmp(type,mng_gAMA,4) == 0)
4166 {
4167 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004168 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004169
cristy3ed852e2009-09-05 21:47:34 +00004170 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4171 continue;
4172 }
4173
4174 if (memcmp(type,mng_cHRM,4) == 0)
4175 {
4176 if (length == 32)
4177 {
cristy8182b072010-05-30 20:10:53 +00004178 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4179 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4180 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4181 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4182 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4183 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4184 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4185 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004186 }
glennrp47b9dd52010-11-24 18:12:06 +00004187
cristy3ed852e2009-09-05 21:47:34 +00004188 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4189 continue;
4190 }
4191
4192 if (memcmp(type,mng_sRGB,4) == 0)
4193 {
4194 if (length == 1)
4195 {
glennrpe610a072010-08-05 17:08:46 +00004196 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004197 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004198 image->gamma=0.45455f;
4199 image->chromaticity.red_primary.x=0.6400f;
4200 image->chromaticity.red_primary.y=0.3300f;
4201 image->chromaticity.green_primary.x=0.3000f;
4202 image->chromaticity.green_primary.y=0.6000f;
4203 image->chromaticity.blue_primary.x=0.1500f;
4204 image->chromaticity.blue_primary.y=0.0600f;
4205 image->chromaticity.white_point.x=0.3127f;
4206 image->chromaticity.white_point.y=0.3290f;
4207 }
glennrp47b9dd52010-11-24 18:12:06 +00004208
cristy3ed852e2009-09-05 21:47:34 +00004209 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4210 continue;
4211 }
4212
4213 if (memcmp(type,mng_oFFs,4) == 0)
4214 {
4215 if (length > 8)
4216 {
glennrp5eae7602011-02-22 15:21:32 +00004217 image->page.x=(ssize_t) mng_get_long(p);
4218 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004219
cristy3ed852e2009-09-05 21:47:34 +00004220 if ((int) p[8] != 0)
4221 {
4222 image->page.x/=10000;
4223 image->page.y/=10000;
4224 }
4225 }
glennrp47b9dd52010-11-24 18:12:06 +00004226
cristy3ed852e2009-09-05 21:47:34 +00004227 if (length)
4228 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004229
cristy3ed852e2009-09-05 21:47:34 +00004230 continue;
4231 }
4232
4233 if (memcmp(type,mng_pHYs,4) == 0)
4234 {
4235 if (length > 8)
4236 {
cristy8182b072010-05-30 20:10:53 +00004237 image->x_resolution=(double) mng_get_long(p);
4238 image->y_resolution=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004239 if ((int) p[8] == PNG_RESOLUTION_METER)
4240 {
4241 image->units=PixelsPerCentimeterResolution;
4242 image->x_resolution=image->x_resolution/100.0f;
4243 image->y_resolution=image->y_resolution/100.0f;
4244 }
4245 }
glennrp0fe50b42010-11-16 03:52:51 +00004246
cristy3ed852e2009-09-05 21:47:34 +00004247 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4248 continue;
4249 }
4250
4251#if 0
4252 if (memcmp(type,mng_iCCP,4) == 0)
4253 {
glennrpfd05d622011-02-25 04:10:33 +00004254 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004255 if (length)
4256 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004257
cristy3ed852e2009-09-05 21:47:34 +00004258 continue;
4259 }
4260#endif
4261
4262 if (length)
4263 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4264
4265 if (memcmp(type,mng_IEND,4))
4266 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004267
cristy3ed852e2009-09-05 21:47:34 +00004268 break;
4269 }
4270
4271
4272 /* IEND found */
4273
4274 /*
4275 Finish up reading image data:
4276
4277 o read main image from color_blob.
4278
4279 o close color_blob.
4280
4281 o if (color_type has alpha)
4282 if alpha_encoding is PNG
4283 read secondary image from alpha_blob via ReadPNG
4284 if alpha_encoding is JPEG
4285 read secondary image from alpha_blob via ReadJPEG
4286
4287 o close alpha_blob.
4288
4289 o copy intensity of secondary image into
cristy4c08aed2011-07-01 19:47:50 +00004290 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004291
4292 o destroy the secondary image.
4293 */
4294
4295 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004296
cristy3ed852e2009-09-05 21:47:34 +00004297 if (logging != MagickFalse)
4298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4299 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004300
cristy3b6fd2e2011-05-20 12:53:50 +00004301 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004302 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004303
cristy3ed852e2009-09-05 21:47:34 +00004304 color_image_info->ping=MagickFalse; /* To do: avoid this */
4305 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004306
cristy3ed852e2009-09-05 21:47:34 +00004307 if (jng_image == (Image *) NULL)
4308 return((Image *) NULL);
4309
4310 (void) RelinquishUniqueFileResource(color_image->filename);
4311 color_image=DestroyImage(color_image);
4312 color_image_info=DestroyImageInfo(color_image_info);
4313
4314 if (jng_image == (Image *) NULL)
4315 return((Image *) NULL);
4316
4317 if (logging != MagickFalse)
4318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4319 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004320
cristy3ed852e2009-09-05 21:47:34 +00004321 image->rows=jng_height;
4322 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004323
cristybb503372010-05-27 20:51:26 +00004324 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004325 {
4326 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
4327 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004328 for (x=(ssize_t) image->columns; x != 0; x--)
4329 {
4330 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4331 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4332 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004333 q+=GetPixelChannels(image);
4334 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004335 }
glennrp47b9dd52010-11-24 18:12:06 +00004336
cristy3ed852e2009-09-05 21:47:34 +00004337 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4338 break;
4339 }
glennrp0fe50b42010-11-16 03:52:51 +00004340
cristy3ed852e2009-09-05 21:47:34 +00004341 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004342
cristy3ed852e2009-09-05 21:47:34 +00004343 if (image_info->ping == MagickFalse)
4344 {
4345 if (jng_color_type >= 12)
4346 {
4347 if (jng_alpha_compression_method == 0)
4348 {
4349 png_byte
4350 data[5];
4351 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4352 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004353 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004354 (void) WriteBlob(alpha_image,4,data);
4355 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4356 }
glennrp0fe50b42010-11-16 03:52:51 +00004357
cristy3ed852e2009-09-05 21:47:34 +00004358 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004359
cristy3ed852e2009-09-05 21:47:34 +00004360 if (logging != MagickFalse)
4361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00004362 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004363
cristy3b6fd2e2011-05-20 12:53:50 +00004364 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004365 "%s",alpha_image->filename);
4366
4367 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004368
cristy3ed852e2009-09-05 21:47:34 +00004369 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004370 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004371 {
4372 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristy4c08aed2011-07-01 19:47:50 +00004373 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +00004374 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004375
cristy3ed852e2009-09-05 21:47:34 +00004376 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00004377 for (x=(ssize_t) image->columns; x != 0; x--)
4378 {
4379 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004380 q+=GetPixelChannels(image);
4381 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004382 }
glennrp0fe50b42010-11-16 03:52:51 +00004383
cristy3ed852e2009-09-05 21:47:34 +00004384 else
cristy4c08aed2011-07-01 19:47:50 +00004385 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004386 {
cristy4c08aed2011-07-01 19:47:50 +00004387 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4388 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004389 image->matte=MagickTrue;
cristyed231572011-07-14 02:18:59 +00004390 q+=GetPixelChannels(image);
4391 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004392 }
glennrp0fe50b42010-11-16 03:52:51 +00004393
cristy3ed852e2009-09-05 21:47:34 +00004394 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4395 break;
4396 }
4397 (void) RelinquishUniqueFileResource(alpha_image->filename);
4398 alpha_image=DestroyImage(alpha_image);
4399 alpha_image_info=DestroyImageInfo(alpha_image_info);
4400 if (jng_image != (Image *) NULL)
4401 jng_image=DestroyImage(jng_image);
4402 }
4403 }
4404
glennrp47b9dd52010-11-24 18:12:06 +00004405 /* Read the JNG image. */
4406
cristy3ed852e2009-09-05 21:47:34 +00004407 if (mng_info->mng_type == 0)
4408 {
4409 mng_info->mng_width=jng_width;
4410 mng_info->mng_height=jng_height;
4411 }
glennrp0fe50b42010-11-16 03:52:51 +00004412
cristy3ed852e2009-09-05 21:47:34 +00004413 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004414 {
4415 image->page.width=jng_width;
4416 image->page.height=jng_height;
4417 }
4418
cristy3ed852e2009-09-05 21:47:34 +00004419 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004420 {
4421 image->page.x=mng_info->x_off[mng_info->object_id];
4422 image->page.y=mng_info->y_off[mng_info->object_id];
4423 }
4424
cristy3ed852e2009-09-05 21:47:34 +00004425 else
glennrp0fe50b42010-11-16 03:52:51 +00004426 {
4427 image->page.y=mng_info->y_off[mng_info->object_id];
4428 }
4429
cristy3ed852e2009-09-05 21:47:34 +00004430 mng_info->image_found++;
4431 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4432 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004433
cristy3ed852e2009-09-05 21:47:34 +00004434 if (logging != MagickFalse)
4435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4436 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004437
cristy3ed852e2009-09-05 21:47:34 +00004438 return(image);
4439}
4440
4441/*
4442%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4443% %
4444% %
4445% %
4446% R e a d J N G I m a g e %
4447% %
4448% %
4449% %
4450%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4451%
4452% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4453% (including the 8-byte signature) and returns it. It allocates the memory
4454% necessary for the new Image structure and returns a pointer to the new
4455% image.
4456%
4457% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4458%
4459% The format of the ReadJNGImage method is:
4460%
4461% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4462% *exception)
4463%
4464% A description of each parameter follows:
4465%
4466% o image_info: the image info.
4467%
4468% o exception: return any errors or warnings in this structure.
4469%
4470*/
4471
4472static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4473{
4474 Image
4475 *image,
4476 *previous;
4477
4478 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004479 have_mng_structure,
4480 logging,
cristy3ed852e2009-09-05 21:47:34 +00004481 status;
4482
4483 MngInfo
4484 *mng_info;
4485
4486 char
4487 magic_number[MaxTextExtent];
4488
cristy3ed852e2009-09-05 21:47:34 +00004489 size_t
4490 count;
4491
4492 /*
4493 Open image file.
4494 */
4495 assert(image_info != (const ImageInfo *) NULL);
4496 assert(image_info->signature == MagickSignature);
4497 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4498 assert(exception != (ExceptionInfo *) NULL);
4499 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004500 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004501 image=AcquireImage(image_info);
4502 mng_info=(MngInfo *) NULL;
4503 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004504
cristy3ed852e2009-09-05 21:47:34 +00004505 if (status == MagickFalse)
4506 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004507
cristy3ed852e2009-09-05 21:47:34 +00004508 if (LocaleCompare(image_info->magick,"JNG") != 0)
4509 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004510
glennrp47b9dd52010-11-24 18:12:06 +00004511 /* Verify JNG signature. */
4512
cristy3ed852e2009-09-05 21:47:34 +00004513 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004514
glennrp3b8763e2011-02-21 12:08:18 +00004515 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004516 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004517
glennrp47b9dd52010-11-24 18:12:06 +00004518 /* Allocate a MngInfo structure. */
4519
cristy3ed852e2009-09-05 21:47:34 +00004520 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004521 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004522
cristy3ed852e2009-09-05 21:47:34 +00004523 if (mng_info == (MngInfo *) NULL)
4524 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004525
glennrp47b9dd52010-11-24 18:12:06 +00004526 /* Initialize members of the MngInfo structure. */
4527
cristy3ed852e2009-09-05 21:47:34 +00004528 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4529 have_mng_structure=MagickTrue;
4530
4531 mng_info->image=image;
4532 previous=image;
4533 image=ReadOneJNGImage(mng_info,image_info,exception);
4534 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004535
cristy3ed852e2009-09-05 21:47:34 +00004536 if (image == (Image *) NULL)
4537 {
4538 if (IsImageObject(previous) != MagickFalse)
4539 {
4540 (void) CloseBlob(previous);
4541 (void) DestroyImageList(previous);
4542 }
glennrp0fe50b42010-11-16 03:52:51 +00004543
cristy3ed852e2009-09-05 21:47:34 +00004544 if (logging != MagickFalse)
4545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4546 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004547
cristy3ed852e2009-09-05 21:47:34 +00004548 return((Image *) NULL);
4549 }
4550 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004551
cristy3ed852e2009-09-05 21:47:34 +00004552 if (image->columns == 0 || image->rows == 0)
4553 {
4554 if (logging != MagickFalse)
4555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4556 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004557
cristy3ed852e2009-09-05 21:47:34 +00004558 ThrowReaderException(CorruptImageError,"CorruptImage");
4559 }
glennrp0fe50b42010-11-16 03:52:51 +00004560
cristy3ed852e2009-09-05 21:47:34 +00004561 if (logging != MagickFalse)
4562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004563
cristy3ed852e2009-09-05 21:47:34 +00004564 return(image);
4565}
4566#endif
4567
4568static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4569{
4570 char
4571 page_geometry[MaxTextExtent];
4572
4573 Image
4574 *image,
4575 *previous;
4576
cristy4383ec82011-01-05 15:42:32 +00004577 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004578 logging,
4579 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004580
cristy3ed852e2009-09-05 21:47:34 +00004581 volatile int
4582 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004583 object_id,
4584 term_chunk_found,
4585 skip_to_iend;
4586
cristybb503372010-05-27 20:51:26 +00004587 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004588 image_count=0;
4589
4590 MagickBooleanType
4591 status;
4592
4593 MagickOffsetType
4594 offset;
4595
4596 MngInfo
4597 *mng_info;
4598
4599 MngBox
4600 default_fb,
4601 fb,
4602 previous_fb;
4603
4604#if defined(MNG_INSERT_LAYERS)
4605 PixelPacket
4606 mng_background_color;
4607#endif
4608
4609 register unsigned char
4610 *p;
4611
cristybb503372010-05-27 20:51:26 +00004612 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004613 i;
4614
4615 size_t
4616 count;
4617
cristybb503372010-05-27 20:51:26 +00004618 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004619 loop_level;
4620
4621 volatile short
4622 skipping_loop;
4623
4624#if defined(MNG_INSERT_LAYERS)
4625 unsigned int
4626 mandatory_back=0;
4627#endif
4628
4629 volatile unsigned int
4630#ifdef MNG_OBJECT_BUFFERS
4631 mng_background_object=0,
4632#endif
4633 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4634
cristybb503372010-05-27 20:51:26 +00004635 size_t
cristy3ed852e2009-09-05 21:47:34 +00004636 default_frame_timeout,
4637 frame_timeout,
4638#if defined(MNG_INSERT_LAYERS)
4639 image_height,
4640 image_width,
4641#endif
4642 length;
4643
glennrp38ea0832010-06-02 18:50:28 +00004644 /* These delays are all measured in image ticks_per_second,
4645 * not in MNG ticks_per_second
4646 */
cristybb503372010-05-27 20:51:26 +00004647 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004648 default_frame_delay,
4649 final_delay,
4650 final_image_delay,
4651 frame_delay,
4652#if defined(MNG_INSERT_LAYERS)
4653 insert_layers,
4654#endif
4655 mng_iterations=1,
4656 simplicity=0,
4657 subframe_height=0,
4658 subframe_width=0;
4659
4660 previous_fb.top=0;
4661 previous_fb.bottom=0;
4662 previous_fb.left=0;
4663 previous_fb.right=0;
4664 default_fb.top=0;
4665 default_fb.bottom=0;
4666 default_fb.left=0;
4667 default_fb.right=0;
4668
glennrp47b9dd52010-11-24 18:12:06 +00004669 /* Open image file. */
4670
cristy3ed852e2009-09-05 21:47:34 +00004671 assert(image_info != (const ImageInfo *) NULL);
4672 assert(image_info->signature == MagickSignature);
4673 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4674 assert(exception != (ExceptionInfo *) NULL);
4675 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004676 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004677 image=AcquireImage(image_info);
4678 mng_info=(MngInfo *) NULL;
4679 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004680
cristy3ed852e2009-09-05 21:47:34 +00004681 if (status == MagickFalse)
4682 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004683
cristy3ed852e2009-09-05 21:47:34 +00004684 first_mng_object=MagickFalse;
4685 skipping_loop=(-1);
4686 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004687
4688 /* Allocate a MngInfo structure. */
4689
cristy73bd4a52010-10-05 11:24:23 +00004690 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004691
cristy3ed852e2009-09-05 21:47:34 +00004692 if (mng_info == (MngInfo *) NULL)
4693 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004694
glennrp47b9dd52010-11-24 18:12:06 +00004695 /* Initialize members of the MngInfo structure. */
4696
cristy3ed852e2009-09-05 21:47:34 +00004697 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4698 mng_info->image=image;
4699 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004700
4701 if (LocaleCompare(image_info->magick,"MNG") == 0)
4702 {
4703 char
4704 magic_number[MaxTextExtent];
4705
glennrp47b9dd52010-11-24 18:12:06 +00004706 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004707 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4708 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4709 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004710
4711 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004712 for (i=0; i < MNG_MAX_OBJECTS; i++)
4713 {
cristybb503372010-05-27 20:51:26 +00004714 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4715 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004716 }
4717 mng_info->exists[0]=MagickTrue;
4718 }
glennrp47b9dd52010-11-24 18:12:06 +00004719
cristy3ed852e2009-09-05 21:47:34 +00004720 first_mng_object=MagickTrue;
4721 mng_type=0;
4722#if defined(MNG_INSERT_LAYERS)
4723 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4724#endif
4725 default_frame_delay=0;
4726 default_frame_timeout=0;
4727 frame_delay=0;
4728 final_delay=1;
4729 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4730 object_id=0;
4731 skip_to_iend=MagickFalse;
4732 term_chunk_found=MagickFalse;
4733 mng_info->framing_mode=1;
4734#if defined(MNG_INSERT_LAYERS)
4735 mandatory_back=MagickFalse;
4736#endif
4737#if defined(MNG_INSERT_LAYERS)
4738 mng_background_color=image->background_color;
4739#endif
4740 default_fb=mng_info->frame;
4741 previous_fb=mng_info->frame;
4742 do
4743 {
4744 char
4745 type[MaxTextExtent];
4746
4747 if (LocaleCompare(image_info->magick,"MNG") == 0)
4748 {
4749 unsigned char
4750 *chunk;
4751
4752 /*
4753 Read a new chunk.
4754 */
4755 type[0]='\0';
4756 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4757 length=ReadBlobMSBLong(image);
4758 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4759
4760 if (logging != MagickFalse)
4761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004762 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4763 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004764
4765 if (length > PNG_UINT_31_MAX)
4766 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004767
cristy3ed852e2009-09-05 21:47:34 +00004768 if (count == 0)
4769 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004770
cristy3ed852e2009-09-05 21:47:34 +00004771 p=NULL;
4772 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004773
cristy3ed852e2009-09-05 21:47:34 +00004774 if (length)
4775 {
4776 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004777
cristy3ed852e2009-09-05 21:47:34 +00004778 if (chunk == (unsigned char *) NULL)
4779 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004780
cristybb503372010-05-27 20:51:26 +00004781 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004782 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004783
cristy3ed852e2009-09-05 21:47:34 +00004784 p=chunk;
4785 }
glennrp0fe50b42010-11-16 03:52:51 +00004786
cristy3ed852e2009-09-05 21:47:34 +00004787 (void) ReadBlobMSBLong(image); /* read crc word */
4788
4789#if !defined(JNG_SUPPORTED)
4790 if (memcmp(type,mng_JHDR,4) == 0)
4791 {
4792 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004793
cristy3ed852e2009-09-05 21:47:34 +00004794 if (mng_info->jhdr_warning == 0)
4795 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4796 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004797
cristy3ed852e2009-09-05 21:47:34 +00004798 mng_info->jhdr_warning++;
4799 }
4800#endif
4801 if (memcmp(type,mng_DHDR,4) == 0)
4802 {
4803 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004804
cristy3ed852e2009-09-05 21:47:34 +00004805 if (mng_info->dhdr_warning == 0)
4806 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4807 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004808
cristy3ed852e2009-09-05 21:47:34 +00004809 mng_info->dhdr_warning++;
4810 }
4811 if (memcmp(type,mng_MEND,4) == 0)
4812 break;
glennrp47b9dd52010-11-24 18:12:06 +00004813
cristy3ed852e2009-09-05 21:47:34 +00004814 if (skip_to_iend)
4815 {
4816 if (memcmp(type,mng_IEND,4) == 0)
4817 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004818
cristy3ed852e2009-09-05 21:47:34 +00004819 if (length)
4820 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004821
cristy3ed852e2009-09-05 21:47:34 +00004822 if (logging != MagickFalse)
4823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4824 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004825
cristy3ed852e2009-09-05 21:47:34 +00004826 continue;
4827 }
glennrp0fe50b42010-11-16 03:52:51 +00004828
cristy3ed852e2009-09-05 21:47:34 +00004829 if (memcmp(type,mng_MHDR,4) == 0)
4830 {
cristybb503372010-05-27 20:51:26 +00004831 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004832 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004833
cristybb503372010-05-27 20:51:26 +00004834 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004835 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004836
cristy3ed852e2009-09-05 21:47:34 +00004837 if (logging != MagickFalse)
4838 {
4839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004840 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004842 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004843 }
glennrp0fe50b42010-11-16 03:52:51 +00004844
cristy3ed852e2009-09-05 21:47:34 +00004845 p+=8;
cristy8182b072010-05-30 20:10:53 +00004846 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004847
cristy3ed852e2009-09-05 21:47:34 +00004848 if (mng_info->ticks_per_second == 0)
4849 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004850
cristy3ed852e2009-09-05 21:47:34 +00004851 else
4852 default_frame_delay=1UL*image->ticks_per_second/
4853 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004854
cristy3ed852e2009-09-05 21:47:34 +00004855 frame_delay=default_frame_delay;
4856 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004857
cristy3ed852e2009-09-05 21:47:34 +00004858 if (length > 16)
4859 {
4860 p+=16;
cristy8182b072010-05-30 20:10:53 +00004861 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004862 }
glennrp0fe50b42010-11-16 03:52:51 +00004863
cristy3ed852e2009-09-05 21:47:34 +00004864 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004865
cristy3ed852e2009-09-05 21:47:34 +00004866 if ((simplicity != 0) && ((simplicity | 11) == 11))
4867 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004868
cristy3ed852e2009-09-05 21:47:34 +00004869 if ((simplicity != 0) && ((simplicity | 9) == 9))
4870 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004871
cristy3ed852e2009-09-05 21:47:34 +00004872#if defined(MNG_INSERT_LAYERS)
4873 if (mng_type != 3)
4874 insert_layers=MagickTrue;
4875#endif
cristy4c08aed2011-07-01 19:47:50 +00004876 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004877 {
glennrp47b9dd52010-11-24 18:12:06 +00004878 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004879 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00004880
cristy3ed852e2009-09-05 21:47:34 +00004881 if (GetNextImageInList(image) == (Image *) NULL)
4882 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004883
cristy3ed852e2009-09-05 21:47:34 +00004884 image=SyncNextImageInList(image);
4885 mng_info->image=image;
4886 }
4887
4888 if ((mng_info->mng_width > 65535L) ||
4889 (mng_info->mng_height > 65535L))
4890 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004891
cristy3b6fd2e2011-05-20 12:53:50 +00004892 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004893 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004894 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004895
cristy3ed852e2009-09-05 21:47:34 +00004896 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004897 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004898 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004899 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004900 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004901
cristy3ed852e2009-09-05 21:47:34 +00004902 for (i=0; i < MNG_MAX_OBJECTS; i++)
4903 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004904
cristy3ed852e2009-09-05 21:47:34 +00004905 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4906 continue;
4907 }
4908
4909 if (memcmp(type,mng_TERM,4) == 0)
4910 {
4911 int
4912 repeat=0;
4913
4914
4915 if (length)
4916 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004917
cristy3ed852e2009-09-05 21:47:34 +00004918 if (repeat == 3)
4919 {
cristy8182b072010-05-30 20:10:53 +00004920 final_delay=(png_uint_32) mng_get_long(&p[2]);
4921 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004922
cristy3ed852e2009-09-05 21:47:34 +00004923 if (mng_iterations == PNG_UINT_31_MAX)
4924 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004925
cristy3ed852e2009-09-05 21:47:34 +00004926 image->iterations=mng_iterations;
4927 term_chunk_found=MagickTrue;
4928 }
glennrp0fe50b42010-11-16 03:52:51 +00004929
cristy3ed852e2009-09-05 21:47:34 +00004930 if (logging != MagickFalse)
4931 {
4932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4933 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004934
cristy3ed852e2009-09-05 21:47:34 +00004935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004936 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004937
cristy3ed852e2009-09-05 21:47:34 +00004938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004939 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004940 }
glennrp0fe50b42010-11-16 03:52:51 +00004941
cristy3ed852e2009-09-05 21:47:34 +00004942 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4943 continue;
4944 }
4945 if (memcmp(type,mng_DEFI,4) == 0)
4946 {
4947 if (mng_type == 3)
4948 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4949 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4950 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004951
cristy3ed852e2009-09-05 21:47:34 +00004952 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004953
cristy3ed852e2009-09-05 21:47:34 +00004954 if (mng_type == 2 && object_id != 0)
4955 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4956 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4957 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004958
cristy3ed852e2009-09-05 21:47:34 +00004959 if (object_id > MNG_MAX_OBJECTS)
4960 {
4961 /*
4962 Instead ofsuing a warning we should allocate a larger
4963 MngInfo structure and continue.
4964 */
4965 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4966 CoderError,"object id too large","`%s'",image->filename);
4967 object_id=MNG_MAX_OBJECTS;
4968 }
glennrp0fe50b42010-11-16 03:52:51 +00004969
cristy3ed852e2009-09-05 21:47:34 +00004970 if (mng_info->exists[object_id])
4971 if (mng_info->frozen[object_id])
4972 {
4973 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4974 (void) ThrowMagickException(&image->exception,
4975 GetMagickModule(),CoderError,
4976 "DEFI cannot redefine a frozen MNG object","`%s'",
4977 image->filename);
4978 continue;
4979 }
glennrp0fe50b42010-11-16 03:52:51 +00004980
cristy3ed852e2009-09-05 21:47:34 +00004981 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004982
cristy3ed852e2009-09-05 21:47:34 +00004983 if (length > 2)
4984 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00004985
cristy3ed852e2009-09-05 21:47:34 +00004986 /*
4987 Extract object offset info.
4988 */
4989 if (length > 11)
4990 {
glennrp0fe50b42010-11-16 03:52:51 +00004991 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4992 (p[5] << 16) | (p[6] << 8) | p[7]);
4993
4994 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4995 (p[9] << 16) | (p[10] << 8) | p[11]);
4996
cristy3ed852e2009-09-05 21:47:34 +00004997 if (logging != MagickFalse)
4998 {
4999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005000 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005001 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005002
cristy3ed852e2009-09-05 21:47:34 +00005003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005004 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005005 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005006 }
5007 }
glennrp0fe50b42010-11-16 03:52:51 +00005008
cristy3ed852e2009-09-05 21:47:34 +00005009 /*
5010 Extract object clipping info.
5011 */
5012 if (length > 27)
5013 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5014 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005015
cristy3ed852e2009-09-05 21:47:34 +00005016 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5017 continue;
5018 }
5019 if (memcmp(type,mng_bKGD,4) == 0)
5020 {
5021 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005022
cristy3ed852e2009-09-05 21:47:34 +00005023 if (length > 5)
5024 {
5025 mng_info->mng_global_bkgd.red=
5026 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005027
cristy3ed852e2009-09-05 21:47:34 +00005028 mng_info->mng_global_bkgd.green=
5029 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005030
cristy3ed852e2009-09-05 21:47:34 +00005031 mng_info->mng_global_bkgd.blue=
5032 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005033
cristy3ed852e2009-09-05 21:47:34 +00005034 mng_info->have_global_bkgd=MagickTrue;
5035 }
glennrp0fe50b42010-11-16 03:52:51 +00005036
cristy3ed852e2009-09-05 21:47:34 +00005037 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5038 continue;
5039 }
5040 if (memcmp(type,mng_BACK,4) == 0)
5041 {
5042#if defined(MNG_INSERT_LAYERS)
5043 if (length > 6)
5044 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005045
cristy3ed852e2009-09-05 21:47:34 +00005046 else
5047 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005048
cristy3ed852e2009-09-05 21:47:34 +00005049 if (mandatory_back && length > 5)
5050 {
5051 mng_background_color.red=
5052 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005053
cristy3ed852e2009-09-05 21:47:34 +00005054 mng_background_color.green=
5055 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005056
cristy3ed852e2009-09-05 21:47:34 +00005057 mng_background_color.blue=
5058 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005059
cristy4c08aed2011-07-01 19:47:50 +00005060 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005061 }
glennrp0fe50b42010-11-16 03:52:51 +00005062
cristy3ed852e2009-09-05 21:47:34 +00005063#ifdef MNG_OBJECT_BUFFERS
5064 if (length > 8)
5065 mng_background_object=(p[7] << 8) | p[8];
5066#endif
5067#endif
5068 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5069 continue;
5070 }
glennrp47b9dd52010-11-24 18:12:06 +00005071
cristy3ed852e2009-09-05 21:47:34 +00005072 if (memcmp(type,mng_PLTE,4) == 0)
5073 {
glennrp47b9dd52010-11-24 18:12:06 +00005074 /* Read global PLTE. */
5075
cristy3ed852e2009-09-05 21:47:34 +00005076 if (length && (length < 769))
5077 {
5078 if (mng_info->global_plte == (png_colorp) NULL)
5079 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5080 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005081
cristybb503372010-05-27 20:51:26 +00005082 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005083 {
5084 mng_info->global_plte[i].red=p[3*i];
5085 mng_info->global_plte[i].green=p[3*i+1];
5086 mng_info->global_plte[i].blue=p[3*i+2];
5087 }
glennrp0fe50b42010-11-16 03:52:51 +00005088
cristy35ef8242010-06-03 16:24:13 +00005089 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005090 }
5091#ifdef MNG_LOOSE
5092 for ( ; i < 256; i++)
5093 {
5094 mng_info->global_plte[i].red=i;
5095 mng_info->global_plte[i].green=i;
5096 mng_info->global_plte[i].blue=i;
5097 }
glennrp0fe50b42010-11-16 03:52:51 +00005098
cristy3ed852e2009-09-05 21:47:34 +00005099 if (length)
5100 mng_info->global_plte_length=256;
5101#endif
5102 else
5103 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005104
cristy3ed852e2009-09-05 21:47:34 +00005105 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5106 continue;
5107 }
glennrp47b9dd52010-11-24 18:12:06 +00005108
cristy3ed852e2009-09-05 21:47:34 +00005109 if (memcmp(type,mng_tRNS,4) == 0)
5110 {
5111 /* read global tRNS */
5112
5113 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005114 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005115 mng_info->global_trns[i]=p[i];
5116
5117#ifdef MNG_LOOSE
5118 for ( ; i < 256; i++)
5119 mng_info->global_trns[i]=255;
5120#endif
cristy12560f32010-06-03 16:51:08 +00005121 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005122 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5123 continue;
5124 }
5125 if (memcmp(type,mng_gAMA,4) == 0)
5126 {
5127 if (length == 4)
5128 {
cristybb503372010-05-27 20:51:26 +00005129 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005130 igamma;
5131
cristy8182b072010-05-30 20:10:53 +00005132 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005133 mng_info->global_gamma=((float) igamma)*0.00001;
5134 mng_info->have_global_gama=MagickTrue;
5135 }
glennrp0fe50b42010-11-16 03:52:51 +00005136
cristy3ed852e2009-09-05 21:47:34 +00005137 else
5138 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005139
cristy3ed852e2009-09-05 21:47:34 +00005140 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5141 continue;
5142 }
5143
5144 if (memcmp(type,mng_cHRM,4) == 0)
5145 {
glennrp47b9dd52010-11-24 18:12:06 +00005146 /* Read global cHRM */
5147
cristy3ed852e2009-09-05 21:47:34 +00005148 if (length == 32)
5149 {
cristy8182b072010-05-30 20:10:53 +00005150 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5151 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5152 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005153 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005154 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005155 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005156 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005157 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005158 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005159 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005160 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005161 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005162 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005163 mng_info->have_global_chrm=MagickTrue;
5164 }
5165 else
5166 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005167
cristy3ed852e2009-09-05 21:47:34 +00005168 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5169 continue;
5170 }
glennrp47b9dd52010-11-24 18:12:06 +00005171
cristy3ed852e2009-09-05 21:47:34 +00005172 if (memcmp(type,mng_sRGB,4) == 0)
5173 {
5174 /*
5175 Read global sRGB.
5176 */
5177 if (length)
5178 {
glennrpe610a072010-08-05 17:08:46 +00005179 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005180 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005181 mng_info->have_global_srgb=MagickTrue;
5182 }
5183 else
5184 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005185
cristy3ed852e2009-09-05 21:47:34 +00005186 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5187 continue;
5188 }
glennrp47b9dd52010-11-24 18:12:06 +00005189
cristy3ed852e2009-09-05 21:47:34 +00005190 if (memcmp(type,mng_iCCP,4) == 0)
5191 {
glennrpfd05d622011-02-25 04:10:33 +00005192 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005193
5194 /*
5195 Read global iCCP.
5196 */
5197 if (length)
5198 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005199
cristy3ed852e2009-09-05 21:47:34 +00005200 continue;
5201 }
glennrp47b9dd52010-11-24 18:12:06 +00005202
cristy3ed852e2009-09-05 21:47:34 +00005203 if (memcmp(type,mng_FRAM,4) == 0)
5204 {
5205 if (mng_type == 3)
5206 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5207 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5208 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005209
cristy3ed852e2009-09-05 21:47:34 +00005210 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5211 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005212
cristy3ed852e2009-09-05 21:47:34 +00005213 frame_delay=default_frame_delay;
5214 frame_timeout=default_frame_timeout;
5215 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005216
cristy3ed852e2009-09-05 21:47:34 +00005217 if (length)
5218 if (p[0])
5219 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005220
cristy3ed852e2009-09-05 21:47:34 +00005221 if (logging != MagickFalse)
5222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5223 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005224
cristy3ed852e2009-09-05 21:47:34 +00005225 if (length > 6)
5226 {
glennrp47b9dd52010-11-24 18:12:06 +00005227 /* Note the delay and frame clipping boundaries. */
5228
cristy3ed852e2009-09-05 21:47:34 +00005229 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005230
cristybb503372010-05-27 20:51:26 +00005231 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005232 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005233
cristy3ed852e2009-09-05 21:47:34 +00005234 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005235
cristybb503372010-05-27 20:51:26 +00005236 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005237 {
5238 int
5239 change_delay,
5240 change_timeout,
5241 change_clipping;
5242
5243 change_delay=(*p++);
5244 change_timeout=(*p++);
5245 change_clipping=(*p++);
5246 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005247
cristy3ed852e2009-09-05 21:47:34 +00005248 if (change_delay)
5249 {
cristy8182b072010-05-30 20:10:53 +00005250 frame_delay=1UL*image->ticks_per_second*
5251 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005252
cristy8182b072010-05-30 20:10:53 +00005253 if (mng_info->ticks_per_second != 0)
5254 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005255
glennrpbb010dd2010-06-01 13:07:15 +00005256 else
5257 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005258
cristy3ed852e2009-09-05 21:47:34 +00005259 if (change_delay == 2)
5260 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005261
cristy3ed852e2009-09-05 21:47:34 +00005262 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005263
cristy3ed852e2009-09-05 21:47:34 +00005264 if (logging != MagickFalse)
5265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005266 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005267 }
glennrp47b9dd52010-11-24 18:12:06 +00005268
cristy3ed852e2009-09-05 21:47:34 +00005269 if (change_timeout)
5270 {
glennrpbb010dd2010-06-01 13:07:15 +00005271 frame_timeout=1UL*image->ticks_per_second*
5272 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005273
glennrpbb010dd2010-06-01 13:07:15 +00005274 if (mng_info->ticks_per_second != 0)
5275 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005276
glennrpbb010dd2010-06-01 13:07:15 +00005277 else
5278 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005279
cristy3ed852e2009-09-05 21:47:34 +00005280 if (change_delay == 2)
5281 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005282
cristy3ed852e2009-09-05 21:47:34 +00005283 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005284
cristy3ed852e2009-09-05 21:47:34 +00005285 if (logging != MagickFalse)
5286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005287 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005288 }
glennrp47b9dd52010-11-24 18:12:06 +00005289
cristy3ed852e2009-09-05 21:47:34 +00005290 if (change_clipping)
5291 {
5292 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5293 p+=17;
5294 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005295
cristy3ed852e2009-09-05 21:47:34 +00005296 if (logging != MagickFalse)
5297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005298 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005299 (double) fb.left,(double) fb.right,(double) fb.top,
5300 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005301
cristy3ed852e2009-09-05 21:47:34 +00005302 if (change_clipping == 2)
5303 default_fb=fb;
5304 }
5305 }
5306 }
5307 mng_info->clip=fb;
5308 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005309
cristybb503372010-05-27 20:51:26 +00005310 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005311 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005312
cristybb503372010-05-27 20:51:26 +00005313 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005314 -mng_info->clip.top);
5315 /*
5316 Insert a background layer behind the frame if framing_mode is 4.
5317 */
5318#if defined(MNG_INSERT_LAYERS)
5319 if (logging != MagickFalse)
5320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005321 " subframe_width=%.20g, subframe_height=%.20g",(double)
5322 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005323
cristy3ed852e2009-09-05 21:47:34 +00005324 if (insert_layers && (mng_info->framing_mode == 4) &&
5325 (subframe_width) && (subframe_height))
5326 {
glennrp47b9dd52010-11-24 18:12:06 +00005327 /* Allocate next image structure. */
cristy4c08aed2011-07-01 19:47:50 +00005328 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005329 {
5330 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005331
cristy3ed852e2009-09-05 21:47:34 +00005332 if (GetNextImageInList(image) == (Image *) NULL)
5333 {
5334 image=DestroyImageList(image);
5335 MngInfoFreeStruct(mng_info,&have_mng_structure);
5336 return((Image *) NULL);
5337 }
glennrp47b9dd52010-11-24 18:12:06 +00005338
cristy3ed852e2009-09-05 21:47:34 +00005339 image=SyncNextImageInList(image);
5340 }
glennrp0fe50b42010-11-16 03:52:51 +00005341
cristy3ed852e2009-09-05 21:47:34 +00005342 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005343
cristy3ed852e2009-09-05 21:47:34 +00005344 if (term_chunk_found)
5345 {
5346 image->start_loop=MagickTrue;
5347 image->iterations=mng_iterations;
5348 term_chunk_found=MagickFalse;
5349 }
glennrp0fe50b42010-11-16 03:52:51 +00005350
cristy3ed852e2009-09-05 21:47:34 +00005351 else
5352 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005353
cristy3ed852e2009-09-05 21:47:34 +00005354 image->columns=subframe_width;
5355 image->rows=subframe_height;
5356 image->page.width=subframe_width;
5357 image->page.height=subframe_height;
5358 image->page.x=mng_info->clip.left;
5359 image->page.y=mng_info->clip.top;
5360 image->background_color=mng_background_color;
5361 image->matte=MagickFalse;
5362 image->delay=0;
5363 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005364
cristy3ed852e2009-09-05 21:47:34 +00005365 if (logging != MagickFalse)
5366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005367 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005368 (double) mng_info->clip.left,(double) mng_info->clip.right,
5369 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005370 }
5371#endif
5372 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5373 continue;
5374 }
5375 if (memcmp(type,mng_CLIP,4) == 0)
5376 {
5377 unsigned int
5378 first_object,
5379 last_object;
5380
5381 /*
5382 Read CLIP.
5383 */
5384 first_object=(p[0] << 8) | p[1];
5385 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005386
cristy3ed852e2009-09-05 21:47:34 +00005387 for (i=(int) first_object; i <= (int) last_object; i++)
5388 {
5389 if (mng_info->exists[i] && !mng_info->frozen[i])
5390 {
5391 MngBox
5392 box;
5393
5394 box=mng_info->object_clip[i];
5395 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5396 }
5397 }
glennrp47b9dd52010-11-24 18:12:06 +00005398
cristy3ed852e2009-09-05 21:47:34 +00005399 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5400 continue;
5401 }
5402 if (memcmp(type,mng_SAVE,4) == 0)
5403 {
5404 for (i=1; i < MNG_MAX_OBJECTS; i++)
5405 if (mng_info->exists[i])
5406 {
5407 mng_info->frozen[i]=MagickTrue;
5408#ifdef MNG_OBJECT_BUFFERS
5409 if (mng_info->ob[i] != (MngBuffer *) NULL)
5410 mng_info->ob[i]->frozen=MagickTrue;
5411#endif
5412 }
glennrp0fe50b42010-11-16 03:52:51 +00005413
cristy3ed852e2009-09-05 21:47:34 +00005414 if (length)
5415 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005416
cristy3ed852e2009-09-05 21:47:34 +00005417 continue;
5418 }
5419
5420 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5421 {
glennrp47b9dd52010-11-24 18:12:06 +00005422 /* Read DISC or SEEK. */
5423
cristy3ed852e2009-09-05 21:47:34 +00005424 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5425 {
5426 for (i=1; i < MNG_MAX_OBJECTS; i++)
5427 MngInfoDiscardObject(mng_info,i);
5428 }
glennrp0fe50b42010-11-16 03:52:51 +00005429
cristy3ed852e2009-09-05 21:47:34 +00005430 else
5431 {
cristybb503372010-05-27 20:51:26 +00005432 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005433 j;
5434
cristybb503372010-05-27 20:51:26 +00005435 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005436 {
5437 i=p[j] << 8 | p[j+1];
5438 MngInfoDiscardObject(mng_info,i);
5439 }
5440 }
glennrp0fe50b42010-11-16 03:52:51 +00005441
cristy3ed852e2009-09-05 21:47:34 +00005442 if (length)
5443 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005444
cristy3ed852e2009-09-05 21:47:34 +00005445 continue;
5446 }
glennrp47b9dd52010-11-24 18:12:06 +00005447
cristy3ed852e2009-09-05 21:47:34 +00005448 if (memcmp(type,mng_MOVE,4) == 0)
5449 {
cristybb503372010-05-27 20:51:26 +00005450 size_t
cristy3ed852e2009-09-05 21:47:34 +00005451 first_object,
5452 last_object;
5453
glennrp47b9dd52010-11-24 18:12:06 +00005454 /* read MOVE */
5455
cristy3ed852e2009-09-05 21:47:34 +00005456 first_object=(p[0] << 8) | p[1];
5457 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005458 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005459 {
5460 if (mng_info->exists[i] && !mng_info->frozen[i])
5461 {
5462 MngPair
5463 new_pair;
5464
5465 MngPair
5466 old_pair;
5467
5468 old_pair.a=mng_info->x_off[i];
5469 old_pair.b=mng_info->y_off[i];
5470 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5471 mng_info->x_off[i]=new_pair.a;
5472 mng_info->y_off[i]=new_pair.b;
5473 }
5474 }
glennrp47b9dd52010-11-24 18:12:06 +00005475
cristy3ed852e2009-09-05 21:47:34 +00005476 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5477 continue;
5478 }
5479
5480 if (memcmp(type,mng_LOOP,4) == 0)
5481 {
cristybb503372010-05-27 20:51:26 +00005482 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005483 loop_level=chunk[0];
5484 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005485
5486 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005487 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005488
cristy3ed852e2009-09-05 21:47:34 +00005489 if (logging != MagickFalse)
5490 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005491 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5492 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005493
cristy3ed852e2009-09-05 21:47:34 +00005494 if (loop_iters == 0)
5495 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005496
cristy3ed852e2009-09-05 21:47:34 +00005497 else
5498 {
5499 mng_info->loop_jump[loop_level]=TellBlob(image);
5500 mng_info->loop_count[loop_level]=loop_iters;
5501 }
glennrp0fe50b42010-11-16 03:52:51 +00005502
cristy3ed852e2009-09-05 21:47:34 +00005503 mng_info->loop_iteration[loop_level]=0;
5504 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5505 continue;
5506 }
glennrp47b9dd52010-11-24 18:12:06 +00005507
cristy3ed852e2009-09-05 21:47:34 +00005508 if (memcmp(type,mng_ENDL,4) == 0)
5509 {
5510 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005511
cristy3ed852e2009-09-05 21:47:34 +00005512 if (skipping_loop > 0)
5513 {
5514 if (skipping_loop == loop_level)
5515 {
5516 /*
5517 Found end of zero-iteration loop.
5518 */
5519 skipping_loop=(-1);
5520 mng_info->loop_active[loop_level]=0;
5521 }
5522 }
glennrp47b9dd52010-11-24 18:12:06 +00005523
cristy3ed852e2009-09-05 21:47:34 +00005524 else
5525 {
5526 if (mng_info->loop_active[loop_level] == 1)
5527 {
5528 mng_info->loop_count[loop_level]--;
5529 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005530
cristy3ed852e2009-09-05 21:47:34 +00005531 if (logging != MagickFalse)
5532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005533 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005534 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005535 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005536
cristy3ed852e2009-09-05 21:47:34 +00005537 if (mng_info->loop_count[loop_level] != 0)
5538 {
5539 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5540 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005541
cristy3ed852e2009-09-05 21:47:34 +00005542 if (offset < 0)
5543 ThrowReaderException(CorruptImageError,
5544 "ImproperImageHeader");
5545 }
glennrp47b9dd52010-11-24 18:12:06 +00005546
cristy3ed852e2009-09-05 21:47:34 +00005547 else
5548 {
5549 short
5550 last_level;
5551
5552 /*
5553 Finished loop.
5554 */
5555 mng_info->loop_active[loop_level]=0;
5556 last_level=(-1);
5557 for (i=0; i < loop_level; i++)
5558 if (mng_info->loop_active[i] == 1)
5559 last_level=(short) i;
5560 loop_level=last_level;
5561 }
5562 }
5563 }
glennrp47b9dd52010-11-24 18:12:06 +00005564
cristy3ed852e2009-09-05 21:47:34 +00005565 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5566 continue;
5567 }
glennrp47b9dd52010-11-24 18:12:06 +00005568
cristy3ed852e2009-09-05 21:47:34 +00005569 if (memcmp(type,mng_CLON,4) == 0)
5570 {
5571 if (mng_info->clon_warning == 0)
5572 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5573 CoderError,"CLON is not implemented yet","`%s'",
5574 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005575
cristy3ed852e2009-09-05 21:47:34 +00005576 mng_info->clon_warning++;
5577 }
glennrp47b9dd52010-11-24 18:12:06 +00005578
cristy3ed852e2009-09-05 21:47:34 +00005579 if (memcmp(type,mng_MAGN,4) == 0)
5580 {
5581 png_uint_16
5582 magn_first,
5583 magn_last,
5584 magn_mb,
5585 magn_ml,
5586 magn_mr,
5587 magn_mt,
5588 magn_mx,
5589 magn_my,
5590 magn_methx,
5591 magn_methy;
5592
5593 if (length > 1)
5594 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005595
cristy3ed852e2009-09-05 21:47:34 +00005596 else
5597 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005598
cristy3ed852e2009-09-05 21:47:34 +00005599 if (length > 3)
5600 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005601
cristy3ed852e2009-09-05 21:47:34 +00005602 else
5603 magn_last=magn_first;
5604#ifndef MNG_OBJECT_BUFFERS
5605 if (magn_first || magn_last)
5606 if (mng_info->magn_warning == 0)
5607 {
5608 (void) ThrowMagickException(&image->exception,
5609 GetMagickModule(),CoderError,
5610 "MAGN is not implemented yet for nonzero objects",
5611 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005612
cristy3ed852e2009-09-05 21:47:34 +00005613 mng_info->magn_warning++;
5614 }
5615#endif
5616 if (length > 4)
5617 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005618
cristy3ed852e2009-09-05 21:47:34 +00005619 else
5620 magn_methx=0;
5621
5622 if (length > 6)
5623 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005624
cristy3ed852e2009-09-05 21:47:34 +00005625 else
5626 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005627
cristy3ed852e2009-09-05 21:47:34 +00005628 if (magn_mx == 0)
5629 magn_mx=1;
5630
5631 if (length > 8)
5632 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005633
cristy3ed852e2009-09-05 21:47:34 +00005634 else
5635 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005636
cristy3ed852e2009-09-05 21:47:34 +00005637 if (magn_my == 0)
5638 magn_my=1;
5639
5640 if (length > 10)
5641 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005642
cristy3ed852e2009-09-05 21:47:34 +00005643 else
5644 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005645
cristy3ed852e2009-09-05 21:47:34 +00005646 if (magn_ml == 0)
5647 magn_ml=1;
5648
5649 if (length > 12)
5650 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005651
cristy3ed852e2009-09-05 21:47:34 +00005652 else
5653 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005654
cristy3ed852e2009-09-05 21:47:34 +00005655 if (magn_mr == 0)
5656 magn_mr=1;
5657
5658 if (length > 14)
5659 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005660
cristy3ed852e2009-09-05 21:47:34 +00005661 else
5662 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005663
cristy3ed852e2009-09-05 21:47:34 +00005664 if (magn_mt == 0)
5665 magn_mt=1;
5666
5667 if (length > 16)
5668 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005669
cristy3ed852e2009-09-05 21:47:34 +00005670 else
5671 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005672
cristy3ed852e2009-09-05 21:47:34 +00005673 if (magn_mb == 0)
5674 magn_mb=1;
5675
5676 if (length > 17)
5677 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005678
cristy3ed852e2009-09-05 21:47:34 +00005679 else
5680 magn_methy=magn_methx;
5681
glennrp47b9dd52010-11-24 18:12:06 +00005682
cristy3ed852e2009-09-05 21:47:34 +00005683 if (magn_methx > 5 || magn_methy > 5)
5684 if (mng_info->magn_warning == 0)
5685 {
5686 (void) ThrowMagickException(&image->exception,
5687 GetMagickModule(),CoderError,
5688 "Unknown MAGN method in MNG datastream","`%s'",
5689 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005690
cristy3ed852e2009-09-05 21:47:34 +00005691 mng_info->magn_warning++;
5692 }
5693#ifdef MNG_OBJECT_BUFFERS
5694 /* Magnify existing objects in the range magn_first to magn_last */
5695#endif
5696 if (magn_first == 0 || magn_last == 0)
5697 {
5698 /* Save the magnification factors for object 0 */
5699 mng_info->magn_mb=magn_mb;
5700 mng_info->magn_ml=magn_ml;
5701 mng_info->magn_mr=magn_mr;
5702 mng_info->magn_mt=magn_mt;
5703 mng_info->magn_mx=magn_mx;
5704 mng_info->magn_my=magn_my;
5705 mng_info->magn_methx=magn_methx;
5706 mng_info->magn_methy=magn_methy;
5707 }
5708 }
glennrp47b9dd52010-11-24 18:12:06 +00005709
cristy3ed852e2009-09-05 21:47:34 +00005710 if (memcmp(type,mng_PAST,4) == 0)
5711 {
5712 if (mng_info->past_warning == 0)
5713 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5714 CoderError,"PAST is not implemented yet","`%s'",
5715 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005716
cristy3ed852e2009-09-05 21:47:34 +00005717 mng_info->past_warning++;
5718 }
glennrp47b9dd52010-11-24 18:12:06 +00005719
cristy3ed852e2009-09-05 21:47:34 +00005720 if (memcmp(type,mng_SHOW,4) == 0)
5721 {
5722 if (mng_info->show_warning == 0)
5723 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5724 CoderError,"SHOW is not implemented yet","`%s'",
5725 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005726
cristy3ed852e2009-09-05 21:47:34 +00005727 mng_info->show_warning++;
5728 }
glennrp47b9dd52010-11-24 18:12:06 +00005729
cristy3ed852e2009-09-05 21:47:34 +00005730 if (memcmp(type,mng_sBIT,4) == 0)
5731 {
5732 if (length < 4)
5733 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005734
cristy3ed852e2009-09-05 21:47:34 +00005735 else
5736 {
5737 mng_info->global_sbit.gray=p[0];
5738 mng_info->global_sbit.red=p[0];
5739 mng_info->global_sbit.green=p[1];
5740 mng_info->global_sbit.blue=p[2];
5741 mng_info->global_sbit.alpha=p[3];
5742 mng_info->have_global_sbit=MagickTrue;
5743 }
5744 }
5745 if (memcmp(type,mng_pHYs,4) == 0)
5746 {
5747 if (length > 8)
5748 {
5749 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005750 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005751 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005752 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005753 mng_info->global_phys_unit_type=p[8];
5754 mng_info->have_global_phys=MagickTrue;
5755 }
glennrp47b9dd52010-11-24 18:12:06 +00005756
cristy3ed852e2009-09-05 21:47:34 +00005757 else
5758 mng_info->have_global_phys=MagickFalse;
5759 }
5760 if (memcmp(type,mng_pHYg,4) == 0)
5761 {
5762 if (mng_info->phyg_warning == 0)
5763 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5764 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005765
cristy3ed852e2009-09-05 21:47:34 +00005766 mng_info->phyg_warning++;
5767 }
5768 if (memcmp(type,mng_BASI,4) == 0)
5769 {
5770 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005771
cristy3ed852e2009-09-05 21:47:34 +00005772 if (mng_info->basi_warning == 0)
5773 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5774 CoderError,"BASI is not implemented yet","`%s'",
5775 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005776
cristy3ed852e2009-09-05 21:47:34 +00005777 mng_info->basi_warning++;
5778#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005779 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005780 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005781 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005782 (p[6] << 8) | p[7]);
5783 basi_color_type=p[8];
5784 basi_compression_method=p[9];
5785 basi_filter_type=p[10];
5786 basi_interlace_method=p[11];
5787 if (length > 11)
5788 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005789
cristy3ed852e2009-09-05 21:47:34 +00005790 else
5791 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005792
cristy3ed852e2009-09-05 21:47:34 +00005793 if (length > 13)
5794 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005795
cristy3ed852e2009-09-05 21:47:34 +00005796 else
5797 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005798
cristy3ed852e2009-09-05 21:47:34 +00005799 if (length > 15)
5800 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005801
cristy3ed852e2009-09-05 21:47:34 +00005802 else
5803 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005804
cristy3ed852e2009-09-05 21:47:34 +00005805 if (length > 17)
5806 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005807
cristy3ed852e2009-09-05 21:47:34 +00005808 else
5809 {
5810 if (basi_sample_depth == 16)
5811 basi_alpha=65535L;
5812 else
5813 basi_alpha=255;
5814 }
glennrp47b9dd52010-11-24 18:12:06 +00005815
cristy3ed852e2009-09-05 21:47:34 +00005816 if (length > 19)
5817 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005818
cristy3ed852e2009-09-05 21:47:34 +00005819 else
5820 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005821
cristy3ed852e2009-09-05 21:47:34 +00005822#endif
5823 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5824 continue;
5825 }
glennrp47b9dd52010-11-24 18:12:06 +00005826
cristy3ed852e2009-09-05 21:47:34 +00005827 if (memcmp(type,mng_IHDR,4)
5828#if defined(JNG_SUPPORTED)
5829 && memcmp(type,mng_JHDR,4)
5830#endif
5831 )
5832 {
5833 /* Not an IHDR or JHDR chunk */
5834 if (length)
5835 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005836
cristy3ed852e2009-09-05 21:47:34 +00005837 continue;
5838 }
5839/* Process IHDR */
5840 if (logging != MagickFalse)
5841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5842 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005843
cristy3ed852e2009-09-05 21:47:34 +00005844 mng_info->exists[object_id]=MagickTrue;
5845 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005846
cristy3ed852e2009-09-05 21:47:34 +00005847 if (mng_info->invisible[object_id])
5848 {
5849 if (logging != MagickFalse)
5850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5851 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005852
cristy3ed852e2009-09-05 21:47:34 +00005853 skip_to_iend=MagickTrue;
5854 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5855 continue;
5856 }
5857#if defined(MNG_INSERT_LAYERS)
5858 if (length < 8)
5859 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005860
cristy8182b072010-05-30 20:10:53 +00005861 image_width=(size_t) mng_get_long(p);
5862 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005863#endif
5864 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5865
5866 /*
5867 Insert a transparent background layer behind the entire animation
5868 if it is not full screen.
5869 */
5870#if defined(MNG_INSERT_LAYERS)
5871 if (insert_layers && mng_type && first_mng_object)
5872 {
5873 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5874 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005875 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005876 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005877 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005878 {
cristy4c08aed2011-07-01 19:47:50 +00005879 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005880 {
5881 /*
5882 Allocate next image structure.
5883 */
5884 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005885
cristy3ed852e2009-09-05 21:47:34 +00005886 if (GetNextImageInList(image) == (Image *) NULL)
5887 {
5888 image=DestroyImageList(image);
5889 MngInfoFreeStruct(mng_info,&have_mng_structure);
5890 return((Image *) NULL);
5891 }
glennrp47b9dd52010-11-24 18:12:06 +00005892
cristy3ed852e2009-09-05 21:47:34 +00005893 image=SyncNextImageInList(image);
5894 }
5895 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005896
cristy3ed852e2009-09-05 21:47:34 +00005897 if (term_chunk_found)
5898 {
5899 image->start_loop=MagickTrue;
5900 image->iterations=mng_iterations;
5901 term_chunk_found=MagickFalse;
5902 }
glennrp47b9dd52010-11-24 18:12:06 +00005903
cristy3ed852e2009-09-05 21:47:34 +00005904 else
5905 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005906
5907 /* Make a background rectangle. */
5908
cristy3ed852e2009-09-05 21:47:34 +00005909 image->delay=0;
5910 image->columns=mng_info->mng_width;
5911 image->rows=mng_info->mng_height;
5912 image->page.width=mng_info->mng_width;
5913 image->page.height=mng_info->mng_height;
5914 image->page.x=0;
5915 image->page.y=0;
5916 image->background_color=mng_background_color;
5917 (void) SetImageBackgroundColor(image);
5918 if (logging != MagickFalse)
5919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005920 " Inserted transparent background layer, W=%.20g, H=%.20g",
5921 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005922 }
5923 }
5924 /*
5925 Insert a background layer behind the upcoming image if
5926 framing_mode is 3, and we haven't already inserted one.
5927 */
5928 if (insert_layers && (mng_info->framing_mode == 3) &&
5929 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5930 (simplicity & 0x08)))
5931 {
cristy4c08aed2011-07-01 19:47:50 +00005932 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005933 {
5934 /*
5935 Allocate next image structure.
5936 */
5937 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005938
cristy3ed852e2009-09-05 21:47:34 +00005939 if (GetNextImageInList(image) == (Image *) NULL)
5940 {
5941 image=DestroyImageList(image);
5942 MngInfoFreeStruct(mng_info,&have_mng_structure);
5943 return((Image *) NULL);
5944 }
glennrp47b9dd52010-11-24 18:12:06 +00005945
cristy3ed852e2009-09-05 21:47:34 +00005946 image=SyncNextImageInList(image);
5947 }
glennrp0fe50b42010-11-16 03:52:51 +00005948
cristy3ed852e2009-09-05 21:47:34 +00005949 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005950
cristy3ed852e2009-09-05 21:47:34 +00005951 if (term_chunk_found)
5952 {
5953 image->start_loop=MagickTrue;
5954 image->iterations=mng_iterations;
5955 term_chunk_found=MagickFalse;
5956 }
glennrp0fe50b42010-11-16 03:52:51 +00005957
cristy3ed852e2009-09-05 21:47:34 +00005958 else
5959 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005960
cristy3ed852e2009-09-05 21:47:34 +00005961 image->delay=0;
5962 image->columns=subframe_width;
5963 image->rows=subframe_height;
5964 image->page.width=subframe_width;
5965 image->page.height=subframe_height;
5966 image->page.x=mng_info->clip.left;
5967 image->page.y=mng_info->clip.top;
5968 image->background_color=mng_background_color;
5969 image->matte=MagickFalse;
5970 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005971
cristy3ed852e2009-09-05 21:47:34 +00005972 if (logging != MagickFalse)
5973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005974 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005975 (double) mng_info->clip.left,(double) mng_info->clip.right,
5976 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005977 }
5978#endif /* MNG_INSERT_LAYERS */
5979 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005980
cristy4c08aed2011-07-01 19:47:50 +00005981 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005982 {
5983 /*
5984 Allocate next image structure.
5985 */
5986 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005987
cristy3ed852e2009-09-05 21:47:34 +00005988 if (GetNextImageInList(image) == (Image *) NULL)
5989 {
5990 image=DestroyImageList(image);
5991 MngInfoFreeStruct(mng_info,&have_mng_structure);
5992 return((Image *) NULL);
5993 }
glennrp47b9dd52010-11-24 18:12:06 +00005994
cristy3ed852e2009-09-05 21:47:34 +00005995 image=SyncNextImageInList(image);
5996 }
5997 mng_info->image=image;
5998 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5999 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006000
cristy3ed852e2009-09-05 21:47:34 +00006001 if (status == MagickFalse)
6002 break;
glennrp0fe50b42010-11-16 03:52:51 +00006003
cristy3ed852e2009-09-05 21:47:34 +00006004 if (term_chunk_found)
6005 {
6006 image->start_loop=MagickTrue;
6007 term_chunk_found=MagickFalse;
6008 }
glennrp0fe50b42010-11-16 03:52:51 +00006009
cristy3ed852e2009-09-05 21:47:34 +00006010 else
6011 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006012
cristy3ed852e2009-09-05 21:47:34 +00006013 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6014 {
6015 image->delay=frame_delay;
6016 frame_delay=default_frame_delay;
6017 }
glennrp0fe50b42010-11-16 03:52:51 +00006018
cristy3ed852e2009-09-05 21:47:34 +00006019 else
6020 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006021
cristy3ed852e2009-09-05 21:47:34 +00006022 image->page.width=mng_info->mng_width;
6023 image->page.height=mng_info->mng_height;
6024 image->page.x=mng_info->x_off[object_id];
6025 image->page.y=mng_info->y_off[object_id];
6026 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006027
cristy3ed852e2009-09-05 21:47:34 +00006028 /*
6029 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6030 */
glennrp47b9dd52010-11-24 18:12:06 +00006031
cristy3ed852e2009-09-05 21:47:34 +00006032 if (logging != MagickFalse)
6033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6034 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6035 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006036
cristybb503372010-05-27 20:51:26 +00006037 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006038
cristy3ed852e2009-09-05 21:47:34 +00006039 if (offset < 0)
6040 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6041 }
6042
6043 previous=image;
6044 mng_info->image=image;
6045 mng_info->mng_type=mng_type;
6046 mng_info->object_id=object_id;
6047
6048 if (memcmp(type,mng_IHDR,4) == 0)
6049 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006050
cristy3ed852e2009-09-05 21:47:34 +00006051#if defined(JNG_SUPPORTED)
6052 else
6053 image=ReadOneJNGImage(mng_info,image_info,exception);
6054#endif
6055
6056 if (image == (Image *) NULL)
6057 {
6058 if (IsImageObject(previous) != MagickFalse)
6059 {
6060 (void) DestroyImageList(previous);
6061 (void) CloseBlob(previous);
6062 }
glennrp47b9dd52010-11-24 18:12:06 +00006063
cristy3ed852e2009-09-05 21:47:34 +00006064 MngInfoFreeStruct(mng_info,&have_mng_structure);
6065 return((Image *) NULL);
6066 }
glennrp0fe50b42010-11-16 03:52:51 +00006067
cristy3ed852e2009-09-05 21:47:34 +00006068 if (image->columns == 0 || image->rows == 0)
6069 {
6070 (void) CloseBlob(image);
6071 image=DestroyImageList(image);
6072 MngInfoFreeStruct(mng_info,&have_mng_structure);
6073 return((Image *) NULL);
6074 }
glennrp0fe50b42010-11-16 03:52:51 +00006075
cristy3ed852e2009-09-05 21:47:34 +00006076 mng_info->image=image;
6077
6078 if (mng_type)
6079 {
6080 MngBox
6081 crop_box;
6082
6083 if (mng_info->magn_methx || mng_info->magn_methy)
6084 {
6085 png_uint_32
6086 magnified_height,
6087 magnified_width;
6088
6089 if (logging != MagickFalse)
6090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6091 " Processing MNG MAGN chunk");
6092
6093 if (mng_info->magn_methx == 1)
6094 {
6095 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006096
cristy3ed852e2009-09-05 21:47:34 +00006097 if (image->columns > 1)
6098 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006099
cristy3ed852e2009-09-05 21:47:34 +00006100 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006101 magnified_width += (png_uint_32)
6102 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006103 }
glennrp47b9dd52010-11-24 18:12:06 +00006104
cristy3ed852e2009-09-05 21:47:34 +00006105 else
6106 {
cristy4e5bc842010-06-09 13:56:01 +00006107 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006108
cristy3ed852e2009-09-05 21:47:34 +00006109 if (image->columns > 1)
6110 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006111
cristy3ed852e2009-09-05 21:47:34 +00006112 if (image->columns > 2)
6113 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006114
cristy3ed852e2009-09-05 21:47:34 +00006115 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006116 magnified_width += (png_uint_32)
6117 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006118 }
glennrp47b9dd52010-11-24 18:12:06 +00006119
cristy3ed852e2009-09-05 21:47:34 +00006120 if (mng_info->magn_methy == 1)
6121 {
6122 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006123
cristy3ed852e2009-09-05 21:47:34 +00006124 if (image->rows > 1)
6125 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006126
cristy3ed852e2009-09-05 21:47:34 +00006127 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006128 magnified_height += (png_uint_32)
6129 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006130 }
glennrp47b9dd52010-11-24 18:12:06 +00006131
cristy3ed852e2009-09-05 21:47:34 +00006132 else
6133 {
cristy4e5bc842010-06-09 13:56:01 +00006134 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006135
cristy3ed852e2009-09-05 21:47:34 +00006136 if (image->rows > 1)
6137 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006138
cristy3ed852e2009-09-05 21:47:34 +00006139 if (image->rows > 2)
6140 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006141
cristy3ed852e2009-09-05 21:47:34 +00006142 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006143 magnified_height += (png_uint_32)
6144 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006145 }
glennrp47b9dd52010-11-24 18:12:06 +00006146
cristy3ed852e2009-09-05 21:47:34 +00006147 if (magnified_height > image->rows ||
6148 magnified_width > image->columns)
6149 {
6150 Image
6151 *large_image;
6152
6153 int
6154 yy;
6155
cristy4c08aed2011-07-01 19:47:50 +00006156 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006157 *next,
6158 *prev;
6159
6160 png_uint_16
6161 magn_methx,
6162 magn_methy;
6163
cristy4c08aed2011-07-01 19:47:50 +00006164 ssize_t
6165 m,
6166 y;
6167
6168 register Quantum
6169 *n,
6170 *q;
6171
6172 register ssize_t
6173 x;
6174
glennrp47b9dd52010-11-24 18:12:06 +00006175 /* Allocate next image structure. */
6176
cristy3ed852e2009-09-05 21:47:34 +00006177 if (logging != MagickFalse)
6178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6179 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006180
cristy3ed852e2009-09-05 21:47:34 +00006181 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00006182
cristy3ed852e2009-09-05 21:47:34 +00006183 if (GetNextImageInList(image) == (Image *) NULL)
6184 {
6185 image=DestroyImageList(image);
6186 MngInfoFreeStruct(mng_info,&have_mng_structure);
6187 return((Image *) NULL);
6188 }
6189
6190 large_image=SyncNextImageInList(image);
6191
6192 large_image->columns=magnified_width;
6193 large_image->rows=magnified_height;
6194
6195 magn_methx=mng_info->magn_methx;
6196 magn_methy=mng_info->magn_methy;
6197
glennrp3faa9a32011-04-23 14:00:25 +00006198#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006199#define QM unsigned short
6200 if (magn_methx != 1 || magn_methy != 1)
6201 {
6202 /*
6203 Scale pixels to unsigned shorts to prevent
6204 overflow of intermediate values of interpolations
6205 */
cristybb503372010-05-27 20:51:26 +00006206 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006207 {
6208 q=GetAuthenticPixels(image,0,y,image->columns,1,
6209 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006210
cristybb503372010-05-27 20:51:26 +00006211 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006212 {
cristy4c08aed2011-07-01 19:47:50 +00006213 SetPixelRed(image,ScaleQuantumToShort(
6214 GetPixelRed(image,q)),q);
6215 SetPixelGreen(image,ScaleQuantumToShort(
6216 GetPixelGreen(image,q)),q);
6217 SetPixelBlue(image,ScaleQuantumToShort(
6218 GetPixelBlue(image,q)),q);
6219 SetPixelAlpha(image,ScaleQuantumToShort(
6220 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006221 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006222 }
glennrp47b9dd52010-11-24 18:12:06 +00006223
cristy3ed852e2009-09-05 21:47:34 +00006224 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6225 break;
6226 }
6227 }
6228#else
6229#define QM Quantum
6230#endif
6231
6232 if (image->matte != MagickFalse)
6233 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006234
cristy3ed852e2009-09-05 21:47:34 +00006235 else
6236 {
cristy4c08aed2011-07-01 19:47:50 +00006237 large_image->background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00006238 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006239
cristy3ed852e2009-09-05 21:47:34 +00006240 if (magn_methx == 4)
6241 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006242
cristy3ed852e2009-09-05 21:47:34 +00006243 if (magn_methx == 5)
6244 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006245
cristy3ed852e2009-09-05 21:47:34 +00006246 if (magn_methy == 4)
6247 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006248
cristy3ed852e2009-09-05 21:47:34 +00006249 if (magn_methy == 5)
6250 magn_methy=3;
6251 }
6252
6253 /* magnify the rows into the right side of the large image */
6254
6255 if (logging != MagickFalse)
6256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006257 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006258 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006259 yy=0;
6260 length=(size_t) image->columns;
cristy4c08aed2011-07-01 19:47:50 +00006261 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6262 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006263
cristy4c08aed2011-07-01 19:47:50 +00006264 if ((prev == (Quantum *) NULL) ||
6265 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006266 {
6267 image=DestroyImageList(image);
6268 MngInfoFreeStruct(mng_info,&have_mng_structure);
6269 ThrowReaderException(ResourceLimitError,
6270 "MemoryAllocationFailed");
6271 }
glennrp47b9dd52010-11-24 18:12:06 +00006272
cristy3ed852e2009-09-05 21:47:34 +00006273 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6274 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006275
cristybb503372010-05-27 20:51:26 +00006276 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006277 {
6278 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006279 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006280
cristybb503372010-05-27 20:51:26 +00006281 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6282 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006283
cristybb503372010-05-27 20:51:26 +00006284 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6285 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006286
cristybb503372010-05-27 20:51:26 +00006287 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006288 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006289
cristy3ed852e2009-09-05 21:47:34 +00006290 else
cristybb503372010-05-27 20:51:26 +00006291 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006292
cristy3ed852e2009-09-05 21:47:34 +00006293 n=prev;
6294 prev=next;
6295 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006296
cristybb503372010-05-27 20:51:26 +00006297 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006298 {
6299 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6300 exception);
6301 (void) CopyMagickMemory(next,n,length);
6302 }
glennrp47b9dd52010-11-24 18:12:06 +00006303
cristy3ed852e2009-09-05 21:47:34 +00006304 for (i=0; i < m; i++, yy++)
6305 {
cristy4c08aed2011-07-01 19:47:50 +00006306 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006307 *pixels;
6308
cristybb503372010-05-27 20:51:26 +00006309 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006310 pixels=prev;
6311 n=next;
6312 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006313 1,exception);
cristy3ed852e2009-09-05 21:47:34 +00006314 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00006315
cristybb503372010-05-27 20:51:26 +00006316 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006317 {
glennrpfd05d622011-02-25 04:10:33 +00006318 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006319 /*
6320 if (image->storage_class == PseudoClass)
6321 {
6322 }
6323 */
6324
6325 if (magn_methy <= 1)
6326 {
glennrpbb4f99d2011-05-22 11:13:17 +00006327 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006328 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
glennrp847370c2011-07-05 17:37:15 +00006329 SetPixelGreen(large_image,GetPixelGreen(image,
6330 pixels),q);
6331 SetPixelBlue(large_image,GetPixelBlue(image,
6332 pixels),q);
6333 SetPixelAlpha(large_image,GetPixelAlpha(image,
6334 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006335 }
glennrp47b9dd52010-11-24 18:12:06 +00006336
cristy3ed852e2009-09-05 21:47:34 +00006337 else if (magn_methy == 2 || magn_methy == 4)
6338 {
6339 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006340 {
glennrp847370c2011-07-05 17:37:15 +00006341 SetPixelRed(large_image,GetPixelRed(image,
6342 pixels),q);
6343 SetPixelGreen(large_image,GetPixelGreen(image,
6344 pixels),q);
6345 SetPixelBlue(large_image,GetPixelBlue(image,
6346 pixels),q);
6347 SetPixelAlpha(large_image,GetPixelAlpha(image,
6348 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006349 }
glennrp47b9dd52010-11-24 18:12:06 +00006350
cristy3ed852e2009-09-05 21:47:34 +00006351 else
6352 {
6353 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006354 SetPixelRed(large_image,((QM) (((ssize_t)
6355 (2*i*(GetPixelRed(image,n)
6356 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006357 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006358 +GetPixelRed(image,pixels)))),q);
6359 SetPixelGreen(large_image,((QM) (((ssize_t)
6360 (2*i*(GetPixelGreen(image,n)
6361 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006362 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006363 +GetPixelGreen(image,pixels)))),q);
6364 SetPixelBlue(large_image,((QM) (((ssize_t)
6365 (2*i*(GetPixelBlue(image,n)
6366 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006367 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006368 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006369
cristy3ed852e2009-09-05 21:47:34 +00006370 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006371 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6372 (2*i*(GetPixelAlpha(image,n)
6373 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006374 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006375 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006376 }
glennrp47b9dd52010-11-24 18:12:06 +00006377
cristy3ed852e2009-09-05 21:47:34 +00006378 if (magn_methy == 4)
6379 {
6380 /* Replicate nearest */
6381 if (i <= ((m+1) << 1))
glennrp847370c2011-07-05 17:37:15 +00006382 SetPixelAlpha(large_image,GetPixelAlpha(image,
6383 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006384 else
glennrp847370c2011-07-05 17:37:15 +00006385 SetPixelAlpha(large_image,GetPixelAlpha(image,
6386 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006387 }
6388 }
glennrp47b9dd52010-11-24 18:12:06 +00006389
cristy3ed852e2009-09-05 21:47:34 +00006390 else /* if (magn_methy == 3 || magn_methy == 5) */
6391 {
6392 /* Replicate nearest */
6393 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006394 {
glennrp847370c2011-07-05 17:37:15 +00006395 SetPixelRed(large_image,GetPixelRed(image,
6396 pixels),q);
6397 SetPixelGreen(large_image,GetPixelGreen(image,
6398 pixels),q);
6399 SetPixelBlue(large_image,GetPixelBlue(image,
6400 pixels),q);
6401 SetPixelAlpha(large_image,GetPixelAlpha(image,
6402 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006403 }
glennrp47b9dd52010-11-24 18:12:06 +00006404
cristy3ed852e2009-09-05 21:47:34 +00006405 else
glennrpbb4f99d2011-05-22 11:13:17 +00006406 {
cristy4c08aed2011-07-01 19:47:50 +00006407 SetPixelRed(large_image,GetPixelRed(image,n),q);
glennrp847370c2011-07-05 17:37:15 +00006408 SetPixelGreen(large_image,GetPixelGreen(image,n),
6409 q);
6410 SetPixelBlue(large_image,GetPixelBlue(image,n),
6411 q);
6412 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6413 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006414 }
glennrp47b9dd52010-11-24 18:12:06 +00006415
cristy3ed852e2009-09-05 21:47:34 +00006416 if (magn_methy == 5)
6417 {
cristy4c08aed2011-07-01 19:47:50 +00006418 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6419 (GetPixelAlpha(image,n)
6420 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006421 +m))/((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006422 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006423 }
6424 }
cristyed231572011-07-14 02:18:59 +00006425 n+=GetPixelChannels(image);
6426 q+=GetPixelChannels(large_image);
6427 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006428 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006429
cristy3ed852e2009-09-05 21:47:34 +00006430 if (SyncAuthenticPixels(large_image,exception) == 0)
6431 break;
glennrp47b9dd52010-11-24 18:12:06 +00006432
cristy3ed852e2009-09-05 21:47:34 +00006433 } /* i */
6434 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006435
cristy4c08aed2011-07-01 19:47:50 +00006436 prev=(Quantum *) RelinquishMagickMemory(prev);
6437 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006438
6439 length=image->columns;
6440
6441 if (logging != MagickFalse)
6442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6443 " Delete original image");
6444
6445 DeleteImageFromList(&image);
6446
6447 image=large_image;
6448
6449 mng_info->image=image;
6450
6451 /* magnify the columns */
6452 if (logging != MagickFalse)
6453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006454 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006455
cristybb503372010-05-27 20:51:26 +00006456 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006457 {
cristy4c08aed2011-07-01 19:47:50 +00006458 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006459 *pixels;
6460
6461 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristyed231572011-07-14 02:18:59 +00006462 pixels=q+(image->columns-length)*GetPixelChannels(image);
6463 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006464
cristybb503372010-05-27 20:51:26 +00006465 for (x=(ssize_t) (image->columns-length);
6466 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006467 {
cristyed231572011-07-14 02:18:59 +00006468 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006469
cristybb503372010-05-27 20:51:26 +00006470 if (x == (ssize_t) (image->columns-length))
6471 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006472
cristybb503372010-05-27 20:51:26 +00006473 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6474 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006475
cristybb503372010-05-27 20:51:26 +00006476 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6477 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006478
cristybb503372010-05-27 20:51:26 +00006479 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006480 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006481
cristy3ed852e2009-09-05 21:47:34 +00006482 else
cristybb503372010-05-27 20:51:26 +00006483 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006484
cristy3ed852e2009-09-05 21:47:34 +00006485 for (i=0; i < m; i++)
6486 {
6487 if (magn_methx <= 1)
6488 {
6489 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006490 SetPixelRed(image,GetPixelRed(image,pixels),q);
6491 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6492 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6493 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006494 }
glennrp47b9dd52010-11-24 18:12:06 +00006495
cristy3ed852e2009-09-05 21:47:34 +00006496 else if (magn_methx == 2 || magn_methx == 4)
6497 {
6498 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006499 {
cristy4c08aed2011-07-01 19:47:50 +00006500 SetPixelRed(image,GetPixelRed(image,pixels),q);
6501 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6502 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6503 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006504 }
glennrp47b9dd52010-11-24 18:12:06 +00006505
cristyed231572011-07-14 02:18:59 +00006506 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006507 else
6508 {
6509 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006510 SetPixelRed(image,(QM) ((2*i*(
6511 GetPixelRed(image,n)
6512 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006513 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006514 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006515
cristy4c08aed2011-07-01 19:47:50 +00006516 SetPixelGreen(image,(QM) ((2*i*(
6517 GetPixelGreen(image,n)
6518 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006519 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006520 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006521
cristy4c08aed2011-07-01 19:47:50 +00006522 SetPixelBlue(image,(QM) ((2*i*(
6523 GetPixelBlue(image,n)
6524 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006525 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006526 GetPixelBlue(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006527 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006528 SetPixelAlpha(image,(QM) ((2*i*(
6529 GetPixelAlpha(image,n)
6530 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006531 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006532 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006533 }
glennrp47b9dd52010-11-24 18:12:06 +00006534
cristy3ed852e2009-09-05 21:47:34 +00006535 if (magn_methx == 4)
6536 {
6537 /* Replicate nearest */
6538 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006539 {
cristy4c08aed2011-07-01 19:47:50 +00006540 SetPixelAlpha(image,
6541 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006542 }
cristy3ed852e2009-09-05 21:47:34 +00006543 else
glennrpbb4f99d2011-05-22 11:13:17 +00006544 {
cristy4c08aed2011-07-01 19:47:50 +00006545 SetPixelAlpha(image,
6546 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006547 }
cristy3ed852e2009-09-05 21:47:34 +00006548 }
6549 }
glennrp47b9dd52010-11-24 18:12:06 +00006550
cristy3ed852e2009-09-05 21:47:34 +00006551 else /* if (magn_methx == 3 || magn_methx == 5) */
6552 {
6553 /* Replicate nearest */
6554 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006555 {
cristy4c08aed2011-07-01 19:47:50 +00006556 SetPixelRed(image,GetPixelRed(image,pixels),q);
6557 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6558 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6559 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006560 }
glennrp47b9dd52010-11-24 18:12:06 +00006561
cristy3ed852e2009-09-05 21:47:34 +00006562 else
glennrpbb4f99d2011-05-22 11:13:17 +00006563 {
cristy4c08aed2011-07-01 19:47:50 +00006564 SetPixelRed(image,GetPixelRed(image,n),q);
6565 SetPixelGreen(image,GetPixelGreen(image,n),q);
6566 SetPixelBlue(image,GetPixelBlue(image,n),q);
6567 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006568 }
glennrp47b9dd52010-11-24 18:12:06 +00006569
cristy3ed852e2009-09-05 21:47:34 +00006570 if (magn_methx == 5)
6571 {
6572 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006573 SetPixelAlpha(image,
6574 (QM) ((2*i*( GetPixelAlpha(image,n)
6575 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006576 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006577 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006578 }
6579 }
cristyed231572011-07-14 02:18:59 +00006580 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006581 }
cristyed231572011-07-14 02:18:59 +00006582 n+=GetPixelChannels(image);
6583 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006584 }
glennrp47b9dd52010-11-24 18:12:06 +00006585
cristy3ed852e2009-09-05 21:47:34 +00006586 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6587 break;
6588 }
glennrp3faa9a32011-04-23 14:00:25 +00006589#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006590 if (magn_methx != 1 || magn_methy != 1)
6591 {
6592 /*
6593 Rescale pixels to Quantum
6594 */
cristybb503372010-05-27 20:51:26 +00006595 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006596 {
6597 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006598
cristybb503372010-05-27 20:51:26 +00006599 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006600 {
cristy4c08aed2011-07-01 19:47:50 +00006601 SetPixelRed(image,ScaleShortToQuantum(
6602 GetPixelRed(image,q)),q);
6603 SetPixelGreen(image,ScaleShortToQuantum(
6604 GetPixelGreen(image,q)),q);
6605 SetPixelBlue(image,ScaleShortToQuantum(
6606 GetPixelBlue(image,q)),q);
6607 SetPixelAlpha(image,ScaleShortToQuantum(
6608 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006609 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006610 }
glennrp47b9dd52010-11-24 18:12:06 +00006611
cristy3ed852e2009-09-05 21:47:34 +00006612 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6613 break;
6614 }
6615 }
6616#endif
6617 if (logging != MagickFalse)
6618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6619 " Finished MAGN processing");
6620 }
6621 }
6622
6623 /*
6624 Crop_box is with respect to the upper left corner of the MNG.
6625 */
6626 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6627 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6628 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6629 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6630 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6631 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6632 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6633 if ((crop_box.left != (mng_info->image_box.left
6634 +mng_info->x_off[object_id])) ||
6635 (crop_box.right != (mng_info->image_box.right
6636 +mng_info->x_off[object_id])) ||
6637 (crop_box.top != (mng_info->image_box.top
6638 +mng_info->y_off[object_id])) ||
6639 (crop_box.bottom != (mng_info->image_box.bottom
6640 +mng_info->y_off[object_id])))
6641 {
6642 if (logging != MagickFalse)
6643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6644 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006645
cristy3ed852e2009-09-05 21:47:34 +00006646 if ((crop_box.left < crop_box.right) &&
6647 (crop_box.top < crop_box.bottom))
6648 {
6649 Image
6650 *im;
6651
6652 RectangleInfo
6653 crop_info;
6654
6655 /*
6656 Crop_info is with respect to the upper left corner of
6657 the image.
6658 */
6659 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6660 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006661 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6662 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006663 image->page.width=image->columns;
6664 image->page.height=image->rows;
6665 image->page.x=0;
6666 image->page.y=0;
6667 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006668
cristy3ed852e2009-09-05 21:47:34 +00006669 if (im != (Image *) NULL)
6670 {
6671 image->columns=im->columns;
6672 image->rows=im->rows;
6673 im=DestroyImage(im);
6674 image->page.width=image->columns;
6675 image->page.height=image->rows;
6676 image->page.x=crop_box.left;
6677 image->page.y=crop_box.top;
6678 }
6679 }
glennrp47b9dd52010-11-24 18:12:06 +00006680
cristy3ed852e2009-09-05 21:47:34 +00006681 else
6682 {
6683 /*
6684 No pixels in crop area. The MNG spec still requires
6685 a layer, though, so make a single transparent pixel in
6686 the top left corner.
6687 */
6688 image->columns=1;
6689 image->rows=1;
6690 image->colors=2;
6691 (void) SetImageBackgroundColor(image);
6692 image->page.width=1;
6693 image->page.height=1;
6694 image->page.x=0;
6695 image->page.y=0;
6696 }
6697 }
6698#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6699 image=mng_info->image;
6700#endif
6701 }
6702
glennrp2b013e42010-11-24 16:55:50 +00006703#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6704 /* PNG does not handle depths greater than 16 so reduce it even
6705 * if lossy
6706 */
6707 if (image->depth > 16)
6708 image->depth=16;
6709#endif
6710
glennrp3faa9a32011-04-23 14:00:25 +00006711#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006712 if (LosslessReduceDepthOK(image) != MagickFalse)
6713 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006714#endif
glennrpd6afd542010-11-19 01:53:05 +00006715
cristy3ed852e2009-09-05 21:47:34 +00006716 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006717
cristy3ed852e2009-09-05 21:47:34 +00006718 if (image_info->number_scenes != 0)
6719 {
6720 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006721 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006722 break;
6723 }
glennrpd6afd542010-11-19 01:53:05 +00006724
cristy3ed852e2009-09-05 21:47:34 +00006725 if (logging != MagickFalse)
6726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6727 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006728
cristy3ed852e2009-09-05 21:47:34 +00006729 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006730
cristy3ed852e2009-09-05 21:47:34 +00006731 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006732
cristy3ed852e2009-09-05 21:47:34 +00006733 if (logging != MagickFalse)
6734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6735 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006736
cristy3ed852e2009-09-05 21:47:34 +00006737#if defined(MNG_INSERT_LAYERS)
6738 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6739 (mng_info->mng_height))
6740 {
6741 /*
6742 Insert a background layer if nothing else was found.
6743 */
6744 if (logging != MagickFalse)
6745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6746 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006747
cristy4c08aed2011-07-01 19:47:50 +00006748 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006749 {
6750 /*
6751 Allocate next image structure.
6752 */
6753 AcquireNextImage(image_info,image);
6754 if (GetNextImageInList(image) == (Image *) NULL)
6755 {
6756 image=DestroyImageList(image);
6757 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006758
cristy3ed852e2009-09-05 21:47:34 +00006759 if (logging != MagickFalse)
6760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6761 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006762
cristy3ed852e2009-09-05 21:47:34 +00006763 return((Image *) NULL);
6764 }
6765 image=SyncNextImageInList(image);
6766 }
6767 image->columns=mng_info->mng_width;
6768 image->rows=mng_info->mng_height;
6769 image->page.width=mng_info->mng_width;
6770 image->page.height=mng_info->mng_height;
6771 image->page.x=0;
6772 image->page.y=0;
6773 image->background_color=mng_background_color;
6774 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006775
cristy3ed852e2009-09-05 21:47:34 +00006776 if (image_info->ping == MagickFalse)
6777 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006778
cristy3ed852e2009-09-05 21:47:34 +00006779 mng_info->image_found++;
6780 }
6781#endif
6782 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006783
cristy3ed852e2009-09-05 21:47:34 +00006784 if (mng_iterations == 1)
6785 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006786
cristy3ed852e2009-09-05 21:47:34 +00006787 while (GetPreviousImageInList(image) != (Image *) NULL)
6788 {
6789 image_count++;
6790 if (image_count > 10*mng_info->image_found)
6791 {
6792 if (logging != MagickFalse)
6793 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006794
cristy3ed852e2009-09-05 21:47:34 +00006795 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6796 CoderError,"Linked list is corrupted, beginning of list not found",
6797 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006798
cristy3ed852e2009-09-05 21:47:34 +00006799 return((Image *) NULL);
6800 }
glennrp0fe50b42010-11-16 03:52:51 +00006801
cristy3ed852e2009-09-05 21:47:34 +00006802 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006803
cristy3ed852e2009-09-05 21:47:34 +00006804 if (GetNextImageInList(image) == (Image *) NULL)
6805 {
6806 if (logging != MagickFalse)
6807 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006808
cristy3ed852e2009-09-05 21:47:34 +00006809 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6810 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6811 image_info->filename);
6812 }
6813 }
glennrp47b9dd52010-11-24 18:12:06 +00006814
cristy3ed852e2009-09-05 21:47:34 +00006815 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6816 GetNextImageInList(image) ==
6817 (Image *) NULL)
6818 {
6819 if (logging != MagickFalse)
6820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6821 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006822
cristy3ed852e2009-09-05 21:47:34 +00006823 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6824 CoderError,"image->next for first image is NULL but shouldn't be.",
6825 "`%s'",image_info->filename);
6826 }
glennrp47b9dd52010-11-24 18:12:06 +00006827
cristy3ed852e2009-09-05 21:47:34 +00006828 if (mng_info->image_found == 0)
6829 {
6830 if (logging != MagickFalse)
6831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6832 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006833
cristy3ed852e2009-09-05 21:47:34 +00006834 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6835 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006836
cristy3ed852e2009-09-05 21:47:34 +00006837 if (image != (Image *) NULL)
6838 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006839
cristy3ed852e2009-09-05 21:47:34 +00006840 MngInfoFreeStruct(mng_info,&have_mng_structure);
6841 return((Image *) NULL);
6842 }
6843
6844 if (mng_info->ticks_per_second)
6845 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6846 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006847
cristy3ed852e2009-09-05 21:47:34 +00006848 else
6849 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006850
cristy3ed852e2009-09-05 21:47:34 +00006851 /* Find final nonzero image delay */
6852 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006853
cristy3ed852e2009-09-05 21:47:34 +00006854 while (GetNextImageInList(image) != (Image *) NULL)
6855 {
6856 if (image->delay)
6857 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006858
cristy3ed852e2009-09-05 21:47:34 +00006859 image=GetNextImageInList(image);
6860 }
glennrp0fe50b42010-11-16 03:52:51 +00006861
cristy3ed852e2009-09-05 21:47:34 +00006862 if (final_delay < final_image_delay)
6863 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006864
cristy3ed852e2009-09-05 21:47:34 +00006865 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006866
cristy3ed852e2009-09-05 21:47:34 +00006867 if (logging != MagickFalse)
6868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006869 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6870 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006871
cristy3ed852e2009-09-05 21:47:34 +00006872 if (logging != MagickFalse)
6873 {
6874 int
6875 scene;
6876
6877 scene=0;
6878 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006879
cristy3ed852e2009-09-05 21:47:34 +00006880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6881 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006882
cristy3ed852e2009-09-05 21:47:34 +00006883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006884 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006885
cristy3ed852e2009-09-05 21:47:34 +00006886 while (GetNextImageInList(image) != (Image *) NULL)
6887 {
6888 image=GetNextImageInList(image);
6889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006890 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006891 }
6892 }
6893
6894 image=GetFirstImageInList(image);
6895#ifdef MNG_COALESCE_LAYERS
6896 if (insert_layers)
6897 {
6898 Image
6899 *next_image,
6900 *next;
6901
cristybb503372010-05-27 20:51:26 +00006902 size_t
cristy3ed852e2009-09-05 21:47:34 +00006903 scene;
6904
6905 if (logging != MagickFalse)
6906 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006907
cristy3ed852e2009-09-05 21:47:34 +00006908 scene=image->scene;
6909 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006910
cristy3ed852e2009-09-05 21:47:34 +00006911 if (next_image == (Image *) NULL)
6912 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006913
cristy3ed852e2009-09-05 21:47:34 +00006914 image=DestroyImageList(image);
6915 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006916
cristy3ed852e2009-09-05 21:47:34 +00006917 for (next=image; next != (Image *) NULL; next=next_image)
6918 {
6919 next->page.width=mng_info->mng_width;
6920 next->page.height=mng_info->mng_height;
6921 next->page.x=0;
6922 next->page.y=0;
6923 next->scene=scene++;
6924 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006925
cristy3ed852e2009-09-05 21:47:34 +00006926 if (next_image == (Image *) NULL)
6927 break;
glennrp47b9dd52010-11-24 18:12:06 +00006928
cristy3ed852e2009-09-05 21:47:34 +00006929 if (next->delay == 0)
6930 {
6931 scene--;
6932 next_image->previous=GetPreviousImageInList(next);
6933 if (GetPreviousImageInList(next) == (Image *) NULL)
6934 image=next_image;
6935 else
6936 next->previous->next=next_image;
6937 next=DestroyImage(next);
6938 }
6939 }
6940 }
6941#endif
6942
6943 while (GetNextImageInList(image) != (Image *) NULL)
6944 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006945
cristy3ed852e2009-09-05 21:47:34 +00006946 image->dispose=BackgroundDispose;
6947
6948 if (logging != MagickFalse)
6949 {
6950 int
6951 scene;
6952
6953 scene=0;
6954 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006955
cristy3ed852e2009-09-05 21:47:34 +00006956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6957 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006958
cristy3ed852e2009-09-05 21:47:34 +00006959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006960 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6961 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006962
cristy3ed852e2009-09-05 21:47:34 +00006963 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006964 {
6965 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006966
cristyf2faecf2010-05-28 19:19:36 +00006967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006968 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6969 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006970 }
6971 }
glennrp47b9dd52010-11-24 18:12:06 +00006972
cristy3ed852e2009-09-05 21:47:34 +00006973 image=GetFirstImageInList(image);
6974 MngInfoFreeStruct(mng_info,&have_mng_structure);
6975 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006976
cristy3ed852e2009-09-05 21:47:34 +00006977 if (logging != MagickFalse)
6978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006979
cristy3ed852e2009-09-05 21:47:34 +00006980 return(GetFirstImageInList(image));
6981}
glennrp25c1e2b2010-03-25 01:39:56 +00006982#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006983static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6984{
6985 printf("Your PNG library is too old: You have libpng-%s\n",
6986 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006987
cristy3ed852e2009-09-05 21:47:34 +00006988 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6989 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006990
cristy3ed852e2009-09-05 21:47:34 +00006991 return(Image *) NULL;
6992}
glennrp47b9dd52010-11-24 18:12:06 +00006993
cristy3ed852e2009-09-05 21:47:34 +00006994static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6995{
6996 return(ReadPNGImage(image_info,exception));
6997}
glennrp25c1e2b2010-03-25 01:39:56 +00006998#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006999#endif
7000
7001/*
7002%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7003% %
7004% %
7005% %
7006% R e g i s t e r P N G I m a g e %
7007% %
7008% %
7009% %
7010%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7011%
7012% RegisterPNGImage() adds properties for the PNG image format to
7013% the list of supported formats. The properties include the image format
7014% tag, a method to read and/or write the format, whether the format
7015% supports the saving of more than one frame to the same file or blob,
7016% whether the format supports native in-memory I/O, and a brief
7017% description of the format.
7018%
7019% The format of the RegisterPNGImage method is:
7020%
cristybb503372010-05-27 20:51:26 +00007021% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007022%
7023*/
cristybb503372010-05-27 20:51:26 +00007024ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007025{
7026 char
7027 version[MaxTextExtent];
7028
7029 MagickInfo
7030 *entry;
7031
7032 static const char
7033 *PNGNote=
7034 {
7035 "See http://www.libpng.org/ for details about the PNG format."
7036 },
glennrp47b9dd52010-11-24 18:12:06 +00007037
cristy3ed852e2009-09-05 21:47:34 +00007038 *JNGNote=
7039 {
7040 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7041 "format."
7042 },
glennrp47b9dd52010-11-24 18:12:06 +00007043
cristy3ed852e2009-09-05 21:47:34 +00007044 *MNGNote=
7045 {
7046 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7047 "format."
7048 };
7049
7050 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007051
cristy3ed852e2009-09-05 21:47:34 +00007052#if defined(PNG_LIBPNG_VER_STRING)
7053 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7054 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007055
cristy3ed852e2009-09-05 21:47:34 +00007056 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7057 {
7058 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7059 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7060 MaxTextExtent);
7061 }
7062#endif
glennrp47b9dd52010-11-24 18:12:06 +00007063
cristy3ed852e2009-09-05 21:47:34 +00007064 entry=SetMagickInfo("MNG");
7065 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007066
cristy3ed852e2009-09-05 21:47:34 +00007067#if defined(MAGICKCORE_PNG_DELEGATE)
7068 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7069 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7070#endif
glennrp47b9dd52010-11-24 18:12:06 +00007071
cristy3ed852e2009-09-05 21:47:34 +00007072 entry->magick=(IsImageFormatHandler *) IsMNG;
7073 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007074
cristy3ed852e2009-09-05 21:47:34 +00007075 if (*version != '\0')
7076 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007077
cristy3ed852e2009-09-05 21:47:34 +00007078 entry->module=ConstantString("PNG");
7079 entry->note=ConstantString(MNGNote);
7080 (void) RegisterMagickInfo(entry);
7081
7082 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007083
cristy3ed852e2009-09-05 21:47:34 +00007084#if defined(MAGICKCORE_PNG_DELEGATE)
7085 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7086 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7087#endif
glennrp47b9dd52010-11-24 18:12:06 +00007088
cristy3ed852e2009-09-05 21:47:34 +00007089 entry->magick=(IsImageFormatHandler *) IsPNG;
7090 entry->adjoin=MagickFalse;
7091 entry->description=ConstantString("Portable Network Graphics");
7092 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007093
cristy3ed852e2009-09-05 21:47:34 +00007094 if (*version != '\0')
7095 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007096
cristy3ed852e2009-09-05 21:47:34 +00007097 entry->note=ConstantString(PNGNote);
7098 (void) RegisterMagickInfo(entry);
7099
7100 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007101
cristy3ed852e2009-09-05 21:47:34 +00007102#if defined(MAGICKCORE_PNG_DELEGATE)
7103 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7104 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7105#endif
glennrp47b9dd52010-11-24 18:12:06 +00007106
cristy3ed852e2009-09-05 21:47:34 +00007107 entry->magick=(IsImageFormatHandler *) IsPNG;
7108 entry->adjoin=MagickFalse;
7109 entry->description=ConstantString(
7110 "8-bit indexed with optional binary transparency");
7111 entry->module=ConstantString("PNG");
7112 (void) RegisterMagickInfo(entry);
7113
7114 entry=SetMagickInfo("PNG24");
7115 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007116
cristy3ed852e2009-09-05 21:47:34 +00007117#if defined(ZLIB_VERSION)
7118 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7119 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007120
cristy3ed852e2009-09-05 21:47:34 +00007121 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7122 {
7123 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7124 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7125 }
7126#endif
glennrp47b9dd52010-11-24 18:12:06 +00007127
cristy3ed852e2009-09-05 21:47:34 +00007128 if (*version != '\0')
7129 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007130
cristy3ed852e2009-09-05 21:47:34 +00007131#if defined(MAGICKCORE_PNG_DELEGATE)
7132 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7133 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7134#endif
glennrp47b9dd52010-11-24 18:12:06 +00007135
cristy3ed852e2009-09-05 21:47:34 +00007136 entry->magick=(IsImageFormatHandler *) IsPNG;
7137 entry->adjoin=MagickFalse;
7138 entry->description=ConstantString("opaque 24-bit RGB");
7139 entry->module=ConstantString("PNG");
7140 (void) RegisterMagickInfo(entry);
7141
7142 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007143
cristy3ed852e2009-09-05 21:47:34 +00007144#if defined(MAGICKCORE_PNG_DELEGATE)
7145 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7146 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7147#endif
glennrp47b9dd52010-11-24 18:12:06 +00007148
cristy3ed852e2009-09-05 21:47:34 +00007149 entry->magick=(IsImageFormatHandler *) IsPNG;
7150 entry->adjoin=MagickFalse;
7151 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7152 entry->module=ConstantString("PNG");
7153 (void) RegisterMagickInfo(entry);
7154
7155 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007156
cristy3ed852e2009-09-05 21:47:34 +00007157#if defined(JNG_SUPPORTED)
7158#if defined(MAGICKCORE_PNG_DELEGATE)
7159 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7160 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7161#endif
7162#endif
glennrp47b9dd52010-11-24 18:12:06 +00007163
cristy3ed852e2009-09-05 21:47:34 +00007164 entry->magick=(IsImageFormatHandler *) IsJNG;
7165 entry->adjoin=MagickFalse;
7166 entry->description=ConstantString("JPEG Network Graphics");
7167 entry->module=ConstantString("PNG");
7168 entry->note=ConstantString(JNGNote);
7169 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007170
cristy18b17442009-10-25 18:36:48 +00007171#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007172 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007173#endif
glennrp47b9dd52010-11-24 18:12:06 +00007174
cristy3ed852e2009-09-05 21:47:34 +00007175 return(MagickImageCoderSignature);
7176}
7177
7178/*
7179%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7180% %
7181% %
7182% %
7183% U n r e g i s t e r P N G I m a g e %
7184% %
7185% %
7186% %
7187%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7188%
7189% UnregisterPNGImage() removes format registrations made by the
7190% PNG module from the list of supported formats.
7191%
7192% The format of the UnregisterPNGImage method is:
7193%
7194% UnregisterPNGImage(void)
7195%
7196*/
7197ModuleExport void UnregisterPNGImage(void)
7198{
7199 (void) UnregisterMagickInfo("MNG");
7200 (void) UnregisterMagickInfo("PNG");
7201 (void) UnregisterMagickInfo("PNG8");
7202 (void) UnregisterMagickInfo("PNG24");
7203 (void) UnregisterMagickInfo("PNG32");
7204 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007205
cristy3ed852e2009-09-05 21:47:34 +00007206#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007207 if (ping_semaphore != (SemaphoreInfo *) NULL)
7208 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007209#endif
7210}
7211
7212#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007213#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007214/*
7215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7216% %
7217% %
7218% %
7219% W r i t e M N G I m a g e %
7220% %
7221% %
7222% %
7223%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7224%
7225% WriteMNGImage() writes an image in the Portable Network Graphics
7226% Group's "Multiple-image Network Graphics" encoded image format.
7227%
7228% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7229%
7230% The format of the WriteMNGImage method is:
7231%
cristy1e178e72011-08-28 19:44:34 +00007232% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7233% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007234%
7235% A description of each parameter follows.
7236%
7237% o image_info: the image info.
7238%
7239% o image: The image.
7240%
cristy1e178e72011-08-28 19:44:34 +00007241% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007242%
7243% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7244% "To do" under ReadPNGImage):
7245%
cristy3ed852e2009-09-05 21:47:34 +00007246% Preserve all unknown and not-yet-handled known chunks found in input
7247% PNG file and copy them into output PNG files according to the PNG
7248% copying rules.
7249%
7250% Write the iCCP chunk at MNG level when (icc profile length > 0)
7251%
7252% Improve selection of color type (use indexed-colour or indexed-colour
7253% with tRNS when 256 or fewer unique RGBA values are present).
7254%
7255% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7256% This will be complicated if we limit ourselves to generating MNG-LC
7257% files. For now we ignore disposal method 3 and simply overlay the next
7258% image on it.
7259%
7260% Check for identical PLTE's or PLTE/tRNS combinations and use a
7261% global MNG PLTE or PLTE/tRNS combination when appropriate.
7262% [mostly done 15 June 1999 but still need to take care of tRNS]
7263%
7264% Check for identical sRGB and replace with a global sRGB (and remove
7265% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7266% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7267% local gAMA/cHRM with local sRGB if appropriate).
7268%
7269% Check for identical sBIT chunks and write global ones.
7270%
7271% Provide option to skip writing the signature tEXt chunks.
7272%
7273% Use signatures to detect identical objects and reuse the first
7274% instance of such objects instead of writing duplicate objects.
7275%
7276% Use a smaller-than-32k value of compression window size when
7277% appropriate.
7278%
7279% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7280% ancillary text chunks and save profiles.
7281%
7282% Provide an option to force LC files (to ensure exact framing rate)
7283% instead of VLC.
7284%
7285% Provide an option to force VLC files instead of LC, even when offsets
7286% are present. This will involve expanding the embedded images with a
7287% transparent region at the top and/or left.
7288*/
7289
cristy3ed852e2009-09-05 21:47:34 +00007290static void
glennrpcf002022011-01-30 02:38:15 +00007291Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007292 png_info *ping_info, unsigned char *profile_type, unsigned char
7293 *profile_description, unsigned char *profile_data, png_uint_32 length)
7294{
cristy3ed852e2009-09-05 21:47:34 +00007295 png_textp
7296 text;
7297
cristybb503372010-05-27 20:51:26 +00007298 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007299 i;
7300
7301 unsigned char
7302 *sp;
7303
7304 png_charp
7305 dp;
7306
7307 png_uint_32
7308 allocated_length,
7309 description_length;
7310
7311 unsigned char
7312 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007313
cristy3ed852e2009-09-05 21:47:34 +00007314 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7315 return;
7316
7317 if (image_info->verbose)
7318 {
glennrp0fe50b42010-11-16 03:52:51 +00007319 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7320 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007321 }
glennrp0fe50b42010-11-16 03:52:51 +00007322
cristy3ed852e2009-09-05 21:47:34 +00007323 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7324 description_length=(png_uint_32) strlen((const char *) profile_description);
7325 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7326 + description_length);
7327 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7328 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7329 text[0].key[0]='\0';
7330 (void) ConcatenateMagickString(text[0].key,
7331 "Raw profile type ",MaxTextExtent);
7332 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7333 sp=profile_data;
7334 dp=text[0].text;
7335 *dp++='\n';
7336 (void) CopyMagickString(dp,(const char *) profile_description,
7337 allocated_length);
7338 dp+=description_length;
7339 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007340 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007341 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007342 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007343
cristybb503372010-05-27 20:51:26 +00007344 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007345 {
7346 if (i%36 == 0)
7347 *dp++='\n';
7348 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7349 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7350 }
glennrp47b9dd52010-11-24 18:12:06 +00007351
cristy3ed852e2009-09-05 21:47:34 +00007352 *dp++='\n';
7353 *dp='\0';
7354 text[0].text_length=(png_size_t) (dp-text[0].text);
7355 text[0].compression=image_info->compression == NoCompression ||
7356 (image_info->compression == UndefinedCompression &&
7357 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007358
cristy3ed852e2009-09-05 21:47:34 +00007359 if (text[0].text_length <= allocated_length)
7360 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007361
cristy3ed852e2009-09-05 21:47:34 +00007362 png_free(ping,text[0].text);
7363 png_free(ping,text[0].key);
7364 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007365}
7366
glennrpcf002022011-01-30 02:38:15 +00007367static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007368 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007369{
7370 char
7371 *name;
7372
7373 const StringInfo
7374 *profile;
7375
7376 unsigned char
7377 *data;
7378
7379 png_uint_32 length;
7380
7381 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007382
7383 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7384 {
cristy3ed852e2009-09-05 21:47:34 +00007385 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007386
cristy3ed852e2009-09-05 21:47:34 +00007387 if (profile != (const StringInfo *) NULL)
7388 {
7389 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007390 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007391
glennrp47b9dd52010-11-24 18:12:06 +00007392 if (LocaleNCompare(name,string,11) == 0)
7393 {
7394 if (logging != MagickFalse)
7395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7396 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007397
glennrpcf002022011-01-30 02:38:15 +00007398 ping_profile=CloneStringInfo(profile);
7399 data=GetStringInfoDatum(ping_profile),
7400 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007401 data[4]=data[3];
7402 data[3]=data[2];
7403 data[2]=data[1];
7404 data[1]=data[0];
7405 (void) WriteBlobMSBULong(image,length-5); /* data length */
7406 (void) WriteBlob(image,length-1,data+1);
7407 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007408 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007409 }
cristy3ed852e2009-09-05 21:47:34 +00007410 }
glennrp47b9dd52010-11-24 18:12:06 +00007411
cristy3ed852e2009-09-05 21:47:34 +00007412 name=GetNextImageProfile(image);
7413 }
glennrp47b9dd52010-11-24 18:12:06 +00007414
cristy3ed852e2009-09-05 21:47:34 +00007415 return(MagickTrue);
7416}
7417
glennrpb9cfe272010-12-21 15:08:06 +00007418
cristy3ed852e2009-09-05 21:47:34 +00007419/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007420static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +00007421 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007422{
7423 Image
7424 *image;
7425
7426 ImageInfo
7427 *image_info;
7428
cristy3ed852e2009-09-05 21:47:34 +00007429 char
7430 s[2];
7431
7432 const char
7433 *name,
7434 *property,
7435 *value;
7436
7437 const StringInfo
7438 *profile;
7439
cristy3ed852e2009-09-05 21:47:34 +00007440 int
cristy3ed852e2009-09-05 21:47:34 +00007441 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007442 pass;
7443
glennrpe9c26dc2010-05-30 01:56:35 +00007444 png_byte
7445 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007446
glennrp39992b42010-11-14 00:03:43 +00007447 png_color
7448 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007449
glennrp5af765f2010-03-30 11:12:18 +00007450 png_color_16
7451 ping_background,
7452 ping_trans_color;
7453
cristy3ed852e2009-09-05 21:47:34 +00007454 png_info
7455 *ping_info;
7456
7457 png_struct
7458 *ping;
7459
glennrp5af765f2010-03-30 11:12:18 +00007460 png_uint_32
7461 ping_height,
7462 ping_width;
7463
cristybb503372010-05-27 20:51:26 +00007464 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007465 y;
7466
7467 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007468 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007469 logging,
glennrp58e01762011-01-07 15:28:54 +00007470 matte,
7471
glennrpda8f3a72011-02-27 23:54:12 +00007472 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007473 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007474 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007475 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007476 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007477 ping_have_bKGD,
7478 ping_have_pHYs,
7479 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007480
7481 ping_exclude_bKGD,
7482 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007483 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007484 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007485 ping_exclude_gAMA,
7486 ping_exclude_iCCP,
7487 /* ping_exclude_iTXt, */
7488 ping_exclude_oFFs,
7489 ping_exclude_pHYs,
7490 ping_exclude_sRGB,
7491 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007492 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007493 ping_exclude_vpAg,
7494 ping_exclude_zCCP, /* hex-encoded iCCP */
7495 ping_exclude_zTXt,
7496
glennrp8d3d6e52011-04-19 04:39:51 +00007497 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007498 ping_need_colortype_warning,
7499
glennrp82b3c532011-03-22 19:20:54 +00007500 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007501 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007502 tried_333,
7503 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007504
7505 QuantumInfo
7506 *quantum_info;
7507
cristybb503372010-05-27 20:51:26 +00007508 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007509 i,
7510 x;
7511
7512 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007513 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007514
glennrp5af765f2010-03-30 11:12:18 +00007515 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007516 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007517 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007518 ping_color_type,
7519 ping_interlace_method,
7520 ping_compression_method,
7521 ping_filter_method,
7522 ping_num_trans;
7523
cristybb503372010-05-27 20:51:26 +00007524 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007525 image_depth,
7526 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007527
cristybb503372010-05-27 20:51:26 +00007528 size_t
cristy3ed852e2009-09-05 21:47:34 +00007529 quality,
7530 rowbytes,
7531 save_image_depth;
7532
glennrpdfd70802010-11-14 01:23:35 +00007533 int
glennrpfd05d622011-02-25 04:10:33 +00007534 j,
glennrpf09bded2011-01-08 01:15:59 +00007535 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007536 number_opaque,
7537 number_semitransparent,
7538 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007539 ping_pHYs_unit_type;
7540
7541 png_uint_32
7542 ping_pHYs_x_resolution,
7543 ping_pHYs_y_resolution;
7544
cristy3ed852e2009-09-05 21:47:34 +00007545 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007546 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007547
glennrpb9cfe272010-12-21 15:08:06 +00007548 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7549 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007550 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007551 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007552
cristy3ed852e2009-09-05 21:47:34 +00007553#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007554 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007555#endif
7556
glennrp5af765f2010-03-30 11:12:18 +00007557 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007558 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007559 ping_color_type=0,
7560 ping_interlace_method=0,
7561 ping_compression_method=0,
7562 ping_filter_method=0,
7563 ping_num_trans = 0;
7564
7565 ping_background.red = 0;
7566 ping_background.green = 0;
7567 ping_background.blue = 0;
7568 ping_background.gray = 0;
7569 ping_background.index = 0;
7570
7571 ping_trans_color.red=0;
7572 ping_trans_color.green=0;
7573 ping_trans_color.blue=0;
7574 ping_trans_color.gray=0;
7575
glennrpdfd70802010-11-14 01:23:35 +00007576 ping_pHYs_unit_type = 0;
7577 ping_pHYs_x_resolution = 0;
7578 ping_pHYs_y_resolution = 0;
7579
glennrpda8f3a72011-02-27 23:54:12 +00007580 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007581 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007582 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007583 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007584 ping_have_bKGD=MagickFalse;
7585 ping_have_pHYs=MagickFalse;
7586 ping_have_tRNS=MagickFalse;
7587
glennrp0e8ea192010-12-24 18:00:33 +00007588 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7589 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007590 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007591 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007592 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007593 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7594 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7595 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7596 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7597 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7598 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007599 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007600 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7601 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7602 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7603
glennrp8d3d6e52011-04-19 04:39:51 +00007604 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007605 ping_need_colortype_warning = MagickFalse;
7606
cristy0d57eec2011-09-04 22:13:56 +00007607 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7608 * i.e., eliminate the ICC profile and set image->rendering_intent.
7609 * Note that this will not involve any changes to the actual pixels
7610 * but merely passes information to applications that read the resulting
7611 * PNG image.
7612 */
7613 if (ping_exclude_sRGB == MagickFalse)
7614 {
7615 char
7616 *name;
7617
7618 const StringInfo
7619 *profile;
7620
7621 ResetImageProfileIterator(image);
7622 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7623 {
7624 profile=GetImageProfile(image,name);
7625
7626 if (profile != (StringInfo *) NULL)
7627 {
7628 if ((LocaleCompare(name,"ICC") == 0) ||
glennrp29a106e2011-09-06 17:11:42 +00007629 (LocaleCompare(name,"ICM") == 0))
7630 {
cristy0d57eec2011-09-04 22:13:56 +00007631 unsigned char
7632 *data;
7633
7634 png_uint_32
7635 length;
7636
glennrp29a106e2011-09-06 17:11:42 +00007637 length=(png_uint_32) GetStringInfoLength(profile);
7638
7639 if (length == 3144)
cristy0d57eec2011-09-04 22:13:56 +00007640 {
glennrp29a106e2011-09-06 17:11:42 +00007641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7642 " got a 3144-byte ICC profile (potentially sRGB)");
cristy0d57eec2011-09-04 22:13:56 +00007643
glennrp29a106e2011-09-06 17:11:42 +00007644 data=GetStringInfoDatum(profile);
7645
7646 if (data[52]=='s' && data[53]=='R' &&
7647 data[54]=='G' && data[55]=='B')
7648 {
cristy0d57eec2011-09-04 22:13:56 +00007649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp29a106e2011-09-06 17:11:42 +00007650 " It is the HP-Microsoft sRGB)");
7651 if (image->rendering_intent==UndefinedIntent);
7652 image->rendering_intent=PerceptualIntent;
7653 }
7654 else
7655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7656 " It is not sRGB (%c%c%c%c)",data[52],
7657 data[53],data[54],data[55]);
7658
cristy0d57eec2011-09-04 22:13:56 +00007659 }
glennrp29a106e2011-09-06 17:11:42 +00007660 else if (length == 3052)
7661 {
7662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7663 " got a 3052-byte ICC profile (potentially sRGB)");
7664
7665 data=GetStringInfoDatum(profile);
7666
7667 if (data[336]=='s' && data[337]=='R' &&
7668 data[338]=='G' && data[339]=='B')
7669 {
7670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7671 " It is the ICC no-black sRGB)");
7672 if (image->rendering_intent==UndefinedIntent);
7673 image->rendering_intent=PerceptualIntent;
7674 }
7675 else
7676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7677 " It is not sRGB (%c%c%c%c)",data[52],
7678 data[53],data[54],data[55]);
7679
7680 }
7681 else
7682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7683 " got a %lu-byte ICC profile",
7684 (unsigned long) length);
7685 }
cristy0d57eec2011-09-04 22:13:56 +00007686 }
7687 name=GetNextImageProfile(image);
7688 }
7689 }
7690
glennrp8bb3a022010-12-13 20:40:04 +00007691 number_opaque = 0;
7692 number_semitransparent = 0;
7693 number_transparent = 0;
7694
glennrpfd05d622011-02-25 04:10:33 +00007695 if (logging != MagickFalse)
7696 {
7697 if (image->storage_class == UndefinedClass)
7698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7699 " storage_class=UndefinedClass");
7700 if (image->storage_class == DirectClass)
7701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7702 " storage_class=DirectClass");
7703 if (image->storage_class == PseudoClass)
7704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7705 " storage_class=PseudoClass");
7706 }
glennrp28af3712011-04-06 18:07:30 +00007707
glennrp7e65e932011-08-19 02:31:16 +00007708 if (image->storage_class == PseudoClass &&
7709 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7710 (mng_info->write_png_colortype != 0 &&
7711 mng_info->write_png_colortype != 4)))
7712 {
7713 (void) SyncImage(image);
7714 image->storage_class = DirectClass;
7715 }
7716
glennrpc6c391a2011-04-27 02:23:56 +00007717 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007718 {
glennrpc6c391a2011-04-27 02:23:56 +00007719 if (image->storage_class != PseudoClass && image->colormap != NULL)
7720 {
7721 /* Free the bogus colormap; it can cause trouble later */
7722 if (logging != MagickFalse)
7723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7724 " Freeing bogus colormap");
7725 (void *) RelinquishMagickMemory(image->colormap);
7726 image->colormap=NULL;
7727 }
glennrp28af3712011-04-06 18:07:30 +00007728 }
glennrpbb4f99d2011-05-22 11:13:17 +00007729
cristy510d06a2011-07-06 23:43:54 +00007730 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007731 (void) TransformImageColorspace(image,RGBColorspace);
glennrp0fe50b42010-11-16 03:52:51 +00007732
glennrp3241bd02010-12-12 04:36:28 +00007733 /*
7734 Sometimes we get PseudoClass images whose RGB values don't match
7735 the colors in the colormap. This code syncs the RGB values.
7736 */
7737 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7738 (void) SyncImage(image);
7739
glennrpa6a06632011-01-19 15:15:34 +00007740#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7741 if (image->depth > 8)
7742 {
7743 if (logging != MagickFalse)
7744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7745 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7746
7747 image->depth=8;
7748 }
7749#endif
7750
glennrp8e58efd2011-05-20 12:16:29 +00007751 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007752 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7753 {
cristy4c08aed2011-07-01 19:47:50 +00007754 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007755 *r;
7756
7757 ExceptionInfo
7758 *exception;
7759
7760 exception=(&image->exception);
7761
7762 if (image->depth > 8)
7763 {
7764#if MAGICKCORE_QUANTUM_DEPTH > 16
7765 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007766 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007767
7768 for (y=0; y < (ssize_t) image->rows; y++)
7769 {
7770 r=GetAuthenticPixels(image,0,y,image->columns,1,
7771 exception);
7772
cristy4c08aed2011-07-01 19:47:50 +00007773 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007774 break;
7775
7776 for (x=0; x < (ssize_t) image->columns; x++)
7777 {
glennrp54cf7972011-08-06 14:28:09 +00007778 LBR16PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007779 r++;
7780 }
glennrpbb4f99d2011-05-22 11:13:17 +00007781
glennrp8e58efd2011-05-20 12:16:29 +00007782 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7783 break;
7784 }
7785
7786 if (image->storage_class == PseudoClass && image->colormap != NULL)
7787 {
cristy3e08f112011-05-24 13:19:30 +00007788 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007789 {
glennrp91d99252011-06-25 14:30:13 +00007790 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007791 }
7792 }
7793#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7794 }
7795
7796 else if (image->depth > 4)
7797 {
7798#if MAGICKCORE_QUANTUM_DEPTH > 8
7799 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007800 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007801
7802 for (y=0; y < (ssize_t) image->rows; y++)
7803 {
7804 r=GetAuthenticPixels(image,0,y,image->columns,1,
7805 exception);
7806
cristy4c08aed2011-07-01 19:47:50 +00007807 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007808 break;
7809
7810 for (x=0; x < (ssize_t) image->columns; x++)
7811 {
glennrp54cf7972011-08-06 14:28:09 +00007812 LBR08PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007813 r++;
7814 }
glennrpbb4f99d2011-05-22 11:13:17 +00007815
glennrp8e58efd2011-05-20 12:16:29 +00007816 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7817 break;
7818 }
7819
7820 if (image->storage_class == PseudoClass && image->colormap != NULL)
7821 {
cristy3e08f112011-05-24 13:19:30 +00007822 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007823 {
glennrp91d99252011-06-25 14:30:13 +00007824 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007825 }
7826 }
7827#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7828 }
7829 else
7830 if (image->depth > 2)
7831 {
7832 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007833 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007834
7835 for (y=0; y < (ssize_t) image->rows; y++)
7836 {
7837 r=GetAuthenticPixels(image,0,y,image->columns,1,
7838 exception);
7839
cristy4c08aed2011-07-01 19:47:50 +00007840 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007841 break;
7842
7843 for (x=0; x < (ssize_t) image->columns; x++)
7844 {
glennrp54cf7972011-08-06 14:28:09 +00007845 LBR04PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007846 r++;
7847 }
glennrpbb4f99d2011-05-22 11:13:17 +00007848
glennrp8e58efd2011-05-20 12:16:29 +00007849 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7850 break;
7851 }
7852
7853 if (image->storage_class == PseudoClass && image->colormap != NULL)
7854 {
cristy3e08f112011-05-24 13:19:30 +00007855 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007856 {
glennrp91d99252011-06-25 14:30:13 +00007857 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007858 }
7859 }
7860 }
7861
7862 else if (image->depth > 1)
7863 {
7864 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007865 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007866
7867 for (y=0; y < (ssize_t) image->rows; y++)
7868 {
7869 r=GetAuthenticPixels(image,0,y,image->columns,1,
7870 exception);
7871
cristy4c08aed2011-07-01 19:47:50 +00007872 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007873 break;
7874
7875 for (x=0; x < (ssize_t) image->columns; x++)
7876 {
glennrp54cf7972011-08-06 14:28:09 +00007877 LBR02PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007878 r++;
7879 }
glennrpbb4f99d2011-05-22 11:13:17 +00007880
glennrp8e58efd2011-05-20 12:16:29 +00007881 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7882 break;
7883 }
7884
7885 if (image->storage_class == PseudoClass && image->colormap != NULL)
7886 {
cristy3e08f112011-05-24 13:19:30 +00007887 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007888 {
glennrp91d99252011-06-25 14:30:13 +00007889 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007890 }
7891 }
7892 }
7893 else
7894 {
7895 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007896 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007897
7898 for (y=0; y < (ssize_t) image->rows; y++)
7899 {
7900 r=GetAuthenticPixels(image,0,y,image->columns,1,
7901 exception);
7902
cristy4c08aed2011-07-01 19:47:50 +00007903 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007904 break;
7905
7906 for (x=0; x < (ssize_t) image->columns; x++)
7907 {
glennrp54cf7972011-08-06 14:28:09 +00007908 LBR01PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007909 r++;
7910 }
glennrpbb4f99d2011-05-22 11:13:17 +00007911
glennrp8e58efd2011-05-20 12:16:29 +00007912 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7913 break;
7914 }
7915
7916 if (image->storage_class == PseudoClass && image->colormap != NULL)
7917 {
cristy3e08f112011-05-24 13:19:30 +00007918 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007919 {
glennrp91d99252011-06-25 14:30:13 +00007920 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007921 }
7922 }
7923 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007924 }
7925
glennrp67b9c1a2011-04-22 18:47:36 +00007926 /* To do: set to next higher multiple of 8 */
7927 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007928 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007929
glennrp2b013e42010-11-24 16:55:50 +00007930#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7931 /* PNG does not handle depths greater than 16 so reduce it even
7932 * if lossy
7933 */
glennrp8e58efd2011-05-20 12:16:29 +00007934 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007935 image->depth=16;
7936#endif
7937
glennrp3faa9a32011-04-23 14:00:25 +00007938#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007939 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007940 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007941 image->depth = 8;
7942#endif
7943
glennrpc8c2f062011-02-25 19:00:33 +00007944 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007945 * we reduce the transparency to binary and run again, then if there
7946 * 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 +00007947 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7948 * palette. Then (To do) we take care of a final reduction that is only
7949 * needed if there are still 256 colors present and one of them has both
7950 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007951 */
glennrp82b3c532011-03-22 19:20:54 +00007952
glennrp8ca51ad2011-05-12 21:22:32 +00007953 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007954 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007955 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007956
glennrp8ca51ad2011-05-12 21:22:32 +00007957 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007958 {
7959 /* BUILD_PALETTE
7960 *
7961 * Sometimes we get DirectClass images that have 256 colors or fewer.
7962 * This code will build a colormap.
7963 *
7964 * Also, sometimes we get PseudoClass images with an out-of-date
7965 * colormap. This code will replace the colormap with a new one.
7966 * Sometimes we get PseudoClass images that have more than 256 colors.
7967 * This code will delete the colormap and change the image to
7968 * DirectClass.
7969 *
cristy4c08aed2011-07-01 19:47:50 +00007970 * If image->matte is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00007971 * even though it sometimes contains left-over non-opaque values.
7972 *
7973 * Also we gather some information (number of opaque, transparent,
7974 * and semitransparent pixels, and whether the image has any non-gray
7975 * pixels or only black-and-white pixels) that we might need later.
7976 *
7977 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7978 * we need to check for bogus non-opaque values, at least.
7979 */
glennrp3c218112010-11-27 15:31:26 +00007980
glennrpd71e86a2011-02-24 01:28:37 +00007981 ExceptionInfo
7982 *exception;
glennrp3c218112010-11-27 15:31:26 +00007983
glennrpd71e86a2011-02-24 01:28:37 +00007984 int
7985 n;
glennrp3c218112010-11-27 15:31:26 +00007986
glennrpd71e86a2011-02-24 01:28:37 +00007987 PixelPacket
7988 opaque[260],
7989 semitransparent[260],
7990 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007991
cristy4c08aed2011-07-01 19:47:50 +00007992 register const Quantum
7993 *s;
glennrp8bb3a022010-12-13 20:40:04 +00007994
cristy4c08aed2011-07-01 19:47:50 +00007995 register Quantum
7996 *q,
glennrpfd05d622011-02-25 04:10:33 +00007997 *r;
7998
glennrpd71e86a2011-02-24 01:28:37 +00007999 if (logging != MagickFalse)
8000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8001 " Enter BUILD_PALETTE:");
8002
8003 if (logging != MagickFalse)
8004 {
glennrp03812ae2010-12-24 01:31:34 +00008005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008006 " image->columns=%.20g",(double) image->columns);
8007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8008 " image->rows=%.20g",(double) image->rows);
8009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8010 " image->matte=%.20g",(double) image->matte);
8011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8012 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008013
glennrpfd05d622011-02-25 04:10:33 +00008014 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008015 {
8016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008017 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008019 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008020
glennrpd71e86a2011-02-24 01:28:37 +00008021 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008022 {
glennrpd71e86a2011-02-24 01:28:37 +00008023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8024 " %d (%d,%d,%d,%d)",
8025 (int) i,
8026 (int) image->colormap[i].red,
8027 (int) image->colormap[i].green,
8028 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008029 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008030 }
glennrp2cc891a2010-12-24 13:44:32 +00008031
glennrpd71e86a2011-02-24 01:28:37 +00008032 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8033 {
8034 if (i > 255)
8035 {
8036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8037 " %d (%d,%d,%d,%d)",
8038 (int) i,
8039 (int) image->colormap[i].red,
8040 (int) image->colormap[i].green,
8041 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008042 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008043 }
8044 }
glennrp03812ae2010-12-24 01:31:34 +00008045 }
glennrp7ddcc222010-12-11 05:01:05 +00008046
glennrpd71e86a2011-02-24 01:28:37 +00008047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8048 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008049
glennrpd71e86a2011-02-24 01:28:37 +00008050 if (image->colors == 0)
8051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8052 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008053
glennrp8d3d6e52011-04-19 04:39:51 +00008054 if (ping_preserve_colormap == MagickFalse)
8055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8056 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008057 }
8058
8059 exception=(&image->exception);
8060
8061 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008062 number_opaque = 0;
8063 number_semitransparent = 0;
8064 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008065
8066 for (y=0; y < (ssize_t) image->rows; y++)
8067 {
8068 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8069
cristyacd2ed22011-08-30 01:44:23 +00008070 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008071 break;
8072
8073 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008074 {
glennrp4737d522011-04-29 03:33:42 +00008075 if (image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008076 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008077 {
8078 if (number_opaque < 259)
8079 {
8080 if (number_opaque == 0)
8081 {
cristy4c08aed2011-07-01 19:47:50 +00008082 GetPixelPacket(image, q, opaque);
8083 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008084 number_opaque=1;
8085 }
glennrp2cc891a2010-12-24 13:44:32 +00008086
glennrpd71e86a2011-02-24 01:28:37 +00008087 for (i=0; i< (ssize_t) number_opaque; i++)
8088 {
cristy4c08aed2011-07-01 19:47:50 +00008089 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008090 break;
8091 }
glennrp7ddcc222010-12-11 05:01:05 +00008092
glennrpd71e86a2011-02-24 01:28:37 +00008093 if (i == (ssize_t) number_opaque &&
8094 number_opaque < 259)
8095 {
8096 number_opaque++;
cristy4c08aed2011-07-01 19:47:50 +00008097 GetPixelPacket(image, q, opaque+i);
8098 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008099 }
8100 }
8101 }
cristy4c08aed2011-07-01 19:47:50 +00008102 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008103 {
8104 if (number_transparent < 259)
8105 {
8106 if (number_transparent == 0)
8107 {
cristy4c08aed2011-07-01 19:47:50 +00008108 GetPixelPacket(image, q, transparent);
8109 ping_trans_color.red=(unsigned short)
8110 GetPixelRed(image,q);
8111 ping_trans_color.green=(unsigned short)
8112 GetPixelGreen(image,q);
8113 ping_trans_color.blue=(unsigned short)
8114 GetPixelBlue(image,q);
8115 ping_trans_color.gray=(unsigned short)
8116 GetPixelRed(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008117 number_transparent = 1;
8118 }
8119
8120 for (i=0; i< (ssize_t) number_transparent; i++)
8121 {
cristy4c08aed2011-07-01 19:47:50 +00008122 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008123 break;
8124 }
8125
8126 if (i == (ssize_t) number_transparent &&
8127 number_transparent < 259)
8128 {
8129 number_transparent++;
cristy4c08aed2011-07-01 19:47:50 +00008130 GetPixelPacket(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008131 }
8132 }
8133 }
8134 else
8135 {
8136 if (number_semitransparent < 259)
8137 {
8138 if (number_semitransparent == 0)
8139 {
cristy4c08aed2011-07-01 19:47:50 +00008140 GetPixelPacket(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008141 number_semitransparent = 1;
8142 }
8143
8144 for (i=0; i< (ssize_t) number_semitransparent; i++)
8145 {
cristy4c08aed2011-07-01 19:47:50 +00008146 if (IsPixelEquivalent(image,q, semitransparent+i)
8147 && GetPixelAlpha(image,q) ==
8148 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008149 break;
8150 }
8151
8152 if (i == (ssize_t) number_semitransparent &&
8153 number_semitransparent < 259)
8154 {
8155 number_semitransparent++;
cristy4c08aed2011-07-01 19:47:50 +00008156 GetPixelPacket(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008157 }
8158 }
8159 }
cristyed231572011-07-14 02:18:59 +00008160 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008161 }
8162 }
8163
cristy4054bfb2011-08-29 23:41:39 +00008164 if (mng_info->write_png8 == MagickFalse &&
8165 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008166 {
8167 /* Add the background color to the palette, if it
8168 * isn't already there.
8169 */
glennrpc6c391a2011-04-27 02:23:56 +00008170 if (logging != MagickFalse)
8171 {
8172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8173 " Check colormap for background (%d,%d,%d)",
8174 (int) image->background_color.red,
8175 (int) image->background_color.green,
8176 (int) image->background_color.blue);
8177 }
glennrpd71e86a2011-02-24 01:28:37 +00008178 for (i=0; i<number_opaque; i++)
8179 {
glennrpca7ad3a2011-04-26 04:44:54 +00008180 if (opaque[i].red == image->background_color.red &&
8181 opaque[i].green == image->background_color.green &&
8182 opaque[i].blue == image->background_color.blue)
8183 break;
glennrpd71e86a2011-02-24 01:28:37 +00008184 }
glennrpd71e86a2011-02-24 01:28:37 +00008185 if (number_opaque < 259 && i == number_opaque)
8186 {
glennrp8e045c82011-04-27 16:40:27 +00008187 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008188 ping_background.index = i;
8189 if (logging != MagickFalse)
8190 {
8191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8192 " background_color index is %d",(int) i);
8193 }
8194
glennrpd71e86a2011-02-24 01:28:37 +00008195 }
glennrpa080bc32011-03-11 18:03:44 +00008196 else if (logging != MagickFalse)
8197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8198 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008199 }
8200
8201 image_colors=number_opaque+number_transparent+number_semitransparent;
8202
glennrpa080bc32011-03-11 18:03:44 +00008203 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8204 {
8205 /* No room for the background color; remove it. */
8206 number_opaque--;
8207 image_colors--;
8208 }
8209
glennrpd71e86a2011-02-24 01:28:37 +00008210 if (logging != MagickFalse)
8211 {
8212 if (image_colors > 256)
8213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8214 " image has more than 256 colors");
8215
8216 else
8217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8218 " image has %d colors",image_colors);
8219 }
8220
glennrp8d3d6e52011-04-19 04:39:51 +00008221 if (ping_preserve_colormap != MagickFalse)
8222 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008223
glennrpfd05d622011-02-25 04:10:33 +00008224 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008225 {
8226 ping_have_color=MagickFalse;
8227 ping_have_non_bw=MagickFalse;
8228
8229 if(image_colors > 256)
8230 {
8231 for (y=0; y < (ssize_t) image->rows; y++)
8232 {
8233 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8234
cristyacd2ed22011-08-30 01:44:23 +00008235 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008236 break;
8237
glennrpe5e6b802011-07-20 14:44:40 +00008238 s=q;
8239 for (x=0; x < (ssize_t) image->columns; x++)
8240 {
8241 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8242 GetPixelRed(image,s) != GetPixelBlue(image,s))
8243 {
8244 ping_have_color=MagickTrue;
8245 ping_have_non_bw=MagickTrue;
8246 break;
8247 }
8248 s+=GetPixelChannels(image);
8249 }
8250
8251 if (ping_have_color != MagickFalse)
8252 break;
8253
glennrpd71e86a2011-02-24 01:28:37 +00008254 /* Worst case is black-and-white; we are looking at every
8255 * pixel twice.
8256 */
8257
glennrpd71e86a2011-02-24 01:28:37 +00008258 if (ping_have_non_bw == MagickFalse)
8259 {
8260 s=q;
8261 for (x=0; x < (ssize_t) image->columns; x++)
8262 {
cristy4c08aed2011-07-01 19:47:50 +00008263 if (GetPixelRed(image,s) != 0 &&
8264 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008265 {
8266 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008267 break;
glennrpd71e86a2011-02-24 01:28:37 +00008268 }
cristyed231572011-07-14 02:18:59 +00008269 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008270 }
glennrpe5e6b802011-07-20 14:44:40 +00008271 }
glennrpd71e86a2011-02-24 01:28:37 +00008272 }
glennrpbb4f99d2011-05-22 11:13:17 +00008273 }
8274 }
glennrpd71e86a2011-02-24 01:28:37 +00008275
8276 if (image_colors < 257)
8277 {
8278 PixelPacket
8279 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008280
glennrpd71e86a2011-02-24 01:28:37 +00008281 /*
8282 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008283 */
8284
glennrpd71e86a2011-02-24 01:28:37 +00008285 if (logging != MagickFalse)
8286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8287 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008288
glennrpd71e86a2011-02-24 01:28:37 +00008289 /* Sort palette, transparent first */;
8290
8291 n = 0;
8292
8293 for (i=0; i<number_transparent; i++)
8294 colormap[n++] = transparent[i];
8295
8296 for (i=0; i<number_semitransparent; i++)
8297 colormap[n++] = semitransparent[i];
8298
8299 for (i=0; i<number_opaque; i++)
8300 colormap[n++] = opaque[i];
8301
glennrpc6c391a2011-04-27 02:23:56 +00008302 ping_background.index +=
8303 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008304
glennrpd71e86a2011-02-24 01:28:37 +00008305 /* image_colors < 257; search the colormap instead of the pixels
8306 * to get ping_have_color and ping_have_non_bw
8307 */
8308 for (i=0; i<n; i++)
8309 {
8310 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008311 {
glennrpd71e86a2011-02-24 01:28:37 +00008312 if (colormap[i].red != colormap[i].green ||
8313 colormap[i].red != colormap[i].blue)
8314 {
8315 ping_have_color=MagickTrue;
8316 ping_have_non_bw=MagickTrue;
8317 break;
8318 }
8319 }
8320
8321 if (ping_have_non_bw == MagickFalse)
8322 {
8323 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008324 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008325 }
glennrp8bb3a022010-12-13 20:40:04 +00008326 }
8327
glennrpd71e86a2011-02-24 01:28:37 +00008328 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8329 (number_transparent == 0 && number_semitransparent == 0)) &&
8330 (((mng_info->write_png_colortype-1) ==
8331 PNG_COLOR_TYPE_PALETTE) ||
8332 (mng_info->write_png_colortype == 0)))
8333 {
glennrp6185c532011-01-14 17:58:40 +00008334 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008335 {
glennrpd71e86a2011-02-24 01:28:37 +00008336 if (n != (ssize_t) image_colors)
8337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8338 " image_colors (%d) and n (%d) don't match",
8339 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008340
glennrpd71e86a2011-02-24 01:28:37 +00008341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8342 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008343 }
glennrp03812ae2010-12-24 01:31:34 +00008344
glennrpd71e86a2011-02-24 01:28:37 +00008345 image->colors = image_colors;
8346
cristy018f07f2011-09-04 21:15:19 +00008347 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008348 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008349 ThrowWriterException(ResourceLimitError,
8350 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008351
8352 for (i=0; i< (ssize_t) image_colors; i++)
8353 image->colormap[i] = colormap[i];
8354
8355 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008356 {
glennrpd71e86a2011-02-24 01:28:37 +00008357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8358 " image->colors=%d (%d)",
8359 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008360
glennrpd71e86a2011-02-24 01:28:37 +00008361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8362 " Update the pixel indexes");
8363 }
glennrp6185c532011-01-14 17:58:40 +00008364
glennrpfd05d622011-02-25 04:10:33 +00008365 /* Sync the pixel indices with the new colormap */
8366
glennrpd71e86a2011-02-24 01:28:37 +00008367 for (y=0; y < (ssize_t) image->rows; y++)
8368 {
8369 q=GetAuthenticPixels(image,0,y,image->columns,1,
8370 exception);
glennrp6185c532011-01-14 17:58:40 +00008371
cristyacd2ed22011-08-30 01:44:23 +00008372 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008373 break;
glennrp6185c532011-01-14 17:58:40 +00008374
glennrpbb4f99d2011-05-22 11:13:17 +00008375
glennrpd71e86a2011-02-24 01:28:37 +00008376 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008377 {
glennrpd71e86a2011-02-24 01:28:37 +00008378 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008379 {
glennrpd71e86a2011-02-24 01:28:37 +00008380 if ((image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008381 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8382 image->colormap[i].red == GetPixelRed(image,q) &&
8383 image->colormap[i].green == GetPixelGreen(image,q) &&
8384 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008385 {
cristy4c08aed2011-07-01 19:47:50 +00008386 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008387 break;
glennrp6185c532011-01-14 17:58:40 +00008388 }
glennrp6185c532011-01-14 17:58:40 +00008389 }
cristyed231572011-07-14 02:18:59 +00008390 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008391 }
glennrp6185c532011-01-14 17:58:40 +00008392
glennrpd71e86a2011-02-24 01:28:37 +00008393 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8394 break;
8395 }
8396 }
8397 }
8398
8399 if (logging != MagickFalse)
8400 {
8401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8402 " image->colors=%d", (int) image->colors);
8403
8404 if (image->colormap != NULL)
8405 {
8406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008407 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008408
8409 for (i=0; i < (ssize_t) image->colors; i++)
8410 {
cristy72988482011-03-29 16:34:38 +00008411 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008412 {
8413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8414 " %d (%d,%d,%d,%d)",
8415 (int) i,
8416 (int) image->colormap[i].red,
8417 (int) image->colormap[i].green,
8418 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008419 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008420 }
glennrp6185c532011-01-14 17:58:40 +00008421 }
8422 }
glennrp03812ae2010-12-24 01:31:34 +00008423
glennrpd71e86a2011-02-24 01:28:37 +00008424 if (number_transparent < 257)
8425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8426 " number_transparent = %d",
8427 number_transparent);
8428 else
glennrp03812ae2010-12-24 01:31:34 +00008429
glennrpd71e86a2011-02-24 01:28:37 +00008430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8431 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008432
glennrpd71e86a2011-02-24 01:28:37 +00008433 if (number_opaque < 257)
8434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8435 " number_opaque = %d",
8436 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008437
glennrpd71e86a2011-02-24 01:28:37 +00008438 else
8439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8440 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008441
glennrpd71e86a2011-02-24 01:28:37 +00008442 if (number_semitransparent < 257)
8443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8444 " number_semitransparent = %d",
8445 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008446
glennrpd71e86a2011-02-24 01:28:37 +00008447 else
8448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8449 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008450
glennrpd71e86a2011-02-24 01:28:37 +00008451 if (ping_have_non_bw == MagickFalse)
8452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8453 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008454
glennrpd71e86a2011-02-24 01:28:37 +00008455 else if (ping_have_color == MagickFalse)
8456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8457 " All pixels and the background are gray");
8458
8459 else
8460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8461 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008462
glennrp03812ae2010-12-24 01:31:34 +00008463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8464 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008465 }
glennrpfd05d622011-02-25 04:10:33 +00008466
glennrpc8c2f062011-02-25 19:00:33 +00008467 if (mng_info->write_png8 == MagickFalse)
8468 break;
glennrpfd05d622011-02-25 04:10:33 +00008469
glennrpc8c2f062011-02-25 19:00:33 +00008470 /* Make any reductions necessary for the PNG8 format */
8471 if (image_colors <= 256 &&
8472 image_colors != 0 && image->colormap != NULL &&
8473 number_semitransparent == 0 &&
8474 number_transparent <= 1)
8475 break;
8476
8477 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008478 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8479 * transparent color so if more than one is transparent we merge
8480 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008481 */
glennrp130fc452011-08-20 03:43:18 +00008482 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008483 {
8484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8485 " Thresholding the alpha channel to binary");
8486
8487 for (y=0; y < (ssize_t) image->rows; y++)
8488 {
8489 r=GetAuthenticPixels(image,0,y,image->columns,1,
8490 exception);
8491
cristy4c08aed2011-07-01 19:47:50 +00008492 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008493 break;
8494
8495 for (x=0; x < (ssize_t) image->columns; x++)
8496 {
glennrpf73547f2011-08-20 04:40:26 +00008497 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008498 {
cristy4c08aed2011-07-01 19:47:50 +00008499 SetPixelPacket(image,&image->background_color,r);
8500 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008501 }
8502 else
cristy4c08aed2011-07-01 19:47:50 +00008503 SetPixelAlpha(image,OpaqueAlpha,r);
cristyed231572011-07-14 02:18:59 +00008504 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008505 }
glennrpbb4f99d2011-05-22 11:13:17 +00008506
glennrpc8c2f062011-02-25 19:00:33 +00008507 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8508 break;
8509
8510 if (image_colors != 0 && image_colors <= 256 &&
8511 image->colormap != NULL)
8512 for (i=0; i<image_colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00008513 image->colormap[i].alpha =
8514 (image->colormap[i].alpha > TransparentAlpha/2 ?
8515 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008516 }
8517 continue;
8518 }
8519
8520 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008521 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8522 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8523 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008524 */
glennrpd3371642011-03-22 19:42:23 +00008525 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8526 {
8527 if (logging != MagickFalse)
8528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8529 " Quantizing the background color to 4-4-4");
8530
8531 tried_444 = MagickTrue;
8532
glennrp91d99252011-06-25 14:30:13 +00008533 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008534
8535 if (logging != MagickFalse)
8536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8537 " Quantizing the pixel colors to 4-4-4");
8538
8539 if (image->colormap == NULL)
8540 {
8541 for (y=0; y < (ssize_t) image->rows; y++)
8542 {
8543 r=GetAuthenticPixels(image,0,y,image->columns,1,
8544 exception);
8545
cristy4c08aed2011-07-01 19:47:50 +00008546 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008547 break;
8548
8549 for (x=0; x < (ssize_t) image->columns; x++)
8550 {
cristy4c08aed2011-07-01 19:47:50 +00008551 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008552 LBR04PixelRGB(r);
glennrpd3371642011-03-22 19:42:23 +00008553 r++;
8554 }
glennrpbb4f99d2011-05-22 11:13:17 +00008555
glennrpd3371642011-03-22 19:42:23 +00008556 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8557 break;
8558 }
8559 }
8560
8561 else /* Should not reach this; colormap already exists and
8562 must be <= 256 */
8563 {
8564 if (logging != MagickFalse)
8565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8566 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008567
glennrpd3371642011-03-22 19:42:23 +00008568 for (i=0; i<image_colors; i++)
8569 {
glennrp91d99252011-06-25 14:30:13 +00008570 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008571 }
8572 }
8573 continue;
8574 }
8575
glennrp82b3c532011-03-22 19:20:54 +00008576 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8577 {
8578 if (logging != MagickFalse)
8579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8580 " Quantizing the background color to 3-3-3");
8581
8582 tried_333 = MagickTrue;
8583
glennrp91d99252011-06-25 14:30:13 +00008584 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008585
8586 if (logging != MagickFalse)
8587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008588 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008589
8590 if (image->colormap == NULL)
8591 {
8592 for (y=0; y < (ssize_t) image->rows; y++)
8593 {
8594 r=GetAuthenticPixels(image,0,y,image->columns,1,
8595 exception);
8596
cristy4c08aed2011-07-01 19:47:50 +00008597 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008598 break;
8599
8600 for (x=0; x < (ssize_t) image->columns; x++)
8601 {
cristy4c08aed2011-07-01 19:47:50 +00008602 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8603 LBR03RGB(r);
glennrp82b3c532011-03-22 19:20:54 +00008604 r++;
8605 }
glennrpbb4f99d2011-05-22 11:13:17 +00008606
glennrp82b3c532011-03-22 19:20:54 +00008607 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8608 break;
8609 }
8610 }
8611
8612 else /* Should not reach this; colormap already exists and
8613 must be <= 256 */
8614 {
8615 if (logging != MagickFalse)
8616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008617 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008618 for (i=0; i<image_colors; i++)
8619 {
glennrp91d99252011-06-25 14:30:13 +00008620 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008621 }
glennrpd3371642011-03-22 19:42:23 +00008622 }
8623 continue;
glennrp82b3c532011-03-22 19:20:54 +00008624 }
glennrpc8c2f062011-02-25 19:00:33 +00008625
glennrp8ca51ad2011-05-12 21:22:32 +00008626 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008627 {
8628 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008630 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008631
glennrp8ca51ad2011-05-12 21:22:32 +00008632 tried_332 = MagickTrue;
8633
glennrp3faa9a32011-04-23 14:00:25 +00008634 /* Red and green were already done so we only quantize the blue
8635 * channel
8636 */
8637
glennrp91d99252011-06-25 14:30:13 +00008638 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008639
glennrpc8c2f062011-02-25 19:00:33 +00008640 if (logging != MagickFalse)
8641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008642 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008643
glennrpc8c2f062011-02-25 19:00:33 +00008644 if (image->colormap == NULL)
8645 {
8646 for (y=0; y < (ssize_t) image->rows; y++)
8647 {
8648 r=GetAuthenticPixels(image,0,y,image->columns,1,
8649 exception);
8650
cristy4c08aed2011-07-01 19:47:50 +00008651 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008652 break;
8653
8654 for (x=0; x < (ssize_t) image->columns; x++)
8655 {
cristy4c08aed2011-07-01 19:47:50 +00008656 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008657 LBR02PixelBlue(r);
glennrp52a479c2011-02-26 21:14:38 +00008658 r++;
glennrpc8c2f062011-02-25 19:00:33 +00008659 }
glennrpbb4f99d2011-05-22 11:13:17 +00008660
glennrpc8c2f062011-02-25 19:00:33 +00008661 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8662 break;
8663 }
8664 }
glennrpfd05d622011-02-25 04:10:33 +00008665
glennrpc8c2f062011-02-25 19:00:33 +00008666 else /* Should not reach this; colormap already exists and
8667 must be <= 256 */
8668 {
8669 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008671 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008672 for (i=0; i<image_colors; i++)
8673 {
glennrp91d99252011-06-25 14:30:13 +00008674 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008675 }
8676 }
8677 continue;
8678 }
8679 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008680
8681 if (image_colors == 0 || image_colors > 256)
8682 {
8683 /* Take care of special case with 256 colors + 1 transparent
8684 * color. We don't need to quantize to 2-3-2-1; we only need to
8685 * eliminate one color, so we'll merge the two darkest red
8686 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8687 */
8688 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8689 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8690 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8691 {
8692 image->background_color.red=ScaleCharToQuantum(0x24);
8693 }
glennrpbb4f99d2011-05-22 11:13:17 +00008694
glennrp8ca51ad2011-05-12 21:22:32 +00008695 if (image->colormap == NULL)
8696 {
8697 for (y=0; y < (ssize_t) image->rows; y++)
8698 {
8699 r=GetAuthenticPixels(image,0,y,image->columns,1,
8700 exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008701
cristy4c08aed2011-07-01 19:47:50 +00008702 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008703 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008704
glennrp8ca51ad2011-05-12 21:22:32 +00008705 for (x=0; x < (ssize_t) image->columns; x++)
8706 {
cristy4c08aed2011-07-01 19:47:50 +00008707 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8708 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8709 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8710 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008711 {
cristy4c08aed2011-07-01 19:47:50 +00008712 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008713 }
cristyed231572011-07-14 02:18:59 +00008714 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008715 }
glennrpbb4f99d2011-05-22 11:13:17 +00008716
glennrp8ca51ad2011-05-12 21:22:32 +00008717 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8718 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008719
glennrp8ca51ad2011-05-12 21:22:32 +00008720 }
8721 }
8722
8723 else
8724 {
8725 for (i=0; i<image_colors; i++)
8726 {
8727 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8728 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8729 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8730 {
8731 image->colormap[i].red=ScaleCharToQuantum(0x24);
8732 }
8733 }
8734 }
8735 }
glennrpd71e86a2011-02-24 01:28:37 +00008736 }
glennrpfd05d622011-02-25 04:10:33 +00008737 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008738
glennrpfd05d622011-02-25 04:10:33 +00008739 /* If we are excluding the tRNS chunk and there is transparency,
8740 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8741 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008742 */
glennrp0e8ea192010-12-24 18:00:33 +00008743 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8744 (number_transparent != 0 || number_semitransparent != 0))
8745 {
glennrpd17915c2011-04-29 14:24:22 +00008746 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008747
8748 if (ping_have_color == MagickFalse)
8749 mng_info->write_png_colortype = 5;
8750
8751 else
8752 mng_info->write_png_colortype = 7;
8753
glennrp8d579662011-02-23 02:05:02 +00008754 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008755 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008756 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008757
glennrp0e8ea192010-12-24 18:00:33 +00008758 }
8759
glennrpfd05d622011-02-25 04:10:33 +00008760 /* See if cheap transparency is possible. It is only possible
8761 * when there is a single transparent color, no semitransparent
8762 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008763 * as the transparent color. We only need this information if
8764 * we are writing a PNG with colortype 0 or 2, and we have not
8765 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008766 */
glennrp5a39f372011-02-25 04:52:16 +00008767 if (number_transparent == 1 &&
8768 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008769 {
8770 ping_have_cheap_transparency = MagickTrue;
8771
8772 if (number_semitransparent != 0)
8773 ping_have_cheap_transparency = MagickFalse;
8774
8775 else if (image_colors == 0 || image_colors > 256 ||
8776 image->colormap == NULL)
8777 {
8778 ExceptionInfo
8779 *exception;
8780
cristy4c08aed2011-07-01 19:47:50 +00008781 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008782 *q;
8783
8784 exception=(&image->exception);
8785
8786 for (y=0; y < (ssize_t) image->rows; y++)
8787 {
8788 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8789
cristyacd2ed22011-08-30 01:44:23 +00008790 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008791 break;
8792
8793 for (x=0; x < (ssize_t) image->columns; x++)
8794 {
cristy4c08aed2011-07-01 19:47:50 +00008795 if (GetPixelAlpha(image,q) != TransparentAlpha &&
glennrp847370c2011-07-05 17:37:15 +00008796 (unsigned short) GetPixelRed(image,q) ==
8797 ping_trans_color.red &&
8798 (unsigned short) GetPixelGreen(image,q) ==
8799 ping_trans_color.green &&
8800 (unsigned short) GetPixelBlue(image,q) ==
8801 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008802 {
8803 ping_have_cheap_transparency = MagickFalse;
8804 break;
8805 }
8806
cristyed231572011-07-14 02:18:59 +00008807 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008808 }
glennrpbb4f99d2011-05-22 11:13:17 +00008809
glennrpfd05d622011-02-25 04:10:33 +00008810 if (ping_have_cheap_transparency == MagickFalse)
8811 break;
8812 }
8813 }
8814 else
8815 {
glennrp67b9c1a2011-04-22 18:47:36 +00008816 /* Assuming that image->colormap[0] is the one transparent color
8817 * and that all others are opaque.
8818 */
glennrpfd05d622011-02-25 04:10:33 +00008819 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008820 for (i=1; i<image_colors; i++)
8821 if (image->colormap[i].red == image->colormap[0].red &&
8822 image->colormap[i].green == image->colormap[0].green &&
8823 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008824 {
glennrp67b9c1a2011-04-22 18:47:36 +00008825 ping_have_cheap_transparency = MagickFalse;
8826 break;
glennrpfd05d622011-02-25 04:10:33 +00008827 }
8828 }
glennrpbb4f99d2011-05-22 11:13:17 +00008829
glennrpfd05d622011-02-25 04:10:33 +00008830 if (logging != MagickFalse)
8831 {
8832 if (ping_have_cheap_transparency == MagickFalse)
8833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8834 " Cheap transparency is not possible.");
8835
8836 else
8837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8838 " Cheap transparency is possible.");
8839 }
8840 }
8841 else
8842 ping_have_cheap_transparency = MagickFalse;
8843
glennrp8640fb52010-11-23 15:48:26 +00008844 image_depth=image->depth;
8845
glennrp26c990a2010-11-23 02:23:20 +00008846 quantum_info = (QuantumInfo *) NULL;
8847 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008848 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008849 image_matte=image->matte;
8850
glennrp0fe50b42010-11-16 03:52:51 +00008851 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008852 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008853
glennrp52a479c2011-02-26 21:14:38 +00008854 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8855 (image->colors == 0 || image->colormap == NULL))
8856 {
glennrp52a479c2011-02-26 21:14:38 +00008857 image_info=DestroyImageInfo(image_info);
8858 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008859 (void) ThrowMagickException(&IMimage->exception,
8860 GetMagickModule(),CoderError,
8861 "Cannot write PNG8 or color-type 3; colormap is NULL",
8862 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008863#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8864 UnlockSemaphoreInfo(ping_semaphore);
8865#endif
8866 return(MagickFalse);
8867 }
8868
cristy3ed852e2009-09-05 21:47:34 +00008869 /*
8870 Allocate the PNG structures
8871 */
8872#ifdef PNG_USER_MEM_SUPPORTED
8873 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008874 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8875 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008876
cristy3ed852e2009-09-05 21:47:34 +00008877#else
8878 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008879 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008880
cristy3ed852e2009-09-05 21:47:34 +00008881#endif
8882 if (ping == (png_struct *) NULL)
8883 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008884
cristy3ed852e2009-09-05 21:47:34 +00008885 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008886
cristy3ed852e2009-09-05 21:47:34 +00008887 if (ping_info == (png_info *) NULL)
8888 {
8889 png_destroy_write_struct(&ping,(png_info **) NULL);
8890 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8891 }
glennrp0fe50b42010-11-16 03:52:51 +00008892
cristy3ed852e2009-09-05 21:47:34 +00008893 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008894 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008895
glennrp5af765f2010-03-30 11:12:18 +00008896 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008897 {
8898 /*
8899 PNG write failed.
8900 */
8901#ifdef PNG_DEBUG
8902 if (image_info->verbose)
8903 (void) printf("PNG write has failed.\n");
8904#endif
8905 png_destroy_write_struct(&ping,&ping_info);
8906#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008907 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008908#endif
glennrpda8f3a72011-02-27 23:54:12 +00008909 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008910 (void) CloseBlob(image);
8911 image_info=DestroyImageInfo(image_info);
8912 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008913 return(MagickFalse);
8914 }
8915 /*
8916 Prepare PNG for writing.
8917 */
8918#if defined(PNG_MNG_FEATURES_SUPPORTED)
8919 if (mng_info->write_mng)
8920 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008921
cristy3ed852e2009-09-05 21:47:34 +00008922#else
8923# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8924 if (mng_info->write_mng)
8925 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008926
cristy3ed852e2009-09-05 21:47:34 +00008927# endif
8928#endif
glennrp2b013e42010-11-24 16:55:50 +00008929
cristy3ed852e2009-09-05 21:47:34 +00008930 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008931
cristy4e5bc842010-06-09 13:56:01 +00008932 ping_width=(png_uint_32) image->columns;
8933 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008934
cristy3ed852e2009-09-05 21:47:34 +00008935 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8936 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008937
cristy3ed852e2009-09-05 21:47:34 +00008938 if (mng_info->write_png_depth != 0)
8939 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008940
cristy3ed852e2009-09-05 21:47:34 +00008941 /* Adjust requested depth to next higher valid depth if necessary */
8942 if (image_depth > 8)
8943 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008944
cristy3ed852e2009-09-05 21:47:34 +00008945 if ((image_depth > 4) && (image_depth < 8))
8946 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008947
cristy3ed852e2009-09-05 21:47:34 +00008948 if (image_depth == 3)
8949 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008950
cristy3ed852e2009-09-05 21:47:34 +00008951 if (logging != MagickFalse)
8952 {
8953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008954 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008956 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008958 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008960 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008962 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008963 }
glennrp8640fb52010-11-23 15:48:26 +00008964
cristy3ed852e2009-09-05 21:47:34 +00008965 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008966 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008967
glennrp26f37912010-12-23 16:22:42 +00008968
cristy3ed852e2009-09-05 21:47:34 +00008969#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008970 if (ping_exclude_pHYs == MagickFalse)
8971 {
cristy3ed852e2009-09-05 21:47:34 +00008972 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8973 (!mng_info->write_mng || !mng_info->equal_physs))
8974 {
glennrp0fe50b42010-11-16 03:52:51 +00008975 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8977 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008978
8979 if (image->units == PixelsPerInchResolution)
8980 {
glennrpdfd70802010-11-14 01:23:35 +00008981 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008982 ping_pHYs_x_resolution=
8983 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8984 ping_pHYs_y_resolution=
8985 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008986 }
glennrpdfd70802010-11-14 01:23:35 +00008987
cristy3ed852e2009-09-05 21:47:34 +00008988 else if (image->units == PixelsPerCentimeterResolution)
8989 {
glennrpdfd70802010-11-14 01:23:35 +00008990 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008991 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8992 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008993 }
glennrp991d11d2010-11-12 21:55:28 +00008994
cristy3ed852e2009-09-05 21:47:34 +00008995 else
8996 {
glennrpdfd70802010-11-14 01:23:35 +00008997 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8998 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8999 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00009000 }
glennrp991d11d2010-11-12 21:55:28 +00009001
glennrp823b55c2011-03-14 18:46:46 +00009002 if (logging != MagickFalse)
9003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9004 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9005 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9006 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009007 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009008 }
glennrp26f37912010-12-23 16:22:42 +00009009 }
cristy3ed852e2009-09-05 21:47:34 +00009010#endif
glennrpa521b2f2010-10-29 04:11:03 +00009011
glennrp26f37912010-12-23 16:22:42 +00009012 if (ping_exclude_bKGD == MagickFalse)
9013 {
glennrpa521b2f2010-10-29 04:11:03 +00009014 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009015 {
glennrpa521b2f2010-10-29 04:11:03 +00009016 unsigned int
9017 mask;
cristy3ed852e2009-09-05 21:47:34 +00009018
glennrpa521b2f2010-10-29 04:11:03 +00009019 mask=0xffff;
9020 if (ping_bit_depth == 8)
9021 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009022
glennrpa521b2f2010-10-29 04:11:03 +00009023 if (ping_bit_depth == 4)
9024 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009025
glennrpa521b2f2010-10-29 04:11:03 +00009026 if (ping_bit_depth == 2)
9027 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009028
glennrpa521b2f2010-10-29 04:11:03 +00009029 if (ping_bit_depth == 1)
9030 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009031
glennrpa521b2f2010-10-29 04:11:03 +00009032 ping_background.red=(png_uint_16)
9033 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009034
glennrpa521b2f2010-10-29 04:11:03 +00009035 ping_background.green=(png_uint_16)
9036 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009037
glennrpa521b2f2010-10-29 04:11:03 +00009038 ping_background.blue=(png_uint_16)
9039 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009040
9041 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009042 }
cristy3ed852e2009-09-05 21:47:34 +00009043
glennrp0fe50b42010-11-16 03:52:51 +00009044 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009045 {
9046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9047 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9049 " background_color index is %d",
9050 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009051
9052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9053 " ping_bit_depth=%d",ping_bit_depth);
9054 }
glennrp0fe50b42010-11-16 03:52:51 +00009055
9056 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009057 }
glennrp0fe50b42010-11-16 03:52:51 +00009058
cristy3ed852e2009-09-05 21:47:34 +00009059 /*
9060 Select the color type.
9061 */
9062 matte=image_matte;
9063 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009064
glennrp1273f7b2011-02-24 03:20:30 +00009065 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009066 {
glennrp0fe50b42010-11-16 03:52:51 +00009067
glennrpfd05d622011-02-25 04:10:33 +00009068 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009069 for reducing the sample depth from 8. */
9070
glennrp0fe50b42010-11-16 03:52:51 +00009071 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009072
glennrp8bb3a022010-12-13 20:40:04 +00009073 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009074
9075 /*
9076 Set image palette.
9077 */
9078 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9079
glennrp0fe50b42010-11-16 03:52:51 +00009080 if (logging != MagickFalse)
9081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9082 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009083 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009084
9085 for (i=0; i < (ssize_t) number_colors; i++)
9086 {
9087 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9088 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9089 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9090 if (logging != MagickFalse)
9091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009092#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009093 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009094#else
9095 " %5ld (%5d,%5d,%5d)",
9096#endif
glennrp0fe50b42010-11-16 03:52:51 +00009097 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9098
9099 }
glennrp2b013e42010-11-24 16:55:50 +00009100
glennrp8bb3a022010-12-13 20:40:04 +00009101 ping_have_PLTE=MagickTrue;
9102 image_depth=ping_bit_depth;
9103 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009104
glennrp58e01762011-01-07 15:28:54 +00009105 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009106 {
glennrp0fe50b42010-11-16 03:52:51 +00009107 /*
9108 Identify which colormap entry is transparent.
9109 */
9110 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009111 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009112
glennrp8bb3a022010-12-13 20:40:04 +00009113 for (i=0; i < (ssize_t) number_transparent; i++)
9114 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009115
glennrp0fe50b42010-11-16 03:52:51 +00009116
glennrp2cc891a2010-12-24 13:44:32 +00009117 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009118 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009119
9120 if (ping_num_trans == 0)
9121 ping_have_tRNS=MagickFalse;
9122
glennrp8bb3a022010-12-13 20:40:04 +00009123 else
9124 ping_have_tRNS=MagickTrue;
9125 }
glennrp0fe50b42010-11-16 03:52:51 +00009126
glennrp1273f7b2011-02-24 03:20:30 +00009127 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009128 {
glennrp1273f7b2011-02-24 03:20:30 +00009129 /*
9130 * Identify which colormap entry is the background color.
9131 */
9132
glennrp4f25bd02011-01-01 18:51:28 +00009133 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9134 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9135 break;
glennrp0fe50b42010-11-16 03:52:51 +00009136
glennrp4f25bd02011-01-01 18:51:28 +00009137 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009138
9139 if (logging != MagickFalse)
9140 {
9141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9142 " background_color index is %d",
9143 (int) ping_background.index);
9144 }
glennrp4f25bd02011-01-01 18:51:28 +00009145 }
cristy3ed852e2009-09-05 21:47:34 +00009146 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009147
glennrp7e65e932011-08-19 02:31:16 +00009148 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009149 {
9150 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009151 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009152 }
glennrp0fe50b42010-11-16 03:52:51 +00009153
glennrp7e65e932011-08-19 02:31:16 +00009154 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009155 {
9156 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009157 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009158 }
glennrp0fe50b42010-11-16 03:52:51 +00009159
glennrp8bb3a022010-12-13 20:40:04 +00009160 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009161 {
glennrp5af765f2010-03-30 11:12:18 +00009162 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009163
glennrp8bb3a022010-12-13 20:40:04 +00009164 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009165 {
glennrp5af765f2010-03-30 11:12:18 +00009166 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009167
glennrp5af765f2010-03-30 11:12:18 +00009168 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9169 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009170 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009171
glennrp8bb3a022010-12-13 20:40:04 +00009172 else
9173 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009174
9175 if (logging != MagickFalse)
9176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9177 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009178 }
glennrp0fe50b42010-11-16 03:52:51 +00009179
glennrp7c4c9e62011-03-21 20:23:32 +00009180 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009181 {
9182 if (logging != MagickFalse)
9183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009184 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009185
glennrpd6bf1612010-12-17 17:28:54 +00009186 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009187 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009188
glennrpd6bf1612010-12-17 17:28:54 +00009189 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009190 {
glennrp5af765f2010-03-30 11:12:18 +00009191 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009192 image_matte=MagickFalse;
9193 }
glennrp0fe50b42010-11-16 03:52:51 +00009194
glennrpd6bf1612010-12-17 17:28:54 +00009195 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009196 {
glennrp5af765f2010-03-30 11:12:18 +00009197 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009198 image_matte=MagickTrue;
9199 }
glennrp0fe50b42010-11-16 03:52:51 +00009200
glennrp5aa37f62011-01-02 03:07:57 +00009201 if (image_info->type == PaletteType ||
9202 image_info->type == PaletteMatteType)
9203 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9204
glennrp7c4c9e62011-03-21 20:23:32 +00009205 if (mng_info->write_png_colortype == 0 &&
9206 (image_info->type == UndefinedType ||
9207 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009208 {
glennrp5aa37f62011-01-02 03:07:57 +00009209 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009210 {
glennrp5aa37f62011-01-02 03:07:57 +00009211 if (image_matte == MagickFalse)
9212 {
9213 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9214 image_matte=MagickFalse;
9215 }
glennrp0fe50b42010-11-16 03:52:51 +00009216
glennrp0b206f52011-01-07 04:55:32 +00009217 else
glennrp5aa37f62011-01-02 03:07:57 +00009218 {
9219 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9220 image_matte=MagickTrue;
9221 }
9222 }
9223 else
glennrp8bb3a022010-12-13 20:40:04 +00009224 {
glennrp5aa37f62011-01-02 03:07:57 +00009225 if (image_matte == MagickFalse)
9226 {
9227 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9228 image_matte=MagickFalse;
9229 }
glennrp8bb3a022010-12-13 20:40:04 +00009230
glennrp0b206f52011-01-07 04:55:32 +00009231 else
glennrp5aa37f62011-01-02 03:07:57 +00009232 {
9233 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9234 image_matte=MagickTrue;
9235 }
9236 }
glennrp0fe50b42010-11-16 03:52:51 +00009237 }
glennrp5aa37f62011-01-02 03:07:57 +00009238
cristy3ed852e2009-09-05 21:47:34 +00009239 }
glennrp0fe50b42010-11-16 03:52:51 +00009240
cristy3ed852e2009-09-05 21:47:34 +00009241 if (logging != MagickFalse)
9242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009243 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009244
glennrp5af765f2010-03-30 11:12:18 +00009245 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009246 {
9247 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9248 ping_color_type == PNG_COLOR_TYPE_RGB ||
9249 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9250 ping_bit_depth=8;
9251 }
cristy3ed852e2009-09-05 21:47:34 +00009252
glennrpd6bf1612010-12-17 17:28:54 +00009253 old_bit_depth=ping_bit_depth;
9254
glennrp5af765f2010-03-30 11:12:18 +00009255 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009256 {
glennrp8d579662011-02-23 02:05:02 +00009257 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9258 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009259 }
glennrp8640fb52010-11-23 15:48:26 +00009260
glennrp5af765f2010-03-30 11:12:18 +00009261 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009262 {
cristy35ef8242010-06-03 16:24:13 +00009263 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009264 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009265
9266 if (image->colors == 0)
9267 {
glennrp0fe50b42010-11-16 03:52:51 +00009268 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00009269 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00009270 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009271 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009272 }
9273
cristy35ef8242010-06-03 16:24:13 +00009274 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009275 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009276 }
glennrp2b013e42010-11-24 16:55:50 +00009277
glennrpd6bf1612010-12-17 17:28:54 +00009278 if (logging != MagickFalse)
9279 {
9280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9281 " Number of colors: %.20g",(double) image_colors);
9282
9283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9284 " Tentative PNG bit depth: %d",ping_bit_depth);
9285 }
9286
9287 if (ping_bit_depth < (int) mng_info->write_png_depth)
9288 ping_bit_depth = mng_info->write_png_depth;
9289 }
glennrp2cc891a2010-12-24 13:44:32 +00009290
glennrp5af765f2010-03-30 11:12:18 +00009291 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009292
cristy3ed852e2009-09-05 21:47:34 +00009293 if (logging != MagickFalse)
9294 {
9295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009296 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009297
cristy3ed852e2009-09-05 21:47:34 +00009298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009299 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009300
cristy3ed852e2009-09-05 21:47:34 +00009301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009302 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009303
cristy3ed852e2009-09-05 21:47:34 +00009304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009305
glennrp8640fb52010-11-23 15:48:26 +00009306 " image->depth: %.20g",(double) image->depth);
9307
9308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009309 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009310 }
9311
glennrp58e01762011-01-07 15:28:54 +00009312 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009313 {
glennrp4f25bd02011-01-01 18:51:28 +00009314 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009315 {
glennrp7c4c9e62011-03-21 20:23:32 +00009316 if (mng_info->write_png_colortype == 0)
9317 {
9318 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009319
glennrp7c4c9e62011-03-21 20:23:32 +00009320 if (ping_have_color != MagickFalse)
9321 ping_color_type=PNG_COLOR_TYPE_RGBA;
9322 }
glennrp4f25bd02011-01-01 18:51:28 +00009323
9324 /*
9325 * Determine if there is any transparent color.
9326 */
9327 if (number_transparent + number_semitransparent == 0)
9328 {
9329 /*
9330 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9331 */
glennrpa6a06632011-01-19 15:15:34 +00009332
glennrp4f25bd02011-01-01 18:51:28 +00009333 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009334
9335 if (mng_info->write_png_colortype == 0)
9336 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009337 }
9338
9339 else
9340 {
9341 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009342 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009343
9344 mask=0xffff;
9345
9346 if (ping_bit_depth == 8)
9347 mask=0x00ff;
9348
9349 if (ping_bit_depth == 4)
9350 mask=0x000f;
9351
9352 if (ping_bit_depth == 2)
9353 mask=0x0003;
9354
9355 if (ping_bit_depth == 1)
9356 mask=0x0001;
9357
9358 ping_trans_color.red=(png_uint_16)
9359 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9360
9361 ping_trans_color.green=(png_uint_16)
9362 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9363
9364 ping_trans_color.blue=(png_uint_16)
9365 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9366
9367 ping_trans_color.gray=(png_uint_16)
cristy4c08aed2011-07-01 19:47:50 +00009368 (ScaleQuantumToShort(GetPixelPacketIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009369 image->colormap)) & mask);
9370
9371 ping_trans_color.index=(png_byte) 0;
9372
9373 ping_have_tRNS=MagickTrue;
9374 }
9375
9376 if (ping_have_tRNS != MagickFalse)
9377 {
9378 /*
glennrpfd05d622011-02-25 04:10:33 +00009379 * Determine if there is one and only one transparent color
9380 * and if so if it is fully transparent.
9381 */
9382 if (ping_have_cheap_transparency == MagickFalse)
9383 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009384 }
9385
9386 if (ping_have_tRNS != MagickFalse)
9387 {
glennrp7c4c9e62011-03-21 20:23:32 +00009388 if (mng_info->write_png_colortype == 0)
9389 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009390
9391 if (image_depth == 8)
9392 {
9393 ping_trans_color.red&=0xff;
9394 ping_trans_color.green&=0xff;
9395 ping_trans_color.blue&=0xff;
9396 ping_trans_color.gray&=0xff;
9397 }
9398 }
9399 }
cristy3ed852e2009-09-05 21:47:34 +00009400 else
9401 {
cristy3ed852e2009-09-05 21:47:34 +00009402 if (image_depth == 8)
9403 {
glennrp5af765f2010-03-30 11:12:18 +00009404 ping_trans_color.red&=0xff;
9405 ping_trans_color.green&=0xff;
9406 ping_trans_color.blue&=0xff;
9407 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009408 }
9409 }
9410 }
glennrp8640fb52010-11-23 15:48:26 +00009411
cristy3ed852e2009-09-05 21:47:34 +00009412 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009413
glennrp2e09f552010-11-14 00:38:48 +00009414 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009415 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009416
glennrp39992b42010-11-14 00:03:43 +00009417 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009418 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009419 ping_have_color == MagickFalse &&
9420 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009421 {
cristy35ef8242010-06-03 16:24:13 +00009422 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009423
cristy3ed852e2009-09-05 21:47:34 +00009424 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009425 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009426
glennrp7c4c9e62011-03-21 20:23:32 +00009427 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009428 {
glennrp5af765f2010-03-30 11:12:18 +00009429 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009430
cristy3ed852e2009-09-05 21:47:34 +00009431 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009432 {
9433 if (logging != MagickFalse)
9434 {
9435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9436 " Scaling ping_trans_color (0)");
9437 }
9438 ping_trans_color.gray*=0x0101;
9439 }
cristy3ed852e2009-09-05 21:47:34 +00009440 }
glennrp0fe50b42010-11-16 03:52:51 +00009441
cristy3ed852e2009-09-05 21:47:34 +00009442 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9443 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009444
glennrp136ee3a2011-04-27 15:47:45 +00009445 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009446 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009447 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009448
cristy3ed852e2009-09-05 21:47:34 +00009449 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009450 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009451
cristy3ed852e2009-09-05 21:47:34 +00009452 else
9453 {
glennrp5af765f2010-03-30 11:12:18 +00009454 ping_bit_depth=8;
9455 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009456 {
9457 if(!mng_info->write_png_depth)
9458 {
glennrp5af765f2010-03-30 11:12:18 +00009459 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009460
cristy35ef8242010-06-03 16:24:13 +00009461 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009462 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009463 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009464 }
9465 }
glennrp2b013e42010-11-24 16:55:50 +00009466
glennrp0fe50b42010-11-16 03:52:51 +00009467 else if (ping_color_type ==
9468 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009469 mng_info->IsPalette)
9470 {
cristy3ed852e2009-09-05 21:47:34 +00009471 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009472
cristy3ed852e2009-09-05 21:47:34 +00009473 int
9474 depth_4_ok=MagickTrue,
9475 depth_2_ok=MagickTrue,
9476 depth_1_ok=MagickTrue;
9477
cristybb503372010-05-27 20:51:26 +00009478 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009479 {
9480 unsigned char
9481 intensity;
9482
9483 intensity=ScaleQuantumToChar(image->colormap[i].red);
9484
9485 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9486 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9487 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9488 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009489 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009490 depth_1_ok=MagickFalse;
9491 }
glennrp2b013e42010-11-24 16:55:50 +00009492
cristy3ed852e2009-09-05 21:47:34 +00009493 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009494 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009495
cristy3ed852e2009-09-05 21:47:34 +00009496 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009497 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009498
cristy3ed852e2009-09-05 21:47:34 +00009499 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009500 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009501 }
9502 }
glennrp2b013e42010-11-24 16:55:50 +00009503
glennrp5af765f2010-03-30 11:12:18 +00009504 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009505 }
glennrp0fe50b42010-11-16 03:52:51 +00009506
cristy3ed852e2009-09-05 21:47:34 +00009507 else
glennrp0fe50b42010-11-16 03:52:51 +00009508
cristy3ed852e2009-09-05 21:47:34 +00009509 if (mng_info->IsPalette)
9510 {
glennrp17a14852010-05-10 03:01:59 +00009511 number_colors=image_colors;
9512
cristy3ed852e2009-09-05 21:47:34 +00009513 if (image_depth <= 8)
9514 {
cristy3ed852e2009-09-05 21:47:34 +00009515 /*
9516 Set image palette.
9517 */
glennrp5af765f2010-03-30 11:12:18 +00009518 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009519
glennrp58e01762011-01-07 15:28:54 +00009520 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009521 {
glennrp9c1eb072010-06-06 22:19:15 +00009522 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009523
glennrp3b51f0e2010-11-27 18:14:08 +00009524 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9526 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009527 }
glennrp0fe50b42010-11-16 03:52:51 +00009528
cristy3ed852e2009-09-05 21:47:34 +00009529 else
9530 {
cristybb503372010-05-27 20:51:26 +00009531 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009532 {
9533 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9534 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9535 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9536 }
glennrp0fe50b42010-11-16 03:52:51 +00009537
glennrp3b51f0e2010-11-27 18:14:08 +00009538 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009540 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009541 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009542
glennrp39992b42010-11-14 00:03:43 +00009543 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009544 }
glennrp0fe50b42010-11-16 03:52:51 +00009545
cristy3ed852e2009-09-05 21:47:34 +00009546 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009547 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009548 {
cristybefe4d22010-06-07 01:18:58 +00009549 size_t
9550 one;
9551
glennrp5af765f2010-03-30 11:12:18 +00009552 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009553 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009554
cristy94b11832011-09-08 19:46:03 +00009555 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009556 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009557 }
glennrp0fe50b42010-11-16 03:52:51 +00009558
glennrp5af765f2010-03-30 11:12:18 +00009559 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009560
glennrp58e01762011-01-07 15:28:54 +00009561 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009562 {
glennrp0fe50b42010-11-16 03:52:51 +00009563 /*
glennrpd6bf1612010-12-17 17:28:54 +00009564 * Set up trans_colors array.
9565 */
glennrp0fe50b42010-11-16 03:52:51 +00009566 assert(number_colors <= 256);
9567
glennrpd6bf1612010-12-17 17:28:54 +00009568 ping_num_trans=(unsigned short) (number_transparent +
9569 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009570
9571 if (ping_num_trans == 0)
9572 ping_have_tRNS=MagickFalse;
9573
glennrpd6bf1612010-12-17 17:28:54 +00009574 else
glennrp0fe50b42010-11-16 03:52:51 +00009575 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009576 if (logging != MagickFalse)
9577 {
9578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9579 " Scaling ping_trans_color (1)");
9580 }
glennrpd6bf1612010-12-17 17:28:54 +00009581 ping_have_tRNS=MagickTrue;
9582
9583 for (i=0; i < ping_num_trans; i++)
9584 {
cristy4c08aed2011-07-01 19:47:50 +00009585 ping_trans_alpha[i]= (png_byte)
9586 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009587 }
glennrp0fe50b42010-11-16 03:52:51 +00009588 }
9589 }
cristy3ed852e2009-09-05 21:47:34 +00009590 }
9591 }
glennrp0fe50b42010-11-16 03:52:51 +00009592
cristy3ed852e2009-09-05 21:47:34 +00009593 else
9594 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009595
cristy3ed852e2009-09-05 21:47:34 +00009596 if (image_depth < 8)
9597 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009598
cristy3ed852e2009-09-05 21:47:34 +00009599 if ((save_image_depth == 16) && (image_depth == 8))
9600 {
glennrp4f25bd02011-01-01 18:51:28 +00009601 if (logging != MagickFalse)
9602 {
9603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9604 " Scaling ping_trans_color from (%d,%d,%d)",
9605 (int) ping_trans_color.red,
9606 (int) ping_trans_color.green,
9607 (int) ping_trans_color.blue);
9608 }
9609
glennrp5af765f2010-03-30 11:12:18 +00009610 ping_trans_color.red*=0x0101;
9611 ping_trans_color.green*=0x0101;
9612 ping_trans_color.blue*=0x0101;
9613 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009614
9615 if (logging != MagickFalse)
9616 {
9617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9618 " to (%d,%d,%d)",
9619 (int) ping_trans_color.red,
9620 (int) ping_trans_color.green,
9621 (int) ping_trans_color.blue);
9622 }
cristy3ed852e2009-09-05 21:47:34 +00009623 }
9624 }
9625
cristy4383ec82011-01-05 15:42:32 +00009626 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9627 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009628
cristy3ed852e2009-09-05 21:47:34 +00009629 /*
9630 Adjust background and transparency samples in sub-8-bit grayscale files.
9631 */
glennrp5af765f2010-03-30 11:12:18 +00009632 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009633 PNG_COLOR_TYPE_GRAY)
9634 {
9635 png_uint_16
9636 maxval;
9637
cristy35ef8242010-06-03 16:24:13 +00009638 size_t
9639 one=1;
9640
cristy22ffd972010-06-03 16:51:47 +00009641 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009642
glennrp4f25bd02011-01-01 18:51:28 +00009643 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009644 {
cristy3ed852e2009-09-05 21:47:34 +00009645
glennrpa521b2f2010-10-29 04:11:03 +00009646 ping_background.gray=(png_uint_16)
glennrp847370c2011-07-05 17:37:15 +00009647 ((maxval/255.)*((GetPixelPacketIntensity(&image->background_color)))
9648 +.5);
cristy3ed852e2009-09-05 21:47:34 +00009649
9650 if (logging != MagickFalse)
9651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009652 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9654 " background_color index is %d",
9655 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009656
glennrp991d11d2010-11-12 21:55:28 +00009657 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009658 }
cristy3ed852e2009-09-05 21:47:34 +00009659
glennrp3e3e20f2011-06-09 04:21:43 +00009660 if (logging != MagickFalse)
9661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9662 " Scaling ping_trans_color.gray from %d",
9663 (int)ping_trans_color.gray);
9664
glennrp9be9b1c2011-06-09 12:21:45 +00009665 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009666 ping_trans_color.gray)+.5);
9667
9668 if (logging != MagickFalse)
9669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9670 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009671 }
glennrp17a14852010-05-10 03:01:59 +00009672
glennrp26f37912010-12-23 16:22:42 +00009673 if (ping_exclude_bKGD == MagickFalse)
9674 {
glennrp1273f7b2011-02-24 03:20:30 +00009675 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009676 {
9677 /*
9678 Identify which colormap entry is the background color.
9679 */
9680
glennrp17a14852010-05-10 03:01:59 +00009681 number_colors=image_colors;
9682
glennrpa521b2f2010-10-29 04:11:03 +00009683 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9684 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009685 break;
9686
9687 ping_background.index=(png_byte) i;
9688
glennrp3b51f0e2010-11-27 18:14:08 +00009689 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009690 {
9691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009692 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009693 }
glennrp0fe50b42010-11-16 03:52:51 +00009694
cristy13d07042010-11-21 20:56:18 +00009695 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009696 {
9697 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009698
9699 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009700 {
9701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9702 " background =(%d,%d,%d)",
9703 (int) ping_background.red,
9704 (int) ping_background.green,
9705 (int) ping_background.blue);
9706 }
9707 }
glennrpa521b2f2010-10-29 04:11:03 +00009708
glennrpd6bf1612010-12-17 17:28:54 +00009709 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009710 {
glennrp3b51f0e2010-11-27 18:14:08 +00009711 if (logging != MagickFalse)
9712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9713 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009714 ping_have_bKGD = MagickFalse;
9715 }
glennrp17a14852010-05-10 03:01:59 +00009716 }
glennrp26f37912010-12-23 16:22:42 +00009717 }
glennrp17a14852010-05-10 03:01:59 +00009718
cristy3ed852e2009-09-05 21:47:34 +00009719 if (logging != MagickFalse)
9720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009721 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009722 /*
9723 Initialize compression level and filtering.
9724 */
9725 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009726 {
9727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9728 " Setting up deflate compression");
9729
9730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9731 " Compression buffer size: 32768");
9732 }
9733
cristy3ed852e2009-09-05 21:47:34 +00009734 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009735
cristy3ed852e2009-09-05 21:47:34 +00009736 if (logging != MagickFalse)
9737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9738 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009739
cristy4054bfb2011-08-29 23:41:39 +00009740 png_set_compression_mem_level(ping, 9);
9741
glennrp10d739e2011-06-29 18:00:52 +00009742 /* Untangle the "-quality" setting:
9743
9744 Undefined is 0; the default is used.
9745 Default is 75
9746
9747 10's digit:
9748
9749 0: Use Z_HUFFMAN_ONLY strategy with the
9750 zlib default compression level
9751
9752 1-9: the zlib compression level
9753
9754 1's digit:
9755
9756 0-4: the PNG filter method
9757
9758 5: libpng adaptive filtering if compression level > 5
9759 libpng filter type "none" if compression level <= 5
9760 or if image is grayscale or palette
9761
9762 6: libpng adaptive filtering
9763
9764 7: "LOCO" filtering (intrapixel differing) if writing
9765 a MNG, othewise "none". Did not work in IM-6.7.0-9
9766 and earlier because of a missing "else".
9767
9768 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009769 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009770
9771 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009772 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009773
9774 Note that using the -quality option, not all combinations of
9775 PNG filter type, zlib compression level, and zlib compression
cristy5e6be1e2011-07-16 01:23:39 +00009776 strategy are possible. This will be addressed soon in a
cristy4054bfb2011-08-29 23:41:39 +00009777 release that accomodates "-define PNG:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +00009778
9779 */
9780
cristy3ed852e2009-09-05 21:47:34 +00009781 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9782 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009783
glennrp18682582011-06-30 18:11:47 +00009784 if (quality <= 9)
9785 {
9786 if (mng_info->write_png_compression_strategy == 0)
9787 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9788 }
9789
9790 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009791 {
9792 int
9793 level;
9794
cristybb503372010-05-27 20:51:26 +00009795 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009796
glennrp18682582011-06-30 18:11:47 +00009797 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009798 }
glennrp0fe50b42010-11-16 03:52:51 +00009799
glennrp18682582011-06-30 18:11:47 +00009800 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009801 {
glennrp18682582011-06-30 18:11:47 +00009802 if ((quality %10) == 8 || (quality %10) == 9)
9803 mng_info->write_png_compression_strategy=Z_RLE;
cristy3ed852e2009-09-05 21:47:34 +00009804 }
glennrp0fe50b42010-11-16 03:52:51 +00009805
glennrp18682582011-06-30 18:11:47 +00009806 if (mng_info->write_png_compression_filter == 0)
9807 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9808
cristy3ed852e2009-09-05 21:47:34 +00009809 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009810 {
glennrp18682582011-06-30 18:11:47 +00009811 if (mng_info->write_png_compression_level)
9812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9813 " Compression level: %d",
9814 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009815
glennrp18682582011-06-30 18:11:47 +00009816 if (mng_info->write_png_compression_strategy)
9817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9818 " Compression strategy: %d",
9819 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009820
glennrp18682582011-06-30 18:11:47 +00009821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9822 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009823
cristy4054bfb2011-08-29 23:41:39 +00009824 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +00009825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9826 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +00009827 else if (mng_info->write_png_compression_filter == 0 ||
9828 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +00009829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9830 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009831 else
9832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9833 " Base filter method: %d",
9834 (int) mng_info->write_png_compression_filter-1);
9835 }
glennrp2b013e42010-11-24 16:55:50 +00009836
glennrp18682582011-06-30 18:11:47 +00009837 if (mng_info->write_png_compression_level != 0)
9838 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9839
9840 if (mng_info->write_png_compression_filter == 6)
9841 {
9842 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9843 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9844 (quality < 50))
9845 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9846 else
9847 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9848 }
cristy4054bfb2011-08-29 23:41:39 +00009849 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +00009850 mng_info->write_png_compression_filter == 10)
9851 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9852
9853 else if (mng_info->write_png_compression_filter == 8)
9854 {
9855#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9856 if (mng_info->write_mng)
9857 {
9858 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9859 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9860 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9861 }
9862#endif
cristy4054bfb2011-08-29 23:41:39 +00009863 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +00009864 }
9865
9866 else if (mng_info->write_png_compression_filter == 9)
9867 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9868
9869 else if (mng_info->write_png_compression_filter != 0)
9870 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9871 mng_info->write_png_compression_filter-1);
9872
9873 if (mng_info->write_png_compression_strategy != 0)
9874 png_set_compression_strategy(ping,
9875 mng_info->write_png_compression_strategy-1);
9876
cristy0d57eec2011-09-04 22:13:56 +00009877 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9878 if (ping_exclude_sRGB != MagickFalse ||
9879 (image->rendering_intent == UndefinedIntent))
9880 {
9881 if ((ping_exclude_tEXt == MagickFalse ||
9882 ping_exclude_zTXt == MagickFalse) &&
9883 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009884 {
9885 ResetImageProfileIterator(image);
9886 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009887 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009888 profile=GetImageProfile(image,name);
9889
9890 if (profile != (StringInfo *) NULL)
9891 {
glennrp5af765f2010-03-30 11:12:18 +00009892#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009893 if ((LocaleCompare(name,"ICC") == 0) ||
9894 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009895 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009896
9897 if (ping_exclude_iCCP == MagickFalse)
9898 {
9899 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009900#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009901 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009902#else
9903 (png_const_bytep) GetStringInfoDatum(profile),
9904#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009905 (png_uint_32) GetStringInfoLength(profile));
9906 }
glennrp26f37912010-12-23 16:22:42 +00009907 }
glennrp0fe50b42010-11-16 03:52:51 +00009908
glennrpc8cbc5d2011-01-01 00:12:34 +00009909 else
cristy3ed852e2009-09-05 21:47:34 +00009910#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009911 if (ping_exclude_zCCP == MagickFalse)
9912 {
glennrpcf002022011-01-30 02:38:15 +00009913 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009914 (unsigned char *) name,(unsigned char *) name,
9915 GetStringInfoDatum(profile),
9916 (png_uint_32) GetStringInfoLength(profile));
9917 }
9918 }
glennrp0b206f52011-01-07 04:55:32 +00009919
glennrpc8cbc5d2011-01-01 00:12:34 +00009920 if (logging != MagickFalse)
9921 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9922 " Setting up text chunk with %s profile",name);
9923
9924 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009925 }
cristy0d57eec2011-09-04 22:13:56 +00009926 }
cristy3ed852e2009-09-05 21:47:34 +00009927 }
9928
9929#if defined(PNG_WRITE_sRGB_SUPPORTED)
9930 if ((mng_info->have_write_global_srgb == 0) &&
9931 ((image->rendering_intent != UndefinedIntent) ||
9932 (image->colorspace == sRGBColorspace)))
9933 {
glennrp26f37912010-12-23 16:22:42 +00009934 if (ping_exclude_sRGB == MagickFalse)
9935 {
9936 /*
9937 Note image rendering intent.
9938 */
9939 if (logging != MagickFalse)
9940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9941 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009942
glennrp26f37912010-12-23 16:22:42 +00009943 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009944 Magick_RenderingIntent_to_PNG_RenderingIntent(
9945 image->rendering_intent)));
glennrp26f37912010-12-23 16:22:42 +00009946 }
cristy3ed852e2009-09-05 21:47:34 +00009947 }
glennrp26f37912010-12-23 16:22:42 +00009948
glennrp5af765f2010-03-30 11:12:18 +00009949 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009950#endif
9951 {
glennrp2cc891a2010-12-24 13:44:32 +00009952 if (ping_exclude_gAMA == MagickFalse &&
9953 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009954 (image->gamma < .45 || image->gamma > .46)))
9955 {
cristy3ed852e2009-09-05 21:47:34 +00009956 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9957 {
9958 /*
9959 Note image gamma.
9960 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9961 */
9962 if (logging != MagickFalse)
9963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9964 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009965
cristy3ed852e2009-09-05 21:47:34 +00009966 png_set_gAMA(ping,ping_info,image->gamma);
9967 }
glennrp26f37912010-12-23 16:22:42 +00009968 }
glennrp2b013e42010-11-24 16:55:50 +00009969
glennrp26f37912010-12-23 16:22:42 +00009970 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009971 {
glennrp26f37912010-12-23 16:22:42 +00009972 if ((mng_info->have_write_global_chrm == 0) &&
9973 (image->chromaticity.red_primary.x != 0.0))
9974 {
9975 /*
9976 Note image chromaticity.
9977 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9978 */
9979 PrimaryInfo
9980 bp,
9981 gp,
9982 rp,
9983 wp;
cristy3ed852e2009-09-05 21:47:34 +00009984
glennrp26f37912010-12-23 16:22:42 +00009985 wp=image->chromaticity.white_point;
9986 rp=image->chromaticity.red_primary;
9987 gp=image->chromaticity.green_primary;
9988 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009989
glennrp26f37912010-12-23 16:22:42 +00009990 if (logging != MagickFalse)
9991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9992 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009993
glennrp26f37912010-12-23 16:22:42 +00009994 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9995 bp.x,bp.y);
9996 }
9997 }
cristy3ed852e2009-09-05 21:47:34 +00009998 }
glennrpdfd70802010-11-14 01:23:35 +00009999
glennrp5af765f2010-03-30 11:12:18 +000010000 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +000010001
10002 if (mng_info->write_mng)
10003 png_set_sig_bytes(ping,8);
10004
10005 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
10006
glennrpd6bf1612010-12-17 17:28:54 +000010007 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +000010008 {
10009 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +000010010 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010011 {
glennrp5af765f2010-03-30 11:12:18 +000010012 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +000010013
glennrp5af765f2010-03-30 11:12:18 +000010014 if (ping_bit_depth < 8)
10015 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +000010016 }
glennrp0fe50b42010-11-16 03:52:51 +000010017
cristy3ed852e2009-09-05 21:47:34 +000010018 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +000010019 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +000010020 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +000010021 }
10022
glennrp0e8ea192010-12-24 18:00:33 +000010023 if (ping_need_colortype_warning != MagickFalse ||
10024 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +000010025 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +000010026 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +000010027 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +000010028 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +000010029 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +000010030 {
10031 if (logging != MagickFalse)
10032 {
glennrp0e8ea192010-12-24 18:00:33 +000010033 if (ping_need_colortype_warning != MagickFalse)
10034 {
10035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10036 " Image has transparency but tRNS chunk was excluded");
10037 }
10038
cristy3ed852e2009-09-05 21:47:34 +000010039 if (mng_info->write_png_depth)
10040 {
10041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10042 " Defined PNG:bit-depth=%u, Computed depth=%u",
10043 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +000010044 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +000010045 }
glennrp0e8ea192010-12-24 18:00:33 +000010046
cristy3ed852e2009-09-05 21:47:34 +000010047 if (mng_info->write_png_colortype)
10048 {
10049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10050 " Defined PNG:color-type=%u, Computed color type=%u",
10051 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +000010052 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010053 }
10054 }
glennrp0e8ea192010-12-24 18:00:33 +000010055
glennrp3bd2e412010-08-10 13:34:52 +000010056 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +000010057 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
10058 }
10059
glennrp58e01762011-01-07 15:28:54 +000010060 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010061 {
10062 /* Add an opaque matte channel */
10063 image->matte = MagickTrue;
10064 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +000010065
glennrpb4a13412010-05-05 12:47:19 +000010066 if (logging != MagickFalse)
10067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10068 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +000010069 }
10070
glennrp0e319732011-01-25 21:53:13 +000010071 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +000010072 {
glennrp991d11d2010-11-12 21:55:28 +000010073 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +000010074 {
glennrp991d11d2010-11-12 21:55:28 +000010075 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010076 if (logging != MagickFalse)
10077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10078 " Setting ping_have_tRNS=MagickTrue.");
10079 }
glennrpe9c26dc2010-05-30 01:56:35 +000010080 }
10081
cristy3ed852e2009-09-05 21:47:34 +000010082 if (logging != MagickFalse)
10083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10084 " Writing PNG header chunks");
10085
glennrp5af765f2010-03-30 11:12:18 +000010086 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10087 ping_bit_depth,ping_color_type,
10088 ping_interlace_method,ping_compression_method,
10089 ping_filter_method);
10090
glennrp39992b42010-11-14 00:03:43 +000010091 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10092 {
glennrpf09bded2011-01-08 01:15:59 +000010093 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010094
glennrp3b51f0e2010-11-27 18:14:08 +000010095 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +000010096 {
glennrp8640fb52010-11-23 15:48:26 +000010097 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +000010098 {
glennrpd6bf1612010-12-17 17:28:54 +000010099 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +000010100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010101 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10102 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010103 (int) palette[i].red,
10104 (int) palette[i].green,
10105 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +000010106 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010107 (int) ping_trans_alpha[i]);
10108 else
10109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010110 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010111 (int) i,
10112 (int) palette[i].red,
10113 (int) palette[i].green,
10114 (int) palette[i].blue);
10115 }
glennrp39992b42010-11-14 00:03:43 +000010116 }
glennrp39992b42010-11-14 00:03:43 +000010117 }
10118
glennrp26f37912010-12-23 16:22:42 +000010119 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010120 {
glennrp26f37912010-12-23 16:22:42 +000010121 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010122 {
glennrp26f37912010-12-23 16:22:42 +000010123 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010124 if (logging)
10125 {
10126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10127 " Setting up bKGD chunk");
10128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10129 " background color = (%d,%d,%d)",
10130 (int) ping_background.red,
10131 (int) ping_background.green,
10132 (int) ping_background.blue);
10133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10134 " index = %d, gray=%d",
10135 (int) ping_background.index,
10136 (int) ping_background.gray);
10137 }
10138 }
glennrp26f37912010-12-23 16:22:42 +000010139 }
10140
10141 if (ping_exclude_pHYs == MagickFalse)
10142 {
10143 if (ping_have_pHYs != MagickFalse)
10144 {
10145 png_set_pHYs(ping,ping_info,
10146 ping_pHYs_x_resolution,
10147 ping_pHYs_y_resolution,
10148 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010149
10150 if (logging)
10151 {
10152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10153 " Setting up pHYs chunk");
10154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10155 " x_resolution=%lu",
10156 (unsigned long) ping_pHYs_x_resolution);
10157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10158 " y_resolution=%lu",
10159 (unsigned long) ping_pHYs_y_resolution);
10160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10161 " unit_type=%lu",
10162 (unsigned long) ping_pHYs_unit_type);
10163 }
glennrp26f37912010-12-23 16:22:42 +000010164 }
glennrpdfd70802010-11-14 01:23:35 +000010165 }
10166
10167#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010168 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010169 {
glennrp26f37912010-12-23 16:22:42 +000010170 if (image->page.x || image->page.y)
10171 {
10172 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10173 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010174
glennrp26f37912010-12-23 16:22:42 +000010175 if (logging != MagickFalse)
10176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10177 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10178 (int) image->page.x, (int) image->page.y);
10179 }
glennrpdfd70802010-11-14 01:23:35 +000010180 }
10181#endif
10182
glennrpda8f3a72011-02-27 23:54:12 +000010183 if (mng_info->need_blob != MagickFalse)
10184 {
10185 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
10186 MagickFalse)
10187 png_error(ping,"WriteBlob Failed");
10188
10189 ping_have_blob=MagickTrue;
10190 }
10191
cristy3ed852e2009-09-05 21:47:34 +000010192 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010193
glennrp39992b42010-11-14 00:03:43 +000010194 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010195 {
glennrp3b51f0e2010-11-27 18:14:08 +000010196 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010197 {
10198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10199 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10200 }
10201
10202 if (ping_color_type == 3)
10203 (void) png_set_tRNS(ping, ping_info,
10204 ping_trans_alpha,
10205 ping_num_trans,
10206 NULL);
10207
10208 else
10209 {
10210 (void) png_set_tRNS(ping, ping_info,
10211 NULL,
10212 0,
10213 &ping_trans_color);
10214
glennrp3b51f0e2010-11-27 18:14:08 +000010215 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010216 {
10217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010218 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010219 (int) ping_trans_color.red,
10220 (int) ping_trans_color.green,
10221 (int) ping_trans_color.blue);
10222 }
10223 }
glennrp991d11d2010-11-12 21:55:28 +000010224 }
10225
cristy3ed852e2009-09-05 21:47:34 +000010226 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010227 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010228
cristy3ed852e2009-09-05 21:47:34 +000010229 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010230
cristy3ed852e2009-09-05 21:47:34 +000010231 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010232 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010233
glennrp26f37912010-12-23 16:22:42 +000010234 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010235 {
glennrp4f25bd02011-01-01 18:51:28 +000010236 if ((image->page.width != 0 && image->page.width != image->columns) ||
10237 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010238 {
10239 unsigned char
10240 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010241
glennrp26f37912010-12-23 16:22:42 +000010242 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10243 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010244 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010245 PNGLong(chunk+4,(png_uint_32) image->page.width);
10246 PNGLong(chunk+8,(png_uint_32) image->page.height);
10247 chunk[12]=0; /* unit = pixels */
10248 (void) WriteBlob(image,13,chunk);
10249 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10250 }
cristy3ed852e2009-09-05 21:47:34 +000010251 }
10252
10253#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010254 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010255#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010256 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010257#undef PNG_HAVE_IDAT
10258#endif
10259
10260 png_set_packing(ping);
10261 /*
10262 Allocate memory.
10263 */
10264 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010265 if (image_depth > 8)
10266 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010267 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010268 {
glennrpb4a13412010-05-05 12:47:19 +000010269 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010270 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010271 break;
glennrp0fe50b42010-11-16 03:52:51 +000010272
glennrpb4a13412010-05-05 12:47:19 +000010273 case PNG_COLOR_TYPE_GRAY_ALPHA:
10274 rowbytes*=2;
10275 break;
glennrp0fe50b42010-11-16 03:52:51 +000010276
glennrpb4a13412010-05-05 12:47:19 +000010277 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010278 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010279 break;
glennrp0fe50b42010-11-16 03:52:51 +000010280
glennrpb4a13412010-05-05 12:47:19 +000010281 default:
10282 break;
cristy3ed852e2009-09-05 21:47:34 +000010283 }
glennrp3b51f0e2010-11-27 18:14:08 +000010284
10285 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010286 {
10287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10288 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010289
glennrpb4a13412010-05-05 12:47:19 +000010290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010291 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010292 }
glennrpcf002022011-01-30 02:38:15 +000010293 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10294 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010295
glennrpcf002022011-01-30 02:38:15 +000010296 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010297 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010298
cristy3ed852e2009-09-05 21:47:34 +000010299 /*
10300 Initialize image scanlines.
10301 */
glennrp5af765f2010-03-30 11:12:18 +000010302 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010303 {
10304 /*
10305 PNG write failed.
10306 */
10307#ifdef PNG_DEBUG
10308 if (image_info->verbose)
10309 (void) printf("PNG write has failed.\n");
10310#endif
10311 png_destroy_write_struct(&ping,&ping_info);
10312 if (quantum_info != (QuantumInfo *) NULL)
10313 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010314 if (ping_pixels != (unsigned char *) NULL)
10315 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010316#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010317 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010318#endif
glennrpda8f3a72011-02-27 23:54:12 +000010319 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010320 (void) CloseBlob(image);
10321 image_info=DestroyImageInfo(image_info);
10322 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010323 return(MagickFalse);
10324 }
cristyed552522009-10-16 14:04:35 +000010325 quantum_info=AcquireQuantumInfo(image_info,image);
10326 if (quantum_info == (QuantumInfo *) NULL)
10327 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010328 quantum_info->format=UndefinedQuantumFormat;
10329 quantum_info->depth=image_depth;
10330 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010331
cristy3ed852e2009-09-05 21:47:34 +000010332 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010333 !mng_info->write_png32) &&
10334 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010335 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010336 image_matte == MagickFalse &&
10337 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010338 {
glennrp8bb3a022010-12-13 20:40:04 +000010339 /* Palette, Bilevel, or Opaque Monochrome */
cristy4c08aed2011-07-01 19:47:50 +000010340 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010341 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010342
cristy3ed852e2009-09-05 21:47:34 +000010343 quantum_info->depth=8;
10344 for (pass=0; pass < num_passes; pass++)
10345 {
10346 /*
10347 Convert PseudoClass image to a PNG monochrome image.
10348 */
cristybb503372010-05-27 20:51:26 +000010349 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010350 {
glennrpd71e86a2011-02-24 01:28:37 +000010351 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10353 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010354
cristy3ed852e2009-09-05 21:47:34 +000010355 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010356
cristy4c08aed2011-07-01 19:47:50 +000010357 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010358 break;
glennrp0fe50b42010-11-16 03:52:51 +000010359
cristy3ed852e2009-09-05 21:47:34 +000010360 if (mng_info->IsPalette)
10361 {
cristy4c08aed2011-07-01 19:47:50 +000010362 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010363 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010364 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10365 mng_info->write_png_depth &&
10366 mng_info->write_png_depth != old_bit_depth)
10367 {
10368 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010369 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010370 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010371 >> (8-old_bit_depth));
10372 }
10373 }
glennrp0fe50b42010-11-16 03:52:51 +000010374
cristy3ed852e2009-09-05 21:47:34 +000010375 else
10376 {
cristy4c08aed2011-07-01 19:47:50 +000010377 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010378 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010379 }
glennrp0fe50b42010-11-16 03:52:51 +000010380
cristy3ed852e2009-09-05 21:47:34 +000010381 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010382 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010383 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010384 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010385
glennrp3b51f0e2010-11-27 18:14:08 +000010386 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10388 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010389
glennrpcf002022011-01-30 02:38:15 +000010390 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010391 }
10392 if (image->previous == (Image *) NULL)
10393 {
10394 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10395 if (status == MagickFalse)
10396 break;
10397 }
10398 }
10399 }
glennrp0fe50b42010-11-16 03:52:51 +000010400
glennrp8bb3a022010-12-13 20:40:04 +000010401 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010402 {
glennrp0fe50b42010-11-16 03:52:51 +000010403 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010404 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010405 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010406 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010407 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010408 {
cristy4c08aed2011-07-01 19:47:50 +000010409 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010410 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010411
glennrp8bb3a022010-12-13 20:40:04 +000010412 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010413 {
glennrp8bb3a022010-12-13 20:40:04 +000010414
cristybb503372010-05-27 20:51:26 +000010415 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010416 {
10417 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010418
cristy4c08aed2011-07-01 19:47:50 +000010419 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010420 break;
glennrp2cc891a2010-12-24 13:44:32 +000010421
glennrp5af765f2010-03-30 11:12:18 +000010422 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010423 {
glennrp8bb3a022010-12-13 20:40:04 +000010424 if (mng_info->IsPalette)
cristy4c08aed2011-07-01 19:47:50 +000010425 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010426 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010427
glennrp8bb3a022010-12-13 20:40:04 +000010428 else
cristy4c08aed2011-07-01 19:47:50 +000010429 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010430 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010431
glennrp3b51f0e2010-11-27 18:14:08 +000010432 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010434 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010435 }
glennrp2cc891a2010-12-24 13:44:32 +000010436
glennrp8bb3a022010-12-13 20:40:04 +000010437 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10438 {
10439 if (logging != MagickFalse && y == 0)
10440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10441 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010442
cristy4c08aed2011-07-01 19:47:50 +000010443 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010444 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010445 }
glennrp2cc891a2010-12-24 13:44:32 +000010446
glennrp3b51f0e2010-11-27 18:14:08 +000010447 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010449 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010450
glennrpcf002022011-01-30 02:38:15 +000010451 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010452 }
glennrp2cc891a2010-12-24 13:44:32 +000010453
glennrp8bb3a022010-12-13 20:40:04 +000010454 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010455 {
glennrp8bb3a022010-12-13 20:40:04 +000010456 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10457 if (status == MagickFalse)
10458 break;
cristy3ed852e2009-09-05 21:47:34 +000010459 }
cristy3ed852e2009-09-05 21:47:34 +000010460 }
10461 }
glennrp8bb3a022010-12-13 20:40:04 +000010462
10463 else
10464 {
cristy4c08aed2011-07-01 19:47:50 +000010465 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010466 *p;
10467
10468 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010469 {
glennrp8bb3a022010-12-13 20:40:04 +000010470 if ((image_depth > 8) || (mng_info->write_png24 ||
10471 mng_info->write_png32 ||
10472 (!mng_info->write_png8 && !mng_info->IsPalette)))
10473 {
10474 for (y=0; y < (ssize_t) image->rows; y++)
10475 {
10476 p=GetVirtualPixels(image,0,y,image->columns,1,
10477 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010478
cristy4c08aed2011-07-01 19:47:50 +000010479 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010480 break;
glennrp2cc891a2010-12-24 13:44:32 +000010481
glennrp8bb3a022010-12-13 20:40:04 +000010482 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10483 {
10484 if (image->storage_class == DirectClass)
cristy4c08aed2011-07-01 19:47:50 +000010485 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010486 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010487
glennrp8bb3a022010-12-13 20:40:04 +000010488 else
cristy4c08aed2011-07-01 19:47:50 +000010489 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010490 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010491 }
glennrp2cc891a2010-12-24 13:44:32 +000010492
glennrp8bb3a022010-12-13 20:40:04 +000010493 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10494 {
cristy4c08aed2011-07-01 19:47:50 +000010495 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010496 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +000010497 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010498
glennrp8bb3a022010-12-13 20:40:04 +000010499 if (logging != MagickFalse && y == 0)
10500 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10501 " Writing GRAY_ALPHA PNG pixels (3)");
10502 }
glennrp2cc891a2010-12-24 13:44:32 +000010503
glennrp8bb3a022010-12-13 20:40:04 +000010504 else if (image_matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +000010505 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010506 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010507
glennrp8bb3a022010-12-13 20:40:04 +000010508 else
cristy4c08aed2011-07-01 19:47:50 +000010509 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010510 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010511
glennrp8bb3a022010-12-13 20:40:04 +000010512 if (logging != MagickFalse && y == 0)
10513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10514 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010515
glennrpcf002022011-01-30 02:38:15 +000010516 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010517 }
10518 }
glennrp2cc891a2010-12-24 13:44:32 +000010519
glennrp8bb3a022010-12-13 20:40:04 +000010520 else
10521 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10522 mng_info->write_png32 ||
10523 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10524 {
10525 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10526 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10527 {
10528 if (logging != MagickFalse)
10529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10530 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010531
glennrp8bb3a022010-12-13 20:40:04 +000010532 quantum_info->depth=8;
10533 image_depth=8;
10534 }
glennrp2cc891a2010-12-24 13:44:32 +000010535
glennrp8bb3a022010-12-13 20:40:04 +000010536 for (y=0; y < (ssize_t) image->rows; y++)
10537 {
10538 if (logging != MagickFalse && y == 0)
10539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10540 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010541
glennrp770d1932011-03-06 22:11:17 +000010542 p=GetVirtualPixels(image,0,y,image->columns,1,
10543 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010544
cristy4c08aed2011-07-01 19:47:50 +000010545 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010546 break;
glennrp2cc891a2010-12-24 13:44:32 +000010547
glennrp8bb3a022010-12-13 20:40:04 +000010548 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010549 {
glennrp4bf89732011-03-21 13:48:28 +000010550 quantum_info->depth=image->depth;
10551
cristy4c08aed2011-07-01 19:47:50 +000010552 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010553 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +000010554 }
glennrp2cc891a2010-12-24 13:44:32 +000010555
glennrp8bb3a022010-12-13 20:40:04 +000010556 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10557 {
10558 if (logging != MagickFalse && y == 0)
10559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10560 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010561
cristy4c08aed2011-07-01 19:47:50 +000010562 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010563 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +000010564 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010565 }
glennrp2cc891a2010-12-24 13:44:32 +000010566
glennrp8bb3a022010-12-13 20:40:04 +000010567 else
glennrp8bb3a022010-12-13 20:40:04 +000010568 {
cristy4c08aed2011-07-01 19:47:50 +000010569 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +000010570 quantum_info,IndexQuantum,ping_pixels,&image->exception);
10571
10572 if (logging != MagickFalse && y <= 2)
10573 {
10574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010575 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010576
10577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10578 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10579 (int)ping_pixels[0],(int)ping_pixels[1]);
10580 }
glennrp8bb3a022010-12-13 20:40:04 +000010581 }
glennrpcf002022011-01-30 02:38:15 +000010582 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010583 }
10584 }
glennrp2cc891a2010-12-24 13:44:32 +000010585
glennrp8bb3a022010-12-13 20:40:04 +000010586 if (image->previous == (Image *) NULL)
10587 {
10588 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10589 if (status == MagickFalse)
10590 break;
10591 }
cristy3ed852e2009-09-05 21:47:34 +000010592 }
glennrp8bb3a022010-12-13 20:40:04 +000010593 }
10594 }
10595
cristyb32b90a2009-09-07 21:45:48 +000010596 if (quantum_info != (QuantumInfo *) NULL)
10597 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010598
10599 if (logging != MagickFalse)
10600 {
10601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010602 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010603
cristy3ed852e2009-09-05 21:47:34 +000010604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010605 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010606
cristy3ed852e2009-09-05 21:47:34 +000010607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010608 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010609
cristy3ed852e2009-09-05 21:47:34 +000010610 if (mng_info->write_png_depth)
10611 {
10612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10613 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
10614 }
glennrp0fe50b42010-11-16 03:52:51 +000010615
cristy3ed852e2009-09-05 21:47:34 +000010616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010617 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010618
cristy3ed852e2009-09-05 21:47:34 +000010619 if (mng_info->write_png_colortype)
10620 {
10621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10622 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
10623 }
glennrp0fe50b42010-11-16 03:52:51 +000010624
cristy3ed852e2009-09-05 21:47:34 +000010625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010626 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010627
cristy3ed852e2009-09-05 21:47:34 +000010628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010629 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010630 }
10631 /*
glennrpa0ed0092011-04-18 16:36:29 +000010632 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010633 */
glennrp823b55c2011-03-14 18:46:46 +000010634 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010635 {
glennrp26f37912010-12-23 16:22:42 +000010636 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010637 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010638 while (property != (const char *) NULL)
10639 {
10640 png_textp
10641 text;
glennrp2cc891a2010-12-24 13:44:32 +000010642
glennrp26f37912010-12-23 16:22:42 +000010643 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +000010644
10645 /* Don't write any "png:" properties; those are just for "identify" */
10646 if (LocaleNCompare(property,"png:",4) != 0 &&
10647
10648 /* Suppress density and units if we wrote a pHYs chunk */
10649 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010650 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010651 LocaleCompare(property,"units") != 0) &&
10652
10653 /* Suppress the IM-generated Date:create and Date:modify */
10654 (ping_exclude_date == MagickFalse ||
10655 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010656 {
glennrpc70af4a2011-03-07 00:08:23 +000010657 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010658 {
glennrpc70af4a2011-03-07 00:08:23 +000010659 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10660 text[0].key=(char *) property;
10661 text[0].text=(char *) value;
10662 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010663
glennrpc70af4a2011-03-07 00:08:23 +000010664 if (ping_exclude_tEXt != MagickFalse)
10665 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10666
10667 else if (ping_exclude_zTXt != MagickFalse)
10668 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10669
10670 else
glennrp26f37912010-12-23 16:22:42 +000010671 {
glennrpc70af4a2011-03-07 00:08:23 +000010672 text[0].compression=image_info->compression == NoCompression ||
10673 (image_info->compression == UndefinedCompression &&
10674 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10675 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010676 }
glennrp2cc891a2010-12-24 13:44:32 +000010677
glennrpc70af4a2011-03-07 00:08:23 +000010678 if (logging != MagickFalse)
10679 {
10680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10681 " Setting up text chunk");
10682
10683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10684 " keyword: %s",text[0].key);
10685 }
10686
10687 png_set_text(ping,ping_info,text,1);
10688 png_free(ping,text);
10689 }
glennrp26f37912010-12-23 16:22:42 +000010690 }
10691 property=GetNextImageProperty(image);
10692 }
cristy3ed852e2009-09-05 21:47:34 +000010693 }
10694
10695 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010696 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010697
10698 if (logging != MagickFalse)
10699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10700 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010701
cristy3ed852e2009-09-05 21:47:34 +000010702 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010703
cristy3ed852e2009-09-05 21:47:34 +000010704 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10705 {
10706 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010707 (ping_width != mng_info->page.width) ||
10708 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010709 {
10710 unsigned char
10711 chunk[32];
10712
10713 /*
10714 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10715 */
10716 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10717 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010718 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010719 chunk[4]=4;
10720 chunk[5]=0; /* frame name separator (no name) */
10721 chunk[6]=1; /* flag for changing delay, for next frame only */
10722 chunk[7]=0; /* flag for changing frame timeout */
10723 chunk[8]=1; /* flag for changing frame clipping for next frame */
10724 chunk[9]=0; /* flag for changing frame sync_id */
10725 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10726 chunk[14]=0; /* clipping boundaries delta type */
10727 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10728 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010729 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010730 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10731 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010732 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010733 (void) WriteBlob(image,31,chunk);
10734 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10735 mng_info->old_framing_mode=4;
10736 mng_info->framing_mode=1;
10737 }
glennrp0fe50b42010-11-16 03:52:51 +000010738
cristy3ed852e2009-09-05 21:47:34 +000010739 else
10740 mng_info->framing_mode=3;
10741 }
10742 if (mng_info->write_mng && !mng_info->need_fram &&
10743 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +000010744 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010745 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010746 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010747
cristy3ed852e2009-09-05 21:47:34 +000010748 /*
10749 Free PNG resources.
10750 */
glennrp5af765f2010-03-30 11:12:18 +000010751
cristy3ed852e2009-09-05 21:47:34 +000010752 png_destroy_write_struct(&ping,&ping_info);
10753
glennrpcf002022011-01-30 02:38:15 +000010754 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010755
10756#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010757 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010758#endif
10759
glennrpda8f3a72011-02-27 23:54:12 +000010760 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010761 (void) CloseBlob(image);
10762
10763 image_info=DestroyImageInfo(image_info);
10764 image=DestroyImage(image);
10765
10766 /* Store bit depth actually written */
10767 s[0]=(char) ping_bit_depth;
10768 s[1]='\0';
10769
10770 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
10771
cristy3ed852e2009-09-05 21:47:34 +000010772 if (logging != MagickFalse)
10773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10774 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010775
cristy3ed852e2009-09-05 21:47:34 +000010776 return(MagickTrue);
10777/* End write one PNG image */
10778}
10779
10780/*
10781%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10782% %
10783% %
10784% %
10785% W r i t e P N G I m a g e %
10786% %
10787% %
10788% %
10789%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10790%
10791% WritePNGImage() writes a Portable Network Graphics (PNG) or
10792% Multiple-image Network Graphics (MNG) image file.
10793%
10794% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10795%
10796% The format of the WritePNGImage method is:
10797%
cristy1e178e72011-08-28 19:44:34 +000010798% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10799% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010800%
10801% A description of each parameter follows:
10802%
10803% o image_info: the image info.
10804%
10805% o image: The image.
10806%
cristy1e178e72011-08-28 19:44:34 +000010807% o exception: return any errors or warnings in this structure.
10808%
cristy3ed852e2009-09-05 21:47:34 +000010809% Returns MagickTrue on success, MagickFalse on failure.
10810%
10811% Communicating with the PNG encoder:
10812%
10813% While the datastream written is always in PNG format and normally would
10814% be given the "png" file extension, this method also writes the following
10815% pseudo-formats which are subsets of PNG:
10816%
glennrp5a39f372011-02-25 04:52:16 +000010817% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10818% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010819% is present, the tRNS chunk must only have values 0 and 255
10820% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010821% transparent). If other values are present they will be
10822% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010823% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000010824% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10825% of any resulting fully-transparent pixels is changed to
10826% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000010827%
10828% If you want better quantization or dithering of the colors
10829% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010830% PNG encoder. The pixels contain 8-bit indices even if
10831% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010832% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010833% PNG grayscale type might be slightly more efficient. Please
10834% note that writing to the PNG8 format may result in loss
10835% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010836%
10837% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10838% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010839% one of the colors as transparent. The only loss incurred
10840% is reduction of sample depth to 8. If the image has more
10841% than one transparent color, has semitransparent pixels, or
10842% has an opaque pixel with the same RGB components as the
10843% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010844%
10845% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10846% transparency is permitted, i.e., the alpha sample for
10847% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010848% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010849% The only loss in data is the reduction of the sample depth
10850% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010851%
10852% o -define: For more precise control of the PNG output, you can use the
10853% Image options "png:bit-depth" and "png:color-type". These
10854% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010855% from the application programming interfaces. The options
10856% are case-independent and are converted to lowercase before
10857% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010858%
10859% png:color-type can be 0, 2, 3, 4, or 6.
10860%
10861% When png:color-type is 0 (Grayscale), png:bit-depth can
10862% be 1, 2, 4, 8, or 16.
10863%
10864% When png:color-type is 2 (RGB), png:bit-depth can
10865% be 8 or 16.
10866%
10867% When png:color-type is 3 (Indexed), png:bit-depth can
10868% be 1, 2, 4, or 8. This refers to the number of bits
10869% used to store the index. The color samples always have
10870% bit-depth 8 in indexed PNG files.
10871%
10872% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10873% png:bit-depth can be 8 or 16.
10874%
glennrp5a39f372011-02-25 04:52:16 +000010875% If the image cannot be written without loss with the requested bit-depth
10876% and color-type, a PNG file will not be written, and the encoder will
10877% return MagickFalse.
10878%
cristy3ed852e2009-09-05 21:47:34 +000010879% Since image encoders should not be responsible for the "heavy lifting",
10880% the user should make sure that ImageMagick has already reduced the
10881% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010882% transparency prior to attempting to write the image with depth, color,
glennrp54dc0692011-07-01 19:02:13 +000010883% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010884%
cristy3ed852e2009-09-05 21:47:34 +000010885% Note that another definition, "png:bit-depth-written" exists, but it
10886% is not intended for external use. It is only used internally by the
10887% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10888%
10889% It is possible to request that the PNG encoder write previously-formatted
10890% ancillary chunks in the output PNG file, using the "-profile" commandline
10891% option as shown below or by setting the profile via a programming
10892% interface:
10893%
10894% -profile PNG-chunk-x:<file>
10895%
10896% where x is a location flag and <file> is a file containing the chunk
10897% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010898% This encoder will compute the chunk length and CRC, so those must not
10899% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010900%
10901% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10902% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10903% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010904% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010905%
glennrpbb8a7332010-11-13 15:17:35 +000010906% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010907%
glennrp3241bd02010-12-12 04:36:28 +000010908% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010909%
glennrpd6afd542010-11-19 01:53:05 +000010910% o 32-bit depth is reduced to 16.
10911% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10912% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010913% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010914% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010915% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010916% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10917% this can be done without loss and a larger bit depth N was not
10918% requested via the "-define PNG:bit-depth=N" option.
10919% o If matte channel is present but only one transparent color is
10920% present, RGB+tRNS is written instead of RGBA
10921% o Opaque matte channel is removed (or added, if color-type 4 or 6
10922% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010923%
cristy3ed852e2009-09-05 21:47:34 +000010924%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10925*/
10926static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +000010927 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010928{
10929 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010930 excluding,
10931 logging,
10932 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010933 status;
10934
10935 MngInfo
10936 *mng_info;
10937
10938 const char
10939 *value;
10940
10941 int
glennrp21f0e622011-01-07 16:20:57 +000010942 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010943 source;
10944
cristy3ed852e2009-09-05 21:47:34 +000010945 /*
10946 Open image file.
10947 */
10948 assert(image_info != (const ImageInfo *) NULL);
10949 assert(image_info->signature == MagickSignature);
10950 assert(image != (Image *) NULL);
10951 assert(image->signature == MagickSignature);
10952 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010953 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010954 /*
10955 Allocate a MngInfo structure.
10956 */
10957 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010958 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010959
cristy3ed852e2009-09-05 21:47:34 +000010960 if (mng_info == (MngInfo *) NULL)
10961 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010962
cristy3ed852e2009-09-05 21:47:34 +000010963 /*
10964 Initialize members of the MngInfo structure.
10965 */
10966 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10967 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010968 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010969 have_mng_structure=MagickTrue;
10970
10971 /* See if user has requested a specific PNG subformat */
10972
10973 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10974 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10975 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10976
10977 if (mng_info->write_png8)
10978 {
glennrp9c1eb072010-06-06 22:19:15 +000010979 mng_info->write_png_colortype = /* 3 */ 4;
10980 mng_info->write_png_depth = 8;
10981 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010982 }
10983
10984 if (mng_info->write_png24)
10985 {
glennrp9c1eb072010-06-06 22:19:15 +000010986 mng_info->write_png_colortype = /* 2 */ 3;
10987 mng_info->write_png_depth = 8;
10988 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010989
glennrp9c1eb072010-06-06 22:19:15 +000010990 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000010991 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010992
glennrp9c1eb072010-06-06 22:19:15 +000010993 else
cristy018f07f2011-09-04 21:15:19 +000010994 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010995
glennrp9c1eb072010-06-06 22:19:15 +000010996 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010997 }
10998
10999 if (mng_info->write_png32)
11000 {
glennrp9c1eb072010-06-06 22:19:15 +000011001 mng_info->write_png_colortype = /* 6 */ 7;
11002 mng_info->write_png_depth = 8;
11003 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011004
glennrp9c1eb072010-06-06 22:19:15 +000011005 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011006 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011007
glennrp9c1eb072010-06-06 22:19:15 +000011008 else
cristy018f07f2011-09-04 21:15:19 +000011009 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011010
glennrp9c1eb072010-06-06 22:19:15 +000011011 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000011012 }
11013
11014 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011015
cristy3ed852e2009-09-05 21:47:34 +000011016 if (value != (char *) NULL)
11017 {
11018 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011019 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011020
cristy3ed852e2009-09-05 21:47:34 +000011021 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011022 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011023
cristy3ed852e2009-09-05 21:47:34 +000011024 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011025 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011026
cristy3ed852e2009-09-05 21:47:34 +000011027 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011028 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011029
cristy3ed852e2009-09-05 21:47:34 +000011030 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011031 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011032
glennrpbb8a7332010-11-13 15:17:35 +000011033 else
11034 (void) ThrowMagickException(&image->exception,
11035 GetMagickModule(),CoderWarning,
11036 "ignoring invalid defined png:bit-depth",
11037 "=%s",value);
11038
cristy3ed852e2009-09-05 21:47:34 +000011039 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011041 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011042 }
glennrp0fe50b42010-11-16 03:52:51 +000011043
cristy3ed852e2009-09-05 21:47:34 +000011044 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011045
cristy3ed852e2009-09-05 21:47:34 +000011046 if (value != (char *) NULL)
11047 {
11048 /* We must store colortype+1 because 0 is a valid colortype */
11049 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011050 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011051
cristy3ed852e2009-09-05 21:47:34 +000011052 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011053 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011054
cristy3ed852e2009-09-05 21:47:34 +000011055 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011056 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011057
cristy3ed852e2009-09-05 21:47:34 +000011058 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011059 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011060
cristy3ed852e2009-09-05 21:47:34 +000011061 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011062 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011063
glennrpbb8a7332010-11-13 15:17:35 +000011064 else
11065 (void) ThrowMagickException(&image->exception,
11066 GetMagickModule(),CoderWarning,
11067 "ignoring invalid defined png:color-type",
11068 "=%s",value);
11069
cristy3ed852e2009-09-05 21:47:34 +000011070 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011072 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011073 }
11074
glennrp0e8ea192010-12-24 18:00:33 +000011075 /* Check for chunks to be excluded:
11076 *
glennrp0dff56c2011-01-29 19:10:02 +000011077 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011078 * listed in the "unused_chunks" array, above.
11079 *
11080 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
11081 * define (in the image properties or in the image artifacts)
11082 * or via a mng_info member. For convenience, in addition
11083 * to or instead of a comma-separated list of chunks, the
11084 * "exclude-chunk" string can be simply "all" or "none".
11085 *
11086 * The exclude-chunk define takes priority over the mng_info.
11087 *
11088 * A "PNG:include-chunk" define takes priority over both the
11089 * mng_info and the "PNG:exclude-chunk" define. Like the
11090 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011091 * well as a comma-separated list. Chunks that are unknown to
11092 * ImageMagick are always excluded, regardless of their "copy-safe"
11093 * status according to the PNG specification, and even if they
11094 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000011095 *
11096 * Finally, all chunks listed in the "unused_chunks" array are
11097 * automatically excluded, regardless of the other instructions
11098 * or lack thereof.
11099 *
11100 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11101 * will not be written and the gAMA chunk will only be written if it
11102 * is not between .45 and .46, or approximately (1.0/2.2).
11103 *
11104 * If you exclude tRNS and the image has transparency, the colortype
11105 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11106 *
11107 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011108 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011109 */
11110
glennrp26f37912010-12-23 16:22:42 +000011111 mng_info->ping_exclude_bKGD=MagickFalse;
11112 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011113 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011114 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11115 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011116 mng_info->ping_exclude_iCCP=MagickFalse;
11117 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11118 mng_info->ping_exclude_oFFs=MagickFalse;
11119 mng_info->ping_exclude_pHYs=MagickFalse;
11120 mng_info->ping_exclude_sRGB=MagickFalse;
11121 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011122 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011123 mng_info->ping_exclude_vpAg=MagickFalse;
11124 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11125 mng_info->ping_exclude_zTXt=MagickFalse;
11126
glennrp8d3d6e52011-04-19 04:39:51 +000011127 mng_info->ping_preserve_colormap=MagickFalse;
11128
11129 value=GetImageArtifact(image,"png:preserve-colormap");
11130 if (value == NULL)
11131 value=GetImageOption(image_info,"png:preserve-colormap");
11132 if (value != NULL)
11133 mng_info->ping_preserve_colormap=MagickTrue;
11134
glennrp18682582011-06-30 18:11:47 +000011135 /* Thes compression-level, compression-strategy, and compression-filter
11136 * defines take precedence over values from the -quality option.
11137 */
11138 value=GetImageArtifact(image,"png:compression-level");
11139 if (value == NULL)
11140 value=GetImageOption(image_info,"png:compression-level");
11141 if (value != NULL)
11142 {
glennrp18682582011-06-30 18:11:47 +000011143 /* We have to add 1 to everything because 0 is a valid input,
11144 * and we want to use 0 (the default) to mean undefined.
11145 */
11146 if (LocaleCompare(value,"0") == 0)
11147 mng_info->write_png_compression_level = 1;
11148
11149 if (LocaleCompare(value,"1") == 0)
11150 mng_info->write_png_compression_level = 2;
11151
11152 else if (LocaleCompare(value,"2") == 0)
11153 mng_info->write_png_compression_level = 3;
11154
11155 else if (LocaleCompare(value,"3") == 0)
11156 mng_info->write_png_compression_level = 4;
11157
11158 else if (LocaleCompare(value,"4") == 0)
11159 mng_info->write_png_compression_level = 5;
11160
11161 else if (LocaleCompare(value,"5") == 0)
11162 mng_info->write_png_compression_level = 6;
11163
11164 else if (LocaleCompare(value,"6") == 0)
11165 mng_info->write_png_compression_level = 7;
11166
11167 else if (LocaleCompare(value,"7") == 0)
11168 mng_info->write_png_compression_level = 8;
11169
11170 else if (LocaleCompare(value,"8") == 0)
11171 mng_info->write_png_compression_level = 9;
11172
11173 else if (LocaleCompare(value,"9") == 0)
11174 mng_info->write_png_compression_level = 10;
11175
11176 else
11177 (void) ThrowMagickException(&image->exception,
11178 GetMagickModule(),CoderWarning,
11179 "ignoring invalid defined png:compression-level",
11180 "=%s",value);
11181 }
11182
11183 value=GetImageArtifact(image,"png:compression-strategy");
11184 if (value == NULL)
11185 value=GetImageOption(image_info,"png:compression-strategy");
11186 if (value != NULL)
11187 {
11188
11189 if (LocaleCompare(value,"0") == 0)
11190 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11191
11192 else if (LocaleCompare(value,"1") == 0)
11193 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11194
11195 else if (LocaleCompare(value,"2") == 0)
11196 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11197
11198 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011199#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011200 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011201#else
11202 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11203#endif
glennrp18682582011-06-30 18:11:47 +000011204
11205 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011206#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011207 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011208#else
11209 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11210#endif
glennrp18682582011-06-30 18:11:47 +000011211
11212 else
11213 (void) ThrowMagickException(&image->exception,
11214 GetMagickModule(),CoderWarning,
11215 "ignoring invalid defined png:compression-strategy",
11216 "=%s",value);
11217 }
11218
11219 value=GetImageArtifact(image,"png:compression-filter");
11220 if (value == NULL)
11221 value=GetImageOption(image_info,"png:compression-filter");
11222 if (value != NULL)
11223 {
11224
11225 /* To do: combinations of filters allowed by libpng
11226 * masks 0x08 through 0xf8
11227 *
11228 * Implement this as a comma-separated list of 0,1,2,3,4,5
11229 * where 5 is a special case meaning PNG_ALL_FILTERS.
11230 */
11231
11232 if (LocaleCompare(value,"0") == 0)
11233 mng_info->write_png_compression_filter = 1;
11234
11235 if (LocaleCompare(value,"1") == 0)
11236 mng_info->write_png_compression_filter = 2;
11237
11238 else if (LocaleCompare(value,"2") == 0)
11239 mng_info->write_png_compression_filter = 3;
11240
11241 else if (LocaleCompare(value,"3") == 0)
11242 mng_info->write_png_compression_filter = 4;
11243
11244 else if (LocaleCompare(value,"4") == 0)
11245 mng_info->write_png_compression_filter = 5;
11246
11247 else if (LocaleCompare(value,"5") == 0)
11248 mng_info->write_png_compression_filter = 6;
11249
glennrp18682582011-06-30 18:11:47 +000011250 else
11251 (void) ThrowMagickException(&image->exception,
11252 GetMagickModule(),CoderWarning,
11253 "ignoring invalid defined png:compression-filter",
11254 "=%s",value);
11255 }
11256
glennrp03812ae2010-12-24 01:31:34 +000011257 excluding=MagickFalse;
11258
glennrp5c7cf4e2010-12-24 00:30:00 +000011259 for (source=0; source<1; source++)
11260 {
11261 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011262 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011263 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011264
11265 if (value == NULL)
11266 value=GetImageArtifact(image,"png:exclude-chunks");
11267 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011268 else
glennrpacba0042010-12-24 14:27:26 +000011269 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011270 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011271
glennrpacba0042010-12-24 14:27:26 +000011272 if (value == NULL)
11273 value=GetImageOption(image_info,"png:exclude-chunks");
11274 }
11275
glennrp03812ae2010-12-24 01:31:34 +000011276 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011277 {
glennrp03812ae2010-12-24 01:31:34 +000011278
11279 size_t
11280 last;
11281
11282 excluding=MagickTrue;
11283
11284 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011285 {
11286 if (source == 0)
11287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11288 " png:exclude-chunk=%s found in image artifacts.\n", value);
11289 else
11290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11291 " png:exclude-chunk=%s found in image properties.\n", value);
11292 }
glennrp03812ae2010-12-24 01:31:34 +000011293
11294 last=strlen(value);
11295
11296 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011297 {
glennrp03812ae2010-12-24 01:31:34 +000011298
11299 if (LocaleNCompare(value+i,"all",3) == 0)
11300 {
11301 mng_info->ping_exclude_bKGD=MagickTrue;
11302 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011303 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011304 mng_info->ping_exclude_EXIF=MagickTrue;
11305 mng_info->ping_exclude_gAMA=MagickTrue;
11306 mng_info->ping_exclude_iCCP=MagickTrue;
11307 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11308 mng_info->ping_exclude_oFFs=MagickTrue;
11309 mng_info->ping_exclude_pHYs=MagickTrue;
11310 mng_info->ping_exclude_sRGB=MagickTrue;
11311 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011312 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011313 mng_info->ping_exclude_vpAg=MagickTrue;
11314 mng_info->ping_exclude_zCCP=MagickTrue;
11315 mng_info->ping_exclude_zTXt=MagickTrue;
11316 i--;
11317 }
glennrp2cc891a2010-12-24 13:44:32 +000011318
glennrp03812ae2010-12-24 01:31:34 +000011319 if (LocaleNCompare(value+i,"none",4) == 0)
11320 {
11321 mng_info->ping_exclude_bKGD=MagickFalse;
11322 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011323 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011324 mng_info->ping_exclude_EXIF=MagickFalse;
11325 mng_info->ping_exclude_gAMA=MagickFalse;
11326 mng_info->ping_exclude_iCCP=MagickFalse;
11327 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11328 mng_info->ping_exclude_oFFs=MagickFalse;
11329 mng_info->ping_exclude_pHYs=MagickFalse;
11330 mng_info->ping_exclude_sRGB=MagickFalse;
11331 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011332 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011333 mng_info->ping_exclude_vpAg=MagickFalse;
11334 mng_info->ping_exclude_zCCP=MagickFalse;
11335 mng_info->ping_exclude_zTXt=MagickFalse;
11336 }
glennrp2cc891a2010-12-24 13:44:32 +000011337
glennrp03812ae2010-12-24 01:31:34 +000011338 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11339 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011340
glennrp03812ae2010-12-24 01:31:34 +000011341 if (LocaleNCompare(value+i,"chrm",4) == 0)
11342 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011343
glennrpa0ed0092011-04-18 16:36:29 +000011344 if (LocaleNCompare(value+i,"date",4) == 0)
11345 mng_info->ping_exclude_date=MagickTrue;
11346
glennrp03812ae2010-12-24 01:31:34 +000011347 if (LocaleNCompare(value+i,"exif",4) == 0)
11348 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011349
glennrp03812ae2010-12-24 01:31:34 +000011350 if (LocaleNCompare(value+i,"gama",4) == 0)
11351 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011352
glennrp03812ae2010-12-24 01:31:34 +000011353 if (LocaleNCompare(value+i,"iccp",4) == 0)
11354 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011355
glennrp03812ae2010-12-24 01:31:34 +000011356 /*
11357 if (LocaleNCompare(value+i,"itxt",4) == 0)
11358 mng_info->ping_exclude_iTXt=MagickTrue;
11359 */
glennrp2cc891a2010-12-24 13:44:32 +000011360
glennrp03812ae2010-12-24 01:31:34 +000011361 if (LocaleNCompare(value+i,"gama",4) == 0)
11362 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011363
glennrp03812ae2010-12-24 01:31:34 +000011364 if (LocaleNCompare(value+i,"offs",4) == 0)
11365 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011366
glennrp03812ae2010-12-24 01:31:34 +000011367 if (LocaleNCompare(value+i,"phys",4) == 0)
11368 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011369
glennrpa1e3b7b2010-12-24 16:37:33 +000011370 if (LocaleNCompare(value+i,"srgb",4) == 0)
11371 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011372
glennrp03812ae2010-12-24 01:31:34 +000011373 if (LocaleNCompare(value+i,"text",4) == 0)
11374 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011375
glennrpa1e3b7b2010-12-24 16:37:33 +000011376 if (LocaleNCompare(value+i,"trns",4) == 0)
11377 mng_info->ping_exclude_tRNS=MagickTrue;
11378
glennrp03812ae2010-12-24 01:31:34 +000011379 if (LocaleNCompare(value+i,"vpag",4) == 0)
11380 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011381
glennrp03812ae2010-12-24 01:31:34 +000011382 if (LocaleNCompare(value+i,"zccp",4) == 0)
11383 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011384
glennrp03812ae2010-12-24 01:31:34 +000011385 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11386 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011387
glennrp03812ae2010-12-24 01:31:34 +000011388 }
glennrpce91ed52010-12-23 22:37:49 +000011389 }
glennrp26f37912010-12-23 16:22:42 +000011390 }
11391
glennrp5c7cf4e2010-12-24 00:30:00 +000011392 for (source=0; source<1; source++)
11393 {
11394 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011395 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011396 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011397
11398 if (value == NULL)
11399 value=GetImageArtifact(image,"png:include-chunks");
11400 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011401 else
glennrpacba0042010-12-24 14:27:26 +000011402 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011403 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011404
glennrpacba0042010-12-24 14:27:26 +000011405 if (value == NULL)
11406 value=GetImageOption(image_info,"png:include-chunks");
11407 }
11408
glennrp03812ae2010-12-24 01:31:34 +000011409 if (value != NULL)
11410 {
11411 size_t
11412 last;
glennrp26f37912010-12-23 16:22:42 +000011413
glennrp03812ae2010-12-24 01:31:34 +000011414 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011415
glennrp03812ae2010-12-24 01:31:34 +000011416 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011417 {
11418 if (source == 0)
11419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11420 " png:include-chunk=%s found in image artifacts.\n", value);
11421 else
11422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11423 " png:include-chunk=%s found in image properties.\n", value);
11424 }
glennrp03812ae2010-12-24 01:31:34 +000011425
11426 last=strlen(value);
11427
11428 for (i=0; i<(int) last; i+=5)
11429 {
11430 if (LocaleNCompare(value+i,"all",3) == 0)
11431 {
11432 mng_info->ping_exclude_bKGD=MagickFalse;
11433 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011434 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011435 mng_info->ping_exclude_EXIF=MagickFalse;
11436 mng_info->ping_exclude_gAMA=MagickFalse;
11437 mng_info->ping_exclude_iCCP=MagickFalse;
11438 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11439 mng_info->ping_exclude_oFFs=MagickFalse;
11440 mng_info->ping_exclude_pHYs=MagickFalse;
11441 mng_info->ping_exclude_sRGB=MagickFalse;
11442 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011443 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011444 mng_info->ping_exclude_vpAg=MagickFalse;
11445 mng_info->ping_exclude_zCCP=MagickFalse;
11446 mng_info->ping_exclude_zTXt=MagickFalse;
11447 i--;
11448 }
glennrp2cc891a2010-12-24 13:44:32 +000011449
glennrp03812ae2010-12-24 01:31:34 +000011450 if (LocaleNCompare(value+i,"none",4) == 0)
11451 {
11452 mng_info->ping_exclude_bKGD=MagickTrue;
11453 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011454 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011455 mng_info->ping_exclude_EXIF=MagickTrue;
11456 mng_info->ping_exclude_gAMA=MagickTrue;
11457 mng_info->ping_exclude_iCCP=MagickTrue;
11458 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11459 mng_info->ping_exclude_oFFs=MagickTrue;
11460 mng_info->ping_exclude_pHYs=MagickTrue;
11461 mng_info->ping_exclude_sRGB=MagickTrue;
11462 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011463 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011464 mng_info->ping_exclude_vpAg=MagickTrue;
11465 mng_info->ping_exclude_zCCP=MagickTrue;
11466 mng_info->ping_exclude_zTXt=MagickTrue;
11467 }
glennrp2cc891a2010-12-24 13:44:32 +000011468
glennrp03812ae2010-12-24 01:31:34 +000011469 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11470 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011471
glennrp03812ae2010-12-24 01:31:34 +000011472 if (LocaleNCompare(value+i,"chrm",4) == 0)
11473 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011474
glennrpa0ed0092011-04-18 16:36:29 +000011475 if (LocaleNCompare(value+i,"date",4) == 0)
11476 mng_info->ping_exclude_date=MagickFalse;
11477
glennrp03812ae2010-12-24 01:31:34 +000011478 if (LocaleNCompare(value+i,"exif",4) == 0)
11479 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011480
glennrp03812ae2010-12-24 01:31:34 +000011481 if (LocaleNCompare(value+i,"gama",4) == 0)
11482 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011483
glennrp03812ae2010-12-24 01:31:34 +000011484 if (LocaleNCompare(value+i,"iccp",4) == 0)
11485 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011486
glennrp03812ae2010-12-24 01:31:34 +000011487 /*
11488 if (LocaleNCompare(value+i,"itxt",4) == 0)
11489 mng_info->ping_exclude_iTXt=MagickFalse;
11490 */
glennrp2cc891a2010-12-24 13:44:32 +000011491
glennrp03812ae2010-12-24 01:31:34 +000011492 if (LocaleNCompare(value+i,"gama",4) == 0)
11493 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011494
glennrp03812ae2010-12-24 01:31:34 +000011495 if (LocaleNCompare(value+i,"offs",4) == 0)
11496 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011497
glennrp03812ae2010-12-24 01:31:34 +000011498 if (LocaleNCompare(value+i,"phys",4) == 0)
11499 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011500
glennrpa1e3b7b2010-12-24 16:37:33 +000011501 if (LocaleNCompare(value+i,"srgb",4) == 0)
11502 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011503
glennrp03812ae2010-12-24 01:31:34 +000011504 if (LocaleNCompare(value+i,"text",4) == 0)
11505 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011506
glennrpa1e3b7b2010-12-24 16:37:33 +000011507 if (LocaleNCompare(value+i,"trns",4) == 0)
11508 mng_info->ping_exclude_tRNS=MagickFalse;
11509
glennrp03812ae2010-12-24 01:31:34 +000011510 if (LocaleNCompare(value+i,"vpag",4) == 0)
11511 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011512
glennrp03812ae2010-12-24 01:31:34 +000011513 if (LocaleNCompare(value+i,"zccp",4) == 0)
11514 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011515
glennrp03812ae2010-12-24 01:31:34 +000011516 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11517 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011518
glennrp03812ae2010-12-24 01:31:34 +000011519 }
glennrpce91ed52010-12-23 22:37:49 +000011520 }
glennrp26f37912010-12-23 16:22:42 +000011521 }
11522
glennrp03812ae2010-12-24 01:31:34 +000011523 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011524 {
11525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11526 " Chunks to be excluded from the output PNG:");
11527 if (mng_info->ping_exclude_bKGD != MagickFalse)
11528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11529 " bKGD");
11530 if (mng_info->ping_exclude_cHRM != MagickFalse)
11531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11532 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011533 if (mng_info->ping_exclude_date != MagickFalse)
11534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11535 " date");
glennrp26f37912010-12-23 16:22:42 +000011536 if (mng_info->ping_exclude_EXIF != MagickFalse)
11537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11538 " EXIF");
11539 if (mng_info->ping_exclude_gAMA != MagickFalse)
11540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11541 " gAMA");
11542 if (mng_info->ping_exclude_iCCP != MagickFalse)
11543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11544 " iCCP");
11545/*
11546 if (mng_info->ping_exclude_iTXt != MagickFalse)
11547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11548 " iTXt");
11549*/
11550 if (mng_info->ping_exclude_oFFs != MagickFalse)
11551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11552 " oFFs");
11553 if (mng_info->ping_exclude_pHYs != MagickFalse)
11554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11555 " pHYs");
11556 if (mng_info->ping_exclude_sRGB != MagickFalse)
11557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11558 " sRGB");
11559 if (mng_info->ping_exclude_tEXt != MagickFalse)
11560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11561 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011562 if (mng_info->ping_exclude_tRNS != MagickFalse)
11563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11564 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011565 if (mng_info->ping_exclude_vpAg != MagickFalse)
11566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11567 " vpAg");
11568 if (mng_info->ping_exclude_zCCP != MagickFalse)
11569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11570 " zCCP");
11571 if (mng_info->ping_exclude_zTXt != MagickFalse)
11572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11573 " zTXt");
11574 }
11575
glennrpb9cfe272010-12-21 15:08:06 +000011576 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011577
cristy018f07f2011-09-04 21:15:19 +000011578 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011579
11580 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011581
cristy3ed852e2009-09-05 21:47:34 +000011582 if (logging != MagickFalse)
11583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011584
cristy3ed852e2009-09-05 21:47:34 +000011585 return(status);
11586}
11587
11588#if defined(JNG_SUPPORTED)
11589
11590/* Write one JNG image */
11591static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +000011592 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011593{
11594 Image
11595 *jpeg_image;
11596
11597 ImageInfo
11598 *jpeg_image_info;
11599
11600 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011601 logging,
cristy3ed852e2009-09-05 21:47:34 +000011602 status;
11603
11604 size_t
11605 length;
11606
11607 unsigned char
11608 *blob,
11609 chunk[80],
11610 *p;
11611
11612 unsigned int
11613 jng_alpha_compression_method,
11614 jng_alpha_sample_depth,
11615 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011616 transparent;
11617
cristybb503372010-05-27 20:51:26 +000011618 size_t
cristy3ed852e2009-09-05 21:47:34 +000011619 jng_quality;
11620
11621 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011622 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011623
11624 blob=(unsigned char *) NULL;
11625 jpeg_image=(Image *) NULL;
11626 jpeg_image_info=(ImageInfo *) NULL;
11627
11628 status=MagickTrue;
11629 transparent=image_info->type==GrayscaleMatteType ||
11630 image_info->type==TrueColorMatteType;
11631 jng_color_type=10;
11632 jng_alpha_sample_depth=0;
11633 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11634 jng_alpha_compression_method=0;
11635
11636 if (image->matte != MagickFalse)
11637 {
11638 /* if any pixels are transparent */
11639 transparent=MagickTrue;
11640 if (image_info->compression==JPEGCompression)
11641 jng_alpha_compression_method=8;
11642 }
11643
11644 if (transparent)
11645 {
cristybd5a96c2011-08-21 00:04:26 +000011646 ChannelType
11647 channel_mask;
11648
cristy3ed852e2009-09-05 21:47:34 +000011649 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011650
cristy3ed852e2009-09-05 21:47:34 +000011651 /* Create JPEG blob, image, and image_info */
11652 if (logging != MagickFalse)
11653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +000011654 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011655
cristy3ed852e2009-09-05 21:47:34 +000011656 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011657
cristy3ed852e2009-09-05 21:47:34 +000011658 if (jpeg_image_info == (ImageInfo *) NULL)
11659 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011660
cristy3ed852e2009-09-05 21:47:34 +000011661 if (logging != MagickFalse)
11662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11663 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011664
cristy3ed852e2009-09-05 21:47:34 +000011665 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011666
cristy3ed852e2009-09-05 21:47:34 +000011667 if (jpeg_image == (Image *) NULL)
11668 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011669
cristy3ed852e2009-09-05 21:47:34 +000011670 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristybd5a96c2011-08-21 00:04:26 +000011671 channel_mask=SetPixelChannelMask(jpeg_image,AlphaChannel);
cristy3139dc22011-07-08 00:11:42 +000011672 status=SeparateImage(jpeg_image);
cristybd5a96c2011-08-21 00:04:26 +000011673 (void) SetPixelChannelMap(jpeg_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +000011674 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011675
cristy3ed852e2009-09-05 21:47:34 +000011676 if (jng_quality >= 1000)
11677 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000011678
cristy3ed852e2009-09-05 21:47:34 +000011679 else
11680 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000011681
cristy3ed852e2009-09-05 21:47:34 +000011682 jpeg_image_info->type=GrayscaleType;
cristy018f07f2011-09-04 21:15:19 +000011683 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000011684 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011685 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011686 "%s",jpeg_image->filename);
11687 }
11688
11689 /* To do: check bit depth of PNG alpha channel */
11690
11691 /* Check if image is grayscale. */
11692 if (image_info->type != TrueColorMatteType && image_info->type !=
11693 TrueColorType && ImageIsGray(image))
11694 jng_color_type-=2;
11695
11696 if (transparent)
11697 {
11698 if (jng_alpha_compression_method==0)
11699 {
11700 const char
11701 *value;
11702
cristy4c08aed2011-07-01 19:47:50 +000011703 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011704 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11705 &image->exception);
11706 if (logging != MagickFalse)
11707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11708 " Creating PNG blob.");
11709 length=0;
11710
11711 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11712 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11713 jpeg_image_info->interlace=NoInterlace;
11714
11715 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11716 &image->exception);
11717
11718 /* Retrieve sample depth used */
11719 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
11720 if (value != (char *) NULL)
11721 jng_alpha_sample_depth= (unsigned int) value[0];
11722 }
11723 else
11724 {
cristy4c08aed2011-07-01 19:47:50 +000011725 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011726
11727 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11728 &image->exception);
11729
11730 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11731 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11732 jpeg_image_info->interlace=NoInterlace;
11733 if (logging != MagickFalse)
11734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11735 " Creating blob.");
11736 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000011737 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000011738 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011739
cristy3ed852e2009-09-05 21:47:34 +000011740 if (logging != MagickFalse)
11741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011742 " Successfully read jpeg_image into a blob, length=%.20g.",
11743 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011744
11745 }
11746 /* Destroy JPEG image and image_info */
11747 jpeg_image=DestroyImage(jpeg_image);
11748 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11749 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11750 }
11751
11752 /* Write JHDR chunk */
11753 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11754 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011755 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011756 PNGLong(chunk+4,(png_uint_32) image->columns);
11757 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011758 chunk[12]=jng_color_type;
11759 chunk[13]=8; /* sample depth */
11760 chunk[14]=8; /*jng_image_compression_method */
11761 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11762 chunk[16]=jng_alpha_sample_depth;
11763 chunk[17]=jng_alpha_compression_method;
11764 chunk[18]=0; /*jng_alpha_filter_method */
11765 chunk[19]=0; /*jng_alpha_interlace_method */
11766 (void) WriteBlob(image,20,chunk);
11767 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11768 if (logging != MagickFalse)
11769 {
11770 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011771 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011772
cristy3ed852e2009-09-05 21:47:34 +000011773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011774 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011775
cristy3ed852e2009-09-05 21:47:34 +000011776 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11777 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011778
cristy3ed852e2009-09-05 21:47:34 +000011779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11780 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011781
cristy3ed852e2009-09-05 21:47:34 +000011782 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11783 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011784
cristy3ed852e2009-09-05 21:47:34 +000011785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11786 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011787
cristy3ed852e2009-09-05 21:47:34 +000011788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11789 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011790
cristy3ed852e2009-09-05 21:47:34 +000011791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11792 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011793
cristy3ed852e2009-09-05 21:47:34 +000011794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11795 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011796
cristy3ed852e2009-09-05 21:47:34 +000011797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11798 " JNG alpha interlace:%5d",0);
11799 }
11800
glennrp0fe50b42010-11-16 03:52:51 +000011801 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011802 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011803
11804 /*
11805 Write leading ancillary chunks
11806 */
11807
11808 if (transparent)
11809 {
11810 /*
11811 Write JNG bKGD chunk
11812 */
11813
11814 unsigned char
11815 blue,
11816 green,
11817 red;
11818
cristybb503372010-05-27 20:51:26 +000011819 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011820 num_bytes;
11821
11822 if (jng_color_type == 8 || jng_color_type == 12)
11823 num_bytes=6L;
11824 else
11825 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011826 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011827 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011828 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011829 red=ScaleQuantumToChar(image->background_color.red);
11830 green=ScaleQuantumToChar(image->background_color.green);
11831 blue=ScaleQuantumToChar(image->background_color.blue);
11832 *(chunk+4)=0;
11833 *(chunk+5)=red;
11834 *(chunk+6)=0;
11835 *(chunk+7)=green;
11836 *(chunk+8)=0;
11837 *(chunk+9)=blue;
11838 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11839 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11840 }
11841
11842 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11843 {
11844 /*
11845 Write JNG sRGB chunk
11846 */
11847 (void) WriteBlobMSBULong(image,1L);
11848 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011849 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011850
cristy3ed852e2009-09-05 21:47:34 +000011851 if (image->rendering_intent != UndefinedIntent)
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 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011855
cristy3ed852e2009-09-05 21:47:34 +000011856 else
glennrpe610a072010-08-05 17:08:46 +000011857 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011858 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011859 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011860
cristy3ed852e2009-09-05 21:47:34 +000011861 (void) WriteBlob(image,5,chunk);
11862 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11863 }
11864 else
11865 {
11866 if (image->gamma != 0.0)
11867 {
11868 /*
11869 Write JNG gAMA chunk
11870 */
11871 (void) WriteBlobMSBULong(image,4L);
11872 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011873 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011874 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011875 (void) WriteBlob(image,8,chunk);
11876 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11877 }
glennrp0fe50b42010-11-16 03:52:51 +000011878
cristy3ed852e2009-09-05 21:47:34 +000011879 if ((mng_info->equal_chrms == MagickFalse) &&
11880 (image->chromaticity.red_primary.x != 0.0))
11881 {
11882 PrimaryInfo
11883 primary;
11884
11885 /*
11886 Write JNG cHRM chunk
11887 */
11888 (void) WriteBlobMSBULong(image,32L);
11889 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011890 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011891 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011892 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11893 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011894 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011895 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11896 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011897 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011898 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11899 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011900 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011901 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11902 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011903 (void) WriteBlob(image,36,chunk);
11904 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11905 }
11906 }
glennrp0fe50b42010-11-16 03:52:51 +000011907
cristy3ed852e2009-09-05 21:47:34 +000011908 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11909 {
11910 /*
11911 Write JNG pHYs chunk
11912 */
11913 (void) WriteBlobMSBULong(image,9L);
11914 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011915 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011916 if (image->units == PixelsPerInchResolution)
11917 {
cristy35ef8242010-06-03 16:24:13 +000011918 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011919 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011920
cristy35ef8242010-06-03 16:24:13 +000011921 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011922 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011923
cristy3ed852e2009-09-05 21:47:34 +000011924 chunk[12]=1;
11925 }
glennrp0fe50b42010-11-16 03:52:51 +000011926
cristy3ed852e2009-09-05 21:47:34 +000011927 else
11928 {
11929 if (image->units == PixelsPerCentimeterResolution)
11930 {
cristy35ef8242010-06-03 16:24:13 +000011931 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011932 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011933
cristy35ef8242010-06-03 16:24:13 +000011934 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011935 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011936
cristy3ed852e2009-09-05 21:47:34 +000011937 chunk[12]=1;
11938 }
glennrp0fe50b42010-11-16 03:52:51 +000011939
cristy3ed852e2009-09-05 21:47:34 +000011940 else
11941 {
cristy35ef8242010-06-03 16:24:13 +000011942 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11943 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011944 chunk[12]=0;
11945 }
11946 }
11947 (void) WriteBlob(image,13,chunk);
11948 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11949 }
11950
11951 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11952 {
11953 /*
11954 Write JNG oFFs chunk
11955 */
11956 (void) WriteBlobMSBULong(image,9L);
11957 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011958 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011959 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11960 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011961 chunk[12]=0;
11962 (void) WriteBlob(image,13,chunk);
11963 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11964 }
11965 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11966 {
11967 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11968 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011969 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011970 PNGLong(chunk+4,(png_uint_32) image->page.width);
11971 PNGLong(chunk+8,(png_uint_32) image->page.height);
11972 chunk[12]=0; /* unit = pixels */
11973 (void) WriteBlob(image,13,chunk);
11974 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11975 }
11976
11977
11978 if (transparent)
11979 {
11980 if (jng_alpha_compression_method==0)
11981 {
cristybb503372010-05-27 20:51:26 +000011982 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011983 i;
11984
cristybb503372010-05-27 20:51:26 +000011985 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011986 len;
11987
11988 /* Write IDAT chunk header */
11989 if (logging != MagickFalse)
11990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011991 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000011992 length);
cristy3ed852e2009-09-05 21:47:34 +000011993
11994 /* Copy IDAT chunks */
11995 len=0;
11996 p=blob+8;
cristybb503372010-05-27 20:51:26 +000011997 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000011998 {
11999 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12000 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012001
cristy3ed852e2009-09-05 21:47:34 +000012002 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12003 {
12004 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000012005 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000012006 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000012007 (void) WriteBlob(image,(size_t) len+4,p);
12008 (void) WriteBlobMSBULong(image,
12009 crc32(0,p,(uInt) len+4));
12010 }
glennrp0fe50b42010-11-16 03:52:51 +000012011
cristy3ed852e2009-09-05 21:47:34 +000012012 else
12013 {
12014 if (logging != MagickFalse)
12015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012016 " Skipping %c%c%c%c chunk, length=%.20g.",
12017 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012018 }
12019 p+=(8+len);
12020 }
12021 }
12022 else
12023 {
12024 /* Write JDAA chunk header */
12025 if (logging != MagickFalse)
12026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012027 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012028 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012029 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012030 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012031 /* Write JDAT chunk(s) data */
12032 (void) WriteBlob(image,4,chunk);
12033 (void) WriteBlob(image,length,blob);
12034 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12035 (uInt) length));
12036 }
12037 blob=(unsigned char *) RelinquishMagickMemory(blob);
12038 }
12039
12040 /* Encode image as a JPEG blob */
12041 if (logging != MagickFalse)
12042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12043 " Creating jpeg_image_info.");
12044 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12045 if (jpeg_image_info == (ImageInfo *) NULL)
12046 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12047
12048 if (logging != MagickFalse)
12049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12050 " Creating jpeg_image.");
12051
12052 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12053 if (jpeg_image == (Image *) NULL)
12054 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12055 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12056
12057 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012058 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012059 jpeg_image->filename);
12060
12061 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12062 &image->exception);
12063
12064 if (logging != MagickFalse)
12065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012066 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12067 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012068
12069 if (jng_color_type == 8 || jng_color_type == 12)
12070 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012071
cristy3ed852e2009-09-05 21:47:34 +000012072 jpeg_image_info->quality=jng_quality % 1000;
12073 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12074 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012075
cristy3ed852e2009-09-05 21:47:34 +000012076 if (logging != MagickFalse)
12077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12078 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012079
cristy3ed852e2009-09-05 21:47:34 +000012080 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000012081
cristy3ed852e2009-09-05 21:47:34 +000012082 if (logging != MagickFalse)
12083 {
12084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012085 " Successfully read jpeg_image into a blob, length=%.20g.",
12086 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012087
12088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012089 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012090 }
glennrp0fe50b42010-11-16 03:52:51 +000012091
cristy3ed852e2009-09-05 21:47:34 +000012092 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012093 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012094 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012095 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012096 (void) WriteBlob(image,4,chunk);
12097 (void) WriteBlob(image,length,blob);
12098 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12099
12100 jpeg_image=DestroyImage(jpeg_image);
12101 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12102 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12103 blob=(unsigned char *) RelinquishMagickMemory(blob);
12104
12105 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012106 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012107
12108 /* Write IEND chunk */
12109 (void) WriteBlobMSBULong(image,0L);
12110 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012111 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012112 (void) WriteBlob(image,4,chunk);
12113 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12114
12115 if (logging != MagickFalse)
12116 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12117 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012118
cristy3ed852e2009-09-05 21:47:34 +000012119 return(status);
12120}
12121
12122
12123/*
12124%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12125% %
12126% %
12127% %
12128% W r i t e J N G I m a g e %
12129% %
12130% %
12131% %
12132%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12133%
12134% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12135%
12136% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12137%
12138% The format of the WriteJNGImage method is:
12139%
cristy1e178e72011-08-28 19:44:34 +000012140% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12141% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012142%
12143% A description of each parameter follows:
12144%
12145% o image_info: the image info.
12146%
12147% o image: The image.
12148%
cristy1e178e72011-08-28 19:44:34 +000012149% o exception: return any errors or warnings in this structure.
12150%
cristy3ed852e2009-09-05 21:47:34 +000012151%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12152*/
cristy1e178e72011-08-28 19:44:34 +000012153static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12154 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012155{
12156 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012157 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012158 logging,
cristy3ed852e2009-09-05 21:47:34 +000012159 status;
12160
12161 MngInfo
12162 *mng_info;
12163
cristy3ed852e2009-09-05 21:47:34 +000012164 /*
12165 Open image file.
12166 */
12167 assert(image_info != (const ImageInfo *) NULL);
12168 assert(image_info->signature == MagickSignature);
12169 assert(image != (Image *) NULL);
12170 assert(image->signature == MagickSignature);
12171 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012172 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012173 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12174 if (status == MagickFalse)
12175 return(status);
12176
12177 /*
12178 Allocate a MngInfo structure.
12179 */
12180 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012181 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012182 if (mng_info == (MngInfo *) NULL)
12183 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12184 /*
12185 Initialize members of the MngInfo structure.
12186 */
12187 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12188 mng_info->image=image;
12189 have_mng_structure=MagickTrue;
12190
12191 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12192
cristy018f07f2011-09-04 21:15:19 +000012193 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012194 (void) CloseBlob(image);
12195
12196 (void) CatchImageException(image);
12197 MngInfoFreeStruct(mng_info,&have_mng_structure);
12198 if (logging != MagickFalse)
12199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12200 return(status);
12201}
12202#endif
12203
cristy1e178e72011-08-28 19:44:34 +000012204static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12205 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012206{
12207 const char
12208 *option;
12209
12210 Image
12211 *next_image;
12212
12213 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012214 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012215 status;
12216
glennrp03812ae2010-12-24 01:31:34 +000012217 volatile MagickBooleanType
12218 logging;
12219
cristy3ed852e2009-09-05 21:47:34 +000012220 MngInfo
12221 *mng_info;
12222
12223 int
cristy3ed852e2009-09-05 21:47:34 +000012224 image_count,
12225 need_iterations,
12226 need_matte;
12227
12228 volatile int
12229#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12230 defined(PNG_MNG_FEATURES_SUPPORTED)
12231 need_local_plte,
12232#endif
12233 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012234 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012235 use_global_plte;
12236
cristybb503372010-05-27 20:51:26 +000012237 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012238 i;
12239
12240 unsigned char
12241 chunk[800];
12242
12243 volatile unsigned int
12244 write_jng,
12245 write_mng;
12246
cristybb503372010-05-27 20:51:26 +000012247 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012248 scene;
12249
cristybb503372010-05-27 20:51:26 +000012250 size_t
cristy3ed852e2009-09-05 21:47:34 +000012251 final_delay=0,
12252 initial_delay;
12253
glennrpd5045b42010-03-24 12:40:35 +000012254#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012255 if (image_info->verbose)
12256 printf("Your PNG library (libpng-%s) is rather old.\n",
12257 PNG_LIBPNG_VER_STRING);
12258#endif
12259
12260 /*
12261 Open image file.
12262 */
12263 assert(image_info != (const ImageInfo *) NULL);
12264 assert(image_info->signature == MagickSignature);
12265 assert(image != (Image *) NULL);
12266 assert(image->signature == MagickSignature);
12267 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012268 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012269 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12270 if (status == MagickFalse)
12271 return(status);
12272
12273 /*
12274 Allocate a MngInfo structure.
12275 */
12276 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012277 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012278 if (mng_info == (MngInfo *) NULL)
12279 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12280 /*
12281 Initialize members of the MngInfo structure.
12282 */
12283 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12284 mng_info->image=image;
12285 have_mng_structure=MagickTrue;
12286 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12287
12288 /*
12289 * See if user has requested a specific PNG subformat to be used
12290 * for all of the PNGs in the MNG being written, e.g.,
12291 *
12292 * convert *.png png8:animation.mng
12293 *
12294 * To do: check -define png:bit_depth and png:color_type as well,
12295 * or perhaps use mng:bit_depth and mng:color_type instead for
12296 * global settings.
12297 */
12298
12299 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12300 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12301 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12302
12303 write_jng=MagickFalse;
12304 if (image_info->compression == JPEGCompression)
12305 write_jng=MagickTrue;
12306
12307 mng_info->adjoin=image_info->adjoin &&
12308 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12309
cristy3ed852e2009-09-05 21:47:34 +000012310 if (logging != MagickFalse)
12311 {
12312 /* Log some info about the input */
12313 Image
12314 *p;
12315
12316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12317 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012318
cristy3ed852e2009-09-05 21:47:34 +000012319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012320 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012321
cristy3ed852e2009-09-05 21:47:34 +000012322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12323 " Type: %d",image_info->type);
12324
12325 scene=0;
12326 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12327 {
12328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012329 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012330
cristy3ed852e2009-09-05 21:47:34 +000012331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012332 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012333
cristy3ed852e2009-09-05 21:47:34 +000012334 if (p->matte)
12335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12336 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012337
cristy3ed852e2009-09-05 21:47:34 +000012338 else
12339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12340 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012341
cristy3ed852e2009-09-05 21:47:34 +000012342 if (p->storage_class == PseudoClass)
12343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12344 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012345
cristy3ed852e2009-09-05 21:47:34 +000012346 else
12347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12348 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012349
cristy3ed852e2009-09-05 21:47:34 +000012350 if (p->colors)
12351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012352 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012353
cristy3ed852e2009-09-05 21:47:34 +000012354 else
12355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12356 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012357
cristy3ed852e2009-09-05 21:47:34 +000012358 if (mng_info->adjoin == MagickFalse)
12359 break;
12360 }
12361 }
12362
cristy3ed852e2009-09-05 21:47:34 +000012363 use_global_plte=MagickFalse;
12364 all_images_are_gray=MagickFalse;
12365#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12366 need_local_plte=MagickTrue;
12367#endif
12368 need_defi=MagickFalse;
12369 need_matte=MagickFalse;
12370 mng_info->framing_mode=1;
12371 mng_info->old_framing_mode=1;
12372
12373 if (write_mng)
12374 if (image_info->page != (char *) NULL)
12375 {
12376 /*
12377 Determine image bounding box.
12378 */
12379 SetGeometry(image,&mng_info->page);
12380 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12381 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12382 }
12383 if (write_mng)
12384 {
12385 unsigned int
12386 need_geom;
12387
12388 unsigned short
12389 red,
12390 green,
12391 blue;
12392
12393 mng_info->page=image->page;
12394 need_geom=MagickTrue;
12395 if (mng_info->page.width || mng_info->page.height)
12396 need_geom=MagickFalse;
12397 /*
12398 Check all the scenes.
12399 */
12400 initial_delay=image->delay;
12401 need_iterations=MagickFalse;
12402 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12403 mng_info->equal_physs=MagickTrue,
12404 mng_info->equal_gammas=MagickTrue;
12405 mng_info->equal_srgbs=MagickTrue;
12406 mng_info->equal_backgrounds=MagickTrue;
12407 image_count=0;
12408#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12409 defined(PNG_MNG_FEATURES_SUPPORTED)
12410 all_images_are_gray=MagickTrue;
12411 mng_info->equal_palettes=MagickFalse;
12412 need_local_plte=MagickFalse;
12413#endif
12414 for (next_image=image; next_image != (Image *) NULL; )
12415 {
12416 if (need_geom)
12417 {
12418 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12419 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012420
cristy3ed852e2009-09-05 21:47:34 +000012421 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12422 mng_info->page.height=next_image->rows+next_image->page.y;
12423 }
glennrp0fe50b42010-11-16 03:52:51 +000012424
cristy3ed852e2009-09-05 21:47:34 +000012425 if (next_image->page.x || next_image->page.y)
12426 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012427
cristy3ed852e2009-09-05 21:47:34 +000012428 if (next_image->matte)
12429 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012430
cristy3ed852e2009-09-05 21:47:34 +000012431 if ((int) next_image->dispose >= BackgroundDispose)
12432 if (next_image->matte || next_image->page.x || next_image->page.y ||
12433 ((next_image->columns < mng_info->page.width) &&
12434 (next_image->rows < mng_info->page.height)))
12435 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012436
cristy3ed852e2009-09-05 21:47:34 +000012437 if (next_image->iterations)
12438 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012439
cristy3ed852e2009-09-05 21:47:34 +000012440 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012441
cristy3ed852e2009-09-05 21:47:34 +000012442 if (final_delay != initial_delay || final_delay > 1UL*
12443 next_image->ticks_per_second)
12444 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012445
cristy3ed852e2009-09-05 21:47:34 +000012446#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12447 defined(PNG_MNG_FEATURES_SUPPORTED)
12448 /*
12449 check for global palette possibility.
12450 */
12451 if (image->matte != MagickFalse)
12452 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012453
cristy3ed852e2009-09-05 21:47:34 +000012454 if (need_local_plte == 0)
12455 {
12456 if (ImageIsGray(image) == MagickFalse)
12457 all_images_are_gray=MagickFalse;
12458 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12459 if (use_global_plte == 0)
12460 use_global_plte=mng_info->equal_palettes;
12461 need_local_plte=!mng_info->equal_palettes;
12462 }
12463#endif
12464 if (GetNextImageInList(next_image) != (Image *) NULL)
12465 {
12466 if (next_image->background_color.red !=
12467 next_image->next->background_color.red ||
12468 next_image->background_color.green !=
12469 next_image->next->background_color.green ||
12470 next_image->background_color.blue !=
12471 next_image->next->background_color.blue)
12472 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012473
cristy3ed852e2009-09-05 21:47:34 +000012474 if (next_image->gamma != next_image->next->gamma)
12475 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012476
cristy3ed852e2009-09-05 21:47:34 +000012477 if (next_image->rendering_intent !=
12478 next_image->next->rendering_intent)
12479 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012480
cristy3ed852e2009-09-05 21:47:34 +000012481 if ((next_image->units != next_image->next->units) ||
12482 (next_image->x_resolution != next_image->next->x_resolution) ||
12483 (next_image->y_resolution != next_image->next->y_resolution))
12484 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012485
cristy3ed852e2009-09-05 21:47:34 +000012486 if (mng_info->equal_chrms)
12487 {
12488 if (next_image->chromaticity.red_primary.x !=
12489 next_image->next->chromaticity.red_primary.x ||
12490 next_image->chromaticity.red_primary.y !=
12491 next_image->next->chromaticity.red_primary.y ||
12492 next_image->chromaticity.green_primary.x !=
12493 next_image->next->chromaticity.green_primary.x ||
12494 next_image->chromaticity.green_primary.y !=
12495 next_image->next->chromaticity.green_primary.y ||
12496 next_image->chromaticity.blue_primary.x !=
12497 next_image->next->chromaticity.blue_primary.x ||
12498 next_image->chromaticity.blue_primary.y !=
12499 next_image->next->chromaticity.blue_primary.y ||
12500 next_image->chromaticity.white_point.x !=
12501 next_image->next->chromaticity.white_point.x ||
12502 next_image->chromaticity.white_point.y !=
12503 next_image->next->chromaticity.white_point.y)
12504 mng_info->equal_chrms=MagickFalse;
12505 }
12506 }
12507 image_count++;
12508 next_image=GetNextImageInList(next_image);
12509 }
12510 if (image_count < 2)
12511 {
12512 mng_info->equal_backgrounds=MagickFalse;
12513 mng_info->equal_chrms=MagickFalse;
12514 mng_info->equal_gammas=MagickFalse;
12515 mng_info->equal_srgbs=MagickFalse;
12516 mng_info->equal_physs=MagickFalse;
12517 use_global_plte=MagickFalse;
12518#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12519 need_local_plte=MagickTrue;
12520#endif
12521 need_iterations=MagickFalse;
12522 }
glennrp0fe50b42010-11-16 03:52:51 +000012523
cristy3ed852e2009-09-05 21:47:34 +000012524 if (mng_info->need_fram == MagickFalse)
12525 {
12526 /*
12527 Only certain framing rates 100/n are exactly representable without
12528 the FRAM chunk but we'll allow some slop in VLC files
12529 */
12530 if (final_delay == 0)
12531 {
12532 if (need_iterations != MagickFalse)
12533 {
12534 /*
12535 It's probably a GIF with loop; don't run it *too* fast.
12536 */
glennrp02617122010-07-28 13:07:35 +000012537 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012538 {
12539 final_delay=10;
12540 (void) ThrowMagickException(&image->exception,
12541 GetMagickModule(),CoderWarning,
12542 "input has zero delay between all frames; assuming",
12543 " 10 cs `%s'","");
12544 }
cristy3ed852e2009-09-05 21:47:34 +000012545 }
12546 else
12547 mng_info->ticks_per_second=0;
12548 }
12549 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012550 mng_info->ticks_per_second=(png_uint_32)
12551 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012552 if (final_delay > 50)
12553 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012554
cristy3ed852e2009-09-05 21:47:34 +000012555 if (final_delay > 75)
12556 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012557
cristy3ed852e2009-09-05 21:47:34 +000012558 if (final_delay > 125)
12559 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012560
cristy3ed852e2009-09-05 21:47:34 +000012561 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12562 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12563 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12564 1UL*image->ticks_per_second))
12565 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12566 }
glennrp0fe50b42010-11-16 03:52:51 +000012567
cristy3ed852e2009-09-05 21:47:34 +000012568 if (mng_info->need_fram != MagickFalse)
12569 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12570 /*
12571 If pseudocolor, we should also check to see if all the
12572 palettes are identical and write a global PLTE if they are.
12573 ../glennrp Feb 99.
12574 */
12575 /*
12576 Write the MNG version 1.0 signature and MHDR chunk.
12577 */
12578 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12579 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12580 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012581 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012582 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12583 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012584 PNGLong(chunk+12,mng_info->ticks_per_second);
12585 PNGLong(chunk+16,0L); /* layer count=unknown */
12586 PNGLong(chunk+20,0L); /* frame count=unknown */
12587 PNGLong(chunk+24,0L); /* play time=unknown */
12588 if (write_jng)
12589 {
12590 if (need_matte)
12591 {
12592 if (need_defi || mng_info->need_fram || use_global_plte)
12593 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012594
cristy3ed852e2009-09-05 21:47:34 +000012595 else
12596 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12597 }
glennrp0fe50b42010-11-16 03:52:51 +000012598
cristy3ed852e2009-09-05 21:47:34 +000012599 else
12600 {
12601 if (need_defi || mng_info->need_fram || use_global_plte)
12602 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012603
cristy3ed852e2009-09-05 21:47:34 +000012604 else
12605 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12606 }
12607 }
glennrp0fe50b42010-11-16 03:52:51 +000012608
cristy3ed852e2009-09-05 21:47:34 +000012609 else
12610 {
12611 if (need_matte)
12612 {
12613 if (need_defi || mng_info->need_fram || use_global_plte)
12614 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012615
cristy3ed852e2009-09-05 21:47:34 +000012616 else
12617 PNGLong(chunk+28,9L); /* simplicity=VLC */
12618 }
glennrp0fe50b42010-11-16 03:52:51 +000012619
cristy3ed852e2009-09-05 21:47:34 +000012620 else
12621 {
12622 if (need_defi || mng_info->need_fram || use_global_plte)
12623 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012624
cristy3ed852e2009-09-05 21:47:34 +000012625 else
12626 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12627 }
12628 }
12629 (void) WriteBlob(image,32,chunk);
12630 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12631 option=GetImageOption(image_info,"mng:need-cacheoff");
12632 if (option != (const char *) NULL)
12633 {
12634 size_t
12635 length;
12636
12637 /*
12638 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12639 */
12640 PNGType(chunk,mng_nEED);
12641 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012642 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012643 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012644 length+=4;
12645 (void) WriteBlob(image,length,chunk);
12646 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12647 }
12648 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12649 (GetNextImageInList(image) != (Image *) NULL) &&
12650 (image->iterations != 1))
12651 {
12652 /*
12653 Write MNG TERM chunk
12654 */
12655 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12656 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012657 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012658 chunk[4]=3; /* repeat animation */
12659 chunk[5]=0; /* show last frame when done */
12660 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12661 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012662
cristy3ed852e2009-09-05 21:47:34 +000012663 if (image->iterations == 0)
12664 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012665
cristy3ed852e2009-09-05 21:47:34 +000012666 else
12667 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012668
cristy3ed852e2009-09-05 21:47:34 +000012669 if (logging != MagickFalse)
12670 {
12671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012672 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12673 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012674
cristy3ed852e2009-09-05 21:47:34 +000012675 if (image->iterations == 0)
12676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012677 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012678
cristy3ed852e2009-09-05 21:47:34 +000012679 else
12680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012681 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012682 }
12683 (void) WriteBlob(image,14,chunk);
12684 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12685 }
12686 /*
12687 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12688 */
12689 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12690 mng_info->equal_srgbs)
12691 {
12692 /*
12693 Write MNG sRGB chunk
12694 */
12695 (void) WriteBlobMSBULong(image,1L);
12696 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012697 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012698
cristy3ed852e2009-09-05 21:47:34 +000012699 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012700 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012701 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012702 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012703
cristy3ed852e2009-09-05 21:47:34 +000012704 else
glennrpe610a072010-08-05 17:08:46 +000012705 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012706 Magick_RenderingIntent_to_PNG_RenderingIntent(
12707 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012708
cristy3ed852e2009-09-05 21:47:34 +000012709 (void) WriteBlob(image,5,chunk);
12710 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12711 mng_info->have_write_global_srgb=MagickTrue;
12712 }
glennrp0fe50b42010-11-16 03:52:51 +000012713
cristy3ed852e2009-09-05 21:47:34 +000012714 else
12715 {
12716 if (image->gamma && mng_info->equal_gammas)
12717 {
12718 /*
12719 Write MNG gAMA chunk
12720 */
12721 (void) WriteBlobMSBULong(image,4L);
12722 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012723 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012724 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012725 (void) WriteBlob(image,8,chunk);
12726 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12727 mng_info->have_write_global_gama=MagickTrue;
12728 }
12729 if (mng_info->equal_chrms)
12730 {
12731 PrimaryInfo
12732 primary;
12733
12734 /*
12735 Write MNG cHRM chunk
12736 */
12737 (void) WriteBlobMSBULong(image,32L);
12738 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012739 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012740 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012741 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12742 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012743 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012744 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12745 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012746 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012747 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12748 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012749 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012750 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12751 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012752 (void) WriteBlob(image,36,chunk);
12753 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12754 mng_info->have_write_global_chrm=MagickTrue;
12755 }
12756 }
12757 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
12758 {
12759 /*
12760 Write MNG pHYs chunk
12761 */
12762 (void) WriteBlobMSBULong(image,9L);
12763 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012764 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012765
cristy3ed852e2009-09-05 21:47:34 +000012766 if (image->units == PixelsPerInchResolution)
12767 {
cristy35ef8242010-06-03 16:24:13 +000012768 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012769 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012770
cristy35ef8242010-06-03 16:24:13 +000012771 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012772 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012773
cristy3ed852e2009-09-05 21:47:34 +000012774 chunk[12]=1;
12775 }
glennrp0fe50b42010-11-16 03:52:51 +000012776
cristy3ed852e2009-09-05 21:47:34 +000012777 else
12778 {
12779 if (image->units == PixelsPerCentimeterResolution)
12780 {
cristy35ef8242010-06-03 16:24:13 +000012781 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012782 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012783
cristy35ef8242010-06-03 16:24:13 +000012784 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012785 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012786
cristy3ed852e2009-09-05 21:47:34 +000012787 chunk[12]=1;
12788 }
glennrp0fe50b42010-11-16 03:52:51 +000012789
cristy3ed852e2009-09-05 21:47:34 +000012790 else
12791 {
cristy35ef8242010-06-03 16:24:13 +000012792 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
12793 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012794 chunk[12]=0;
12795 }
12796 }
12797 (void) WriteBlob(image,13,chunk);
12798 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12799 }
12800 /*
12801 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12802 or does not cover the entire frame.
12803 */
12804 if (write_mng && (image->matte || image->page.x > 0 ||
12805 image->page.y > 0 || (image->page.width &&
12806 (image->page.width+image->page.x < mng_info->page.width))
12807 || (image->page.height && (image->page.height+image->page.y
12808 < mng_info->page.height))))
12809 {
12810 (void) WriteBlobMSBULong(image,6L);
12811 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012812 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012813 red=ScaleQuantumToShort(image->background_color.red);
12814 green=ScaleQuantumToShort(image->background_color.green);
12815 blue=ScaleQuantumToShort(image->background_color.blue);
12816 PNGShort(chunk+4,red);
12817 PNGShort(chunk+6,green);
12818 PNGShort(chunk+8,blue);
12819 (void) WriteBlob(image,10,chunk);
12820 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12821 if (mng_info->equal_backgrounds)
12822 {
12823 (void) WriteBlobMSBULong(image,6L);
12824 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012825 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012826 (void) WriteBlob(image,10,chunk);
12827 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12828 }
12829 }
12830
12831#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12832 if ((need_local_plte == MagickFalse) &&
12833 (image->storage_class == PseudoClass) &&
12834 (all_images_are_gray == MagickFalse))
12835 {
cristybb503372010-05-27 20:51:26 +000012836 size_t
cristy3ed852e2009-09-05 21:47:34 +000012837 data_length;
12838
12839 /*
12840 Write MNG PLTE chunk
12841 */
12842 data_length=3*image->colors;
12843 (void) WriteBlobMSBULong(image,data_length);
12844 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012845 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012846
cristybb503372010-05-27 20:51:26 +000012847 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012848 {
12849 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
12850 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
12851 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
12852 }
glennrp0fe50b42010-11-16 03:52:51 +000012853
cristy3ed852e2009-09-05 21:47:34 +000012854 (void) WriteBlob(image,data_length+4,chunk);
12855 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12856 mng_info->have_write_global_plte=MagickTrue;
12857 }
12858#endif
12859 }
12860 scene=0;
12861 mng_info->delay=0;
12862#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12863 defined(PNG_MNG_FEATURES_SUPPORTED)
12864 mng_info->equal_palettes=MagickFalse;
12865#endif
12866 do
12867 {
12868 if (mng_info->adjoin)
12869 {
12870#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12871 defined(PNG_MNG_FEATURES_SUPPORTED)
12872 /*
12873 If we aren't using a global palette for the entire MNG, check to
12874 see if we can use one for two or more consecutive images.
12875 */
12876 if (need_local_plte && use_global_plte && !all_images_are_gray)
12877 {
12878 if (mng_info->IsPalette)
12879 {
12880 /*
12881 When equal_palettes is true, this image has the same palette
12882 as the previous PseudoClass image
12883 */
12884 mng_info->have_write_global_plte=mng_info->equal_palettes;
12885 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12886 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12887 {
12888 /*
12889 Write MNG PLTE chunk
12890 */
cristybb503372010-05-27 20:51:26 +000012891 size_t
cristy3ed852e2009-09-05 21:47:34 +000012892 data_length;
12893
12894 data_length=3*image->colors;
12895 (void) WriteBlobMSBULong(image,data_length);
12896 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012897 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012898
cristybb503372010-05-27 20:51:26 +000012899 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012900 {
12901 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12902 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12903 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12904 }
glennrp0fe50b42010-11-16 03:52:51 +000012905
cristy3ed852e2009-09-05 21:47:34 +000012906 (void) WriteBlob(image,data_length+4,chunk);
12907 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12908 (uInt) (data_length+4)));
12909 mng_info->have_write_global_plte=MagickTrue;
12910 }
12911 }
12912 else
12913 mng_info->have_write_global_plte=MagickFalse;
12914 }
12915#endif
12916 if (need_defi)
12917 {
cristybb503372010-05-27 20:51:26 +000012918 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012919 previous_x,
12920 previous_y;
12921
12922 if (scene)
12923 {
12924 previous_x=mng_info->page.x;
12925 previous_y=mng_info->page.y;
12926 }
12927 else
12928 {
12929 previous_x=0;
12930 previous_y=0;
12931 }
12932 mng_info->page=image->page;
12933 if ((mng_info->page.x != previous_x) ||
12934 (mng_info->page.y != previous_y))
12935 {
12936 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12937 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012938 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012939 chunk[4]=0; /* object 0 MSB */
12940 chunk[5]=0; /* object 0 LSB */
12941 chunk[6]=0; /* visible */
12942 chunk[7]=0; /* abstract */
12943 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12944 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12945 (void) WriteBlob(image,16,chunk);
12946 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12947 }
12948 }
12949 }
12950
12951 mng_info->write_mng=write_mng;
12952
12953 if ((int) image->dispose >= 3)
12954 mng_info->framing_mode=3;
12955
12956 if (mng_info->need_fram && mng_info->adjoin &&
12957 ((image->delay != mng_info->delay) ||
12958 (mng_info->framing_mode != mng_info->old_framing_mode)))
12959 {
12960 if (image->delay == mng_info->delay)
12961 {
12962 /*
12963 Write a MNG FRAM chunk with the new framing mode.
12964 */
12965 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12966 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012967 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012968 chunk[4]=(unsigned char) mng_info->framing_mode;
12969 (void) WriteBlob(image,5,chunk);
12970 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12971 }
12972 else
12973 {
12974 /*
12975 Write a MNG FRAM chunk with the delay.
12976 */
12977 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12978 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012979 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012980 chunk[4]=(unsigned char) mng_info->framing_mode;
12981 chunk[5]=0; /* frame name separator (no name) */
12982 chunk[6]=2; /* flag for changing default delay */
12983 chunk[7]=0; /* flag for changing frame timeout */
12984 chunk[8]=0; /* flag for changing frame clipping */
12985 chunk[9]=0; /* flag for changing frame sync_id */
12986 PNGLong(chunk+10,(png_uint_32)
12987 ((mng_info->ticks_per_second*
12988 image->delay)/MagickMax(image->ticks_per_second,1)));
12989 (void) WriteBlob(image,14,chunk);
12990 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000012991 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000012992 }
12993 mng_info->old_framing_mode=mng_info->framing_mode;
12994 }
12995
12996#if defined(JNG_SUPPORTED)
12997 if (image_info->compression == JPEGCompression)
12998 {
12999 ImageInfo
13000 *write_info;
13001
13002 if (logging != MagickFalse)
13003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13004 " Writing JNG object.");
13005 /* To do: specify the desired alpha compression method. */
13006 write_info=CloneImageInfo(image_info);
13007 write_info->compression=UndefinedCompression;
cristy018f07f2011-09-04 21:15:19 +000013008 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013009 write_info=DestroyImageInfo(write_info);
13010 }
13011 else
13012#endif
13013 {
13014 if (logging != MagickFalse)
13015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13016 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013017
glennrpb9cfe272010-12-21 15:08:06 +000013018 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013019 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013020
13021 /* We don't want any ancillary chunks written */
13022 mng_info->ping_exclude_bKGD=MagickTrue;
13023 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013024 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013025 mng_info->ping_exclude_EXIF=MagickTrue;
13026 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013027 mng_info->ping_exclude_iCCP=MagickTrue;
13028 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13029 mng_info->ping_exclude_oFFs=MagickTrue;
13030 mng_info->ping_exclude_pHYs=MagickTrue;
13031 mng_info->ping_exclude_sRGB=MagickTrue;
13032 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013033 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013034 mng_info->ping_exclude_vpAg=MagickTrue;
13035 mng_info->ping_exclude_zCCP=MagickTrue;
13036 mng_info->ping_exclude_zTXt=MagickTrue;
13037
cristy018f07f2011-09-04 21:15:19 +000013038 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013039 }
13040
13041 if (status == MagickFalse)
13042 {
13043 MngInfoFreeStruct(mng_info,&have_mng_structure);
13044 (void) CloseBlob(image);
13045 return(MagickFalse);
13046 }
13047 (void) CatchImageException(image);
13048 if (GetNextImageInList(image) == (Image *) NULL)
13049 break;
13050 image=SyncNextImageInList(image);
13051 status=SetImageProgress(image,SaveImagesTag,scene++,
13052 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013053
cristy3ed852e2009-09-05 21:47:34 +000013054 if (status == MagickFalse)
13055 break;
glennrp0fe50b42010-11-16 03:52:51 +000013056
cristy3ed852e2009-09-05 21:47:34 +000013057 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013058
cristy3ed852e2009-09-05 21:47:34 +000013059 if (write_mng)
13060 {
13061 while (GetPreviousImageInList(image) != (Image *) NULL)
13062 image=GetPreviousImageInList(image);
13063 /*
13064 Write the MEND chunk.
13065 */
13066 (void) WriteBlobMSBULong(image,0x00000000L);
13067 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013068 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013069 (void) WriteBlob(image,4,chunk);
13070 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13071 }
13072 /*
13073 Relinquish resources.
13074 */
13075 (void) CloseBlob(image);
13076 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013077
cristy3ed852e2009-09-05 21:47:34 +000013078 if (logging != MagickFalse)
13079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013080
cristy3ed852e2009-09-05 21:47:34 +000013081 return(MagickTrue);
13082}
glennrpd5045b42010-03-24 12:40:35 +000013083#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013084
cristy3ed852e2009-09-05 21:47:34 +000013085static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13086{
13087 image=image;
13088 printf("Your PNG library is too old: You have libpng-%s\n",
13089 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013090
cristy3ed852e2009-09-05 21:47:34 +000013091 ThrowBinaryException(CoderError,"PNG library is too old",
13092 image_info->filename);
13093}
glennrp39992b42010-11-14 00:03:43 +000013094
cristy3ed852e2009-09-05 21:47:34 +000013095static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13096{
13097 return(WritePNGImage(image_info,image));
13098}
glennrpd5045b42010-03-24 12:40:35 +000013099#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013100#endif