blob: 9be177d7c3aa9b41dea8227f7c1b14d4603a156f [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");
2305 return(MagickFalse);
2306 }
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 */
2712 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2713 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 {
3702 (void) SetImageType(image,TrueColorType);
3703 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)
3707 (void) SetImageType(image,TrueColorMatteType);
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,
7421 const ImageInfo *IMimage_info,Image *IMimage)
7422{
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
glennrp8bb3a022010-12-13 20:40:04 +00007607 number_opaque = 0;
7608 number_semitransparent = 0;
7609 number_transparent = 0;
7610
glennrpfd05d622011-02-25 04:10:33 +00007611 if (logging != MagickFalse)
7612 {
7613 if (image->storage_class == UndefinedClass)
7614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7615 " storage_class=UndefinedClass");
7616 if (image->storage_class == DirectClass)
7617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7618 " storage_class=DirectClass");
7619 if (image->storage_class == PseudoClass)
7620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7621 " storage_class=PseudoClass");
7622 }
glennrp28af3712011-04-06 18:07:30 +00007623
glennrp7e65e932011-08-19 02:31:16 +00007624 if (image->storage_class == PseudoClass &&
7625 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7626 (mng_info->write_png_colortype != 0 &&
7627 mng_info->write_png_colortype != 4)))
7628 {
7629 (void) SyncImage(image);
7630 image->storage_class = DirectClass;
7631 }
7632
glennrpc6c391a2011-04-27 02:23:56 +00007633 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007634 {
glennrpc6c391a2011-04-27 02:23:56 +00007635 if (image->storage_class != PseudoClass && image->colormap != NULL)
7636 {
7637 /* Free the bogus colormap; it can cause trouble later */
7638 if (logging != MagickFalse)
7639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7640 " Freeing bogus colormap");
7641 (void *) RelinquishMagickMemory(image->colormap);
7642 image->colormap=NULL;
7643 }
glennrp28af3712011-04-06 18:07:30 +00007644 }
glennrpbb4f99d2011-05-22 11:13:17 +00007645
cristy510d06a2011-07-06 23:43:54 +00007646 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007647 (void) TransformImageColorspace(image,RGBColorspace);
glennrp0fe50b42010-11-16 03:52:51 +00007648
glennrp3241bd02010-12-12 04:36:28 +00007649 /*
7650 Sometimes we get PseudoClass images whose RGB values don't match
7651 the colors in the colormap. This code syncs the RGB values.
7652 */
7653 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7654 (void) SyncImage(image);
7655
glennrpa6a06632011-01-19 15:15:34 +00007656#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7657 if (image->depth > 8)
7658 {
7659 if (logging != MagickFalse)
7660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7661 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7662
7663 image->depth=8;
7664 }
7665#endif
7666
glennrp8e58efd2011-05-20 12:16:29 +00007667 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007668 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7669 {
cristy4c08aed2011-07-01 19:47:50 +00007670 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007671 *r;
7672
7673 ExceptionInfo
7674 *exception;
7675
7676 exception=(&image->exception);
7677
7678 if (image->depth > 8)
7679 {
7680#if MAGICKCORE_QUANTUM_DEPTH > 16
7681 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007682 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007683
7684 for (y=0; y < (ssize_t) image->rows; y++)
7685 {
7686 r=GetAuthenticPixels(image,0,y,image->columns,1,
7687 exception);
7688
cristy4c08aed2011-07-01 19:47:50 +00007689 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007690 break;
7691
7692 for (x=0; x < (ssize_t) image->columns; x++)
7693 {
glennrp54cf7972011-08-06 14:28:09 +00007694 LBR16PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007695 r++;
7696 }
glennrpbb4f99d2011-05-22 11:13:17 +00007697
glennrp8e58efd2011-05-20 12:16:29 +00007698 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7699 break;
7700 }
7701
7702 if (image->storage_class == PseudoClass && image->colormap != NULL)
7703 {
cristy3e08f112011-05-24 13:19:30 +00007704 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007705 {
glennrp91d99252011-06-25 14:30:13 +00007706 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007707 }
7708 }
7709#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7710 }
7711
7712 else if (image->depth > 4)
7713 {
7714#if MAGICKCORE_QUANTUM_DEPTH > 8
7715 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007716 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007717
7718 for (y=0; y < (ssize_t) image->rows; y++)
7719 {
7720 r=GetAuthenticPixels(image,0,y,image->columns,1,
7721 exception);
7722
cristy4c08aed2011-07-01 19:47:50 +00007723 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007724 break;
7725
7726 for (x=0; x < (ssize_t) image->columns; x++)
7727 {
glennrp54cf7972011-08-06 14:28:09 +00007728 LBR08PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007729 r++;
7730 }
glennrpbb4f99d2011-05-22 11:13:17 +00007731
glennrp8e58efd2011-05-20 12:16:29 +00007732 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7733 break;
7734 }
7735
7736 if (image->storage_class == PseudoClass && image->colormap != NULL)
7737 {
cristy3e08f112011-05-24 13:19:30 +00007738 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007739 {
glennrp91d99252011-06-25 14:30:13 +00007740 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007741 }
7742 }
7743#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7744 }
7745 else
7746 if (image->depth > 2)
7747 {
7748 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007749 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007750
7751 for (y=0; y < (ssize_t) image->rows; y++)
7752 {
7753 r=GetAuthenticPixels(image,0,y,image->columns,1,
7754 exception);
7755
cristy4c08aed2011-07-01 19:47:50 +00007756 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007757 break;
7758
7759 for (x=0; x < (ssize_t) image->columns; x++)
7760 {
glennrp54cf7972011-08-06 14:28:09 +00007761 LBR04PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007762 r++;
7763 }
glennrpbb4f99d2011-05-22 11:13:17 +00007764
glennrp8e58efd2011-05-20 12:16:29 +00007765 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7766 break;
7767 }
7768
7769 if (image->storage_class == PseudoClass && image->colormap != NULL)
7770 {
cristy3e08f112011-05-24 13:19:30 +00007771 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007772 {
glennrp91d99252011-06-25 14:30:13 +00007773 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007774 }
7775 }
7776 }
7777
7778 else if (image->depth > 1)
7779 {
7780 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007781 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007782
7783 for (y=0; y < (ssize_t) image->rows; y++)
7784 {
7785 r=GetAuthenticPixels(image,0,y,image->columns,1,
7786 exception);
7787
cristy4c08aed2011-07-01 19:47:50 +00007788 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007789 break;
7790
7791 for (x=0; x < (ssize_t) image->columns; x++)
7792 {
glennrp54cf7972011-08-06 14:28:09 +00007793 LBR02PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007794 r++;
7795 }
glennrpbb4f99d2011-05-22 11:13:17 +00007796
glennrp8e58efd2011-05-20 12:16:29 +00007797 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7798 break;
7799 }
7800
7801 if (image->storage_class == PseudoClass && image->colormap != NULL)
7802 {
cristy3e08f112011-05-24 13:19:30 +00007803 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007804 {
glennrp91d99252011-06-25 14:30:13 +00007805 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007806 }
7807 }
7808 }
7809 else
7810 {
7811 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007812 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007813
7814 for (y=0; y < (ssize_t) image->rows; y++)
7815 {
7816 r=GetAuthenticPixels(image,0,y,image->columns,1,
7817 exception);
7818
cristy4c08aed2011-07-01 19:47:50 +00007819 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007820 break;
7821
7822 for (x=0; x < (ssize_t) image->columns; x++)
7823 {
glennrp54cf7972011-08-06 14:28:09 +00007824 LBR01PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007825 r++;
7826 }
glennrpbb4f99d2011-05-22 11:13:17 +00007827
glennrp8e58efd2011-05-20 12:16:29 +00007828 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7829 break;
7830 }
7831
7832 if (image->storage_class == PseudoClass && image->colormap != NULL)
7833 {
cristy3e08f112011-05-24 13:19:30 +00007834 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007835 {
glennrp91d99252011-06-25 14:30:13 +00007836 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007837 }
7838 }
7839 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007840 }
7841
glennrp67b9c1a2011-04-22 18:47:36 +00007842 /* To do: set to next higher multiple of 8 */
7843 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007844 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007845
glennrp2b013e42010-11-24 16:55:50 +00007846#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7847 /* PNG does not handle depths greater than 16 so reduce it even
7848 * if lossy
7849 */
glennrp8e58efd2011-05-20 12:16:29 +00007850 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007851 image->depth=16;
7852#endif
7853
glennrp3faa9a32011-04-23 14:00:25 +00007854#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007855 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007856 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007857 image->depth = 8;
7858#endif
7859
glennrpc8c2f062011-02-25 19:00:33 +00007860 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007861 * we reduce the transparency to binary and run again, then if there
7862 * 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 +00007863 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7864 * palette. Then (To do) we take care of a final reduction that is only
7865 * needed if there are still 256 colors present and one of them has both
7866 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007867 */
glennrp82b3c532011-03-22 19:20:54 +00007868
glennrp8ca51ad2011-05-12 21:22:32 +00007869 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007870 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007871 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007872
glennrp8ca51ad2011-05-12 21:22:32 +00007873 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007874 {
7875 /* BUILD_PALETTE
7876 *
7877 * Sometimes we get DirectClass images that have 256 colors or fewer.
7878 * This code will build a colormap.
7879 *
7880 * Also, sometimes we get PseudoClass images with an out-of-date
7881 * colormap. This code will replace the colormap with a new one.
7882 * Sometimes we get PseudoClass images that have more than 256 colors.
7883 * This code will delete the colormap and change the image to
7884 * DirectClass.
7885 *
cristy4c08aed2011-07-01 19:47:50 +00007886 * If image->matte is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00007887 * even though it sometimes contains left-over non-opaque values.
7888 *
7889 * Also we gather some information (number of opaque, transparent,
7890 * and semitransparent pixels, and whether the image has any non-gray
7891 * pixels or only black-and-white pixels) that we might need later.
7892 *
7893 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7894 * we need to check for bogus non-opaque values, at least.
7895 */
glennrp3c218112010-11-27 15:31:26 +00007896
glennrpd71e86a2011-02-24 01:28:37 +00007897 ExceptionInfo
7898 *exception;
glennrp3c218112010-11-27 15:31:26 +00007899
glennrpd71e86a2011-02-24 01:28:37 +00007900 int
7901 n;
glennrp3c218112010-11-27 15:31:26 +00007902
glennrpd71e86a2011-02-24 01:28:37 +00007903 PixelPacket
7904 opaque[260],
7905 semitransparent[260],
7906 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007907
cristy4c08aed2011-07-01 19:47:50 +00007908 register const Quantum
7909 *s;
glennrp8bb3a022010-12-13 20:40:04 +00007910
cristy4c08aed2011-07-01 19:47:50 +00007911 register Quantum
7912 *q,
glennrpfd05d622011-02-25 04:10:33 +00007913 *r;
7914
glennrpd71e86a2011-02-24 01:28:37 +00007915 if (logging != MagickFalse)
7916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7917 " Enter BUILD_PALETTE:");
7918
7919 if (logging != MagickFalse)
7920 {
glennrp03812ae2010-12-24 01:31:34 +00007921 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007922 " image->columns=%.20g",(double) image->columns);
7923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7924 " image->rows=%.20g",(double) image->rows);
7925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7926 " image->matte=%.20g",(double) image->matte);
7927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7928 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00007929
glennrpfd05d622011-02-25 04:10:33 +00007930 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00007931 {
7932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007933 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00007934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00007935 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00007936
glennrpd71e86a2011-02-24 01:28:37 +00007937 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00007938 {
glennrpd71e86a2011-02-24 01:28:37 +00007939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7940 " %d (%d,%d,%d,%d)",
7941 (int) i,
7942 (int) image->colormap[i].red,
7943 (int) image->colormap[i].green,
7944 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00007945 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00007946 }
glennrp2cc891a2010-12-24 13:44:32 +00007947
glennrpd71e86a2011-02-24 01:28:37 +00007948 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7949 {
7950 if (i > 255)
7951 {
7952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7953 " %d (%d,%d,%d,%d)",
7954 (int) i,
7955 (int) image->colormap[i].red,
7956 (int) image->colormap[i].green,
7957 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00007958 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00007959 }
7960 }
glennrp03812ae2010-12-24 01:31:34 +00007961 }
glennrp7ddcc222010-12-11 05:01:05 +00007962
glennrpd71e86a2011-02-24 01:28:37 +00007963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7964 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00007965
glennrpd71e86a2011-02-24 01:28:37 +00007966 if (image->colors == 0)
7967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7968 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00007969
glennrp8d3d6e52011-04-19 04:39:51 +00007970 if (ping_preserve_colormap == MagickFalse)
7971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7972 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00007973 }
7974
7975 exception=(&image->exception);
7976
7977 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00007978 number_opaque = 0;
7979 number_semitransparent = 0;
7980 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00007981
7982 for (y=0; y < (ssize_t) image->rows; y++)
7983 {
7984 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7985
cristyacd2ed22011-08-30 01:44:23 +00007986 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00007987 break;
7988
7989 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00007990 {
glennrp4737d522011-04-29 03:33:42 +00007991 if (image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00007992 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00007993 {
7994 if (number_opaque < 259)
7995 {
7996 if (number_opaque == 0)
7997 {
cristy4c08aed2011-07-01 19:47:50 +00007998 GetPixelPacket(image, q, opaque);
7999 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008000 number_opaque=1;
8001 }
glennrp2cc891a2010-12-24 13:44:32 +00008002
glennrpd71e86a2011-02-24 01:28:37 +00008003 for (i=0; i< (ssize_t) number_opaque; i++)
8004 {
cristy4c08aed2011-07-01 19:47:50 +00008005 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008006 break;
8007 }
glennrp7ddcc222010-12-11 05:01:05 +00008008
glennrpd71e86a2011-02-24 01:28:37 +00008009 if (i == (ssize_t) number_opaque &&
8010 number_opaque < 259)
8011 {
8012 number_opaque++;
cristy4c08aed2011-07-01 19:47:50 +00008013 GetPixelPacket(image, q, opaque+i);
8014 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008015 }
8016 }
8017 }
cristy4c08aed2011-07-01 19:47:50 +00008018 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008019 {
8020 if (number_transparent < 259)
8021 {
8022 if (number_transparent == 0)
8023 {
cristy4c08aed2011-07-01 19:47:50 +00008024 GetPixelPacket(image, q, transparent);
8025 ping_trans_color.red=(unsigned short)
8026 GetPixelRed(image,q);
8027 ping_trans_color.green=(unsigned short)
8028 GetPixelGreen(image,q);
8029 ping_trans_color.blue=(unsigned short)
8030 GetPixelBlue(image,q);
8031 ping_trans_color.gray=(unsigned short)
8032 GetPixelRed(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008033 number_transparent = 1;
8034 }
8035
8036 for (i=0; i< (ssize_t) number_transparent; i++)
8037 {
cristy4c08aed2011-07-01 19:47:50 +00008038 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008039 break;
8040 }
8041
8042 if (i == (ssize_t) number_transparent &&
8043 number_transparent < 259)
8044 {
8045 number_transparent++;
cristy4c08aed2011-07-01 19:47:50 +00008046 GetPixelPacket(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008047 }
8048 }
8049 }
8050 else
8051 {
8052 if (number_semitransparent < 259)
8053 {
8054 if (number_semitransparent == 0)
8055 {
cristy4c08aed2011-07-01 19:47:50 +00008056 GetPixelPacket(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008057 number_semitransparent = 1;
8058 }
8059
8060 for (i=0; i< (ssize_t) number_semitransparent; i++)
8061 {
cristy4c08aed2011-07-01 19:47:50 +00008062 if (IsPixelEquivalent(image,q, semitransparent+i)
8063 && GetPixelAlpha(image,q) ==
8064 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008065 break;
8066 }
8067
8068 if (i == (ssize_t) number_semitransparent &&
8069 number_semitransparent < 259)
8070 {
8071 number_semitransparent++;
cristy4c08aed2011-07-01 19:47:50 +00008072 GetPixelPacket(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008073 }
8074 }
8075 }
cristyed231572011-07-14 02:18:59 +00008076 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008077 }
8078 }
8079
cristy4054bfb2011-08-29 23:41:39 +00008080 if (mng_info->write_png8 == MagickFalse &&
8081 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008082 {
8083 /* Add the background color to the palette, if it
8084 * isn't already there.
8085 */
glennrpc6c391a2011-04-27 02:23:56 +00008086 if (logging != MagickFalse)
8087 {
8088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8089 " Check colormap for background (%d,%d,%d)",
8090 (int) image->background_color.red,
8091 (int) image->background_color.green,
8092 (int) image->background_color.blue);
8093 }
glennrpd71e86a2011-02-24 01:28:37 +00008094 for (i=0; i<number_opaque; i++)
8095 {
glennrpca7ad3a2011-04-26 04:44:54 +00008096 if (opaque[i].red == image->background_color.red &&
8097 opaque[i].green == image->background_color.green &&
8098 opaque[i].blue == image->background_color.blue)
8099 break;
glennrpd71e86a2011-02-24 01:28:37 +00008100 }
glennrpd71e86a2011-02-24 01:28:37 +00008101 if (number_opaque < 259 && i == number_opaque)
8102 {
glennrp8e045c82011-04-27 16:40:27 +00008103 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008104 ping_background.index = i;
8105 if (logging != MagickFalse)
8106 {
8107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8108 " background_color index is %d",(int) i);
8109 }
8110
glennrpd71e86a2011-02-24 01:28:37 +00008111 }
glennrpa080bc32011-03-11 18:03:44 +00008112 else if (logging != MagickFalse)
8113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8114 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008115 }
8116
8117 image_colors=number_opaque+number_transparent+number_semitransparent;
8118
glennrpa080bc32011-03-11 18:03:44 +00008119 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8120 {
8121 /* No room for the background color; remove it. */
8122 number_opaque--;
8123 image_colors--;
8124 }
8125
glennrpd71e86a2011-02-24 01:28:37 +00008126 if (logging != MagickFalse)
8127 {
8128 if (image_colors > 256)
8129 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8130 " image has more than 256 colors");
8131
8132 else
8133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8134 " image has %d colors",image_colors);
8135 }
8136
glennrp8d3d6e52011-04-19 04:39:51 +00008137 if (ping_preserve_colormap != MagickFalse)
8138 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008139
glennrpfd05d622011-02-25 04:10:33 +00008140 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008141 {
8142 ping_have_color=MagickFalse;
8143 ping_have_non_bw=MagickFalse;
8144
8145 if(image_colors > 256)
8146 {
8147 for (y=0; y < (ssize_t) image->rows; y++)
8148 {
8149 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8150
cristyacd2ed22011-08-30 01:44:23 +00008151 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008152 break;
8153
glennrpe5e6b802011-07-20 14:44:40 +00008154 s=q;
8155 for (x=0; x < (ssize_t) image->columns; x++)
8156 {
8157 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8158 GetPixelRed(image,s) != GetPixelBlue(image,s))
8159 {
8160 ping_have_color=MagickTrue;
8161 ping_have_non_bw=MagickTrue;
8162 break;
8163 }
8164 s+=GetPixelChannels(image);
8165 }
8166
8167 if (ping_have_color != MagickFalse)
8168 break;
8169
glennrpd71e86a2011-02-24 01:28:37 +00008170 /* Worst case is black-and-white; we are looking at every
8171 * pixel twice.
8172 */
8173
glennrpd71e86a2011-02-24 01:28:37 +00008174 if (ping_have_non_bw == MagickFalse)
8175 {
8176 s=q;
8177 for (x=0; x < (ssize_t) image->columns; x++)
8178 {
cristy4c08aed2011-07-01 19:47:50 +00008179 if (GetPixelRed(image,s) != 0 &&
8180 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008181 {
8182 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008183 break;
glennrpd71e86a2011-02-24 01:28:37 +00008184 }
cristyed231572011-07-14 02:18:59 +00008185 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008186 }
glennrpe5e6b802011-07-20 14:44:40 +00008187 }
glennrpd71e86a2011-02-24 01:28:37 +00008188 }
glennrpbb4f99d2011-05-22 11:13:17 +00008189 }
8190 }
glennrpd71e86a2011-02-24 01:28:37 +00008191
8192 if (image_colors < 257)
8193 {
8194 PixelPacket
8195 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008196
glennrpd71e86a2011-02-24 01:28:37 +00008197 /*
8198 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008199 */
8200
glennrpd71e86a2011-02-24 01:28:37 +00008201 if (logging != MagickFalse)
8202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8203 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008204
glennrpd71e86a2011-02-24 01:28:37 +00008205 /* Sort palette, transparent first */;
8206
8207 n = 0;
8208
8209 for (i=0; i<number_transparent; i++)
8210 colormap[n++] = transparent[i];
8211
8212 for (i=0; i<number_semitransparent; i++)
8213 colormap[n++] = semitransparent[i];
8214
8215 for (i=0; i<number_opaque; i++)
8216 colormap[n++] = opaque[i];
8217
glennrpc6c391a2011-04-27 02:23:56 +00008218 ping_background.index +=
8219 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008220
glennrpd71e86a2011-02-24 01:28:37 +00008221 /* image_colors < 257; search the colormap instead of the pixels
8222 * to get ping_have_color and ping_have_non_bw
8223 */
8224 for (i=0; i<n; i++)
8225 {
8226 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008227 {
glennrpd71e86a2011-02-24 01:28:37 +00008228 if (colormap[i].red != colormap[i].green ||
8229 colormap[i].red != colormap[i].blue)
8230 {
8231 ping_have_color=MagickTrue;
8232 ping_have_non_bw=MagickTrue;
8233 break;
8234 }
8235 }
8236
8237 if (ping_have_non_bw == MagickFalse)
8238 {
8239 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008240 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008241 }
glennrp8bb3a022010-12-13 20:40:04 +00008242 }
8243
glennrpd71e86a2011-02-24 01:28:37 +00008244 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8245 (number_transparent == 0 && number_semitransparent == 0)) &&
8246 (((mng_info->write_png_colortype-1) ==
8247 PNG_COLOR_TYPE_PALETTE) ||
8248 (mng_info->write_png_colortype == 0)))
8249 {
glennrp6185c532011-01-14 17:58:40 +00008250 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008251 {
glennrpd71e86a2011-02-24 01:28:37 +00008252 if (n != (ssize_t) image_colors)
8253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8254 " image_colors (%d) and n (%d) don't match",
8255 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008256
glennrpd71e86a2011-02-24 01:28:37 +00008257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8258 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008259 }
glennrp03812ae2010-12-24 01:31:34 +00008260
glennrpd71e86a2011-02-24 01:28:37 +00008261 image->colors = image_colors;
8262
8263 if (AcquireImageColormap(image,image_colors) ==
8264 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008265 ThrowWriterException(ResourceLimitError,
8266 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008267
8268 for (i=0; i< (ssize_t) image_colors; i++)
8269 image->colormap[i] = colormap[i];
8270
8271 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008272 {
glennrpd71e86a2011-02-24 01:28:37 +00008273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8274 " image->colors=%d (%d)",
8275 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008276
glennrpd71e86a2011-02-24 01:28:37 +00008277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8278 " Update the pixel indexes");
8279 }
glennrp6185c532011-01-14 17:58:40 +00008280
glennrpfd05d622011-02-25 04:10:33 +00008281 /* Sync the pixel indices with the new colormap */
8282
glennrpd71e86a2011-02-24 01:28:37 +00008283 for (y=0; y < (ssize_t) image->rows; y++)
8284 {
8285 q=GetAuthenticPixels(image,0,y,image->columns,1,
8286 exception);
glennrp6185c532011-01-14 17:58:40 +00008287
cristyacd2ed22011-08-30 01:44:23 +00008288 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008289 break;
glennrp6185c532011-01-14 17:58:40 +00008290
glennrpbb4f99d2011-05-22 11:13:17 +00008291
glennrpd71e86a2011-02-24 01:28:37 +00008292 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008293 {
glennrpd71e86a2011-02-24 01:28:37 +00008294 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008295 {
glennrpd71e86a2011-02-24 01:28:37 +00008296 if ((image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008297 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8298 image->colormap[i].red == GetPixelRed(image,q) &&
8299 image->colormap[i].green == GetPixelGreen(image,q) &&
8300 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008301 {
cristy4c08aed2011-07-01 19:47:50 +00008302 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008303 break;
glennrp6185c532011-01-14 17:58:40 +00008304 }
glennrp6185c532011-01-14 17:58:40 +00008305 }
cristyed231572011-07-14 02:18:59 +00008306 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008307 }
glennrp6185c532011-01-14 17:58:40 +00008308
glennrpd71e86a2011-02-24 01:28:37 +00008309 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8310 break;
8311 }
8312 }
8313 }
8314
8315 if (logging != MagickFalse)
8316 {
8317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8318 " image->colors=%d", (int) image->colors);
8319
8320 if (image->colormap != NULL)
8321 {
8322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008323 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008324
8325 for (i=0; i < (ssize_t) image->colors; i++)
8326 {
cristy72988482011-03-29 16:34:38 +00008327 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008328 {
8329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8330 " %d (%d,%d,%d,%d)",
8331 (int) i,
8332 (int) image->colormap[i].red,
8333 (int) image->colormap[i].green,
8334 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008335 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008336 }
glennrp6185c532011-01-14 17:58:40 +00008337 }
8338 }
glennrp03812ae2010-12-24 01:31:34 +00008339
glennrpd71e86a2011-02-24 01:28:37 +00008340 if (number_transparent < 257)
8341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8342 " number_transparent = %d",
8343 number_transparent);
8344 else
glennrp03812ae2010-12-24 01:31:34 +00008345
glennrpd71e86a2011-02-24 01:28:37 +00008346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8347 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008348
glennrpd71e86a2011-02-24 01:28:37 +00008349 if (number_opaque < 257)
8350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8351 " number_opaque = %d",
8352 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008353
glennrpd71e86a2011-02-24 01:28:37 +00008354 else
8355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8356 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008357
glennrpd71e86a2011-02-24 01:28:37 +00008358 if (number_semitransparent < 257)
8359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8360 " number_semitransparent = %d",
8361 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008362
glennrpd71e86a2011-02-24 01:28:37 +00008363 else
8364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8365 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008366
glennrpd71e86a2011-02-24 01:28:37 +00008367 if (ping_have_non_bw == MagickFalse)
8368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8369 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008370
glennrpd71e86a2011-02-24 01:28:37 +00008371 else if (ping_have_color == MagickFalse)
8372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8373 " All pixels and the background are gray");
8374
8375 else
8376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8377 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008378
glennrp03812ae2010-12-24 01:31:34 +00008379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8380 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008381 }
glennrpfd05d622011-02-25 04:10:33 +00008382
glennrpc8c2f062011-02-25 19:00:33 +00008383 if (mng_info->write_png8 == MagickFalse)
8384 break;
glennrpfd05d622011-02-25 04:10:33 +00008385
glennrpc8c2f062011-02-25 19:00:33 +00008386 /* Make any reductions necessary for the PNG8 format */
8387 if (image_colors <= 256 &&
8388 image_colors != 0 && image->colormap != NULL &&
8389 number_semitransparent == 0 &&
8390 number_transparent <= 1)
8391 break;
8392
8393 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008394 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8395 * transparent color so if more than one is transparent we merge
8396 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008397 */
glennrp130fc452011-08-20 03:43:18 +00008398 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008399 {
8400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8401 " Thresholding the alpha channel to binary");
8402
8403 for (y=0; y < (ssize_t) image->rows; y++)
8404 {
8405 r=GetAuthenticPixels(image,0,y,image->columns,1,
8406 exception);
8407
cristy4c08aed2011-07-01 19:47:50 +00008408 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008409 break;
8410
8411 for (x=0; x < (ssize_t) image->columns; x++)
8412 {
glennrpf73547f2011-08-20 04:40:26 +00008413 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008414 {
cristy4c08aed2011-07-01 19:47:50 +00008415 SetPixelPacket(image,&image->background_color,r);
8416 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008417 }
8418 else
cristy4c08aed2011-07-01 19:47:50 +00008419 SetPixelAlpha(image,OpaqueAlpha,r);
cristyed231572011-07-14 02:18:59 +00008420 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008421 }
glennrpbb4f99d2011-05-22 11:13:17 +00008422
glennrpc8c2f062011-02-25 19:00:33 +00008423 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8424 break;
8425
8426 if (image_colors != 0 && image_colors <= 256 &&
8427 image->colormap != NULL)
8428 for (i=0; i<image_colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00008429 image->colormap[i].alpha =
8430 (image->colormap[i].alpha > TransparentAlpha/2 ?
8431 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008432 }
8433 continue;
8434 }
8435
8436 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008437 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8438 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8439 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008440 */
glennrpd3371642011-03-22 19:42:23 +00008441 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8442 {
8443 if (logging != MagickFalse)
8444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8445 " Quantizing the background color to 4-4-4");
8446
8447 tried_444 = MagickTrue;
8448
glennrp91d99252011-06-25 14:30:13 +00008449 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008450
8451 if (logging != MagickFalse)
8452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8453 " Quantizing the pixel colors to 4-4-4");
8454
8455 if (image->colormap == NULL)
8456 {
8457 for (y=0; y < (ssize_t) image->rows; y++)
8458 {
8459 r=GetAuthenticPixels(image,0,y,image->columns,1,
8460 exception);
8461
cristy4c08aed2011-07-01 19:47:50 +00008462 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008463 break;
8464
8465 for (x=0; x < (ssize_t) image->columns; x++)
8466 {
cristy4c08aed2011-07-01 19:47:50 +00008467 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008468 LBR04PixelRGB(r);
glennrpd3371642011-03-22 19:42:23 +00008469 r++;
8470 }
glennrpbb4f99d2011-05-22 11:13:17 +00008471
glennrpd3371642011-03-22 19:42:23 +00008472 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8473 break;
8474 }
8475 }
8476
8477 else /* Should not reach this; colormap already exists and
8478 must be <= 256 */
8479 {
8480 if (logging != MagickFalse)
8481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8482 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008483
glennrpd3371642011-03-22 19:42:23 +00008484 for (i=0; i<image_colors; i++)
8485 {
glennrp91d99252011-06-25 14:30:13 +00008486 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008487 }
8488 }
8489 continue;
8490 }
8491
glennrp82b3c532011-03-22 19:20:54 +00008492 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8493 {
8494 if (logging != MagickFalse)
8495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8496 " Quantizing the background color to 3-3-3");
8497
8498 tried_333 = MagickTrue;
8499
glennrp91d99252011-06-25 14:30:13 +00008500 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008501
8502 if (logging != MagickFalse)
8503 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008504 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008505
8506 if (image->colormap == NULL)
8507 {
8508 for (y=0; y < (ssize_t) image->rows; y++)
8509 {
8510 r=GetAuthenticPixels(image,0,y,image->columns,1,
8511 exception);
8512
cristy4c08aed2011-07-01 19:47:50 +00008513 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008514 break;
8515
8516 for (x=0; x < (ssize_t) image->columns; x++)
8517 {
cristy4c08aed2011-07-01 19:47:50 +00008518 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8519 LBR03RGB(r);
glennrp82b3c532011-03-22 19:20:54 +00008520 r++;
8521 }
glennrpbb4f99d2011-05-22 11:13:17 +00008522
glennrp82b3c532011-03-22 19:20:54 +00008523 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8524 break;
8525 }
8526 }
8527
8528 else /* Should not reach this; colormap already exists and
8529 must be <= 256 */
8530 {
8531 if (logging != MagickFalse)
8532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008533 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008534 for (i=0; i<image_colors; i++)
8535 {
glennrp91d99252011-06-25 14:30:13 +00008536 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008537 }
glennrpd3371642011-03-22 19:42:23 +00008538 }
8539 continue;
glennrp82b3c532011-03-22 19:20:54 +00008540 }
glennrpc8c2f062011-02-25 19:00:33 +00008541
glennrp8ca51ad2011-05-12 21:22:32 +00008542 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008543 {
8544 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008546 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008547
glennrp8ca51ad2011-05-12 21:22:32 +00008548 tried_332 = MagickTrue;
8549
glennrp3faa9a32011-04-23 14:00:25 +00008550 /* Red and green were already done so we only quantize the blue
8551 * channel
8552 */
8553
glennrp91d99252011-06-25 14:30:13 +00008554 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008555
glennrpc8c2f062011-02-25 19:00:33 +00008556 if (logging != MagickFalse)
8557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008558 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008559
glennrpc8c2f062011-02-25 19:00:33 +00008560 if (image->colormap == NULL)
8561 {
8562 for (y=0; y < (ssize_t) image->rows; y++)
8563 {
8564 r=GetAuthenticPixels(image,0,y,image->columns,1,
8565 exception);
8566
cristy4c08aed2011-07-01 19:47:50 +00008567 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008568 break;
8569
8570 for (x=0; x < (ssize_t) image->columns; x++)
8571 {
cristy4c08aed2011-07-01 19:47:50 +00008572 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008573 LBR02PixelBlue(r);
glennrp52a479c2011-02-26 21:14:38 +00008574 r++;
glennrpc8c2f062011-02-25 19:00:33 +00008575 }
glennrpbb4f99d2011-05-22 11:13:17 +00008576
glennrpc8c2f062011-02-25 19:00:33 +00008577 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8578 break;
8579 }
8580 }
glennrpfd05d622011-02-25 04:10:33 +00008581
glennrpc8c2f062011-02-25 19:00:33 +00008582 else /* Should not reach this; colormap already exists and
8583 must be <= 256 */
8584 {
8585 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008587 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008588 for (i=0; i<image_colors; i++)
8589 {
glennrp91d99252011-06-25 14:30:13 +00008590 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008591 }
8592 }
8593 continue;
8594 }
8595 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008596
8597 if (image_colors == 0 || image_colors > 256)
8598 {
8599 /* Take care of special case with 256 colors + 1 transparent
8600 * color. We don't need to quantize to 2-3-2-1; we only need to
8601 * eliminate one color, so we'll merge the two darkest red
8602 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8603 */
8604 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8605 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8606 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8607 {
8608 image->background_color.red=ScaleCharToQuantum(0x24);
8609 }
glennrpbb4f99d2011-05-22 11:13:17 +00008610
glennrp8ca51ad2011-05-12 21:22:32 +00008611 if (image->colormap == NULL)
8612 {
8613 for (y=0; y < (ssize_t) image->rows; y++)
8614 {
8615 r=GetAuthenticPixels(image,0,y,image->columns,1,
8616 exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008617
cristy4c08aed2011-07-01 19:47:50 +00008618 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008619 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008620
glennrp8ca51ad2011-05-12 21:22:32 +00008621 for (x=0; x < (ssize_t) image->columns; x++)
8622 {
cristy4c08aed2011-07-01 19:47:50 +00008623 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8624 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8625 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8626 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008627 {
cristy4c08aed2011-07-01 19:47:50 +00008628 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008629 }
cristyed231572011-07-14 02:18:59 +00008630 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008631 }
glennrpbb4f99d2011-05-22 11:13:17 +00008632
glennrp8ca51ad2011-05-12 21:22:32 +00008633 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8634 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008635
glennrp8ca51ad2011-05-12 21:22:32 +00008636 }
8637 }
8638
8639 else
8640 {
8641 for (i=0; i<image_colors; i++)
8642 {
8643 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8644 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8645 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8646 {
8647 image->colormap[i].red=ScaleCharToQuantum(0x24);
8648 }
8649 }
8650 }
8651 }
glennrpd71e86a2011-02-24 01:28:37 +00008652 }
glennrpfd05d622011-02-25 04:10:33 +00008653 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008654
glennrpfd05d622011-02-25 04:10:33 +00008655 /* If we are excluding the tRNS chunk and there is transparency,
8656 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8657 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008658 */
glennrp0e8ea192010-12-24 18:00:33 +00008659 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8660 (number_transparent != 0 || number_semitransparent != 0))
8661 {
glennrpd17915c2011-04-29 14:24:22 +00008662 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008663
8664 if (ping_have_color == MagickFalse)
8665 mng_info->write_png_colortype = 5;
8666
8667 else
8668 mng_info->write_png_colortype = 7;
8669
glennrp8d579662011-02-23 02:05:02 +00008670 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008671 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008672 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008673
glennrp0e8ea192010-12-24 18:00:33 +00008674 }
8675
glennrpfd05d622011-02-25 04:10:33 +00008676 /* See if cheap transparency is possible. It is only possible
8677 * when there is a single transparent color, no semitransparent
8678 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008679 * as the transparent color. We only need this information if
8680 * we are writing a PNG with colortype 0 or 2, and we have not
8681 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008682 */
glennrp5a39f372011-02-25 04:52:16 +00008683 if (number_transparent == 1 &&
8684 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008685 {
8686 ping_have_cheap_transparency = MagickTrue;
8687
8688 if (number_semitransparent != 0)
8689 ping_have_cheap_transparency = MagickFalse;
8690
8691 else if (image_colors == 0 || image_colors > 256 ||
8692 image->colormap == NULL)
8693 {
8694 ExceptionInfo
8695 *exception;
8696
cristy4c08aed2011-07-01 19:47:50 +00008697 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008698 *q;
8699
8700 exception=(&image->exception);
8701
8702 for (y=0; y < (ssize_t) image->rows; y++)
8703 {
8704 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8705
cristyacd2ed22011-08-30 01:44:23 +00008706 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008707 break;
8708
8709 for (x=0; x < (ssize_t) image->columns; x++)
8710 {
cristy4c08aed2011-07-01 19:47:50 +00008711 if (GetPixelAlpha(image,q) != TransparentAlpha &&
glennrp847370c2011-07-05 17:37:15 +00008712 (unsigned short) GetPixelRed(image,q) ==
8713 ping_trans_color.red &&
8714 (unsigned short) GetPixelGreen(image,q) ==
8715 ping_trans_color.green &&
8716 (unsigned short) GetPixelBlue(image,q) ==
8717 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008718 {
8719 ping_have_cheap_transparency = MagickFalse;
8720 break;
8721 }
8722
cristyed231572011-07-14 02:18:59 +00008723 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008724 }
glennrpbb4f99d2011-05-22 11:13:17 +00008725
glennrpfd05d622011-02-25 04:10:33 +00008726 if (ping_have_cheap_transparency == MagickFalse)
8727 break;
8728 }
8729 }
8730 else
8731 {
glennrp67b9c1a2011-04-22 18:47:36 +00008732 /* Assuming that image->colormap[0] is the one transparent color
8733 * and that all others are opaque.
8734 */
glennrpfd05d622011-02-25 04:10:33 +00008735 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008736 for (i=1; i<image_colors; i++)
8737 if (image->colormap[i].red == image->colormap[0].red &&
8738 image->colormap[i].green == image->colormap[0].green &&
8739 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008740 {
glennrp67b9c1a2011-04-22 18:47:36 +00008741 ping_have_cheap_transparency = MagickFalse;
8742 break;
glennrpfd05d622011-02-25 04:10:33 +00008743 }
8744 }
glennrpbb4f99d2011-05-22 11:13:17 +00008745
glennrpfd05d622011-02-25 04:10:33 +00008746 if (logging != MagickFalse)
8747 {
8748 if (ping_have_cheap_transparency == MagickFalse)
8749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8750 " Cheap transparency is not possible.");
8751
8752 else
8753 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8754 " Cheap transparency is possible.");
8755 }
8756 }
8757 else
8758 ping_have_cheap_transparency = MagickFalse;
8759
glennrp8640fb52010-11-23 15:48:26 +00008760 image_depth=image->depth;
8761
glennrp26c990a2010-11-23 02:23:20 +00008762 quantum_info = (QuantumInfo *) NULL;
8763 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008764 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008765 image_matte=image->matte;
8766
glennrp0fe50b42010-11-16 03:52:51 +00008767 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008768 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008769
glennrp52a479c2011-02-26 21:14:38 +00008770 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8771 (image->colors == 0 || image->colormap == NULL))
8772 {
glennrp52a479c2011-02-26 21:14:38 +00008773 image_info=DestroyImageInfo(image_info);
8774 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008775 (void) ThrowMagickException(&IMimage->exception,
8776 GetMagickModule(),CoderError,
8777 "Cannot write PNG8 or color-type 3; colormap is NULL",
8778 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008779#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8780 UnlockSemaphoreInfo(ping_semaphore);
8781#endif
8782 return(MagickFalse);
8783 }
8784
cristy3ed852e2009-09-05 21:47:34 +00008785 /*
8786 Allocate the PNG structures
8787 */
8788#ifdef PNG_USER_MEM_SUPPORTED
8789 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008790 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8791 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008792
cristy3ed852e2009-09-05 21:47:34 +00008793#else
8794 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008795 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008796
cristy3ed852e2009-09-05 21:47:34 +00008797#endif
8798 if (ping == (png_struct *) NULL)
8799 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008800
cristy3ed852e2009-09-05 21:47:34 +00008801 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008802
cristy3ed852e2009-09-05 21:47:34 +00008803 if (ping_info == (png_info *) NULL)
8804 {
8805 png_destroy_write_struct(&ping,(png_info **) NULL);
8806 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8807 }
glennrp0fe50b42010-11-16 03:52:51 +00008808
cristy3ed852e2009-09-05 21:47:34 +00008809 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008810 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008811
glennrp5af765f2010-03-30 11:12:18 +00008812 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008813 {
8814 /*
8815 PNG write failed.
8816 */
8817#ifdef PNG_DEBUG
8818 if (image_info->verbose)
8819 (void) printf("PNG write has failed.\n");
8820#endif
8821 png_destroy_write_struct(&ping,&ping_info);
8822#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008823 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008824#endif
glennrpda8f3a72011-02-27 23:54:12 +00008825 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008826 (void) CloseBlob(image);
8827 image_info=DestroyImageInfo(image_info);
8828 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008829 return(MagickFalse);
8830 }
8831 /*
8832 Prepare PNG for writing.
8833 */
8834#if defined(PNG_MNG_FEATURES_SUPPORTED)
8835 if (mng_info->write_mng)
8836 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008837
cristy3ed852e2009-09-05 21:47:34 +00008838#else
8839# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8840 if (mng_info->write_mng)
8841 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008842
cristy3ed852e2009-09-05 21:47:34 +00008843# endif
8844#endif
glennrp2b013e42010-11-24 16:55:50 +00008845
cristy3ed852e2009-09-05 21:47:34 +00008846 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008847
cristy4e5bc842010-06-09 13:56:01 +00008848 ping_width=(png_uint_32) image->columns;
8849 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008850
cristy3ed852e2009-09-05 21:47:34 +00008851 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8852 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008853
cristy3ed852e2009-09-05 21:47:34 +00008854 if (mng_info->write_png_depth != 0)
8855 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008856
cristy3ed852e2009-09-05 21:47:34 +00008857 /* Adjust requested depth to next higher valid depth if necessary */
8858 if (image_depth > 8)
8859 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008860
cristy3ed852e2009-09-05 21:47:34 +00008861 if ((image_depth > 4) && (image_depth < 8))
8862 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008863
cristy3ed852e2009-09-05 21:47:34 +00008864 if (image_depth == 3)
8865 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008866
cristy3ed852e2009-09-05 21:47:34 +00008867 if (logging != MagickFalse)
8868 {
8869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008870 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008872 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008874 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008876 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008878 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008879 }
glennrp8640fb52010-11-23 15:48:26 +00008880
cristy3ed852e2009-09-05 21:47:34 +00008881 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008882 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008883
glennrp26f37912010-12-23 16:22:42 +00008884
cristy3ed852e2009-09-05 21:47:34 +00008885#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008886 if (ping_exclude_pHYs == MagickFalse)
8887 {
cristy3ed852e2009-09-05 21:47:34 +00008888 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8889 (!mng_info->write_mng || !mng_info->equal_physs))
8890 {
glennrp0fe50b42010-11-16 03:52:51 +00008891 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8893 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008894
8895 if (image->units == PixelsPerInchResolution)
8896 {
glennrpdfd70802010-11-14 01:23:35 +00008897 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008898 ping_pHYs_x_resolution=
8899 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8900 ping_pHYs_y_resolution=
8901 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008902 }
glennrpdfd70802010-11-14 01:23:35 +00008903
cristy3ed852e2009-09-05 21:47:34 +00008904 else if (image->units == PixelsPerCentimeterResolution)
8905 {
glennrpdfd70802010-11-14 01:23:35 +00008906 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008907 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8908 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008909 }
glennrp991d11d2010-11-12 21:55:28 +00008910
cristy3ed852e2009-09-05 21:47:34 +00008911 else
8912 {
glennrpdfd70802010-11-14 01:23:35 +00008913 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8914 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8915 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00008916 }
glennrp991d11d2010-11-12 21:55:28 +00008917
glennrp823b55c2011-03-14 18:46:46 +00008918 if (logging != MagickFalse)
8919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8920 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8921 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8922 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00008923 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008924 }
glennrp26f37912010-12-23 16:22:42 +00008925 }
cristy3ed852e2009-09-05 21:47:34 +00008926#endif
glennrpa521b2f2010-10-29 04:11:03 +00008927
glennrp26f37912010-12-23 16:22:42 +00008928 if (ping_exclude_bKGD == MagickFalse)
8929 {
glennrpa521b2f2010-10-29 04:11:03 +00008930 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00008931 {
glennrpa521b2f2010-10-29 04:11:03 +00008932 unsigned int
8933 mask;
cristy3ed852e2009-09-05 21:47:34 +00008934
glennrpa521b2f2010-10-29 04:11:03 +00008935 mask=0xffff;
8936 if (ping_bit_depth == 8)
8937 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00008938
glennrpa521b2f2010-10-29 04:11:03 +00008939 if (ping_bit_depth == 4)
8940 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00008941
glennrpa521b2f2010-10-29 04:11:03 +00008942 if (ping_bit_depth == 2)
8943 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00008944
glennrpa521b2f2010-10-29 04:11:03 +00008945 if (ping_bit_depth == 1)
8946 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00008947
glennrpa521b2f2010-10-29 04:11:03 +00008948 ping_background.red=(png_uint_16)
8949 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008950
glennrpa521b2f2010-10-29 04:11:03 +00008951 ping_background.green=(png_uint_16)
8952 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008953
glennrpa521b2f2010-10-29 04:11:03 +00008954 ping_background.blue=(png_uint_16)
8955 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00008956
8957 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00008958 }
cristy3ed852e2009-09-05 21:47:34 +00008959
glennrp0fe50b42010-11-16 03:52:51 +00008960 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00008961 {
8962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8963 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00008964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8965 " background_color index is %d",
8966 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00008967
8968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8969 " ping_bit_depth=%d",ping_bit_depth);
8970 }
glennrp0fe50b42010-11-16 03:52:51 +00008971
8972 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008973 }
glennrp0fe50b42010-11-16 03:52:51 +00008974
cristy3ed852e2009-09-05 21:47:34 +00008975 /*
8976 Select the color type.
8977 */
8978 matte=image_matte;
8979 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00008980
glennrp1273f7b2011-02-24 03:20:30 +00008981 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00008982 {
glennrp0fe50b42010-11-16 03:52:51 +00008983
glennrpfd05d622011-02-25 04:10:33 +00008984 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00008985 for reducing the sample depth from 8. */
8986
glennrp0fe50b42010-11-16 03:52:51 +00008987 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00008988
glennrp8bb3a022010-12-13 20:40:04 +00008989 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008990
8991 /*
8992 Set image palette.
8993 */
8994 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8995
glennrp0fe50b42010-11-16 03:52:51 +00008996 if (logging != MagickFalse)
8997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8998 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00008999 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009000
9001 for (i=0; i < (ssize_t) number_colors; i++)
9002 {
9003 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9004 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9005 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9006 if (logging != MagickFalse)
9007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009008#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009009 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009010#else
9011 " %5ld (%5d,%5d,%5d)",
9012#endif
glennrp0fe50b42010-11-16 03:52:51 +00009013 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9014
9015 }
glennrp2b013e42010-11-24 16:55:50 +00009016
glennrp8bb3a022010-12-13 20:40:04 +00009017 ping_have_PLTE=MagickTrue;
9018 image_depth=ping_bit_depth;
9019 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009020
glennrp58e01762011-01-07 15:28:54 +00009021 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009022 {
glennrp0fe50b42010-11-16 03:52:51 +00009023 /*
9024 Identify which colormap entry is transparent.
9025 */
9026 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009027 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009028
glennrp8bb3a022010-12-13 20:40:04 +00009029 for (i=0; i < (ssize_t) number_transparent; i++)
9030 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009031
glennrp0fe50b42010-11-16 03:52:51 +00009032
glennrp2cc891a2010-12-24 13:44:32 +00009033 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009034 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009035
9036 if (ping_num_trans == 0)
9037 ping_have_tRNS=MagickFalse;
9038
glennrp8bb3a022010-12-13 20:40:04 +00009039 else
9040 ping_have_tRNS=MagickTrue;
9041 }
glennrp0fe50b42010-11-16 03:52:51 +00009042
glennrp1273f7b2011-02-24 03:20:30 +00009043 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009044 {
glennrp1273f7b2011-02-24 03:20:30 +00009045 /*
9046 * Identify which colormap entry is the background color.
9047 */
9048
glennrp4f25bd02011-01-01 18:51:28 +00009049 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9050 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9051 break;
glennrp0fe50b42010-11-16 03:52:51 +00009052
glennrp4f25bd02011-01-01 18:51:28 +00009053 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009054
9055 if (logging != MagickFalse)
9056 {
9057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9058 " background_color index is %d",
9059 (int) ping_background.index);
9060 }
glennrp4f25bd02011-01-01 18:51:28 +00009061 }
cristy3ed852e2009-09-05 21:47:34 +00009062 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009063
glennrp7e65e932011-08-19 02:31:16 +00009064 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009065 {
9066 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009067 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009068 }
glennrp0fe50b42010-11-16 03:52:51 +00009069
glennrp7e65e932011-08-19 02:31:16 +00009070 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009071 {
9072 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009073 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009074 }
glennrp0fe50b42010-11-16 03:52:51 +00009075
glennrp8bb3a022010-12-13 20:40:04 +00009076 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009077 {
glennrp5af765f2010-03-30 11:12:18 +00009078 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009079
glennrp8bb3a022010-12-13 20:40:04 +00009080 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009081 {
glennrp5af765f2010-03-30 11:12:18 +00009082 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009083
glennrp5af765f2010-03-30 11:12:18 +00009084 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9085 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009086 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009087
glennrp8bb3a022010-12-13 20:40:04 +00009088 else
9089 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009090
9091 if (logging != MagickFalse)
9092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9093 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009094 }
glennrp0fe50b42010-11-16 03:52:51 +00009095
glennrp7c4c9e62011-03-21 20:23:32 +00009096 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009097 {
9098 if (logging != MagickFalse)
9099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009100 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009101
glennrpd6bf1612010-12-17 17:28:54 +00009102 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009103 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009104
glennrpd6bf1612010-12-17 17:28:54 +00009105 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009106 {
glennrp5af765f2010-03-30 11:12:18 +00009107 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009108 image_matte=MagickFalse;
9109 }
glennrp0fe50b42010-11-16 03:52:51 +00009110
glennrpd6bf1612010-12-17 17:28:54 +00009111 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009112 {
glennrp5af765f2010-03-30 11:12:18 +00009113 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009114 image_matte=MagickTrue;
9115 }
glennrp0fe50b42010-11-16 03:52:51 +00009116
glennrp5aa37f62011-01-02 03:07:57 +00009117 if (image_info->type == PaletteType ||
9118 image_info->type == PaletteMatteType)
9119 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9120
glennrp7c4c9e62011-03-21 20:23:32 +00009121 if (mng_info->write_png_colortype == 0 &&
9122 (image_info->type == UndefinedType ||
9123 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009124 {
glennrp5aa37f62011-01-02 03:07:57 +00009125 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009126 {
glennrp5aa37f62011-01-02 03:07:57 +00009127 if (image_matte == MagickFalse)
9128 {
9129 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9130 image_matte=MagickFalse;
9131 }
glennrp0fe50b42010-11-16 03:52:51 +00009132
glennrp0b206f52011-01-07 04:55:32 +00009133 else
glennrp5aa37f62011-01-02 03:07:57 +00009134 {
9135 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9136 image_matte=MagickTrue;
9137 }
9138 }
9139 else
glennrp8bb3a022010-12-13 20:40:04 +00009140 {
glennrp5aa37f62011-01-02 03:07:57 +00009141 if (image_matte == MagickFalse)
9142 {
9143 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9144 image_matte=MagickFalse;
9145 }
glennrp8bb3a022010-12-13 20:40:04 +00009146
glennrp0b206f52011-01-07 04:55:32 +00009147 else
glennrp5aa37f62011-01-02 03:07:57 +00009148 {
9149 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9150 image_matte=MagickTrue;
9151 }
9152 }
glennrp0fe50b42010-11-16 03:52:51 +00009153 }
glennrp5aa37f62011-01-02 03:07:57 +00009154
cristy3ed852e2009-09-05 21:47:34 +00009155 }
glennrp0fe50b42010-11-16 03:52:51 +00009156
cristy3ed852e2009-09-05 21:47:34 +00009157 if (logging != MagickFalse)
9158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009159 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009160
glennrp5af765f2010-03-30 11:12:18 +00009161 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009162 {
9163 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9164 ping_color_type == PNG_COLOR_TYPE_RGB ||
9165 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9166 ping_bit_depth=8;
9167 }
cristy3ed852e2009-09-05 21:47:34 +00009168
glennrpd6bf1612010-12-17 17:28:54 +00009169 old_bit_depth=ping_bit_depth;
9170
glennrp5af765f2010-03-30 11:12:18 +00009171 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009172 {
glennrp8d579662011-02-23 02:05:02 +00009173 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9174 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009175 }
glennrp8640fb52010-11-23 15:48:26 +00009176
glennrp5af765f2010-03-30 11:12:18 +00009177 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009178 {
cristy35ef8242010-06-03 16:24:13 +00009179 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009180 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009181
9182 if (image->colors == 0)
9183 {
glennrp0fe50b42010-11-16 03:52:51 +00009184 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00009185 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00009186 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009187 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009188 }
9189
cristy35ef8242010-06-03 16:24:13 +00009190 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009191 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009192 }
glennrp2b013e42010-11-24 16:55:50 +00009193
glennrpd6bf1612010-12-17 17:28:54 +00009194 if (logging != MagickFalse)
9195 {
9196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9197 " Number of colors: %.20g",(double) image_colors);
9198
9199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9200 " Tentative PNG bit depth: %d",ping_bit_depth);
9201 }
9202
9203 if (ping_bit_depth < (int) mng_info->write_png_depth)
9204 ping_bit_depth = mng_info->write_png_depth;
9205 }
glennrp2cc891a2010-12-24 13:44:32 +00009206
glennrp5af765f2010-03-30 11:12:18 +00009207 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009208
cristy3ed852e2009-09-05 21:47:34 +00009209 if (logging != MagickFalse)
9210 {
9211 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009212 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009213
cristy3ed852e2009-09-05 21:47:34 +00009214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009215 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009216
cristy3ed852e2009-09-05 21:47:34 +00009217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009218 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009219
cristy3ed852e2009-09-05 21:47:34 +00009220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009221
glennrp8640fb52010-11-23 15:48:26 +00009222 " image->depth: %.20g",(double) image->depth);
9223
9224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009225 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009226 }
9227
glennrp58e01762011-01-07 15:28:54 +00009228 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009229 {
glennrp4f25bd02011-01-01 18:51:28 +00009230 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009231 {
glennrp7c4c9e62011-03-21 20:23:32 +00009232 if (mng_info->write_png_colortype == 0)
9233 {
9234 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009235
glennrp7c4c9e62011-03-21 20:23:32 +00009236 if (ping_have_color != MagickFalse)
9237 ping_color_type=PNG_COLOR_TYPE_RGBA;
9238 }
glennrp4f25bd02011-01-01 18:51:28 +00009239
9240 /*
9241 * Determine if there is any transparent color.
9242 */
9243 if (number_transparent + number_semitransparent == 0)
9244 {
9245 /*
9246 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9247 */
glennrpa6a06632011-01-19 15:15:34 +00009248
glennrp4f25bd02011-01-01 18:51:28 +00009249 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009250
9251 if (mng_info->write_png_colortype == 0)
9252 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009253 }
9254
9255 else
9256 {
9257 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009258 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009259
9260 mask=0xffff;
9261
9262 if (ping_bit_depth == 8)
9263 mask=0x00ff;
9264
9265 if (ping_bit_depth == 4)
9266 mask=0x000f;
9267
9268 if (ping_bit_depth == 2)
9269 mask=0x0003;
9270
9271 if (ping_bit_depth == 1)
9272 mask=0x0001;
9273
9274 ping_trans_color.red=(png_uint_16)
9275 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9276
9277 ping_trans_color.green=(png_uint_16)
9278 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9279
9280 ping_trans_color.blue=(png_uint_16)
9281 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9282
9283 ping_trans_color.gray=(png_uint_16)
cristy4c08aed2011-07-01 19:47:50 +00009284 (ScaleQuantumToShort(GetPixelPacketIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009285 image->colormap)) & mask);
9286
9287 ping_trans_color.index=(png_byte) 0;
9288
9289 ping_have_tRNS=MagickTrue;
9290 }
9291
9292 if (ping_have_tRNS != MagickFalse)
9293 {
9294 /*
glennrpfd05d622011-02-25 04:10:33 +00009295 * Determine if there is one and only one transparent color
9296 * and if so if it is fully transparent.
9297 */
9298 if (ping_have_cheap_transparency == MagickFalse)
9299 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009300 }
9301
9302 if (ping_have_tRNS != MagickFalse)
9303 {
glennrp7c4c9e62011-03-21 20:23:32 +00009304 if (mng_info->write_png_colortype == 0)
9305 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009306
9307 if (image_depth == 8)
9308 {
9309 ping_trans_color.red&=0xff;
9310 ping_trans_color.green&=0xff;
9311 ping_trans_color.blue&=0xff;
9312 ping_trans_color.gray&=0xff;
9313 }
9314 }
9315 }
cristy3ed852e2009-09-05 21:47:34 +00009316 else
9317 {
cristy3ed852e2009-09-05 21:47:34 +00009318 if (image_depth == 8)
9319 {
glennrp5af765f2010-03-30 11:12:18 +00009320 ping_trans_color.red&=0xff;
9321 ping_trans_color.green&=0xff;
9322 ping_trans_color.blue&=0xff;
9323 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009324 }
9325 }
9326 }
glennrp8640fb52010-11-23 15:48:26 +00009327
cristy3ed852e2009-09-05 21:47:34 +00009328 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009329
glennrp2e09f552010-11-14 00:38:48 +00009330 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009331 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009332
glennrp39992b42010-11-14 00:03:43 +00009333 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009334 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009335 ping_have_color == MagickFalse &&
9336 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009337 {
cristy35ef8242010-06-03 16:24:13 +00009338 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009339
cristy3ed852e2009-09-05 21:47:34 +00009340 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009341 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009342
glennrp7c4c9e62011-03-21 20:23:32 +00009343 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009344 {
glennrp5af765f2010-03-30 11:12:18 +00009345 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009346
cristy3ed852e2009-09-05 21:47:34 +00009347 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009348 {
9349 if (logging != MagickFalse)
9350 {
9351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9352 " Scaling ping_trans_color (0)");
9353 }
9354 ping_trans_color.gray*=0x0101;
9355 }
cristy3ed852e2009-09-05 21:47:34 +00009356 }
glennrp0fe50b42010-11-16 03:52:51 +00009357
cristy3ed852e2009-09-05 21:47:34 +00009358 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9359 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009360
glennrp136ee3a2011-04-27 15:47:45 +00009361 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009362 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009363 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009364
cristy3ed852e2009-09-05 21:47:34 +00009365 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009366 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009367
cristy3ed852e2009-09-05 21:47:34 +00009368 else
9369 {
glennrp5af765f2010-03-30 11:12:18 +00009370 ping_bit_depth=8;
9371 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009372 {
9373 if(!mng_info->write_png_depth)
9374 {
glennrp5af765f2010-03-30 11:12:18 +00009375 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009376
cristy35ef8242010-06-03 16:24:13 +00009377 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009378 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009379 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009380 }
9381 }
glennrp2b013e42010-11-24 16:55:50 +00009382
glennrp0fe50b42010-11-16 03:52:51 +00009383 else if (ping_color_type ==
9384 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009385 mng_info->IsPalette)
9386 {
cristy3ed852e2009-09-05 21:47:34 +00009387 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009388
cristy3ed852e2009-09-05 21:47:34 +00009389 int
9390 depth_4_ok=MagickTrue,
9391 depth_2_ok=MagickTrue,
9392 depth_1_ok=MagickTrue;
9393
cristybb503372010-05-27 20:51:26 +00009394 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009395 {
9396 unsigned char
9397 intensity;
9398
9399 intensity=ScaleQuantumToChar(image->colormap[i].red);
9400
9401 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9402 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9403 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9404 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009405 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009406 depth_1_ok=MagickFalse;
9407 }
glennrp2b013e42010-11-24 16:55:50 +00009408
cristy3ed852e2009-09-05 21:47:34 +00009409 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009410 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009411
cristy3ed852e2009-09-05 21:47:34 +00009412 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009413 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009414
cristy3ed852e2009-09-05 21:47:34 +00009415 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009416 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009417 }
9418 }
glennrp2b013e42010-11-24 16:55:50 +00009419
glennrp5af765f2010-03-30 11:12:18 +00009420 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009421 }
glennrp0fe50b42010-11-16 03:52:51 +00009422
cristy3ed852e2009-09-05 21:47:34 +00009423 else
glennrp0fe50b42010-11-16 03:52:51 +00009424
cristy3ed852e2009-09-05 21:47:34 +00009425 if (mng_info->IsPalette)
9426 {
glennrp17a14852010-05-10 03:01:59 +00009427 number_colors=image_colors;
9428
cristy3ed852e2009-09-05 21:47:34 +00009429 if (image_depth <= 8)
9430 {
cristy3ed852e2009-09-05 21:47:34 +00009431 /*
9432 Set image palette.
9433 */
glennrp5af765f2010-03-30 11:12:18 +00009434 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009435
glennrp58e01762011-01-07 15:28:54 +00009436 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009437 {
glennrp9c1eb072010-06-06 22:19:15 +00009438 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009439
glennrp3b51f0e2010-11-27 18:14:08 +00009440 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9442 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009443 }
glennrp0fe50b42010-11-16 03:52:51 +00009444
cristy3ed852e2009-09-05 21:47:34 +00009445 else
9446 {
cristybb503372010-05-27 20:51:26 +00009447 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009448 {
9449 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9450 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9451 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9452 }
glennrp0fe50b42010-11-16 03:52:51 +00009453
glennrp3b51f0e2010-11-27 18:14:08 +00009454 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009456 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009457 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009458
glennrp39992b42010-11-14 00:03:43 +00009459 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009460 }
glennrp0fe50b42010-11-16 03:52:51 +00009461
cristy3ed852e2009-09-05 21:47:34 +00009462 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009463 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009464 {
cristybefe4d22010-06-07 01:18:58 +00009465 size_t
9466 one;
9467
glennrp5af765f2010-03-30 11:12:18 +00009468 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009469 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009470
glennrpd17915c2011-04-29 14:24:22 +00009471 while ((one << ping_bit_depth) < (ssize_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009472 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009473 }
glennrp0fe50b42010-11-16 03:52:51 +00009474
glennrp5af765f2010-03-30 11:12:18 +00009475 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009476
glennrp58e01762011-01-07 15:28:54 +00009477 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009478 {
glennrp0fe50b42010-11-16 03:52:51 +00009479 /*
glennrpd6bf1612010-12-17 17:28:54 +00009480 * Set up trans_colors array.
9481 */
glennrp0fe50b42010-11-16 03:52:51 +00009482 assert(number_colors <= 256);
9483
glennrpd6bf1612010-12-17 17:28:54 +00009484 ping_num_trans=(unsigned short) (number_transparent +
9485 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009486
9487 if (ping_num_trans == 0)
9488 ping_have_tRNS=MagickFalse;
9489
glennrpd6bf1612010-12-17 17:28:54 +00009490 else
glennrp0fe50b42010-11-16 03:52:51 +00009491 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009492 if (logging != MagickFalse)
9493 {
9494 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9495 " Scaling ping_trans_color (1)");
9496 }
glennrpd6bf1612010-12-17 17:28:54 +00009497 ping_have_tRNS=MagickTrue;
9498
9499 for (i=0; i < ping_num_trans; i++)
9500 {
cristy4c08aed2011-07-01 19:47:50 +00009501 ping_trans_alpha[i]= (png_byte)
9502 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009503 }
glennrp0fe50b42010-11-16 03:52:51 +00009504 }
9505 }
cristy3ed852e2009-09-05 21:47:34 +00009506 }
9507 }
glennrp0fe50b42010-11-16 03:52:51 +00009508
cristy3ed852e2009-09-05 21:47:34 +00009509 else
9510 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009511
cristy3ed852e2009-09-05 21:47:34 +00009512 if (image_depth < 8)
9513 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009514
cristy3ed852e2009-09-05 21:47:34 +00009515 if ((save_image_depth == 16) && (image_depth == 8))
9516 {
glennrp4f25bd02011-01-01 18:51:28 +00009517 if (logging != MagickFalse)
9518 {
9519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9520 " Scaling ping_trans_color from (%d,%d,%d)",
9521 (int) ping_trans_color.red,
9522 (int) ping_trans_color.green,
9523 (int) ping_trans_color.blue);
9524 }
9525
glennrp5af765f2010-03-30 11:12:18 +00009526 ping_trans_color.red*=0x0101;
9527 ping_trans_color.green*=0x0101;
9528 ping_trans_color.blue*=0x0101;
9529 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009530
9531 if (logging != MagickFalse)
9532 {
9533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9534 " to (%d,%d,%d)",
9535 (int) ping_trans_color.red,
9536 (int) ping_trans_color.green,
9537 (int) ping_trans_color.blue);
9538 }
cristy3ed852e2009-09-05 21:47:34 +00009539 }
9540 }
9541
cristy4383ec82011-01-05 15:42:32 +00009542 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9543 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009544
cristy3ed852e2009-09-05 21:47:34 +00009545 /*
9546 Adjust background and transparency samples in sub-8-bit grayscale files.
9547 */
glennrp5af765f2010-03-30 11:12:18 +00009548 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009549 PNG_COLOR_TYPE_GRAY)
9550 {
9551 png_uint_16
9552 maxval;
9553
cristy35ef8242010-06-03 16:24:13 +00009554 size_t
9555 one=1;
9556
cristy22ffd972010-06-03 16:51:47 +00009557 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009558
glennrp4f25bd02011-01-01 18:51:28 +00009559 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009560 {
cristy3ed852e2009-09-05 21:47:34 +00009561
glennrpa521b2f2010-10-29 04:11:03 +00009562 ping_background.gray=(png_uint_16)
glennrp847370c2011-07-05 17:37:15 +00009563 ((maxval/255.)*((GetPixelPacketIntensity(&image->background_color)))
9564 +.5);
cristy3ed852e2009-09-05 21:47:34 +00009565
9566 if (logging != MagickFalse)
9567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009568 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9570 " background_color index is %d",
9571 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009572
glennrp991d11d2010-11-12 21:55:28 +00009573 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009574 }
cristy3ed852e2009-09-05 21:47:34 +00009575
glennrp3e3e20f2011-06-09 04:21:43 +00009576 if (logging != MagickFalse)
9577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9578 " Scaling ping_trans_color.gray from %d",
9579 (int)ping_trans_color.gray);
9580
glennrp9be9b1c2011-06-09 12:21:45 +00009581 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009582 ping_trans_color.gray)+.5);
9583
9584 if (logging != MagickFalse)
9585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9586 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009587 }
glennrp17a14852010-05-10 03:01:59 +00009588
glennrp26f37912010-12-23 16:22:42 +00009589 if (ping_exclude_bKGD == MagickFalse)
9590 {
glennrp1273f7b2011-02-24 03:20:30 +00009591 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009592 {
9593 /*
9594 Identify which colormap entry is the background color.
9595 */
9596
glennrp17a14852010-05-10 03:01:59 +00009597 number_colors=image_colors;
9598
glennrpa521b2f2010-10-29 04:11:03 +00009599 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9600 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009601 break;
9602
9603 ping_background.index=(png_byte) i;
9604
glennrp3b51f0e2010-11-27 18:14:08 +00009605 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009606 {
9607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009608 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009609 }
glennrp0fe50b42010-11-16 03:52:51 +00009610
cristy13d07042010-11-21 20:56:18 +00009611 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009612 {
9613 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009614
9615 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009616 {
9617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9618 " background =(%d,%d,%d)",
9619 (int) ping_background.red,
9620 (int) ping_background.green,
9621 (int) ping_background.blue);
9622 }
9623 }
glennrpa521b2f2010-10-29 04:11:03 +00009624
glennrpd6bf1612010-12-17 17:28:54 +00009625 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009626 {
glennrp3b51f0e2010-11-27 18:14:08 +00009627 if (logging != MagickFalse)
9628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9629 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009630 ping_have_bKGD = MagickFalse;
9631 }
glennrp17a14852010-05-10 03:01:59 +00009632 }
glennrp26f37912010-12-23 16:22:42 +00009633 }
glennrp17a14852010-05-10 03:01:59 +00009634
cristy3ed852e2009-09-05 21:47:34 +00009635 if (logging != MagickFalse)
9636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009637 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009638 /*
9639 Initialize compression level and filtering.
9640 */
9641 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009642 {
9643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9644 " Setting up deflate compression");
9645
9646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9647 " Compression buffer size: 32768");
9648 }
9649
cristy3ed852e2009-09-05 21:47:34 +00009650 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009651
cristy3ed852e2009-09-05 21:47:34 +00009652 if (logging != MagickFalse)
9653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9654 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009655
cristy4054bfb2011-08-29 23:41:39 +00009656 png_set_compression_mem_level(ping, 9);
9657
glennrp10d739e2011-06-29 18:00:52 +00009658 /* Untangle the "-quality" setting:
9659
9660 Undefined is 0; the default is used.
9661 Default is 75
9662
9663 10's digit:
9664
9665 0: Use Z_HUFFMAN_ONLY strategy with the
9666 zlib default compression level
9667
9668 1-9: the zlib compression level
9669
9670 1's digit:
9671
9672 0-4: the PNG filter method
9673
9674 5: libpng adaptive filtering if compression level > 5
9675 libpng filter type "none" if compression level <= 5
9676 or if image is grayscale or palette
9677
9678 6: libpng adaptive filtering
9679
9680 7: "LOCO" filtering (intrapixel differing) if writing
9681 a MNG, othewise "none". Did not work in IM-6.7.0-9
9682 and earlier because of a missing "else".
9683
9684 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009685 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009686
9687 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009688 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009689
9690 Note that using the -quality option, not all combinations of
9691 PNG filter type, zlib compression level, and zlib compression
cristy5e6be1e2011-07-16 01:23:39 +00009692 strategy are possible. This will be addressed soon in a
cristy4054bfb2011-08-29 23:41:39 +00009693 release that accomodates "-define PNG:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +00009694
9695 */
9696
cristy3ed852e2009-09-05 21:47:34 +00009697 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9698 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009699
glennrp18682582011-06-30 18:11:47 +00009700 if (quality <= 9)
9701 {
9702 if (mng_info->write_png_compression_strategy == 0)
9703 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9704 }
9705
9706 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009707 {
9708 int
9709 level;
9710
cristybb503372010-05-27 20:51:26 +00009711 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009712
glennrp18682582011-06-30 18:11:47 +00009713 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009714 }
glennrp0fe50b42010-11-16 03:52:51 +00009715
glennrp18682582011-06-30 18:11:47 +00009716 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009717 {
glennrp18682582011-06-30 18:11:47 +00009718 if ((quality %10) == 8 || (quality %10) == 9)
9719 mng_info->write_png_compression_strategy=Z_RLE;
cristy3ed852e2009-09-05 21:47:34 +00009720 }
glennrp0fe50b42010-11-16 03:52:51 +00009721
glennrp18682582011-06-30 18:11:47 +00009722 if (mng_info->write_png_compression_filter == 0)
9723 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9724
cristy3ed852e2009-09-05 21:47:34 +00009725 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009726 {
glennrp18682582011-06-30 18:11:47 +00009727 if (mng_info->write_png_compression_level)
9728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9729 " Compression level: %d",
9730 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009731
glennrp18682582011-06-30 18:11:47 +00009732 if (mng_info->write_png_compression_strategy)
9733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9734 " Compression strategy: %d",
9735 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009736
glennrp18682582011-06-30 18:11:47 +00009737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9738 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009739
cristy4054bfb2011-08-29 23:41:39 +00009740 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +00009741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9742 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +00009743 else if (mng_info->write_png_compression_filter == 0 ||
9744 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +00009745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9746 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009747 else
9748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9749 " Base filter method: %d",
9750 (int) mng_info->write_png_compression_filter-1);
9751 }
glennrp2b013e42010-11-24 16:55:50 +00009752
glennrp18682582011-06-30 18:11:47 +00009753 if (mng_info->write_png_compression_level != 0)
9754 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9755
9756 if (mng_info->write_png_compression_filter == 6)
9757 {
9758 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9759 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9760 (quality < 50))
9761 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9762 else
9763 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9764 }
cristy4054bfb2011-08-29 23:41:39 +00009765 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +00009766 mng_info->write_png_compression_filter == 10)
9767 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9768
9769 else if (mng_info->write_png_compression_filter == 8)
9770 {
9771#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9772 if (mng_info->write_mng)
9773 {
9774 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9775 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9776 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9777 }
9778#endif
cristy4054bfb2011-08-29 23:41:39 +00009779 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +00009780 }
9781
9782 else if (mng_info->write_png_compression_filter == 9)
9783 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9784
9785 else if (mng_info->write_png_compression_filter != 0)
9786 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9787 mng_info->write_png_compression_filter-1);
9788
9789 if (mng_info->write_png_compression_strategy != 0)
9790 png_set_compression_strategy(ping,
9791 mng_info->write_png_compression_strategy-1);
9792
cristy3ed852e2009-09-05 21:47:34 +00009793
cristy4054bfb2011-08-29 23:41:39 +00009794 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9795 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009796 {
9797 ResetImageProfileIterator(image);
9798 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009799 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009800 profile=GetImageProfile(image,name);
9801
9802 if (profile != (StringInfo *) NULL)
9803 {
glennrp5af765f2010-03-30 11:12:18 +00009804#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009805 if ((LocaleCompare(name,"ICC") == 0) ||
9806 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009807 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009808
9809 if (ping_exclude_iCCP == MagickFalse)
9810 {
9811 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009812#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009813 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009814#else
9815 (png_const_bytep) GetStringInfoDatum(profile),
9816#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009817 (png_uint_32) GetStringInfoLength(profile));
9818 }
glennrp26f37912010-12-23 16:22:42 +00009819 }
glennrp0fe50b42010-11-16 03:52:51 +00009820
glennrpc8cbc5d2011-01-01 00:12:34 +00009821 else
cristy3ed852e2009-09-05 21:47:34 +00009822#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009823 if (ping_exclude_zCCP == MagickFalse)
9824 {
glennrpcf002022011-01-30 02:38:15 +00009825 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009826 (unsigned char *) name,(unsigned char *) name,
9827 GetStringInfoDatum(profile),
9828 (png_uint_32) GetStringInfoLength(profile));
9829 }
9830 }
glennrp0b206f52011-01-07 04:55:32 +00009831
glennrpc8cbc5d2011-01-01 00:12:34 +00009832 if (logging != MagickFalse)
9833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9834 " Setting up text chunk with %s profile",name);
9835
9836 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009837 }
cristy3ed852e2009-09-05 21:47:34 +00009838 }
9839
9840#if defined(PNG_WRITE_sRGB_SUPPORTED)
9841 if ((mng_info->have_write_global_srgb == 0) &&
9842 ((image->rendering_intent != UndefinedIntent) ||
9843 (image->colorspace == sRGBColorspace)))
9844 {
glennrp26f37912010-12-23 16:22:42 +00009845 if (ping_exclude_sRGB == MagickFalse)
9846 {
9847 /*
9848 Note image rendering intent.
9849 */
9850 if (logging != MagickFalse)
9851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9852 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009853
glennrp26f37912010-12-23 16:22:42 +00009854 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009855 Magick_RenderingIntent_to_PNG_RenderingIntent(
9856 image->rendering_intent)));
cristy4054bfb2011-08-29 23:41:39 +00009857
9858 if (ping_exclude_gAMA == MagickFalse)
9859 png_set_gAMA(ping,ping_info,0.45455);
glennrp26f37912010-12-23 16:22:42 +00009860 }
cristy3ed852e2009-09-05 21:47:34 +00009861 }
glennrp26f37912010-12-23 16:22:42 +00009862
glennrp5af765f2010-03-30 11:12:18 +00009863 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009864#endif
9865 {
glennrp2cc891a2010-12-24 13:44:32 +00009866 if (ping_exclude_gAMA == MagickFalse &&
9867 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009868 (image->gamma < .45 || image->gamma > .46)))
9869 {
cristy3ed852e2009-09-05 21:47:34 +00009870 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9871 {
9872 /*
9873 Note image gamma.
9874 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9875 */
9876 if (logging != MagickFalse)
9877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9878 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009879
cristy3ed852e2009-09-05 21:47:34 +00009880 png_set_gAMA(ping,ping_info,image->gamma);
9881 }
glennrp26f37912010-12-23 16:22:42 +00009882 }
glennrp2b013e42010-11-24 16:55:50 +00009883
glennrp26f37912010-12-23 16:22:42 +00009884 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009885 {
glennrp26f37912010-12-23 16:22:42 +00009886 if ((mng_info->have_write_global_chrm == 0) &&
9887 (image->chromaticity.red_primary.x != 0.0))
9888 {
9889 /*
9890 Note image chromaticity.
9891 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9892 */
9893 PrimaryInfo
9894 bp,
9895 gp,
9896 rp,
9897 wp;
cristy3ed852e2009-09-05 21:47:34 +00009898
glennrp26f37912010-12-23 16:22:42 +00009899 wp=image->chromaticity.white_point;
9900 rp=image->chromaticity.red_primary;
9901 gp=image->chromaticity.green_primary;
9902 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009903
glennrp26f37912010-12-23 16:22:42 +00009904 if (logging != MagickFalse)
9905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9906 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009907
glennrp26f37912010-12-23 16:22:42 +00009908 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9909 bp.x,bp.y);
9910 }
9911 }
cristy3ed852e2009-09-05 21:47:34 +00009912 }
glennrpdfd70802010-11-14 01:23:35 +00009913
glennrp5af765f2010-03-30 11:12:18 +00009914 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009915
9916 if (mng_info->write_mng)
9917 png_set_sig_bytes(ping,8);
9918
9919 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9920
glennrpd6bf1612010-12-17 17:28:54 +00009921 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009922 {
9923 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +00009924 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009925 {
glennrp5af765f2010-03-30 11:12:18 +00009926 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +00009927
glennrp5af765f2010-03-30 11:12:18 +00009928 if (ping_bit_depth < 8)
9929 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00009930 }
glennrp0fe50b42010-11-16 03:52:51 +00009931
cristy3ed852e2009-09-05 21:47:34 +00009932 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +00009933 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00009934 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009935 }
9936
glennrp0e8ea192010-12-24 18:00:33 +00009937 if (ping_need_colortype_warning != MagickFalse ||
9938 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00009939 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00009940 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00009941 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00009942 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +00009943 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +00009944 {
9945 if (logging != MagickFalse)
9946 {
glennrp0e8ea192010-12-24 18:00:33 +00009947 if (ping_need_colortype_warning != MagickFalse)
9948 {
9949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9950 " Image has transparency but tRNS chunk was excluded");
9951 }
9952
cristy3ed852e2009-09-05 21:47:34 +00009953 if (mng_info->write_png_depth)
9954 {
9955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9956 " Defined PNG:bit-depth=%u, Computed depth=%u",
9957 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00009958 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009959 }
glennrp0e8ea192010-12-24 18:00:33 +00009960
cristy3ed852e2009-09-05 21:47:34 +00009961 if (mng_info->write_png_colortype)
9962 {
9963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9964 " Defined PNG:color-type=%u, Computed color type=%u",
9965 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00009966 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009967 }
9968 }
glennrp0e8ea192010-12-24 18:00:33 +00009969
glennrp3bd2e412010-08-10 13:34:52 +00009970 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +00009971 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9972 }
9973
glennrp58e01762011-01-07 15:28:54 +00009974 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009975 {
9976 /* Add an opaque matte channel */
9977 image->matte = MagickTrue;
9978 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +00009979
glennrpb4a13412010-05-05 12:47:19 +00009980 if (logging != MagickFalse)
9981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9982 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00009983 }
9984
glennrp0e319732011-01-25 21:53:13 +00009985 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +00009986 {
glennrp991d11d2010-11-12 21:55:28 +00009987 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +00009988 {
glennrp991d11d2010-11-12 21:55:28 +00009989 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +00009990 if (logging != MagickFalse)
9991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9992 " Setting ping_have_tRNS=MagickTrue.");
9993 }
glennrpe9c26dc2010-05-30 01:56:35 +00009994 }
9995
cristy3ed852e2009-09-05 21:47:34 +00009996 if (logging != MagickFalse)
9997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9998 " Writing PNG header chunks");
9999
glennrp5af765f2010-03-30 11:12:18 +000010000 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10001 ping_bit_depth,ping_color_type,
10002 ping_interlace_method,ping_compression_method,
10003 ping_filter_method);
10004
glennrp39992b42010-11-14 00:03:43 +000010005 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10006 {
glennrpf09bded2011-01-08 01:15:59 +000010007 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010008
glennrp3b51f0e2010-11-27 18:14:08 +000010009 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +000010010 {
glennrp8640fb52010-11-23 15:48:26 +000010011 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +000010012 {
glennrpd6bf1612010-12-17 17:28:54 +000010013 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +000010014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010015 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10016 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010017 (int) palette[i].red,
10018 (int) palette[i].green,
10019 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +000010020 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010021 (int) ping_trans_alpha[i]);
10022 else
10023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010024 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010025 (int) i,
10026 (int) palette[i].red,
10027 (int) palette[i].green,
10028 (int) palette[i].blue);
10029 }
glennrp39992b42010-11-14 00:03:43 +000010030 }
glennrp39992b42010-11-14 00:03:43 +000010031 }
10032
glennrp26f37912010-12-23 16:22:42 +000010033 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010034 {
glennrp26f37912010-12-23 16:22:42 +000010035 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010036 {
glennrp26f37912010-12-23 16:22:42 +000010037 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010038 if (logging)
10039 {
10040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10041 " Setting up bKGD chunk");
10042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10043 " background color = (%d,%d,%d)",
10044 (int) ping_background.red,
10045 (int) ping_background.green,
10046 (int) ping_background.blue);
10047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10048 " index = %d, gray=%d",
10049 (int) ping_background.index,
10050 (int) ping_background.gray);
10051 }
10052 }
glennrp26f37912010-12-23 16:22:42 +000010053 }
10054
10055 if (ping_exclude_pHYs == MagickFalse)
10056 {
10057 if (ping_have_pHYs != MagickFalse)
10058 {
10059 png_set_pHYs(ping,ping_info,
10060 ping_pHYs_x_resolution,
10061 ping_pHYs_y_resolution,
10062 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010063
10064 if (logging)
10065 {
10066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10067 " Setting up pHYs chunk");
10068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10069 " x_resolution=%lu",
10070 (unsigned long) ping_pHYs_x_resolution);
10071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10072 " y_resolution=%lu",
10073 (unsigned long) ping_pHYs_y_resolution);
10074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10075 " unit_type=%lu",
10076 (unsigned long) ping_pHYs_unit_type);
10077 }
glennrp26f37912010-12-23 16:22:42 +000010078 }
glennrpdfd70802010-11-14 01:23:35 +000010079 }
10080
10081#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010082 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010083 {
glennrp26f37912010-12-23 16:22:42 +000010084 if (image->page.x || image->page.y)
10085 {
10086 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10087 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010088
glennrp26f37912010-12-23 16:22:42 +000010089 if (logging != MagickFalse)
10090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10091 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10092 (int) image->page.x, (int) image->page.y);
10093 }
glennrpdfd70802010-11-14 01:23:35 +000010094 }
10095#endif
10096
glennrpda8f3a72011-02-27 23:54:12 +000010097 if (mng_info->need_blob != MagickFalse)
10098 {
10099 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
10100 MagickFalse)
10101 png_error(ping,"WriteBlob Failed");
10102
10103 ping_have_blob=MagickTrue;
10104 }
10105
cristy3ed852e2009-09-05 21:47:34 +000010106 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010107
glennrp39992b42010-11-14 00:03:43 +000010108 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010109 {
glennrp3b51f0e2010-11-27 18:14:08 +000010110 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010111 {
10112 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10113 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10114 }
10115
10116 if (ping_color_type == 3)
10117 (void) png_set_tRNS(ping, ping_info,
10118 ping_trans_alpha,
10119 ping_num_trans,
10120 NULL);
10121
10122 else
10123 {
10124 (void) png_set_tRNS(ping, ping_info,
10125 NULL,
10126 0,
10127 &ping_trans_color);
10128
glennrp3b51f0e2010-11-27 18:14:08 +000010129 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010130 {
10131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010132 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010133 (int) ping_trans_color.red,
10134 (int) ping_trans_color.green,
10135 (int) ping_trans_color.blue);
10136 }
10137 }
glennrp991d11d2010-11-12 21:55:28 +000010138 }
10139
cristy3ed852e2009-09-05 21:47:34 +000010140 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010141 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010142
cristy3ed852e2009-09-05 21:47:34 +000010143 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010144
cristy3ed852e2009-09-05 21:47:34 +000010145 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010146 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010147
glennrp26f37912010-12-23 16:22:42 +000010148 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010149 {
glennrp4f25bd02011-01-01 18:51:28 +000010150 if ((image->page.width != 0 && image->page.width != image->columns) ||
10151 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010152 {
10153 unsigned char
10154 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010155
glennrp26f37912010-12-23 16:22:42 +000010156 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10157 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010158 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010159 PNGLong(chunk+4,(png_uint_32) image->page.width);
10160 PNGLong(chunk+8,(png_uint_32) image->page.height);
10161 chunk[12]=0; /* unit = pixels */
10162 (void) WriteBlob(image,13,chunk);
10163 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10164 }
cristy3ed852e2009-09-05 21:47:34 +000010165 }
10166
10167#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010168 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010169#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010170 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010171#undef PNG_HAVE_IDAT
10172#endif
10173
10174 png_set_packing(ping);
10175 /*
10176 Allocate memory.
10177 */
10178 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010179 if (image_depth > 8)
10180 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010181 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010182 {
glennrpb4a13412010-05-05 12:47:19 +000010183 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010184 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010185 break;
glennrp0fe50b42010-11-16 03:52:51 +000010186
glennrpb4a13412010-05-05 12:47:19 +000010187 case PNG_COLOR_TYPE_GRAY_ALPHA:
10188 rowbytes*=2;
10189 break;
glennrp0fe50b42010-11-16 03:52:51 +000010190
glennrpb4a13412010-05-05 12:47:19 +000010191 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010192 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010193 break;
glennrp0fe50b42010-11-16 03:52:51 +000010194
glennrpb4a13412010-05-05 12:47:19 +000010195 default:
10196 break;
cristy3ed852e2009-09-05 21:47:34 +000010197 }
glennrp3b51f0e2010-11-27 18:14:08 +000010198
10199 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010200 {
10201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10202 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010203
glennrpb4a13412010-05-05 12:47:19 +000010204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010205 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010206 }
glennrpcf002022011-01-30 02:38:15 +000010207 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10208 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010209
glennrpcf002022011-01-30 02:38:15 +000010210 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010211 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010212
cristy3ed852e2009-09-05 21:47:34 +000010213 /*
10214 Initialize image scanlines.
10215 */
glennrp5af765f2010-03-30 11:12:18 +000010216 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010217 {
10218 /*
10219 PNG write failed.
10220 */
10221#ifdef PNG_DEBUG
10222 if (image_info->verbose)
10223 (void) printf("PNG write has failed.\n");
10224#endif
10225 png_destroy_write_struct(&ping,&ping_info);
10226 if (quantum_info != (QuantumInfo *) NULL)
10227 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010228 if (ping_pixels != (unsigned char *) NULL)
10229 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010230#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010231 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010232#endif
glennrpda8f3a72011-02-27 23:54:12 +000010233 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010234 (void) CloseBlob(image);
10235 image_info=DestroyImageInfo(image_info);
10236 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010237 return(MagickFalse);
10238 }
cristyed552522009-10-16 14:04:35 +000010239 quantum_info=AcquireQuantumInfo(image_info,image);
10240 if (quantum_info == (QuantumInfo *) NULL)
10241 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010242 quantum_info->format=UndefinedQuantumFormat;
10243 quantum_info->depth=image_depth;
10244 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010245
cristy3ed852e2009-09-05 21:47:34 +000010246 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010247 !mng_info->write_png32) &&
10248 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010249 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010250 image_matte == MagickFalse &&
10251 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010252 {
glennrp8bb3a022010-12-13 20:40:04 +000010253 /* Palette, Bilevel, or Opaque Monochrome */
cristy4c08aed2011-07-01 19:47:50 +000010254 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010255 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010256
cristy3ed852e2009-09-05 21:47:34 +000010257 quantum_info->depth=8;
10258 for (pass=0; pass < num_passes; pass++)
10259 {
10260 /*
10261 Convert PseudoClass image to a PNG monochrome image.
10262 */
cristybb503372010-05-27 20:51:26 +000010263 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010264 {
glennrpd71e86a2011-02-24 01:28:37 +000010265 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10267 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010268
cristy3ed852e2009-09-05 21:47:34 +000010269 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010270
cristy4c08aed2011-07-01 19:47:50 +000010271 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010272 break;
glennrp0fe50b42010-11-16 03:52:51 +000010273
cristy3ed852e2009-09-05 21:47:34 +000010274 if (mng_info->IsPalette)
10275 {
cristy4c08aed2011-07-01 19:47:50 +000010276 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010277 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010278 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10279 mng_info->write_png_depth &&
10280 mng_info->write_png_depth != old_bit_depth)
10281 {
10282 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010283 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010284 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010285 >> (8-old_bit_depth));
10286 }
10287 }
glennrp0fe50b42010-11-16 03:52:51 +000010288
cristy3ed852e2009-09-05 21:47:34 +000010289 else
10290 {
cristy4c08aed2011-07-01 19:47:50 +000010291 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010292 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010293 }
glennrp0fe50b42010-11-16 03:52:51 +000010294
cristy3ed852e2009-09-05 21:47:34 +000010295 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010296 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010297 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010298 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010299
glennrp3b51f0e2010-11-27 18:14:08 +000010300 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10302 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010303
glennrpcf002022011-01-30 02:38:15 +000010304 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010305 }
10306 if (image->previous == (Image *) NULL)
10307 {
10308 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10309 if (status == MagickFalse)
10310 break;
10311 }
10312 }
10313 }
glennrp0fe50b42010-11-16 03:52:51 +000010314
glennrp8bb3a022010-12-13 20:40:04 +000010315 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010316 {
glennrp0fe50b42010-11-16 03:52:51 +000010317 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010318 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010319 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010320 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010321 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010322 {
cristy4c08aed2011-07-01 19:47:50 +000010323 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010324 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010325
glennrp8bb3a022010-12-13 20:40:04 +000010326 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010327 {
glennrp8bb3a022010-12-13 20:40:04 +000010328
cristybb503372010-05-27 20:51:26 +000010329 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010330 {
10331 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010332
cristy4c08aed2011-07-01 19:47:50 +000010333 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010334 break;
glennrp2cc891a2010-12-24 13:44:32 +000010335
glennrp5af765f2010-03-30 11:12:18 +000010336 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010337 {
glennrp8bb3a022010-12-13 20:40:04 +000010338 if (mng_info->IsPalette)
cristy4c08aed2011-07-01 19:47:50 +000010339 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010340 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010341
glennrp8bb3a022010-12-13 20:40:04 +000010342 else
cristy4c08aed2011-07-01 19:47:50 +000010343 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010344 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010345
glennrp3b51f0e2010-11-27 18:14:08 +000010346 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010348 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010349 }
glennrp2cc891a2010-12-24 13:44:32 +000010350
glennrp8bb3a022010-12-13 20:40:04 +000010351 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10352 {
10353 if (logging != MagickFalse && y == 0)
10354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10355 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010356
cristy4c08aed2011-07-01 19:47:50 +000010357 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010358 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010359 }
glennrp2cc891a2010-12-24 13:44:32 +000010360
glennrp3b51f0e2010-11-27 18:14:08 +000010361 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010363 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010364
glennrpcf002022011-01-30 02:38:15 +000010365 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010366 }
glennrp2cc891a2010-12-24 13:44:32 +000010367
glennrp8bb3a022010-12-13 20:40:04 +000010368 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010369 {
glennrp8bb3a022010-12-13 20:40:04 +000010370 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10371 if (status == MagickFalse)
10372 break;
cristy3ed852e2009-09-05 21:47:34 +000010373 }
cristy3ed852e2009-09-05 21:47:34 +000010374 }
10375 }
glennrp8bb3a022010-12-13 20:40:04 +000010376
10377 else
10378 {
cristy4c08aed2011-07-01 19:47:50 +000010379 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010380 *p;
10381
10382 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010383 {
glennrp8bb3a022010-12-13 20:40:04 +000010384 if ((image_depth > 8) || (mng_info->write_png24 ||
10385 mng_info->write_png32 ||
10386 (!mng_info->write_png8 && !mng_info->IsPalette)))
10387 {
10388 for (y=0; y < (ssize_t) image->rows; y++)
10389 {
10390 p=GetVirtualPixels(image,0,y,image->columns,1,
10391 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010392
cristy4c08aed2011-07-01 19:47:50 +000010393 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010394 break;
glennrp2cc891a2010-12-24 13:44:32 +000010395
glennrp8bb3a022010-12-13 20:40:04 +000010396 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10397 {
10398 if (image->storage_class == DirectClass)
cristy4c08aed2011-07-01 19:47:50 +000010399 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010400 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010401
glennrp8bb3a022010-12-13 20:40:04 +000010402 else
cristy4c08aed2011-07-01 19:47:50 +000010403 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010404 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010405 }
glennrp2cc891a2010-12-24 13:44:32 +000010406
glennrp8bb3a022010-12-13 20:40:04 +000010407 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10408 {
cristy4c08aed2011-07-01 19:47:50 +000010409 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010410 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +000010411 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010412
glennrp8bb3a022010-12-13 20:40:04 +000010413 if (logging != MagickFalse && y == 0)
10414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10415 " Writing GRAY_ALPHA PNG pixels (3)");
10416 }
glennrp2cc891a2010-12-24 13:44:32 +000010417
glennrp8bb3a022010-12-13 20:40:04 +000010418 else if (image_matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +000010419 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010420 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010421
glennrp8bb3a022010-12-13 20:40:04 +000010422 else
cristy4c08aed2011-07-01 19:47:50 +000010423 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010424 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010425
glennrp8bb3a022010-12-13 20:40:04 +000010426 if (logging != MagickFalse && y == 0)
10427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10428 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010429
glennrpcf002022011-01-30 02:38:15 +000010430 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010431 }
10432 }
glennrp2cc891a2010-12-24 13:44:32 +000010433
glennrp8bb3a022010-12-13 20:40:04 +000010434 else
10435 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10436 mng_info->write_png32 ||
10437 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10438 {
10439 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10440 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10441 {
10442 if (logging != MagickFalse)
10443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10444 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010445
glennrp8bb3a022010-12-13 20:40:04 +000010446 quantum_info->depth=8;
10447 image_depth=8;
10448 }
glennrp2cc891a2010-12-24 13:44:32 +000010449
glennrp8bb3a022010-12-13 20:40:04 +000010450 for (y=0; y < (ssize_t) image->rows; y++)
10451 {
10452 if (logging != MagickFalse && y == 0)
10453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10454 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010455
glennrp770d1932011-03-06 22:11:17 +000010456 p=GetVirtualPixels(image,0,y,image->columns,1,
10457 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010458
cristy4c08aed2011-07-01 19:47:50 +000010459 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010460 break;
glennrp2cc891a2010-12-24 13:44:32 +000010461
glennrp8bb3a022010-12-13 20:40:04 +000010462 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010463 {
glennrp4bf89732011-03-21 13:48:28 +000010464 quantum_info->depth=image->depth;
10465
cristy4c08aed2011-07-01 19:47:50 +000010466 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010467 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +000010468 }
glennrp2cc891a2010-12-24 13:44:32 +000010469
glennrp8bb3a022010-12-13 20:40:04 +000010470 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10471 {
10472 if (logging != MagickFalse && y == 0)
10473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10474 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010475
cristy4c08aed2011-07-01 19:47:50 +000010476 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010477 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +000010478 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010479 }
glennrp2cc891a2010-12-24 13:44:32 +000010480
glennrp8bb3a022010-12-13 20:40:04 +000010481 else
glennrp8bb3a022010-12-13 20:40:04 +000010482 {
cristy4c08aed2011-07-01 19:47:50 +000010483 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +000010484 quantum_info,IndexQuantum,ping_pixels,&image->exception);
10485
10486 if (logging != MagickFalse && y <= 2)
10487 {
10488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010489 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010490
10491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10492 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10493 (int)ping_pixels[0],(int)ping_pixels[1]);
10494 }
glennrp8bb3a022010-12-13 20:40:04 +000010495 }
glennrpcf002022011-01-30 02:38:15 +000010496 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010497 }
10498 }
glennrp2cc891a2010-12-24 13:44:32 +000010499
glennrp8bb3a022010-12-13 20:40:04 +000010500 if (image->previous == (Image *) NULL)
10501 {
10502 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10503 if (status == MagickFalse)
10504 break;
10505 }
cristy3ed852e2009-09-05 21:47:34 +000010506 }
glennrp8bb3a022010-12-13 20:40:04 +000010507 }
10508 }
10509
cristyb32b90a2009-09-07 21:45:48 +000010510 if (quantum_info != (QuantumInfo *) NULL)
10511 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010512
10513 if (logging != MagickFalse)
10514 {
10515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010516 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010517
cristy3ed852e2009-09-05 21:47:34 +000010518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010519 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010520
cristy3ed852e2009-09-05 21:47:34 +000010521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010522 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010523
cristy3ed852e2009-09-05 21:47:34 +000010524 if (mng_info->write_png_depth)
10525 {
10526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10527 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
10528 }
glennrp0fe50b42010-11-16 03:52:51 +000010529
cristy3ed852e2009-09-05 21:47:34 +000010530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010531 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010532
cristy3ed852e2009-09-05 21:47:34 +000010533 if (mng_info->write_png_colortype)
10534 {
10535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10536 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
10537 }
glennrp0fe50b42010-11-16 03:52:51 +000010538
cristy3ed852e2009-09-05 21:47:34 +000010539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010540 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010541
cristy3ed852e2009-09-05 21:47:34 +000010542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010543 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010544 }
10545 /*
glennrpa0ed0092011-04-18 16:36:29 +000010546 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010547 */
glennrp823b55c2011-03-14 18:46:46 +000010548 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010549 {
glennrp26f37912010-12-23 16:22:42 +000010550 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010551 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010552 while (property != (const char *) NULL)
10553 {
10554 png_textp
10555 text;
glennrp2cc891a2010-12-24 13:44:32 +000010556
glennrp26f37912010-12-23 16:22:42 +000010557 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +000010558
10559 /* Don't write any "png:" properties; those are just for "identify" */
10560 if (LocaleNCompare(property,"png:",4) != 0 &&
10561
10562 /* Suppress density and units if we wrote a pHYs chunk */
10563 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010564 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010565 LocaleCompare(property,"units") != 0) &&
10566
10567 /* Suppress the IM-generated Date:create and Date:modify */
10568 (ping_exclude_date == MagickFalse ||
10569 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010570 {
glennrpc70af4a2011-03-07 00:08:23 +000010571 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010572 {
glennrpc70af4a2011-03-07 00:08:23 +000010573 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10574 text[0].key=(char *) property;
10575 text[0].text=(char *) value;
10576 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010577
glennrpc70af4a2011-03-07 00:08:23 +000010578 if (ping_exclude_tEXt != MagickFalse)
10579 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10580
10581 else if (ping_exclude_zTXt != MagickFalse)
10582 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10583
10584 else
glennrp26f37912010-12-23 16:22:42 +000010585 {
glennrpc70af4a2011-03-07 00:08:23 +000010586 text[0].compression=image_info->compression == NoCompression ||
10587 (image_info->compression == UndefinedCompression &&
10588 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10589 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010590 }
glennrp2cc891a2010-12-24 13:44:32 +000010591
glennrpc70af4a2011-03-07 00:08:23 +000010592 if (logging != MagickFalse)
10593 {
10594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10595 " Setting up text chunk");
10596
10597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10598 " keyword: %s",text[0].key);
10599 }
10600
10601 png_set_text(ping,ping_info,text,1);
10602 png_free(ping,text);
10603 }
glennrp26f37912010-12-23 16:22:42 +000010604 }
10605 property=GetNextImageProperty(image);
10606 }
cristy3ed852e2009-09-05 21:47:34 +000010607 }
10608
10609 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010610 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010611
10612 if (logging != MagickFalse)
10613 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10614 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010615
cristy3ed852e2009-09-05 21:47:34 +000010616 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010617
cristy3ed852e2009-09-05 21:47:34 +000010618 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10619 {
10620 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010621 (ping_width != mng_info->page.width) ||
10622 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010623 {
10624 unsigned char
10625 chunk[32];
10626
10627 /*
10628 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10629 */
10630 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10631 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010632 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010633 chunk[4]=4;
10634 chunk[5]=0; /* frame name separator (no name) */
10635 chunk[6]=1; /* flag for changing delay, for next frame only */
10636 chunk[7]=0; /* flag for changing frame timeout */
10637 chunk[8]=1; /* flag for changing frame clipping for next frame */
10638 chunk[9]=0; /* flag for changing frame sync_id */
10639 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10640 chunk[14]=0; /* clipping boundaries delta type */
10641 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10642 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010643 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010644 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10645 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010646 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010647 (void) WriteBlob(image,31,chunk);
10648 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10649 mng_info->old_framing_mode=4;
10650 mng_info->framing_mode=1;
10651 }
glennrp0fe50b42010-11-16 03:52:51 +000010652
cristy3ed852e2009-09-05 21:47:34 +000010653 else
10654 mng_info->framing_mode=3;
10655 }
10656 if (mng_info->write_mng && !mng_info->need_fram &&
10657 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +000010658 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010659 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010660 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010661
cristy3ed852e2009-09-05 21:47:34 +000010662 /*
10663 Free PNG resources.
10664 */
glennrp5af765f2010-03-30 11:12:18 +000010665
cristy3ed852e2009-09-05 21:47:34 +000010666 png_destroy_write_struct(&ping,&ping_info);
10667
glennrpcf002022011-01-30 02:38:15 +000010668 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010669
10670#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010671 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010672#endif
10673
glennrpda8f3a72011-02-27 23:54:12 +000010674 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010675 (void) CloseBlob(image);
10676
10677 image_info=DestroyImageInfo(image_info);
10678 image=DestroyImage(image);
10679
10680 /* Store bit depth actually written */
10681 s[0]=(char) ping_bit_depth;
10682 s[1]='\0';
10683
10684 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
10685
cristy3ed852e2009-09-05 21:47:34 +000010686 if (logging != MagickFalse)
10687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10688 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010689
cristy3ed852e2009-09-05 21:47:34 +000010690 return(MagickTrue);
10691/* End write one PNG image */
10692}
10693
10694/*
10695%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10696% %
10697% %
10698% %
10699% W r i t e P N G I m a g e %
10700% %
10701% %
10702% %
10703%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10704%
10705% WritePNGImage() writes a Portable Network Graphics (PNG) or
10706% Multiple-image Network Graphics (MNG) image file.
10707%
10708% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10709%
10710% The format of the WritePNGImage method is:
10711%
cristy1e178e72011-08-28 19:44:34 +000010712% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10713% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010714%
10715% A description of each parameter follows:
10716%
10717% o image_info: the image info.
10718%
10719% o image: The image.
10720%
cristy1e178e72011-08-28 19:44:34 +000010721% o exception: return any errors or warnings in this structure.
10722%
cristy3ed852e2009-09-05 21:47:34 +000010723% Returns MagickTrue on success, MagickFalse on failure.
10724%
10725% Communicating with the PNG encoder:
10726%
10727% While the datastream written is always in PNG format and normally would
10728% be given the "png" file extension, this method also writes the following
10729% pseudo-formats which are subsets of PNG:
10730%
glennrp5a39f372011-02-25 04:52:16 +000010731% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10732% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010733% is present, the tRNS chunk must only have values 0 and 255
10734% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010735% transparent). If other values are present they will be
10736% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010737% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000010738% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10739% of any resulting fully-transparent pixels is changed to
10740% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000010741%
10742% If you want better quantization or dithering of the colors
10743% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010744% PNG encoder. The pixels contain 8-bit indices even if
10745% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010746% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010747% PNG grayscale type might be slightly more efficient. Please
10748% note that writing to the PNG8 format may result in loss
10749% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010750%
10751% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10752% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010753% one of the colors as transparent. The only loss incurred
10754% is reduction of sample depth to 8. If the image has more
10755% than one transparent color, has semitransparent pixels, or
10756% has an opaque pixel with the same RGB components as the
10757% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010758%
10759% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10760% transparency is permitted, i.e., the alpha sample for
10761% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010762% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010763% The only loss in data is the reduction of the sample depth
10764% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010765%
10766% o -define: For more precise control of the PNG output, you can use the
10767% Image options "png:bit-depth" and "png:color-type". These
10768% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010769% from the application programming interfaces. The options
10770% are case-independent and are converted to lowercase before
10771% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010772%
10773% png:color-type can be 0, 2, 3, 4, or 6.
10774%
10775% When png:color-type is 0 (Grayscale), png:bit-depth can
10776% be 1, 2, 4, 8, or 16.
10777%
10778% When png:color-type is 2 (RGB), png:bit-depth can
10779% be 8 or 16.
10780%
10781% When png:color-type is 3 (Indexed), png:bit-depth can
10782% be 1, 2, 4, or 8. This refers to the number of bits
10783% used to store the index. The color samples always have
10784% bit-depth 8 in indexed PNG files.
10785%
10786% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10787% png:bit-depth can be 8 or 16.
10788%
glennrp5a39f372011-02-25 04:52:16 +000010789% If the image cannot be written without loss with the requested bit-depth
10790% and color-type, a PNG file will not be written, and the encoder will
10791% return MagickFalse.
10792%
cristy3ed852e2009-09-05 21:47:34 +000010793% Since image encoders should not be responsible for the "heavy lifting",
10794% the user should make sure that ImageMagick has already reduced the
10795% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010796% transparency prior to attempting to write the image with depth, color,
glennrp54dc0692011-07-01 19:02:13 +000010797% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010798%
cristy3ed852e2009-09-05 21:47:34 +000010799% Note that another definition, "png:bit-depth-written" exists, but it
10800% is not intended for external use. It is only used internally by the
10801% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10802%
10803% It is possible to request that the PNG encoder write previously-formatted
10804% ancillary chunks in the output PNG file, using the "-profile" commandline
10805% option as shown below or by setting the profile via a programming
10806% interface:
10807%
10808% -profile PNG-chunk-x:<file>
10809%
10810% where x is a location flag and <file> is a file containing the chunk
10811% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010812% This encoder will compute the chunk length and CRC, so those must not
10813% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010814%
10815% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10816% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10817% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010818% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010819%
glennrpbb8a7332010-11-13 15:17:35 +000010820% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010821%
glennrp3241bd02010-12-12 04:36:28 +000010822% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010823%
glennrpd6afd542010-11-19 01:53:05 +000010824% o 32-bit depth is reduced to 16.
10825% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10826% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010827% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010828% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010829% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010830% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10831% this can be done without loss and a larger bit depth N was not
10832% requested via the "-define PNG:bit-depth=N" option.
10833% o If matte channel is present but only one transparent color is
10834% present, RGB+tRNS is written instead of RGBA
10835% o Opaque matte channel is removed (or added, if color-type 4 or 6
10836% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010837%
cristy3ed852e2009-09-05 21:47:34 +000010838%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10839*/
10840static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +000010841 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010842{
10843 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010844 excluding,
10845 logging,
10846 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010847 status;
10848
10849 MngInfo
10850 *mng_info;
10851
10852 const char
10853 *value;
10854
10855 int
glennrp21f0e622011-01-07 16:20:57 +000010856 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010857 source;
10858
cristy3ed852e2009-09-05 21:47:34 +000010859 /*
10860 Open image file.
10861 */
10862 assert(image_info != (const ImageInfo *) NULL);
10863 assert(image_info->signature == MagickSignature);
10864 assert(image != (Image *) NULL);
10865 assert(image->signature == MagickSignature);
10866 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010867 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010868 /*
10869 Allocate a MngInfo structure.
10870 */
10871 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010872 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010873
cristy3ed852e2009-09-05 21:47:34 +000010874 if (mng_info == (MngInfo *) NULL)
10875 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010876
cristy3ed852e2009-09-05 21:47:34 +000010877 /*
10878 Initialize members of the MngInfo structure.
10879 */
10880 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10881 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010882 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010883 have_mng_structure=MagickTrue;
10884
10885 /* See if user has requested a specific PNG subformat */
10886
10887 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10888 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10889 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10890
10891 if (mng_info->write_png8)
10892 {
glennrp9c1eb072010-06-06 22:19:15 +000010893 mng_info->write_png_colortype = /* 3 */ 4;
10894 mng_info->write_png_depth = 8;
10895 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010896 }
10897
10898 if (mng_info->write_png24)
10899 {
glennrp9c1eb072010-06-06 22:19:15 +000010900 mng_info->write_png_colortype = /* 2 */ 3;
10901 mng_info->write_png_depth = 8;
10902 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010903
glennrp9c1eb072010-06-06 22:19:15 +000010904 if (image->matte == MagickTrue)
10905 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010906
glennrp9c1eb072010-06-06 22:19:15 +000010907 else
10908 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010909
glennrp9c1eb072010-06-06 22:19:15 +000010910 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010911 }
10912
10913 if (mng_info->write_png32)
10914 {
glennrp9c1eb072010-06-06 22:19:15 +000010915 mng_info->write_png_colortype = /* 6 */ 7;
10916 mng_info->write_png_depth = 8;
10917 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010918
glennrp9c1eb072010-06-06 22:19:15 +000010919 if (image->matte == MagickTrue)
10920 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010921
glennrp9c1eb072010-06-06 22:19:15 +000010922 else
10923 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010924
glennrp9c1eb072010-06-06 22:19:15 +000010925 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010926 }
10927
10928 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000010929
cristy3ed852e2009-09-05 21:47:34 +000010930 if (value != (char *) NULL)
10931 {
10932 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010933 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010934
cristy3ed852e2009-09-05 21:47:34 +000010935 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010936 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000010937
cristy3ed852e2009-09-05 21:47:34 +000010938 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010939 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010940
cristy3ed852e2009-09-05 21:47:34 +000010941 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010942 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010943
cristy3ed852e2009-09-05 21:47:34 +000010944 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010945 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000010946
glennrpbb8a7332010-11-13 15:17:35 +000010947 else
10948 (void) ThrowMagickException(&image->exception,
10949 GetMagickModule(),CoderWarning,
10950 "ignoring invalid defined png:bit-depth",
10951 "=%s",value);
10952
cristy3ed852e2009-09-05 21:47:34 +000010953 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010954 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000010955 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010956 }
glennrp0fe50b42010-11-16 03:52:51 +000010957
cristy3ed852e2009-09-05 21:47:34 +000010958 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000010959
cristy3ed852e2009-09-05 21:47:34 +000010960 if (value != (char *) NULL)
10961 {
10962 /* We must store colortype+1 because 0 is a valid colortype */
10963 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010964 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010965
cristy3ed852e2009-09-05 21:47:34 +000010966 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010967 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000010968
cristy3ed852e2009-09-05 21:47:34 +000010969 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010970 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010971
cristy3ed852e2009-09-05 21:47:34 +000010972 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010973 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000010974
cristy3ed852e2009-09-05 21:47:34 +000010975 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010976 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000010977
glennrpbb8a7332010-11-13 15:17:35 +000010978 else
10979 (void) ThrowMagickException(&image->exception,
10980 GetMagickModule(),CoderWarning,
10981 "ignoring invalid defined png:color-type",
10982 "=%s",value);
10983
cristy3ed852e2009-09-05 21:47:34 +000010984 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010986 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010987 }
10988
glennrp0e8ea192010-12-24 18:00:33 +000010989 /* Check for chunks to be excluded:
10990 *
glennrp0dff56c2011-01-29 19:10:02 +000010991 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000010992 * listed in the "unused_chunks" array, above.
10993 *
10994 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10995 * define (in the image properties or in the image artifacts)
10996 * or via a mng_info member. For convenience, in addition
10997 * to or instead of a comma-separated list of chunks, the
10998 * "exclude-chunk" string can be simply "all" or "none".
10999 *
11000 * The exclude-chunk define takes priority over the mng_info.
11001 *
11002 * A "PNG:include-chunk" define takes priority over both the
11003 * mng_info and the "PNG:exclude-chunk" define. Like the
11004 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011005 * well as a comma-separated list. Chunks that are unknown to
11006 * ImageMagick are always excluded, regardless of their "copy-safe"
11007 * status according to the PNG specification, and even if they
11008 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000011009 *
11010 * Finally, all chunks listed in the "unused_chunks" array are
11011 * automatically excluded, regardless of the other instructions
11012 * or lack thereof.
11013 *
11014 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11015 * will not be written and the gAMA chunk will only be written if it
11016 * is not between .45 and .46, or approximately (1.0/2.2).
11017 *
11018 * If you exclude tRNS and the image has transparency, the colortype
11019 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11020 *
11021 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011022 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011023 */
11024
glennrp26f37912010-12-23 16:22:42 +000011025 mng_info->ping_exclude_bKGD=MagickFalse;
11026 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011027 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011028 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11029 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011030 mng_info->ping_exclude_iCCP=MagickFalse;
11031 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11032 mng_info->ping_exclude_oFFs=MagickFalse;
11033 mng_info->ping_exclude_pHYs=MagickFalse;
11034 mng_info->ping_exclude_sRGB=MagickFalse;
11035 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011036 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011037 mng_info->ping_exclude_vpAg=MagickFalse;
11038 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11039 mng_info->ping_exclude_zTXt=MagickFalse;
11040
glennrp8d3d6e52011-04-19 04:39:51 +000011041 mng_info->ping_preserve_colormap=MagickFalse;
11042
11043 value=GetImageArtifact(image,"png:preserve-colormap");
11044 if (value == NULL)
11045 value=GetImageOption(image_info,"png:preserve-colormap");
11046 if (value != NULL)
11047 mng_info->ping_preserve_colormap=MagickTrue;
11048
glennrp18682582011-06-30 18:11:47 +000011049 /* Thes compression-level, compression-strategy, and compression-filter
11050 * defines take precedence over values from the -quality option.
11051 */
11052 value=GetImageArtifact(image,"png:compression-level");
11053 if (value == NULL)
11054 value=GetImageOption(image_info,"png:compression-level");
11055 if (value != NULL)
11056 {
glennrp18682582011-06-30 18:11:47 +000011057 /* We have to add 1 to everything because 0 is a valid input,
11058 * and we want to use 0 (the default) to mean undefined.
11059 */
11060 if (LocaleCompare(value,"0") == 0)
11061 mng_info->write_png_compression_level = 1;
11062
11063 if (LocaleCompare(value,"1") == 0)
11064 mng_info->write_png_compression_level = 2;
11065
11066 else if (LocaleCompare(value,"2") == 0)
11067 mng_info->write_png_compression_level = 3;
11068
11069 else if (LocaleCompare(value,"3") == 0)
11070 mng_info->write_png_compression_level = 4;
11071
11072 else if (LocaleCompare(value,"4") == 0)
11073 mng_info->write_png_compression_level = 5;
11074
11075 else if (LocaleCompare(value,"5") == 0)
11076 mng_info->write_png_compression_level = 6;
11077
11078 else if (LocaleCompare(value,"6") == 0)
11079 mng_info->write_png_compression_level = 7;
11080
11081 else if (LocaleCompare(value,"7") == 0)
11082 mng_info->write_png_compression_level = 8;
11083
11084 else if (LocaleCompare(value,"8") == 0)
11085 mng_info->write_png_compression_level = 9;
11086
11087 else if (LocaleCompare(value,"9") == 0)
11088 mng_info->write_png_compression_level = 10;
11089
11090 else
11091 (void) ThrowMagickException(&image->exception,
11092 GetMagickModule(),CoderWarning,
11093 "ignoring invalid defined png:compression-level",
11094 "=%s",value);
11095 }
11096
11097 value=GetImageArtifact(image,"png:compression-strategy");
11098 if (value == NULL)
11099 value=GetImageOption(image_info,"png:compression-strategy");
11100 if (value != NULL)
11101 {
11102
11103 if (LocaleCompare(value,"0") == 0)
11104 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11105
11106 else if (LocaleCompare(value,"1") == 0)
11107 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11108
11109 else if (LocaleCompare(value,"2") == 0)
11110 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11111
11112 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011113#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011114 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011115#else
11116 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11117#endif
glennrp18682582011-06-30 18:11:47 +000011118
11119 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011120#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011121 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011122#else
11123 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11124#endif
glennrp18682582011-06-30 18:11:47 +000011125
11126 else
11127 (void) ThrowMagickException(&image->exception,
11128 GetMagickModule(),CoderWarning,
11129 "ignoring invalid defined png:compression-strategy",
11130 "=%s",value);
11131 }
11132
11133 value=GetImageArtifact(image,"png:compression-filter");
11134 if (value == NULL)
11135 value=GetImageOption(image_info,"png:compression-filter");
11136 if (value != NULL)
11137 {
11138
11139 /* To do: combinations of filters allowed by libpng
11140 * masks 0x08 through 0xf8
11141 *
11142 * Implement this as a comma-separated list of 0,1,2,3,4,5
11143 * where 5 is a special case meaning PNG_ALL_FILTERS.
11144 */
11145
11146 if (LocaleCompare(value,"0") == 0)
11147 mng_info->write_png_compression_filter = 1;
11148
11149 if (LocaleCompare(value,"1") == 0)
11150 mng_info->write_png_compression_filter = 2;
11151
11152 else if (LocaleCompare(value,"2") == 0)
11153 mng_info->write_png_compression_filter = 3;
11154
11155 else if (LocaleCompare(value,"3") == 0)
11156 mng_info->write_png_compression_filter = 4;
11157
11158 else if (LocaleCompare(value,"4") == 0)
11159 mng_info->write_png_compression_filter = 5;
11160
11161 else if (LocaleCompare(value,"5") == 0)
11162 mng_info->write_png_compression_filter = 6;
11163
glennrp18682582011-06-30 18:11:47 +000011164 else
11165 (void) ThrowMagickException(&image->exception,
11166 GetMagickModule(),CoderWarning,
11167 "ignoring invalid defined png:compression-filter",
11168 "=%s",value);
11169 }
11170
glennrp03812ae2010-12-24 01:31:34 +000011171 excluding=MagickFalse;
11172
glennrp5c7cf4e2010-12-24 00:30:00 +000011173 for (source=0; source<1; source++)
11174 {
11175 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011176 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011177 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011178
11179 if (value == NULL)
11180 value=GetImageArtifact(image,"png:exclude-chunks");
11181 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011182 else
glennrpacba0042010-12-24 14:27:26 +000011183 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011184 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011185
glennrpacba0042010-12-24 14:27:26 +000011186 if (value == NULL)
11187 value=GetImageOption(image_info,"png:exclude-chunks");
11188 }
11189
glennrp03812ae2010-12-24 01:31:34 +000011190 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011191 {
glennrp03812ae2010-12-24 01:31:34 +000011192
11193 size_t
11194 last;
11195
11196 excluding=MagickTrue;
11197
11198 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011199 {
11200 if (source == 0)
11201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11202 " png:exclude-chunk=%s found in image artifacts.\n", value);
11203 else
11204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11205 " png:exclude-chunk=%s found in image properties.\n", value);
11206 }
glennrp03812ae2010-12-24 01:31:34 +000011207
11208 last=strlen(value);
11209
11210 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011211 {
glennrp03812ae2010-12-24 01:31:34 +000011212
11213 if (LocaleNCompare(value+i,"all",3) == 0)
11214 {
11215 mng_info->ping_exclude_bKGD=MagickTrue;
11216 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011217 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011218 mng_info->ping_exclude_EXIF=MagickTrue;
11219 mng_info->ping_exclude_gAMA=MagickTrue;
11220 mng_info->ping_exclude_iCCP=MagickTrue;
11221 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11222 mng_info->ping_exclude_oFFs=MagickTrue;
11223 mng_info->ping_exclude_pHYs=MagickTrue;
11224 mng_info->ping_exclude_sRGB=MagickTrue;
11225 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011226 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011227 mng_info->ping_exclude_vpAg=MagickTrue;
11228 mng_info->ping_exclude_zCCP=MagickTrue;
11229 mng_info->ping_exclude_zTXt=MagickTrue;
11230 i--;
11231 }
glennrp2cc891a2010-12-24 13:44:32 +000011232
glennrp03812ae2010-12-24 01:31:34 +000011233 if (LocaleNCompare(value+i,"none",4) == 0)
11234 {
11235 mng_info->ping_exclude_bKGD=MagickFalse;
11236 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011237 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011238 mng_info->ping_exclude_EXIF=MagickFalse;
11239 mng_info->ping_exclude_gAMA=MagickFalse;
11240 mng_info->ping_exclude_iCCP=MagickFalse;
11241 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11242 mng_info->ping_exclude_oFFs=MagickFalse;
11243 mng_info->ping_exclude_pHYs=MagickFalse;
11244 mng_info->ping_exclude_sRGB=MagickFalse;
11245 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011246 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011247 mng_info->ping_exclude_vpAg=MagickFalse;
11248 mng_info->ping_exclude_zCCP=MagickFalse;
11249 mng_info->ping_exclude_zTXt=MagickFalse;
11250 }
glennrp2cc891a2010-12-24 13:44:32 +000011251
glennrp03812ae2010-12-24 01:31:34 +000011252 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11253 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011254
glennrp03812ae2010-12-24 01:31:34 +000011255 if (LocaleNCompare(value+i,"chrm",4) == 0)
11256 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011257
glennrpa0ed0092011-04-18 16:36:29 +000011258 if (LocaleNCompare(value+i,"date",4) == 0)
11259 mng_info->ping_exclude_date=MagickTrue;
11260
glennrp03812ae2010-12-24 01:31:34 +000011261 if (LocaleNCompare(value+i,"exif",4) == 0)
11262 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011263
glennrp03812ae2010-12-24 01:31:34 +000011264 if (LocaleNCompare(value+i,"gama",4) == 0)
11265 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011266
glennrp03812ae2010-12-24 01:31:34 +000011267 if (LocaleNCompare(value+i,"iccp",4) == 0)
11268 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011269
glennrp03812ae2010-12-24 01:31:34 +000011270 /*
11271 if (LocaleNCompare(value+i,"itxt",4) == 0)
11272 mng_info->ping_exclude_iTXt=MagickTrue;
11273 */
glennrp2cc891a2010-12-24 13:44:32 +000011274
glennrp03812ae2010-12-24 01:31:34 +000011275 if (LocaleNCompare(value+i,"gama",4) == 0)
11276 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011277
glennrp03812ae2010-12-24 01:31:34 +000011278 if (LocaleNCompare(value+i,"offs",4) == 0)
11279 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011280
glennrp03812ae2010-12-24 01:31:34 +000011281 if (LocaleNCompare(value+i,"phys",4) == 0)
11282 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011283
glennrpa1e3b7b2010-12-24 16:37:33 +000011284 if (LocaleNCompare(value+i,"srgb",4) == 0)
11285 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011286
glennrp03812ae2010-12-24 01:31:34 +000011287 if (LocaleNCompare(value+i,"text",4) == 0)
11288 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011289
glennrpa1e3b7b2010-12-24 16:37:33 +000011290 if (LocaleNCompare(value+i,"trns",4) == 0)
11291 mng_info->ping_exclude_tRNS=MagickTrue;
11292
glennrp03812ae2010-12-24 01:31:34 +000011293 if (LocaleNCompare(value+i,"vpag",4) == 0)
11294 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011295
glennrp03812ae2010-12-24 01:31:34 +000011296 if (LocaleNCompare(value+i,"zccp",4) == 0)
11297 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011298
glennrp03812ae2010-12-24 01:31:34 +000011299 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11300 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011301
glennrp03812ae2010-12-24 01:31:34 +000011302 }
glennrpce91ed52010-12-23 22:37:49 +000011303 }
glennrp26f37912010-12-23 16:22:42 +000011304 }
11305
glennrp5c7cf4e2010-12-24 00:30:00 +000011306 for (source=0; source<1; source++)
11307 {
11308 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011309 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011310 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011311
11312 if (value == NULL)
11313 value=GetImageArtifact(image,"png:include-chunks");
11314 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011315 else
glennrpacba0042010-12-24 14:27:26 +000011316 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011317 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011318
glennrpacba0042010-12-24 14:27:26 +000011319 if (value == NULL)
11320 value=GetImageOption(image_info,"png:include-chunks");
11321 }
11322
glennrp03812ae2010-12-24 01:31:34 +000011323 if (value != NULL)
11324 {
11325 size_t
11326 last;
glennrp26f37912010-12-23 16:22:42 +000011327
glennrp03812ae2010-12-24 01:31:34 +000011328 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011329
glennrp03812ae2010-12-24 01:31:34 +000011330 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011331 {
11332 if (source == 0)
11333 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11334 " png:include-chunk=%s found in image artifacts.\n", value);
11335 else
11336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11337 " png:include-chunk=%s found in image properties.\n", value);
11338 }
glennrp03812ae2010-12-24 01:31:34 +000011339
11340 last=strlen(value);
11341
11342 for (i=0; i<(int) last; i+=5)
11343 {
11344 if (LocaleNCompare(value+i,"all",3) == 0)
11345 {
11346 mng_info->ping_exclude_bKGD=MagickFalse;
11347 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011348 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011349 mng_info->ping_exclude_EXIF=MagickFalse;
11350 mng_info->ping_exclude_gAMA=MagickFalse;
11351 mng_info->ping_exclude_iCCP=MagickFalse;
11352 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11353 mng_info->ping_exclude_oFFs=MagickFalse;
11354 mng_info->ping_exclude_pHYs=MagickFalse;
11355 mng_info->ping_exclude_sRGB=MagickFalse;
11356 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011357 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011358 mng_info->ping_exclude_vpAg=MagickFalse;
11359 mng_info->ping_exclude_zCCP=MagickFalse;
11360 mng_info->ping_exclude_zTXt=MagickFalse;
11361 i--;
11362 }
glennrp2cc891a2010-12-24 13:44:32 +000011363
glennrp03812ae2010-12-24 01:31:34 +000011364 if (LocaleNCompare(value+i,"none",4) == 0)
11365 {
11366 mng_info->ping_exclude_bKGD=MagickTrue;
11367 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011368 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011369 mng_info->ping_exclude_EXIF=MagickTrue;
11370 mng_info->ping_exclude_gAMA=MagickTrue;
11371 mng_info->ping_exclude_iCCP=MagickTrue;
11372 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11373 mng_info->ping_exclude_oFFs=MagickTrue;
11374 mng_info->ping_exclude_pHYs=MagickTrue;
11375 mng_info->ping_exclude_sRGB=MagickTrue;
11376 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011377 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011378 mng_info->ping_exclude_vpAg=MagickTrue;
11379 mng_info->ping_exclude_zCCP=MagickTrue;
11380 mng_info->ping_exclude_zTXt=MagickTrue;
11381 }
glennrp2cc891a2010-12-24 13:44:32 +000011382
glennrp03812ae2010-12-24 01:31:34 +000011383 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11384 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011385
glennrp03812ae2010-12-24 01:31:34 +000011386 if (LocaleNCompare(value+i,"chrm",4) == 0)
11387 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011388
glennrpa0ed0092011-04-18 16:36:29 +000011389 if (LocaleNCompare(value+i,"date",4) == 0)
11390 mng_info->ping_exclude_date=MagickFalse;
11391
glennrp03812ae2010-12-24 01:31:34 +000011392 if (LocaleNCompare(value+i,"exif",4) == 0)
11393 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011394
glennrp03812ae2010-12-24 01:31:34 +000011395 if (LocaleNCompare(value+i,"gama",4) == 0)
11396 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011397
glennrp03812ae2010-12-24 01:31:34 +000011398 if (LocaleNCompare(value+i,"iccp",4) == 0)
11399 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011400
glennrp03812ae2010-12-24 01:31:34 +000011401 /*
11402 if (LocaleNCompare(value+i,"itxt",4) == 0)
11403 mng_info->ping_exclude_iTXt=MagickFalse;
11404 */
glennrp2cc891a2010-12-24 13:44:32 +000011405
glennrp03812ae2010-12-24 01:31:34 +000011406 if (LocaleNCompare(value+i,"gama",4) == 0)
11407 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011408
glennrp03812ae2010-12-24 01:31:34 +000011409 if (LocaleNCompare(value+i,"offs",4) == 0)
11410 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011411
glennrp03812ae2010-12-24 01:31:34 +000011412 if (LocaleNCompare(value+i,"phys",4) == 0)
11413 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011414
glennrpa1e3b7b2010-12-24 16:37:33 +000011415 if (LocaleNCompare(value+i,"srgb",4) == 0)
11416 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011417
glennrp03812ae2010-12-24 01:31:34 +000011418 if (LocaleNCompare(value+i,"text",4) == 0)
11419 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011420
glennrpa1e3b7b2010-12-24 16:37:33 +000011421 if (LocaleNCompare(value+i,"trns",4) == 0)
11422 mng_info->ping_exclude_tRNS=MagickFalse;
11423
glennrp03812ae2010-12-24 01:31:34 +000011424 if (LocaleNCompare(value+i,"vpag",4) == 0)
11425 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011426
glennrp03812ae2010-12-24 01:31:34 +000011427 if (LocaleNCompare(value+i,"zccp",4) == 0)
11428 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011429
glennrp03812ae2010-12-24 01:31:34 +000011430 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11431 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011432
glennrp03812ae2010-12-24 01:31:34 +000011433 }
glennrpce91ed52010-12-23 22:37:49 +000011434 }
glennrp26f37912010-12-23 16:22:42 +000011435 }
11436
glennrp03812ae2010-12-24 01:31:34 +000011437 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011438 {
11439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11440 " Chunks to be excluded from the output PNG:");
11441 if (mng_info->ping_exclude_bKGD != MagickFalse)
11442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11443 " bKGD");
11444 if (mng_info->ping_exclude_cHRM != MagickFalse)
11445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11446 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011447 if (mng_info->ping_exclude_date != MagickFalse)
11448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11449 " date");
glennrp26f37912010-12-23 16:22:42 +000011450 if (mng_info->ping_exclude_EXIF != MagickFalse)
11451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11452 " EXIF");
11453 if (mng_info->ping_exclude_gAMA != MagickFalse)
11454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11455 " gAMA");
11456 if (mng_info->ping_exclude_iCCP != MagickFalse)
11457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11458 " iCCP");
11459/*
11460 if (mng_info->ping_exclude_iTXt != MagickFalse)
11461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11462 " iTXt");
11463*/
11464 if (mng_info->ping_exclude_oFFs != MagickFalse)
11465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11466 " oFFs");
11467 if (mng_info->ping_exclude_pHYs != MagickFalse)
11468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11469 " pHYs");
11470 if (mng_info->ping_exclude_sRGB != MagickFalse)
11471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11472 " sRGB");
11473 if (mng_info->ping_exclude_tEXt != MagickFalse)
11474 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11475 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011476 if (mng_info->ping_exclude_tRNS != MagickFalse)
11477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11478 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011479 if (mng_info->ping_exclude_vpAg != MagickFalse)
11480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11481 " vpAg");
11482 if (mng_info->ping_exclude_zCCP != MagickFalse)
11483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11484 " zCCP");
11485 if (mng_info->ping_exclude_zTXt != MagickFalse)
11486 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11487 " zTXt");
11488 }
11489
glennrpb9cfe272010-12-21 15:08:06 +000011490 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011491
glennrpb9cfe272010-12-21 15:08:06 +000011492 status=WriteOnePNGImage(mng_info,image_info,image);
cristy3ed852e2009-09-05 21:47:34 +000011493
11494 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011495
cristy3ed852e2009-09-05 21:47:34 +000011496 if (logging != MagickFalse)
11497 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011498
cristy3ed852e2009-09-05 21:47:34 +000011499 return(status);
11500}
11501
11502#if defined(JNG_SUPPORTED)
11503
11504/* Write one JNG image */
11505static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11506 const ImageInfo *image_info,Image *image)
11507{
11508 Image
11509 *jpeg_image;
11510
11511 ImageInfo
11512 *jpeg_image_info;
11513
11514 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011515 logging,
cristy3ed852e2009-09-05 21:47:34 +000011516 status;
11517
11518 size_t
11519 length;
11520
11521 unsigned char
11522 *blob,
11523 chunk[80],
11524 *p;
11525
11526 unsigned int
11527 jng_alpha_compression_method,
11528 jng_alpha_sample_depth,
11529 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011530 transparent;
11531
cristybb503372010-05-27 20:51:26 +000011532 size_t
cristy3ed852e2009-09-05 21:47:34 +000011533 jng_quality;
11534
11535 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011536 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011537
11538 blob=(unsigned char *) NULL;
11539 jpeg_image=(Image *) NULL;
11540 jpeg_image_info=(ImageInfo *) NULL;
11541
11542 status=MagickTrue;
11543 transparent=image_info->type==GrayscaleMatteType ||
11544 image_info->type==TrueColorMatteType;
11545 jng_color_type=10;
11546 jng_alpha_sample_depth=0;
11547 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11548 jng_alpha_compression_method=0;
11549
11550 if (image->matte != MagickFalse)
11551 {
11552 /* if any pixels are transparent */
11553 transparent=MagickTrue;
11554 if (image_info->compression==JPEGCompression)
11555 jng_alpha_compression_method=8;
11556 }
11557
11558 if (transparent)
11559 {
cristybd5a96c2011-08-21 00:04:26 +000011560 ChannelType
11561 channel_mask;
11562
cristy3ed852e2009-09-05 21:47:34 +000011563 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011564
cristy3ed852e2009-09-05 21:47:34 +000011565 /* Create JPEG blob, image, and image_info */
11566 if (logging != MagickFalse)
11567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +000011568 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011569
cristy3ed852e2009-09-05 21:47:34 +000011570 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011571
cristy3ed852e2009-09-05 21:47:34 +000011572 if (jpeg_image_info == (ImageInfo *) NULL)
11573 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011574
cristy3ed852e2009-09-05 21:47:34 +000011575 if (logging != MagickFalse)
11576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11577 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011578
cristy3ed852e2009-09-05 21:47:34 +000011579 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011580
cristy3ed852e2009-09-05 21:47:34 +000011581 if (jpeg_image == (Image *) NULL)
11582 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011583
cristy3ed852e2009-09-05 21:47:34 +000011584 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristybd5a96c2011-08-21 00:04:26 +000011585 channel_mask=SetPixelChannelMask(jpeg_image,AlphaChannel);
cristy3139dc22011-07-08 00:11:42 +000011586 status=SeparateImage(jpeg_image);
cristybd5a96c2011-08-21 00:04:26 +000011587 (void) SetPixelChannelMap(jpeg_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +000011588 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011589
cristy3ed852e2009-09-05 21:47:34 +000011590 if (jng_quality >= 1000)
11591 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000011592
cristy3ed852e2009-09-05 21:47:34 +000011593 else
11594 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000011595
cristy3ed852e2009-09-05 21:47:34 +000011596 jpeg_image_info->type=GrayscaleType;
11597 (void) SetImageType(jpeg_image,GrayscaleType);
11598 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011599 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011600 "%s",jpeg_image->filename);
11601 }
11602
11603 /* To do: check bit depth of PNG alpha channel */
11604
11605 /* Check if image is grayscale. */
11606 if (image_info->type != TrueColorMatteType && image_info->type !=
11607 TrueColorType && ImageIsGray(image))
11608 jng_color_type-=2;
11609
11610 if (transparent)
11611 {
11612 if (jng_alpha_compression_method==0)
11613 {
11614 const char
11615 *value;
11616
cristy4c08aed2011-07-01 19:47:50 +000011617 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011618 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11619 &image->exception);
11620 if (logging != MagickFalse)
11621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11622 " Creating PNG blob.");
11623 length=0;
11624
11625 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11626 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11627 jpeg_image_info->interlace=NoInterlace;
11628
11629 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11630 &image->exception);
11631
11632 /* Retrieve sample depth used */
11633 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
11634 if (value != (char *) NULL)
11635 jng_alpha_sample_depth= (unsigned int) value[0];
11636 }
11637 else
11638 {
cristy4c08aed2011-07-01 19:47:50 +000011639 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011640
11641 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11642 &image->exception);
11643
11644 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11645 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11646 jpeg_image_info->interlace=NoInterlace;
11647 if (logging != MagickFalse)
11648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11649 " Creating blob.");
11650 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000011651 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000011652 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011653
cristy3ed852e2009-09-05 21:47:34 +000011654 if (logging != MagickFalse)
11655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011656 " Successfully read jpeg_image into a blob, length=%.20g.",
11657 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011658
11659 }
11660 /* Destroy JPEG image and image_info */
11661 jpeg_image=DestroyImage(jpeg_image);
11662 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11663 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11664 }
11665
11666 /* Write JHDR chunk */
11667 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11668 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011669 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011670 PNGLong(chunk+4,(png_uint_32) image->columns);
11671 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011672 chunk[12]=jng_color_type;
11673 chunk[13]=8; /* sample depth */
11674 chunk[14]=8; /*jng_image_compression_method */
11675 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11676 chunk[16]=jng_alpha_sample_depth;
11677 chunk[17]=jng_alpha_compression_method;
11678 chunk[18]=0; /*jng_alpha_filter_method */
11679 chunk[19]=0; /*jng_alpha_interlace_method */
11680 (void) WriteBlob(image,20,chunk);
11681 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11682 if (logging != MagickFalse)
11683 {
11684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011685 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011686
cristy3ed852e2009-09-05 21:47:34 +000011687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011688 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011689
cristy3ed852e2009-09-05 21:47:34 +000011690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11691 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011692
cristy3ed852e2009-09-05 21:47:34 +000011693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11694 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011695
cristy3ed852e2009-09-05 21:47:34 +000011696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11697 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011698
cristy3ed852e2009-09-05 21:47:34 +000011699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11700 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011701
cristy3ed852e2009-09-05 21:47:34 +000011702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11703 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011704
cristy3ed852e2009-09-05 21:47:34 +000011705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11706 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011707
cristy3ed852e2009-09-05 21:47:34 +000011708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11709 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011710
cristy3ed852e2009-09-05 21:47:34 +000011711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11712 " JNG alpha interlace:%5d",0);
11713 }
11714
glennrp0fe50b42010-11-16 03:52:51 +000011715 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011716 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011717
11718 /*
11719 Write leading ancillary chunks
11720 */
11721
11722 if (transparent)
11723 {
11724 /*
11725 Write JNG bKGD chunk
11726 */
11727
11728 unsigned char
11729 blue,
11730 green,
11731 red;
11732
cristybb503372010-05-27 20:51:26 +000011733 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011734 num_bytes;
11735
11736 if (jng_color_type == 8 || jng_color_type == 12)
11737 num_bytes=6L;
11738 else
11739 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011740 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011741 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011742 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011743 red=ScaleQuantumToChar(image->background_color.red);
11744 green=ScaleQuantumToChar(image->background_color.green);
11745 blue=ScaleQuantumToChar(image->background_color.blue);
11746 *(chunk+4)=0;
11747 *(chunk+5)=red;
11748 *(chunk+6)=0;
11749 *(chunk+7)=green;
11750 *(chunk+8)=0;
11751 *(chunk+9)=blue;
11752 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11753 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11754 }
11755
11756 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11757 {
11758 /*
11759 Write JNG sRGB chunk
11760 */
11761 (void) WriteBlobMSBULong(image,1L);
11762 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011763 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011764
cristy3ed852e2009-09-05 21:47:34 +000011765 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011766 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011767 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011768 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011769
cristy3ed852e2009-09-05 21:47:34 +000011770 else
glennrpe610a072010-08-05 17:08:46 +000011771 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011772 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011773 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011774
cristy3ed852e2009-09-05 21:47:34 +000011775 (void) WriteBlob(image,5,chunk);
11776 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11777 }
11778 else
11779 {
11780 if (image->gamma != 0.0)
11781 {
11782 /*
11783 Write JNG gAMA chunk
11784 */
11785 (void) WriteBlobMSBULong(image,4L);
11786 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011787 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011788 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011789 (void) WriteBlob(image,8,chunk);
11790 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11791 }
glennrp0fe50b42010-11-16 03:52:51 +000011792
cristy3ed852e2009-09-05 21:47:34 +000011793 if ((mng_info->equal_chrms == MagickFalse) &&
11794 (image->chromaticity.red_primary.x != 0.0))
11795 {
11796 PrimaryInfo
11797 primary;
11798
11799 /*
11800 Write JNG cHRM chunk
11801 */
11802 (void) WriteBlobMSBULong(image,32L);
11803 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011804 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011805 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011806 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11807 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011808 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011809 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11810 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011811 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011812 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11813 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011814 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011815 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11816 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011817 (void) WriteBlob(image,36,chunk);
11818 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11819 }
11820 }
glennrp0fe50b42010-11-16 03:52:51 +000011821
cristy3ed852e2009-09-05 21:47:34 +000011822 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11823 {
11824 /*
11825 Write JNG pHYs chunk
11826 */
11827 (void) WriteBlobMSBULong(image,9L);
11828 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011829 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011830 if (image->units == PixelsPerInchResolution)
11831 {
cristy35ef8242010-06-03 16:24:13 +000011832 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011833 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011834
cristy35ef8242010-06-03 16:24:13 +000011835 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011836 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011837
cristy3ed852e2009-09-05 21:47:34 +000011838 chunk[12]=1;
11839 }
glennrp0fe50b42010-11-16 03:52:51 +000011840
cristy3ed852e2009-09-05 21:47:34 +000011841 else
11842 {
11843 if (image->units == PixelsPerCentimeterResolution)
11844 {
cristy35ef8242010-06-03 16:24:13 +000011845 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011846 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011847
cristy35ef8242010-06-03 16:24:13 +000011848 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011849 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011850
cristy3ed852e2009-09-05 21:47:34 +000011851 chunk[12]=1;
11852 }
glennrp0fe50b42010-11-16 03:52:51 +000011853
cristy3ed852e2009-09-05 21:47:34 +000011854 else
11855 {
cristy35ef8242010-06-03 16:24:13 +000011856 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11857 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011858 chunk[12]=0;
11859 }
11860 }
11861 (void) WriteBlob(image,13,chunk);
11862 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11863 }
11864
11865 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11866 {
11867 /*
11868 Write JNG oFFs chunk
11869 */
11870 (void) WriteBlobMSBULong(image,9L);
11871 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011872 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011873 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11874 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011875 chunk[12]=0;
11876 (void) WriteBlob(image,13,chunk);
11877 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11878 }
11879 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11880 {
11881 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11882 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011883 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011884 PNGLong(chunk+4,(png_uint_32) image->page.width);
11885 PNGLong(chunk+8,(png_uint_32) image->page.height);
11886 chunk[12]=0; /* unit = pixels */
11887 (void) WriteBlob(image,13,chunk);
11888 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11889 }
11890
11891
11892 if (transparent)
11893 {
11894 if (jng_alpha_compression_method==0)
11895 {
cristybb503372010-05-27 20:51:26 +000011896 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011897 i;
11898
cristybb503372010-05-27 20:51:26 +000011899 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011900 len;
11901
11902 /* Write IDAT chunk header */
11903 if (logging != MagickFalse)
11904 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011905 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000011906 length);
cristy3ed852e2009-09-05 21:47:34 +000011907
11908 /* Copy IDAT chunks */
11909 len=0;
11910 p=blob+8;
cristybb503372010-05-27 20:51:26 +000011911 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000011912 {
11913 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11914 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000011915
cristy3ed852e2009-09-05 21:47:34 +000011916 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11917 {
11918 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000011919 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000011920 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000011921 (void) WriteBlob(image,(size_t) len+4,p);
11922 (void) WriteBlobMSBULong(image,
11923 crc32(0,p,(uInt) len+4));
11924 }
glennrp0fe50b42010-11-16 03:52:51 +000011925
cristy3ed852e2009-09-05 21:47:34 +000011926 else
11927 {
11928 if (logging != MagickFalse)
11929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011930 " Skipping %c%c%c%c chunk, length=%.20g.",
11931 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000011932 }
11933 p+=(8+len);
11934 }
11935 }
11936 else
11937 {
11938 /* Write JDAA chunk header */
11939 if (logging != MagickFalse)
11940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011941 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000011942 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011943 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000011944 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000011945 /* Write JDAT chunk(s) data */
11946 (void) WriteBlob(image,4,chunk);
11947 (void) WriteBlob(image,length,blob);
11948 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11949 (uInt) length));
11950 }
11951 blob=(unsigned char *) RelinquishMagickMemory(blob);
11952 }
11953
11954 /* Encode image as a JPEG blob */
11955 if (logging != MagickFalse)
11956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11957 " Creating jpeg_image_info.");
11958 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11959 if (jpeg_image_info == (ImageInfo *) NULL)
11960 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11961
11962 if (logging != MagickFalse)
11963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11964 " Creating jpeg_image.");
11965
11966 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11967 if (jpeg_image == (Image *) NULL)
11968 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11969 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11970
11971 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011972 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000011973 jpeg_image->filename);
11974
11975 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11976 &image->exception);
11977
11978 if (logging != MagickFalse)
11979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011980 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11981 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011982
11983 if (jng_color_type == 8 || jng_color_type == 12)
11984 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000011985
cristy3ed852e2009-09-05 21:47:34 +000011986 jpeg_image_info->quality=jng_quality % 1000;
11987 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11988 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000011989
cristy3ed852e2009-09-05 21:47:34 +000011990 if (logging != MagickFalse)
11991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11992 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000011993
cristy3ed852e2009-09-05 21:47:34 +000011994 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011995
cristy3ed852e2009-09-05 21:47:34 +000011996 if (logging != MagickFalse)
11997 {
11998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011999 " Successfully read jpeg_image into a blob, length=%.20g.",
12000 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012001
12002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012003 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012004 }
glennrp0fe50b42010-11-16 03:52:51 +000012005
cristy3ed852e2009-09-05 21:47:34 +000012006 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012007 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012008 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012009 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012010 (void) WriteBlob(image,4,chunk);
12011 (void) WriteBlob(image,length,blob);
12012 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12013
12014 jpeg_image=DestroyImage(jpeg_image);
12015 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12016 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12017 blob=(unsigned char *) RelinquishMagickMemory(blob);
12018
12019 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012020 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012021
12022 /* Write IEND chunk */
12023 (void) WriteBlobMSBULong(image,0L);
12024 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012025 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012026 (void) WriteBlob(image,4,chunk);
12027 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12028
12029 if (logging != MagickFalse)
12030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12031 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012032
cristy3ed852e2009-09-05 21:47:34 +000012033 return(status);
12034}
12035
12036
12037/*
12038%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12039% %
12040% %
12041% %
12042% W r i t e J N G I m a g e %
12043% %
12044% %
12045% %
12046%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12047%
12048% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12049%
12050% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12051%
12052% The format of the WriteJNGImage method is:
12053%
cristy1e178e72011-08-28 19:44:34 +000012054% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12055% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012056%
12057% A description of each parameter follows:
12058%
12059% o image_info: the image info.
12060%
12061% o image: The image.
12062%
cristy1e178e72011-08-28 19:44:34 +000012063% o exception: return any errors or warnings in this structure.
12064%
cristy3ed852e2009-09-05 21:47:34 +000012065%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12066*/
cristy1e178e72011-08-28 19:44:34 +000012067static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12068 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012069{
12070 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012071 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012072 logging,
cristy3ed852e2009-09-05 21:47:34 +000012073 status;
12074
12075 MngInfo
12076 *mng_info;
12077
cristy3ed852e2009-09-05 21:47:34 +000012078 /*
12079 Open image file.
12080 */
12081 assert(image_info != (const ImageInfo *) NULL);
12082 assert(image_info->signature == MagickSignature);
12083 assert(image != (Image *) NULL);
12084 assert(image->signature == MagickSignature);
12085 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012086 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012087 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12088 if (status == MagickFalse)
12089 return(status);
12090
12091 /*
12092 Allocate a MngInfo structure.
12093 */
12094 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012095 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012096 if (mng_info == (MngInfo *) NULL)
12097 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12098 /*
12099 Initialize members of the MngInfo structure.
12100 */
12101 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12102 mng_info->image=image;
12103 have_mng_structure=MagickTrue;
12104
12105 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12106
12107 status=WriteOneJNGImage(mng_info,image_info,image);
12108 (void) CloseBlob(image);
12109
12110 (void) CatchImageException(image);
12111 MngInfoFreeStruct(mng_info,&have_mng_structure);
12112 if (logging != MagickFalse)
12113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12114 return(status);
12115}
12116#endif
12117
cristy1e178e72011-08-28 19:44:34 +000012118static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12119 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012120{
12121 const char
12122 *option;
12123
12124 Image
12125 *next_image;
12126
12127 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012128 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012129 status;
12130
glennrp03812ae2010-12-24 01:31:34 +000012131 volatile MagickBooleanType
12132 logging;
12133
cristy3ed852e2009-09-05 21:47:34 +000012134 MngInfo
12135 *mng_info;
12136
12137 int
cristy3ed852e2009-09-05 21:47:34 +000012138 image_count,
12139 need_iterations,
12140 need_matte;
12141
12142 volatile int
12143#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12144 defined(PNG_MNG_FEATURES_SUPPORTED)
12145 need_local_plte,
12146#endif
12147 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012148 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012149 use_global_plte;
12150
cristybb503372010-05-27 20:51:26 +000012151 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012152 i;
12153
12154 unsigned char
12155 chunk[800];
12156
12157 volatile unsigned int
12158 write_jng,
12159 write_mng;
12160
cristybb503372010-05-27 20:51:26 +000012161 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012162 scene;
12163
cristybb503372010-05-27 20:51:26 +000012164 size_t
cristy3ed852e2009-09-05 21:47:34 +000012165 final_delay=0,
12166 initial_delay;
12167
glennrpd5045b42010-03-24 12:40:35 +000012168#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012169 if (image_info->verbose)
12170 printf("Your PNG library (libpng-%s) is rather old.\n",
12171 PNG_LIBPNG_VER_STRING);
12172#endif
12173
12174 /*
12175 Open image file.
12176 */
12177 assert(image_info != (const ImageInfo *) NULL);
12178 assert(image_info->signature == MagickSignature);
12179 assert(image != (Image *) NULL);
12180 assert(image->signature == MagickSignature);
12181 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012182 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012183 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12184 if (status == MagickFalse)
12185 return(status);
12186
12187 /*
12188 Allocate a MngInfo structure.
12189 */
12190 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012191 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012192 if (mng_info == (MngInfo *) NULL)
12193 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12194 /*
12195 Initialize members of the MngInfo structure.
12196 */
12197 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12198 mng_info->image=image;
12199 have_mng_structure=MagickTrue;
12200 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12201
12202 /*
12203 * See if user has requested a specific PNG subformat to be used
12204 * for all of the PNGs in the MNG being written, e.g.,
12205 *
12206 * convert *.png png8:animation.mng
12207 *
12208 * To do: check -define png:bit_depth and png:color_type as well,
12209 * or perhaps use mng:bit_depth and mng:color_type instead for
12210 * global settings.
12211 */
12212
12213 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12214 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12215 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12216
12217 write_jng=MagickFalse;
12218 if (image_info->compression == JPEGCompression)
12219 write_jng=MagickTrue;
12220
12221 mng_info->adjoin=image_info->adjoin &&
12222 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12223
cristy3ed852e2009-09-05 21:47:34 +000012224 if (logging != MagickFalse)
12225 {
12226 /* Log some info about the input */
12227 Image
12228 *p;
12229
12230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12231 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012232
cristy3ed852e2009-09-05 21:47:34 +000012233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012234 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012235
cristy3ed852e2009-09-05 21:47:34 +000012236 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12237 " Type: %d",image_info->type);
12238
12239 scene=0;
12240 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12241 {
12242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012243 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012244
cristy3ed852e2009-09-05 21:47:34 +000012245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012246 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012247
cristy3ed852e2009-09-05 21:47:34 +000012248 if (p->matte)
12249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12250 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012251
cristy3ed852e2009-09-05 21:47:34 +000012252 else
12253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12254 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012255
cristy3ed852e2009-09-05 21:47:34 +000012256 if (p->storage_class == PseudoClass)
12257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12258 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012259
cristy3ed852e2009-09-05 21:47:34 +000012260 else
12261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12262 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012263
cristy3ed852e2009-09-05 21:47:34 +000012264 if (p->colors)
12265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012266 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012267
cristy3ed852e2009-09-05 21:47:34 +000012268 else
12269 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12270 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012271
cristy3ed852e2009-09-05 21:47:34 +000012272 if (mng_info->adjoin == MagickFalse)
12273 break;
12274 }
12275 }
12276
cristy3ed852e2009-09-05 21:47:34 +000012277 use_global_plte=MagickFalse;
12278 all_images_are_gray=MagickFalse;
12279#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12280 need_local_plte=MagickTrue;
12281#endif
12282 need_defi=MagickFalse;
12283 need_matte=MagickFalse;
12284 mng_info->framing_mode=1;
12285 mng_info->old_framing_mode=1;
12286
12287 if (write_mng)
12288 if (image_info->page != (char *) NULL)
12289 {
12290 /*
12291 Determine image bounding box.
12292 */
12293 SetGeometry(image,&mng_info->page);
12294 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12295 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12296 }
12297 if (write_mng)
12298 {
12299 unsigned int
12300 need_geom;
12301
12302 unsigned short
12303 red,
12304 green,
12305 blue;
12306
12307 mng_info->page=image->page;
12308 need_geom=MagickTrue;
12309 if (mng_info->page.width || mng_info->page.height)
12310 need_geom=MagickFalse;
12311 /*
12312 Check all the scenes.
12313 */
12314 initial_delay=image->delay;
12315 need_iterations=MagickFalse;
12316 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12317 mng_info->equal_physs=MagickTrue,
12318 mng_info->equal_gammas=MagickTrue;
12319 mng_info->equal_srgbs=MagickTrue;
12320 mng_info->equal_backgrounds=MagickTrue;
12321 image_count=0;
12322#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12323 defined(PNG_MNG_FEATURES_SUPPORTED)
12324 all_images_are_gray=MagickTrue;
12325 mng_info->equal_palettes=MagickFalse;
12326 need_local_plte=MagickFalse;
12327#endif
12328 for (next_image=image; next_image != (Image *) NULL; )
12329 {
12330 if (need_geom)
12331 {
12332 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12333 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012334
cristy3ed852e2009-09-05 21:47:34 +000012335 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12336 mng_info->page.height=next_image->rows+next_image->page.y;
12337 }
glennrp0fe50b42010-11-16 03:52:51 +000012338
cristy3ed852e2009-09-05 21:47:34 +000012339 if (next_image->page.x || next_image->page.y)
12340 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012341
cristy3ed852e2009-09-05 21:47:34 +000012342 if (next_image->matte)
12343 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012344
cristy3ed852e2009-09-05 21:47:34 +000012345 if ((int) next_image->dispose >= BackgroundDispose)
12346 if (next_image->matte || next_image->page.x || next_image->page.y ||
12347 ((next_image->columns < mng_info->page.width) &&
12348 (next_image->rows < mng_info->page.height)))
12349 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012350
cristy3ed852e2009-09-05 21:47:34 +000012351 if (next_image->iterations)
12352 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012353
cristy3ed852e2009-09-05 21:47:34 +000012354 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012355
cristy3ed852e2009-09-05 21:47:34 +000012356 if (final_delay != initial_delay || final_delay > 1UL*
12357 next_image->ticks_per_second)
12358 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012359
cristy3ed852e2009-09-05 21:47:34 +000012360#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12361 defined(PNG_MNG_FEATURES_SUPPORTED)
12362 /*
12363 check for global palette possibility.
12364 */
12365 if (image->matte != MagickFalse)
12366 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012367
cristy3ed852e2009-09-05 21:47:34 +000012368 if (need_local_plte == 0)
12369 {
12370 if (ImageIsGray(image) == MagickFalse)
12371 all_images_are_gray=MagickFalse;
12372 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12373 if (use_global_plte == 0)
12374 use_global_plte=mng_info->equal_palettes;
12375 need_local_plte=!mng_info->equal_palettes;
12376 }
12377#endif
12378 if (GetNextImageInList(next_image) != (Image *) NULL)
12379 {
12380 if (next_image->background_color.red !=
12381 next_image->next->background_color.red ||
12382 next_image->background_color.green !=
12383 next_image->next->background_color.green ||
12384 next_image->background_color.blue !=
12385 next_image->next->background_color.blue)
12386 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012387
cristy3ed852e2009-09-05 21:47:34 +000012388 if (next_image->gamma != next_image->next->gamma)
12389 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012390
cristy3ed852e2009-09-05 21:47:34 +000012391 if (next_image->rendering_intent !=
12392 next_image->next->rendering_intent)
12393 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012394
cristy3ed852e2009-09-05 21:47:34 +000012395 if ((next_image->units != next_image->next->units) ||
12396 (next_image->x_resolution != next_image->next->x_resolution) ||
12397 (next_image->y_resolution != next_image->next->y_resolution))
12398 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012399
cristy3ed852e2009-09-05 21:47:34 +000012400 if (mng_info->equal_chrms)
12401 {
12402 if (next_image->chromaticity.red_primary.x !=
12403 next_image->next->chromaticity.red_primary.x ||
12404 next_image->chromaticity.red_primary.y !=
12405 next_image->next->chromaticity.red_primary.y ||
12406 next_image->chromaticity.green_primary.x !=
12407 next_image->next->chromaticity.green_primary.x ||
12408 next_image->chromaticity.green_primary.y !=
12409 next_image->next->chromaticity.green_primary.y ||
12410 next_image->chromaticity.blue_primary.x !=
12411 next_image->next->chromaticity.blue_primary.x ||
12412 next_image->chromaticity.blue_primary.y !=
12413 next_image->next->chromaticity.blue_primary.y ||
12414 next_image->chromaticity.white_point.x !=
12415 next_image->next->chromaticity.white_point.x ||
12416 next_image->chromaticity.white_point.y !=
12417 next_image->next->chromaticity.white_point.y)
12418 mng_info->equal_chrms=MagickFalse;
12419 }
12420 }
12421 image_count++;
12422 next_image=GetNextImageInList(next_image);
12423 }
12424 if (image_count < 2)
12425 {
12426 mng_info->equal_backgrounds=MagickFalse;
12427 mng_info->equal_chrms=MagickFalse;
12428 mng_info->equal_gammas=MagickFalse;
12429 mng_info->equal_srgbs=MagickFalse;
12430 mng_info->equal_physs=MagickFalse;
12431 use_global_plte=MagickFalse;
12432#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12433 need_local_plte=MagickTrue;
12434#endif
12435 need_iterations=MagickFalse;
12436 }
glennrp0fe50b42010-11-16 03:52:51 +000012437
cristy3ed852e2009-09-05 21:47:34 +000012438 if (mng_info->need_fram == MagickFalse)
12439 {
12440 /*
12441 Only certain framing rates 100/n are exactly representable without
12442 the FRAM chunk but we'll allow some slop in VLC files
12443 */
12444 if (final_delay == 0)
12445 {
12446 if (need_iterations != MagickFalse)
12447 {
12448 /*
12449 It's probably a GIF with loop; don't run it *too* fast.
12450 */
glennrp02617122010-07-28 13:07:35 +000012451 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012452 {
12453 final_delay=10;
12454 (void) ThrowMagickException(&image->exception,
12455 GetMagickModule(),CoderWarning,
12456 "input has zero delay between all frames; assuming",
12457 " 10 cs `%s'","");
12458 }
cristy3ed852e2009-09-05 21:47:34 +000012459 }
12460 else
12461 mng_info->ticks_per_second=0;
12462 }
12463 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012464 mng_info->ticks_per_second=(png_uint_32)
12465 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012466 if (final_delay > 50)
12467 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012468
cristy3ed852e2009-09-05 21:47:34 +000012469 if (final_delay > 75)
12470 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012471
cristy3ed852e2009-09-05 21:47:34 +000012472 if (final_delay > 125)
12473 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012474
cristy3ed852e2009-09-05 21:47:34 +000012475 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12476 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12477 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12478 1UL*image->ticks_per_second))
12479 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12480 }
glennrp0fe50b42010-11-16 03:52:51 +000012481
cristy3ed852e2009-09-05 21:47:34 +000012482 if (mng_info->need_fram != MagickFalse)
12483 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12484 /*
12485 If pseudocolor, we should also check to see if all the
12486 palettes are identical and write a global PLTE if they are.
12487 ../glennrp Feb 99.
12488 */
12489 /*
12490 Write the MNG version 1.0 signature and MHDR chunk.
12491 */
12492 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12493 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12494 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012495 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012496 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12497 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012498 PNGLong(chunk+12,mng_info->ticks_per_second);
12499 PNGLong(chunk+16,0L); /* layer count=unknown */
12500 PNGLong(chunk+20,0L); /* frame count=unknown */
12501 PNGLong(chunk+24,0L); /* play time=unknown */
12502 if (write_jng)
12503 {
12504 if (need_matte)
12505 {
12506 if (need_defi || mng_info->need_fram || use_global_plte)
12507 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012508
cristy3ed852e2009-09-05 21:47:34 +000012509 else
12510 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12511 }
glennrp0fe50b42010-11-16 03:52:51 +000012512
cristy3ed852e2009-09-05 21:47:34 +000012513 else
12514 {
12515 if (need_defi || mng_info->need_fram || use_global_plte)
12516 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012517
cristy3ed852e2009-09-05 21:47:34 +000012518 else
12519 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12520 }
12521 }
glennrp0fe50b42010-11-16 03:52:51 +000012522
cristy3ed852e2009-09-05 21:47:34 +000012523 else
12524 {
12525 if (need_matte)
12526 {
12527 if (need_defi || mng_info->need_fram || use_global_plte)
12528 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012529
cristy3ed852e2009-09-05 21:47:34 +000012530 else
12531 PNGLong(chunk+28,9L); /* simplicity=VLC */
12532 }
glennrp0fe50b42010-11-16 03:52:51 +000012533
cristy3ed852e2009-09-05 21:47:34 +000012534 else
12535 {
12536 if (need_defi || mng_info->need_fram || use_global_plte)
12537 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012538
cristy3ed852e2009-09-05 21:47:34 +000012539 else
12540 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12541 }
12542 }
12543 (void) WriteBlob(image,32,chunk);
12544 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12545 option=GetImageOption(image_info,"mng:need-cacheoff");
12546 if (option != (const char *) NULL)
12547 {
12548 size_t
12549 length;
12550
12551 /*
12552 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12553 */
12554 PNGType(chunk,mng_nEED);
12555 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012556 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012557 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012558 length+=4;
12559 (void) WriteBlob(image,length,chunk);
12560 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12561 }
12562 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12563 (GetNextImageInList(image) != (Image *) NULL) &&
12564 (image->iterations != 1))
12565 {
12566 /*
12567 Write MNG TERM chunk
12568 */
12569 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12570 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012571 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012572 chunk[4]=3; /* repeat animation */
12573 chunk[5]=0; /* show last frame when done */
12574 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12575 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012576
cristy3ed852e2009-09-05 21:47:34 +000012577 if (image->iterations == 0)
12578 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012579
cristy3ed852e2009-09-05 21:47:34 +000012580 else
12581 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012582
cristy3ed852e2009-09-05 21:47:34 +000012583 if (logging != MagickFalse)
12584 {
12585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012586 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12587 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012588
cristy3ed852e2009-09-05 21:47:34 +000012589 if (image->iterations == 0)
12590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012591 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012592
cristy3ed852e2009-09-05 21:47:34 +000012593 else
12594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012595 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012596 }
12597 (void) WriteBlob(image,14,chunk);
12598 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12599 }
12600 /*
12601 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12602 */
12603 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12604 mng_info->equal_srgbs)
12605 {
12606 /*
12607 Write MNG sRGB chunk
12608 */
12609 (void) WriteBlobMSBULong(image,1L);
12610 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012611 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012612
cristy3ed852e2009-09-05 21:47:34 +000012613 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012614 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012615 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012616 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012617
cristy3ed852e2009-09-05 21:47:34 +000012618 else
glennrpe610a072010-08-05 17:08:46 +000012619 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012620 Magick_RenderingIntent_to_PNG_RenderingIntent(
12621 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012622
cristy3ed852e2009-09-05 21:47:34 +000012623 (void) WriteBlob(image,5,chunk);
12624 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12625 mng_info->have_write_global_srgb=MagickTrue;
12626 }
glennrp0fe50b42010-11-16 03:52:51 +000012627
cristy3ed852e2009-09-05 21:47:34 +000012628 else
12629 {
12630 if (image->gamma && mng_info->equal_gammas)
12631 {
12632 /*
12633 Write MNG gAMA chunk
12634 */
12635 (void) WriteBlobMSBULong(image,4L);
12636 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012637 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012638 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012639 (void) WriteBlob(image,8,chunk);
12640 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12641 mng_info->have_write_global_gama=MagickTrue;
12642 }
12643 if (mng_info->equal_chrms)
12644 {
12645 PrimaryInfo
12646 primary;
12647
12648 /*
12649 Write MNG cHRM chunk
12650 */
12651 (void) WriteBlobMSBULong(image,32L);
12652 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012653 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012654 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012655 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12656 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012657 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012658 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12659 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012660 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012661 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12662 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012663 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012664 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12665 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012666 (void) WriteBlob(image,36,chunk);
12667 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12668 mng_info->have_write_global_chrm=MagickTrue;
12669 }
12670 }
12671 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
12672 {
12673 /*
12674 Write MNG pHYs chunk
12675 */
12676 (void) WriteBlobMSBULong(image,9L);
12677 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012678 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012679
cristy3ed852e2009-09-05 21:47:34 +000012680 if (image->units == PixelsPerInchResolution)
12681 {
cristy35ef8242010-06-03 16:24:13 +000012682 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012683 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012684
cristy35ef8242010-06-03 16:24:13 +000012685 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012686 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012687
cristy3ed852e2009-09-05 21:47:34 +000012688 chunk[12]=1;
12689 }
glennrp0fe50b42010-11-16 03:52:51 +000012690
cristy3ed852e2009-09-05 21:47:34 +000012691 else
12692 {
12693 if (image->units == PixelsPerCentimeterResolution)
12694 {
cristy35ef8242010-06-03 16:24:13 +000012695 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012696 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012697
cristy35ef8242010-06-03 16:24:13 +000012698 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012699 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012700
cristy3ed852e2009-09-05 21:47:34 +000012701 chunk[12]=1;
12702 }
glennrp0fe50b42010-11-16 03:52:51 +000012703
cristy3ed852e2009-09-05 21:47:34 +000012704 else
12705 {
cristy35ef8242010-06-03 16:24:13 +000012706 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
12707 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012708 chunk[12]=0;
12709 }
12710 }
12711 (void) WriteBlob(image,13,chunk);
12712 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12713 }
12714 /*
12715 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12716 or does not cover the entire frame.
12717 */
12718 if (write_mng && (image->matte || image->page.x > 0 ||
12719 image->page.y > 0 || (image->page.width &&
12720 (image->page.width+image->page.x < mng_info->page.width))
12721 || (image->page.height && (image->page.height+image->page.y
12722 < mng_info->page.height))))
12723 {
12724 (void) WriteBlobMSBULong(image,6L);
12725 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012726 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012727 red=ScaleQuantumToShort(image->background_color.red);
12728 green=ScaleQuantumToShort(image->background_color.green);
12729 blue=ScaleQuantumToShort(image->background_color.blue);
12730 PNGShort(chunk+4,red);
12731 PNGShort(chunk+6,green);
12732 PNGShort(chunk+8,blue);
12733 (void) WriteBlob(image,10,chunk);
12734 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12735 if (mng_info->equal_backgrounds)
12736 {
12737 (void) WriteBlobMSBULong(image,6L);
12738 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012739 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012740 (void) WriteBlob(image,10,chunk);
12741 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12742 }
12743 }
12744
12745#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12746 if ((need_local_plte == MagickFalse) &&
12747 (image->storage_class == PseudoClass) &&
12748 (all_images_are_gray == MagickFalse))
12749 {
cristybb503372010-05-27 20:51:26 +000012750 size_t
cristy3ed852e2009-09-05 21:47:34 +000012751 data_length;
12752
12753 /*
12754 Write MNG PLTE chunk
12755 */
12756 data_length=3*image->colors;
12757 (void) WriteBlobMSBULong(image,data_length);
12758 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012759 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012760
cristybb503372010-05-27 20:51:26 +000012761 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012762 {
12763 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
12764 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
12765 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
12766 }
glennrp0fe50b42010-11-16 03:52:51 +000012767
cristy3ed852e2009-09-05 21:47:34 +000012768 (void) WriteBlob(image,data_length+4,chunk);
12769 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12770 mng_info->have_write_global_plte=MagickTrue;
12771 }
12772#endif
12773 }
12774 scene=0;
12775 mng_info->delay=0;
12776#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12777 defined(PNG_MNG_FEATURES_SUPPORTED)
12778 mng_info->equal_palettes=MagickFalse;
12779#endif
12780 do
12781 {
12782 if (mng_info->adjoin)
12783 {
12784#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12785 defined(PNG_MNG_FEATURES_SUPPORTED)
12786 /*
12787 If we aren't using a global palette for the entire MNG, check to
12788 see if we can use one for two or more consecutive images.
12789 */
12790 if (need_local_plte && use_global_plte && !all_images_are_gray)
12791 {
12792 if (mng_info->IsPalette)
12793 {
12794 /*
12795 When equal_palettes is true, this image has the same palette
12796 as the previous PseudoClass image
12797 */
12798 mng_info->have_write_global_plte=mng_info->equal_palettes;
12799 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12800 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12801 {
12802 /*
12803 Write MNG PLTE chunk
12804 */
cristybb503372010-05-27 20:51:26 +000012805 size_t
cristy3ed852e2009-09-05 21:47:34 +000012806 data_length;
12807
12808 data_length=3*image->colors;
12809 (void) WriteBlobMSBULong(image,data_length);
12810 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012811 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012812
cristybb503372010-05-27 20:51:26 +000012813 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012814 {
12815 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12816 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12817 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12818 }
glennrp0fe50b42010-11-16 03:52:51 +000012819
cristy3ed852e2009-09-05 21:47:34 +000012820 (void) WriteBlob(image,data_length+4,chunk);
12821 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12822 (uInt) (data_length+4)));
12823 mng_info->have_write_global_plte=MagickTrue;
12824 }
12825 }
12826 else
12827 mng_info->have_write_global_plte=MagickFalse;
12828 }
12829#endif
12830 if (need_defi)
12831 {
cristybb503372010-05-27 20:51:26 +000012832 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012833 previous_x,
12834 previous_y;
12835
12836 if (scene)
12837 {
12838 previous_x=mng_info->page.x;
12839 previous_y=mng_info->page.y;
12840 }
12841 else
12842 {
12843 previous_x=0;
12844 previous_y=0;
12845 }
12846 mng_info->page=image->page;
12847 if ((mng_info->page.x != previous_x) ||
12848 (mng_info->page.y != previous_y))
12849 {
12850 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12851 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012852 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012853 chunk[4]=0; /* object 0 MSB */
12854 chunk[5]=0; /* object 0 LSB */
12855 chunk[6]=0; /* visible */
12856 chunk[7]=0; /* abstract */
12857 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12858 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12859 (void) WriteBlob(image,16,chunk);
12860 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12861 }
12862 }
12863 }
12864
12865 mng_info->write_mng=write_mng;
12866
12867 if ((int) image->dispose >= 3)
12868 mng_info->framing_mode=3;
12869
12870 if (mng_info->need_fram && mng_info->adjoin &&
12871 ((image->delay != mng_info->delay) ||
12872 (mng_info->framing_mode != mng_info->old_framing_mode)))
12873 {
12874 if (image->delay == mng_info->delay)
12875 {
12876 /*
12877 Write a MNG FRAM chunk with the new framing mode.
12878 */
12879 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12880 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012881 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012882 chunk[4]=(unsigned char) mng_info->framing_mode;
12883 (void) WriteBlob(image,5,chunk);
12884 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12885 }
12886 else
12887 {
12888 /*
12889 Write a MNG FRAM chunk with the delay.
12890 */
12891 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12892 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012893 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012894 chunk[4]=(unsigned char) mng_info->framing_mode;
12895 chunk[5]=0; /* frame name separator (no name) */
12896 chunk[6]=2; /* flag for changing default delay */
12897 chunk[7]=0; /* flag for changing frame timeout */
12898 chunk[8]=0; /* flag for changing frame clipping */
12899 chunk[9]=0; /* flag for changing frame sync_id */
12900 PNGLong(chunk+10,(png_uint_32)
12901 ((mng_info->ticks_per_second*
12902 image->delay)/MagickMax(image->ticks_per_second,1)));
12903 (void) WriteBlob(image,14,chunk);
12904 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000012905 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000012906 }
12907 mng_info->old_framing_mode=mng_info->framing_mode;
12908 }
12909
12910#if defined(JNG_SUPPORTED)
12911 if (image_info->compression == JPEGCompression)
12912 {
12913 ImageInfo
12914 *write_info;
12915
12916 if (logging != MagickFalse)
12917 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12918 " Writing JNG object.");
12919 /* To do: specify the desired alpha compression method. */
12920 write_info=CloneImageInfo(image_info);
12921 write_info->compression=UndefinedCompression;
12922 status=WriteOneJNGImage(mng_info,write_info,image);
12923 write_info=DestroyImageInfo(write_info);
12924 }
12925 else
12926#endif
12927 {
12928 if (logging != MagickFalse)
12929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12930 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000012931
glennrpb9cfe272010-12-21 15:08:06 +000012932 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000012933 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000012934
12935 /* We don't want any ancillary chunks written */
12936 mng_info->ping_exclude_bKGD=MagickTrue;
12937 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000012938 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012939 mng_info->ping_exclude_EXIF=MagickTrue;
12940 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012941 mng_info->ping_exclude_iCCP=MagickTrue;
12942 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12943 mng_info->ping_exclude_oFFs=MagickTrue;
12944 mng_info->ping_exclude_pHYs=MagickTrue;
12945 mng_info->ping_exclude_sRGB=MagickTrue;
12946 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000012947 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012948 mng_info->ping_exclude_vpAg=MagickTrue;
12949 mng_info->ping_exclude_zCCP=MagickTrue;
12950 mng_info->ping_exclude_zTXt=MagickTrue;
12951
cristy3ed852e2009-09-05 21:47:34 +000012952 status=WriteOnePNGImage(mng_info,image_info,image);
12953 }
12954
12955 if (status == MagickFalse)
12956 {
12957 MngInfoFreeStruct(mng_info,&have_mng_structure);
12958 (void) CloseBlob(image);
12959 return(MagickFalse);
12960 }
12961 (void) CatchImageException(image);
12962 if (GetNextImageInList(image) == (Image *) NULL)
12963 break;
12964 image=SyncNextImageInList(image);
12965 status=SetImageProgress(image,SaveImagesTag,scene++,
12966 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000012967
cristy3ed852e2009-09-05 21:47:34 +000012968 if (status == MagickFalse)
12969 break;
glennrp0fe50b42010-11-16 03:52:51 +000012970
cristy3ed852e2009-09-05 21:47:34 +000012971 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000012972
cristy3ed852e2009-09-05 21:47:34 +000012973 if (write_mng)
12974 {
12975 while (GetPreviousImageInList(image) != (Image *) NULL)
12976 image=GetPreviousImageInList(image);
12977 /*
12978 Write the MEND chunk.
12979 */
12980 (void) WriteBlobMSBULong(image,0x00000000L);
12981 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000012982 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000012983 (void) WriteBlob(image,4,chunk);
12984 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12985 }
12986 /*
12987 Relinquish resources.
12988 */
12989 (void) CloseBlob(image);
12990 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012991
cristy3ed852e2009-09-05 21:47:34 +000012992 if (logging != MagickFalse)
12993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012994
cristy3ed852e2009-09-05 21:47:34 +000012995 return(MagickTrue);
12996}
glennrpd5045b42010-03-24 12:40:35 +000012997#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000012998
cristy3ed852e2009-09-05 21:47:34 +000012999static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13000{
13001 image=image;
13002 printf("Your PNG library is too old: You have libpng-%s\n",
13003 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013004
cristy3ed852e2009-09-05 21:47:34 +000013005 ThrowBinaryException(CoderError,"PNG library is too old",
13006 image_info->filename);
13007}
glennrp39992b42010-11-14 00:03:43 +000013008
cristy3ed852e2009-09-05 21:47:34 +000013009static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13010{
13011 return(WritePNGImage(image_info,image));
13012}
glennrpd5045b42010-03-24 12:40:35 +000013013#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013014#endif