blob: c4ebd84b0c29e5d9b7d9aa48aaff034132867414 [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% %
cristy1454be72011-12-19 01:52:48 +000021% Copyright 1999-2012 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
glennrp1a2061f2011-10-18 12:30:45 +0000132 * and PixelInfos all have the same image->depth, and for use
glennrp8e58efd2011-05-20 12:16:29 +0000133 * in PNG8 quantization.
134 */
135
136
137/* LBR01: Replicate top bit */
138
glennrp05001c32011-08-06 13:04:16 +0000139#define LBR01PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000140 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
141 0 : QuantumRange);
142
glennrp91d99252011-06-25 14:30:13 +0000143#define LBR01PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000144 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
145 0 : QuantumRange);
146
glennrp91d99252011-06-25 14:30:13 +0000147#define LBR01PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000148 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
149 0 : QuantumRange);
150
cristy4c08aed2011-07-01 19:47:50 +0000151#define LBR01PacketAlpha(pixelpacket) \
152 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000153 0 : QuantumRange);
154
glennrp91d99252011-06-25 14:30:13 +0000155#define LBR01PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000156 { \
glennrp05001c32011-08-06 13:04:16 +0000157 LBR01PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000158 LBR01PacketGreen((pixelpacket)); \
159 LBR01PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000160 }
glennrp8e58efd2011-05-20 12:16:29 +0000161
glennrp91d99252011-06-25 14:30:13 +0000162#define LBR01PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000163 { \
glennrp91d99252011-06-25 14:30:13 +0000164 LBR01PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000165 LBR01PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000166 }
glennrp8e58efd2011-05-20 12:16:29 +0000167
cristyef618312011-06-25 12:26:44 +0000168#define LBR01PixelRed(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000169 (ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000170 0 : QuantumRange);
171
glennrp54cf7972011-08-06 14:28:09 +0000172#define LBR01PixelGreen(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000173 (ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000174 0 : QuantumRange);
175
glennrp54cf7972011-08-06 14:28:09 +0000176#define LBR01PixelBlue(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000177 (ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000178 0 : QuantumRange);
179
glennrp54cf7972011-08-06 14:28:09 +0000180#define LBR01PixelAlpha(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000181 (ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000182 0 : QuantumRange);
183
glennrp54cf7972011-08-06 14:28:09 +0000184#define LBR01PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000185 { \
cristyef618312011-06-25 12:26:44 +0000186 LBR01PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000187 LBR01PixelGreen((pixel)); \
188 LBR01PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000189 }
glennrp8e58efd2011-05-20 12:16:29 +0000190
glennrp54cf7972011-08-06 14:28:09 +0000191#define LBR01PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000192 { \
glennrp54cf7972011-08-06 14:28:09 +0000193 LBR01PixelRGB((pixel)); \
194 LBR01PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000195 }
glennrp8e58efd2011-05-20 12:16:29 +0000196
197/* LBR02: Replicate top 2 bits */
198
glennrp05001c32011-08-06 13:04:16 +0000199#define LBR02PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000200 { \
201 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
202 (pixelpacket).red=ScaleCharToQuantum( \
203 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
204 }
glennrp91d99252011-06-25 14:30:13 +0000205#define LBR02PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000206 { \
207 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
208 (pixelpacket).green=ScaleCharToQuantum( \
209 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
210 }
glennrp91d99252011-06-25 14:30:13 +0000211#define LBR02PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000212 { \
213 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
214 (pixelpacket).blue=ScaleCharToQuantum( \
215 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
216 }
cristy4c08aed2011-07-01 19:47:50 +0000217#define LBR02PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000218 { \
cristy4c08aed2011-07-01 19:47:50 +0000219 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
220 (pixelpacket).alpha=ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000221 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
222 }
223
glennrp91d99252011-06-25 14:30:13 +0000224#define LBR02PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000225 { \
glennrp05001c32011-08-06 13:04:16 +0000226 LBR02PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000227 LBR02PacketGreen((pixelpacket)); \
228 LBR02PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000229 }
glennrp8e58efd2011-05-20 12:16:29 +0000230
glennrp91d99252011-06-25 14:30:13 +0000231#define LBR02PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000232 { \
glennrp91d99252011-06-25 14:30:13 +0000233 LBR02PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000234 LBR02PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000235 }
glennrp8e58efd2011-05-20 12:16:29 +0000236
cristyef618312011-06-25 12:26:44 +0000237#define LBR02PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000238 { \
cristy4c08aed2011-07-01 19:47:50 +0000239 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000240 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000241 SetPixelRed(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000242 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
243 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000244 }
glennrp54cf7972011-08-06 14:28:09 +0000245#define LBR02PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000246 { \
cristy4c08aed2011-07-01 19:47:50 +0000247 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000248 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000249 SetPixelGreen(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000250 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
251 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000252 }
glennrp54cf7972011-08-06 14:28:09 +0000253#define LBR02PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000254 { \
255 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000256 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
257 SetPixelBlue(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000258 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
259 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000260 }
glennrp54cf7972011-08-06 14:28:09 +0000261#define LBR02PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000262 { \
263 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000264 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
265 SetPixelAlpha(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000266 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
267 (pixel) ); \
glennrp8e58efd2011-05-20 12:16:29 +0000268 }
269
glennrp54cf7972011-08-06 14:28:09 +0000270#define LBR02PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000271 { \
cristyef618312011-06-25 12:26:44 +0000272 LBR02PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000273 LBR02PixelGreen((pixel)); \
274 LBR02PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000275 }
glennrp8e58efd2011-05-20 12:16:29 +0000276
glennrp54cf7972011-08-06 14:28:09 +0000277#define LBR02PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000278 { \
glennrp54cf7972011-08-06 14:28:09 +0000279 LBR02PixelRGB((pixel)); \
280 LBR02PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000281 }
glennrp8e58efd2011-05-20 12:16:29 +0000282
283/* LBR03: Replicate top 3 bits (only used with opaque pixels during
284 PNG8 quantization) */
285
glennrp05001c32011-08-06 13:04:16 +0000286#define LBR03PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000287 { \
288 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
289 (pixelpacket).red=ScaleCharToQuantum( \
290 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
291 }
glennrp91d99252011-06-25 14:30:13 +0000292#define LBR03PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000293 { \
294 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
295 (pixelpacket).green=ScaleCharToQuantum( \
296 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
297 }
glennrp91d99252011-06-25 14:30:13 +0000298#define LBR03PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000299 { \
300 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
301 (pixelpacket).blue=ScaleCharToQuantum( \
302 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
303 }
304
glennrp91d99252011-06-25 14:30:13 +0000305#define LBR03PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000306 { \
glennrp05001c32011-08-06 13:04:16 +0000307 LBR03PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000308 LBR03PacketGreen((pixelpacket)); \
309 LBR03PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000310 }
glennrp8e58efd2011-05-20 12:16:29 +0000311
cristyef618312011-06-25 12:26:44 +0000312#define LBR03PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000313 { \
cristy4c08aed2011-07-01 19:47:50 +0000314 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000315 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000316 SetPixelRed(image, ScaleCharToQuantum( \
317 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000318 }
cristy4c08aed2011-07-01 19:47:50 +0000319#define LBR03Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000320 { \
cristy4c08aed2011-07-01 19:47:50 +0000321 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000322 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000323 SetPixelGreen(image, ScaleCharToQuantum( \
324 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000325 }
cristy4c08aed2011-07-01 19:47:50 +0000326#define LBR03Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000327 { \
cristy4c08aed2011-07-01 19:47:50 +0000328 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000329 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000330 SetPixelBlue(image, ScaleCharToQuantum( \
331 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000332 }
333
cristy4c08aed2011-07-01 19:47:50 +0000334#define LBR03RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000335 { \
cristyef618312011-06-25 12:26:44 +0000336 LBR03PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000337 LBR03Green((pixel)); \
338 LBR03Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000339 }
glennrp8e58efd2011-05-20 12:16:29 +0000340
341/* LBR04: Replicate top 4 bits */
342
glennrp05001c32011-08-06 13:04:16 +0000343#define LBR04PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000344 { \
345 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
346 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
347 }
glennrp91d99252011-06-25 14:30:13 +0000348#define LBR04PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000349 { \
350 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
351 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
352 }
glennrp91d99252011-06-25 14:30:13 +0000353#define LBR04PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000354 { \
355 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
356 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
357 }
cristy4c08aed2011-07-01 19:47:50 +0000358#define LBR04PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000359 { \
cristy4c08aed2011-07-01 19:47:50 +0000360 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
361 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
glennrp8e58efd2011-05-20 12:16:29 +0000362 }
363
glennrp91d99252011-06-25 14:30:13 +0000364#define LBR04PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000365 { \
glennrp05001c32011-08-06 13:04:16 +0000366 LBR04PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000367 LBR04PacketGreen((pixelpacket)); \
368 LBR04PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000369 }
glennrp8e58efd2011-05-20 12:16:29 +0000370
glennrp91d99252011-06-25 14:30:13 +0000371#define LBR04PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000372 { \
glennrp91d99252011-06-25 14:30:13 +0000373 LBR04PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000374 LBR04PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000375 }
glennrp8e58efd2011-05-20 12:16:29 +0000376
cristyef618312011-06-25 12:26:44 +0000377#define LBR04PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000378 { \
cristy4c08aed2011-07-01 19:47:50 +0000379 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000380 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000381 SetPixelRed(image,\
382 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000383 }
glennrp54cf7972011-08-06 14:28:09 +0000384#define LBR04PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000385 { \
cristy4c08aed2011-07-01 19:47:50 +0000386 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000387 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000388 SetPixelGreen(image,\
389 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000390 }
glennrp54cf7972011-08-06 14:28:09 +0000391#define LBR04PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000392 { \
393 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000394 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
395 SetPixelBlue(image,\
396 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000397 }
glennrp54cf7972011-08-06 14:28:09 +0000398#define LBR04PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000399 { \
400 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000401 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
402 SetPixelAlpha(image,\
403 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000404 }
405
glennrp54cf7972011-08-06 14:28:09 +0000406#define LBR04PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000407 { \
cristyef618312011-06-25 12:26:44 +0000408 LBR04PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000409 LBR04PixelGreen((pixel)); \
410 LBR04PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000411 }
glennrp8e58efd2011-05-20 12:16:29 +0000412
glennrp54cf7972011-08-06 14:28:09 +0000413#define LBR04PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000414 { \
glennrp54cf7972011-08-06 14:28:09 +0000415 LBR04PixelRGB((pixel)); \
416 LBR04PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000417 }
glennrp8e58efd2011-05-20 12:16:29 +0000418
419
420/* LBR08: Replicate top 8 bits */
421
glennrp05001c32011-08-06 13:04:16 +0000422#define LBR08PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000423 { \
424 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
425 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
426 }
glennrp91d99252011-06-25 14:30:13 +0000427#define LBR08PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000428 { \
429 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
430 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
431 }
glennrp91d99252011-06-25 14:30:13 +0000432#define LBR08PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000433 { \
434 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
435 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
436 }
cristy4c08aed2011-07-01 19:47:50 +0000437#define LBR08PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000438 { \
cristy4c08aed2011-07-01 19:47:50 +0000439 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
440 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000441 }
442
glennrp91d99252011-06-25 14:30:13 +0000443#define LBR08PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000444 { \
glennrp05001c32011-08-06 13:04:16 +0000445 LBR08PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000446 LBR08PacketGreen((pixelpacket)); \
447 LBR08PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000448 }
glennrp8e58efd2011-05-20 12:16:29 +0000449
glennrp91d99252011-06-25 14:30:13 +0000450#define LBR08PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000451 { \
glennrp91d99252011-06-25 14:30:13 +0000452 LBR08PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000453 LBR08PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000454 }
glennrp8e58efd2011-05-20 12:16:29 +0000455
cristyef618312011-06-25 12:26:44 +0000456#define LBR08PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000457 { \
458 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000459 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
460 SetPixelRed(image,\
461 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000462 }
glennrp54cf7972011-08-06 14:28:09 +0000463#define LBR08PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000464 { \
465 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000466 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
467 SetPixelGreen(image,\
468 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000469 }
glennrp54cf7972011-08-06 14:28:09 +0000470#define LBR08PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000471 { \
472 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000473 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
474 SetPixelBlue(image,\
475 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000476 }
glennrp54cf7972011-08-06 14:28:09 +0000477#define LBR08PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000478 { \
479 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000480 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
481 SetPixelAlpha(image,\
482 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000483 }
484
glennrp54cf7972011-08-06 14:28:09 +0000485#define LBR08PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000486 { \
cristyef618312011-06-25 12:26:44 +0000487 LBR08PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000488 LBR08PixelGreen((pixel)); \
489 LBR08PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000490 }
glennrp8e58efd2011-05-20 12:16:29 +0000491
glennrp54cf7972011-08-06 14:28:09 +0000492#define LBR08PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000493 { \
glennrp54cf7972011-08-06 14:28:09 +0000494 LBR08PixelRGB((pixel)); \
495 LBR08PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000496 }
glennrp8e58efd2011-05-20 12:16:29 +0000497
498
499/* LBR16: Replicate top 16 bits */
500
glennrp05001c32011-08-06 13:04:16 +0000501#define LBR16PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000502 { \
503 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
504 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
505 }
glennrp91d99252011-06-25 14:30:13 +0000506#define LBR16PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000507 { \
508 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
509 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
510 }
glennrp91d99252011-06-25 14:30:13 +0000511#define LBR16PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000512 { \
513 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
514 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
515 }
cristy4c08aed2011-07-01 19:47:50 +0000516#define LBR16PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000517 { \
cristy4c08aed2011-07-01 19:47:50 +0000518 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
519 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000520 }
521
glennrp91d99252011-06-25 14:30:13 +0000522#define LBR16PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000523 { \
glennrp05001c32011-08-06 13:04:16 +0000524 LBR16PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000525 LBR16PacketGreen((pixelpacket)); \
526 LBR16PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000527 }
glennrp8e58efd2011-05-20 12:16:29 +0000528
glennrp91d99252011-06-25 14:30:13 +0000529#define LBR16PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000530 { \
glennrp91d99252011-06-25 14:30:13 +0000531 LBR16PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000532 LBR16PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000533 }
glennrp8e58efd2011-05-20 12:16:29 +0000534
cristyef618312011-06-25 12:26:44 +0000535#define LBR16PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000536 { \
537 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000538 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
539 SetPixelRed(image,\
540 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000541 }
glennrp54cf7972011-08-06 14:28:09 +0000542#define LBR16PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000543 { \
544 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000545 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
546 SetPixelGreen(image,\
547 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000548 }
glennrp54cf7972011-08-06 14:28:09 +0000549#define LBR16PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000550 { \
551 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000552 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
553 SetPixelBlue(image,\
554 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000555 }
glennrp54cf7972011-08-06 14:28:09 +0000556#define LBR16PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000557 { \
558 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000559 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
560 SetPixelAlpha(image,\
561 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000562 }
563
glennrp54cf7972011-08-06 14:28:09 +0000564#define LBR16PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000565 { \
cristyef618312011-06-25 12:26:44 +0000566 LBR16PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000567 LBR16PixelGreen((pixel)); \
568 LBR16PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000569 }
glennrp8e58efd2011-05-20 12:16:29 +0000570
glennrp54cf7972011-08-06 14:28:09 +0000571#define LBR16PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000572 { \
glennrp54cf7972011-08-06 14:28:09 +0000573 LBR16PixelRGB((pixel)); \
574 LBR16PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000575 }
glennrp8e58efd2011-05-20 12:16:29 +0000576
cristy3ed852e2009-09-05 21:47:34 +0000577/*
578 Establish thread safety.
579 setjmp/longjmp is claimed to be safe on these platforms:
580 setjmp/longjmp is alleged to be unsafe on these platforms:
581*/
582#ifndef SETJMP_IS_THREAD_SAFE
583#define PNG_SETJMP_NOT_THREAD_SAFE
584#endif
585
586#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
587static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000588 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000589#endif
590
591/*
592 This temporary until I set up malloc'ed object attributes array.
593 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
594 waste more memory.
595*/
596#define MNG_MAX_OBJECTS 256
597
598/*
599 If this not defined, spec is interpreted strictly. If it is
600 defined, an attempt will be made to recover from some errors,
601 including
602 o global PLTE too short
603*/
604#undef MNG_LOOSE
605
606/*
607 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
608 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
609 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
610 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
611 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
612 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
613 will be enabled by default in libpng-1.2.0.
614*/
cristy3ed852e2009-09-05 21:47:34 +0000615#ifdef PNG_MNG_FEATURES_SUPPORTED
616# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
617# define PNG_READ_EMPTY_PLTE_SUPPORTED
618# endif
619# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
620# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
621# endif
622#endif
623
624/*
cristybb503372010-05-27 20:51:26 +0000625 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000626 This macro is only defined in libpng-1.0.3 and later.
627 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
628*/
629#ifndef PNG_UINT_31_MAX
630#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
631#endif
632
633/*
634 Constant strings for known chunk types. If you need to add a chunk,
635 add a string holding the name here. To make the code more
636 portable, we use ASCII numbers like this, not characters.
637*/
638
glennrp85dcf872011-12-07 02:51:47 +0000639static png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
640static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
641static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
642static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
643static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
644static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
645static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
646static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
647static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
648static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
649static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
650static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
651static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
652static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
653static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
654static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
655static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
656static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
657static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
658static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
659static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
660static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
661static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
662static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
663static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
664static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
665static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
666static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
667static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
668static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
669static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
670static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
671static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
672static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000673
674#if defined(JNG_SUPPORTED)
glennrp85dcf872011-12-07 02:51:47 +0000675static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
676static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
677static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
678static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
679static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
680static png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000681#endif
682
683/*
684Other known chunks that are not yet supported by ImageMagick:
glennrp85dcf872011-12-07 02:51:47 +0000685static png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
686static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
687static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
688static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
689static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
690static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
691static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
692static png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000693*/
694
695typedef struct _MngBox
696{
cristy8182b072010-05-30 20:10:53 +0000697 long
cristy3ed852e2009-09-05 21:47:34 +0000698 left,
699 right,
700 top,
701 bottom;
702} MngBox;
703
704typedef struct _MngPair
705{
cristy8182b072010-05-30 20:10:53 +0000706 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000707 a,
708 b;
709} MngPair;
710
711#ifdef MNG_OBJECT_BUFFERS
712typedef struct _MngBuffer
713{
714
cristybb503372010-05-27 20:51:26 +0000715 size_t
cristy3ed852e2009-09-05 21:47:34 +0000716 height,
717 width;
718
719 Image
720 *image;
721
722 png_color
723 plte[256];
724
725 int
726 reference_count;
727
728 unsigned char
729 alpha_sample_depth,
730 compression_method,
731 color_type,
732 concrete,
733 filter_method,
734 frozen,
735 image_type,
736 interlace_method,
737 pixel_sample_depth,
738 plte_length,
739 sample_depth,
740 viewable;
741} MngBuffer;
742#endif
743
744typedef struct _MngInfo
745{
746
747#ifdef MNG_OBJECT_BUFFERS
748 MngBuffer
749 *ob[MNG_MAX_OBJECTS];
750#endif
751
752 Image *
753 image;
754
755 RectangleInfo
756 page;
757
758 int
759 adjoin,
760#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
761 bytes_in_read_buffer,
762 found_empty_plte,
763#endif
764 equal_backgrounds,
765 equal_chrms,
766 equal_gammas,
767#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
768 defined(PNG_MNG_FEATURES_SUPPORTED)
769 equal_palettes,
770#endif
771 equal_physs,
772 equal_srgbs,
773 framing_mode,
774 have_global_bkgd,
775 have_global_chrm,
776 have_global_gama,
777 have_global_phys,
778 have_global_sbit,
779 have_global_srgb,
780 have_saved_bkgd_index,
781 have_write_global_chrm,
782 have_write_global_gama,
783 have_write_global_plte,
784 have_write_global_srgb,
785 need_fram,
786 object_id,
787 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000788 saved_bkgd_index;
789
790 int
791 new_number_colors;
792
cristybb503372010-05-27 20:51:26 +0000793 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000794 image_found,
795 loop_count[256],
796 loop_iteration[256],
797 scenes_found,
798 x_off[MNG_MAX_OBJECTS],
799 y_off[MNG_MAX_OBJECTS];
800
801 MngBox
802 clip,
803 frame,
804 image_box,
805 object_clip[MNG_MAX_OBJECTS];
806
807 unsigned char
808 /* These flags could be combined into one byte */
809 exists[MNG_MAX_OBJECTS],
810 frozen[MNG_MAX_OBJECTS],
811 loop_active[256],
812 invisible[MNG_MAX_OBJECTS],
813 viewable[MNG_MAX_OBJECTS];
814
815 MagickOffsetType
816 loop_jump[256];
817
818 png_colorp
819 global_plte;
820
821 png_color_8
822 global_sbit;
823
824 png_byte
825#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
826 read_buffer[8],
827#endif
828 global_trns[256];
829
830 float
831 global_gamma;
832
833 ChromaticityInfo
834 global_chrm;
835
836 RenderingIntent
837 global_srgb_intent;
838
cristy35ef8242010-06-03 16:24:13 +0000839 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000840 delay,
841 global_plte_length,
842 global_trns_length,
843 global_x_pixels_per_unit,
844 global_y_pixels_per_unit,
845 mng_width,
846 mng_height,
847 ticks_per_second;
848
glennrpb9cfe272010-12-21 15:08:06 +0000849 MagickBooleanType
850 need_blob;
851
cristy3ed852e2009-09-05 21:47:34 +0000852 unsigned int
853 IsPalette,
854 global_phys_unit_type,
855 basi_warning,
856 clon_warning,
857 dhdr_warning,
858 jhdr_warning,
859 magn_warning,
860 past_warning,
861 phyg_warning,
862 phys_warning,
863 sbit_warning,
864 show_warning,
865 mng_type,
866 write_mng,
867 write_png_colortype,
868 write_png_depth,
glennrp18682582011-06-30 18:11:47 +0000869 write_png_compression_level,
870 write_png_compression_strategy,
871 write_png_compression_filter,
cristy3ed852e2009-09-05 21:47:34 +0000872 write_png8,
873 write_png24,
874 write_png32;
875
876#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000877 size_t
cristy3ed852e2009-09-05 21:47:34 +0000878 basi_width,
879 basi_height;
880
881 unsigned int
882 basi_depth,
883 basi_color_type,
884 basi_compression_method,
885 basi_filter_type,
886 basi_interlace_method,
887 basi_red,
888 basi_green,
889 basi_blue,
890 basi_alpha,
891 basi_viewable;
892#endif
893
894 png_uint_16
895 magn_first,
896 magn_last,
897 magn_mb,
898 magn_ml,
899 magn_mr,
900 magn_mt,
901 magn_mx,
902 magn_my,
903 magn_methx,
904 magn_methy;
905
cristy101ab702011-10-13 13:06:32 +0000906 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000907 mng_global_bkgd;
908
glennrp26f37912010-12-23 16:22:42 +0000909 /* Added at version 6.6.6-7 */
910 MagickBooleanType
911 ping_exclude_bKGD,
912 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000913 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000914 ping_exclude_EXIF,
915 ping_exclude_gAMA,
916 ping_exclude_iCCP,
917 /* ping_exclude_iTXt, */
918 ping_exclude_oFFs,
919 ping_exclude_pHYs,
920 ping_exclude_sRGB,
921 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000922 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000923 ping_exclude_vpAg,
924 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000925 ping_exclude_zTXt,
926 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000927
cristy3ed852e2009-09-05 21:47:34 +0000928} MngInfo;
929#endif /* VER */
930
931/*
932 Forward declarations.
933*/
934static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000935 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000936
cristy3ed852e2009-09-05 21:47:34 +0000937static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000938 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000939
cristy3ed852e2009-09-05 21:47:34 +0000940#if defined(JNG_SUPPORTED)
941static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000942 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000943#endif
944
glennrp0c3e06b2010-11-19 13:45:02 +0000945#if PNG_LIBPNG_VER > 10011
946
glennrpfd05d622011-02-25 04:10:33 +0000947
glennrp0c3e06b2010-11-19 13:45:02 +0000948#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
949static MagickBooleanType
cristyc82a27b2011-10-21 01:07:16 +0000950LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
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 {
cristyc82a27b2011-10-21 01:07:16 +00001012 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0c3e06b2010-11-19 13:45:02 +00001013
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*/
cristyc82a27b2011-10-21 01:07:16 +00001130static MagickBooleanType ImageIsGray(Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001131{
cristy4c08aed2011-07-01 19:47:50 +00001132 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001133 *p;
1134
cristybb503372010-05-27 20:51:26 +00001135 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001136 i,
1137 x,
1138 y;
1139
1140 assert(image != (Image *) NULL);
1141 assert(image->signature == MagickSignature);
1142 if (image->debug != MagickFalse)
1143 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1144
1145 if (image->storage_class == PseudoClass)
1146 {
cristybb503372010-05-27 20:51:26 +00001147 for (i=0; i < (ssize_t) image->colors; i++)
cristy101ab702011-10-13 13:06:32 +00001148 if (IsPixelInfoGray(image->colormap+i) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001149 return(MagickFalse);
1150 return(MagickTrue);
1151 }
cristybb503372010-05-27 20:51:26 +00001152 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001153 {
cristyc82a27b2011-10-21 01:07:16 +00001154 p=GetVirtualPixels(image,0,y,image->columns,1,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
cristyc82a27b2011-10-21 01:07:16 +00001723typedef struct _PNGErrorInfo
cristy3ed852e2009-09-05 21:47:34 +00001724{
1725 Image
1726 *image;
1727
cristyc82a27b2011-10-21 01:07:16 +00001728 ExceptionInfo
1729 *exception;
1730} PNGErrorInfo;
glennrp0fe50b42010-11-16 03:52:51 +00001731
cristyc82a27b2011-10-21 01:07:16 +00001732static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1733{
1734 ExceptionInfo
1735 *exception;
glennrp0fe50b42010-11-16 03:52:51 +00001736
cristyc82a27b2011-10-21 01:07:16 +00001737 Image
1738 *image;
1739
1740 PNGErrorInfo
1741 *error_info;
1742
1743 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1744 image=error_info->image;
1745 exception=error_info->exception;
1746
1747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1748 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1749
1750 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1751 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001752
glennrpe4017e32011-01-08 17:16:09 +00001753#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001754 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1755 * are building with libpng-1.4.x and can be ignored.
1756 */
cristy3ed852e2009-09-05 21:47:34 +00001757 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001758#else
1759 png_longjmp(ping,1);
1760#endif
cristy3ed852e2009-09-05 21:47:34 +00001761}
1762
glennrpcf002022011-01-30 02:38:15 +00001763static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001764{
cristyc82a27b2011-10-21 01:07:16 +00001765 ExceptionInfo
1766 *exception;
1767
cristy3ed852e2009-09-05 21:47:34 +00001768 Image
1769 *image;
1770
cristyc82a27b2011-10-21 01:07:16 +00001771 PNGErrorInfo
1772 *error_info;
1773
cristy3ed852e2009-09-05 21:47:34 +00001774 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1775 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001776
cristyc82a27b2011-10-21 01:07:16 +00001777 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1778 image=error_info->image;
1779 exception=error_info->exception;
1780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1781 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001782
cristyc82a27b2011-10-21 01:07:16 +00001783 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
cristy3ed852e2009-09-05 21:47:34 +00001784 message,"`%s'",image->filename);
1785}
1786
1787#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001788static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001789{
1790#if (PNG_LIBPNG_VER < 10011)
1791 png_voidp
1792 ret;
1793
cristydf0d90e2011-12-12 01:03:55 +00001794 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001795 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001796
cristy3ed852e2009-09-05 21:47:34 +00001797 if (ret == NULL)
1798 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001799
cristy3ed852e2009-09-05 21:47:34 +00001800 return(ret);
1801#else
cristydf0d90e2011-12-12 01:03:55 +00001802 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001803 return((png_voidp) AcquireMagickMemory((size_t) size));
1804#endif
1805}
1806
1807/*
1808 Free a pointer. It is removed from the list at the same time.
1809*/
glennrpcf002022011-01-30 02:38:15 +00001810static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001811{
glennrp3bd393f2011-12-21 18:54:53 +00001812 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001813 ptr=RelinquishMagickMemory(ptr);
1814 return((png_free_ptr) NULL);
1815}
1816#endif
1817
1818#if defined(__cplusplus) || defined(c_plusplus)
1819}
1820#endif
1821
1822static int
glennrpcf002022011-01-30 02:38:15 +00001823Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristyd15e6592011-10-15 00:13:06 +00001824 png_textp text,int ii,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001825{
cristybb503372010-05-27 20:51:26 +00001826 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001827 i;
1828
1829 register unsigned char
1830 *dp;
1831
1832 register png_charp
1833 sp;
1834
1835 png_uint_32
1836 length,
1837 nibbles;
1838
1839 StringInfo
1840 *profile;
1841
glennrp0c3e06b2010-11-19 13:45:02 +00001842 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001843 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1844 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1845 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1846 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1847 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1848 13,14,15};
1849
1850 sp=text[ii].text+1;
1851 /* look for newline */
1852 while (*sp != '\n')
1853 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001854
cristy3ed852e2009-09-05 21:47:34 +00001855 /* look for length */
1856 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1857 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001858
cristyf2f27272009-12-17 14:48:46 +00001859 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001860
glennrp97f90e22011-02-22 05:47:58 +00001861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1862 " length: %lu",(unsigned long) length);
1863
cristy3ed852e2009-09-05 21:47:34 +00001864 while (*sp != ' ' && *sp != '\n')
1865 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001866
cristy3ed852e2009-09-05 21:47:34 +00001867 /* allocate space */
1868 if (length == 0)
1869 {
cristyc82a27b2011-10-21 01:07:16 +00001870 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001871 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1872 return(MagickFalse);
1873 }
glennrp0fe50b42010-11-16 03:52:51 +00001874
cristy8723e4b2011-09-01 13:11:19 +00001875 profile=BlobToStringInfo((const void *) NULL,length);
glennrp0fe50b42010-11-16 03:52:51 +00001876
cristy3ed852e2009-09-05 21:47:34 +00001877 if (profile == (StringInfo *) NULL)
1878 {
cristyc82a27b2011-10-21 01:07:16 +00001879 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001880 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1881 "unable to copy profile");
1882 return(MagickFalse);
1883 }
glennrp0fe50b42010-11-16 03:52:51 +00001884
cristy3ed852e2009-09-05 21:47:34 +00001885 /* copy profile, skipping white space and column 1 "=" signs */
1886 dp=GetStringInfoDatum(profile);
1887 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001888
cristybb503372010-05-27 20:51:26 +00001889 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001890 {
1891 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1892 {
1893 if (*sp == '\0')
1894 {
cristyc82a27b2011-10-21 01:07:16 +00001895 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001896 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1897 profile=DestroyStringInfo(profile);
1898 return(MagickFalse);
1899 }
1900 sp++;
1901 }
glennrp0fe50b42010-11-16 03:52:51 +00001902
cristy3ed852e2009-09-05 21:47:34 +00001903 if (i%2 == 0)
1904 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001905
cristy3ed852e2009-09-05 21:47:34 +00001906 else
1907 (*dp++)+=unhex[(int) *sp++];
1908 }
1909 /*
1910 We have already read "Raw profile type.
1911 */
cristyd15e6592011-10-15 00:13:06 +00001912 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001913 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001914
cristy3ed852e2009-09-05 21:47:34 +00001915 if (image_info->verbose)
1916 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001917
cristy3ed852e2009-09-05 21:47:34 +00001918 return MagickTrue;
1919}
1920
1921#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1922static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1923{
1924 Image
1925 *image;
1926
1927
1928 /* The unknown chunk structure contains the chunk data:
1929 png_byte name[5];
1930 png_byte *data;
1931 png_size_t size;
1932
1933 Note that libpng has already taken care of the CRC handling.
1934 */
1935
1936
1937 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1938 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1939 return(0); /* Did not recognize */
1940
1941 /* recognized vpAg */
1942
1943 if (chunk->size != 9)
1944 return(-1); /* Error return */
1945
1946 if (chunk->data[8] != 0)
1947 return(0); /* ImageMagick requires pixel units */
1948
1949 image=(Image *) png_get_user_chunk_ptr(ping);
1950
cristybb503372010-05-27 20:51:26 +00001951 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001952 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001953
cristybb503372010-05-27 20:51:26 +00001954 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001955 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1956
1957 /* Return one of the following: */
1958 /* return(-n); chunk had an error */
1959 /* return(0); did not recognize */
1960 /* return(n); success */
1961
1962 return(1);
1963
1964}
1965#endif
1966
1967/*
1968%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1969% %
1970% %
1971% %
1972% R e a d O n e P N G I m a g e %
1973% %
1974% %
1975% %
1976%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1977%
1978% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1979% (minus the 8-byte signature) and returns it. It allocates the memory
1980% necessary for the new Image structure and returns a pointer to the new
1981% image.
1982%
1983% The format of the ReadOnePNGImage method is:
1984%
1985% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1986% ExceptionInfo *exception)
1987%
1988% A description of each parameter follows:
1989%
1990% o mng_info: Specifies a pointer to a MngInfo structure.
1991%
1992% o image_info: the image info.
1993%
1994% o exception: return any errors or warnings in this structure.
1995%
1996*/
1997static Image *ReadOnePNGImage(MngInfo *mng_info,
1998 const ImageInfo *image_info, ExceptionInfo *exception)
1999{
2000 /* Read one PNG image */
2001
glennrpcc95c3f2011-04-18 16:46:48 +00002002 /* To do: Read the tIME chunk into the date:modify property */
2003 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2004
cristy3ed852e2009-09-05 21:47:34 +00002005 Image
2006 *image;
2007
2008 int
glennrp4eb39312011-03-30 21:34:55 +00002009 intent,
glennrpcb395ac2011-03-30 19:50:23 +00002010 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00002011 num_text,
glennrp4eb39312011-03-30 21:34:55 +00002012 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00002013 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00002014 pass,
2015 ping_bit_depth,
2016 ping_color_type,
2017 ping_interlace_method,
2018 ping_compression_method,
2019 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00002020 ping_num_trans,
2021 unit_type;
2022
2023 double
2024 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002025
2026 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002027 logging,
cristy3ed852e2009-09-05 21:47:34 +00002028 status;
2029
cristyc82a27b2011-10-21 01:07:16 +00002030 PixelInfo
2031 transparent_color;
2032
2033 PNGErrorInfo
2034 error_info;
2035
glennrpfaa852b2010-03-30 12:17:00 +00002036 png_bytep
2037 ping_trans_alpha;
2038
2039 png_color_16p
2040 ping_background,
2041 ping_trans_color;
2042
cristy3ed852e2009-09-05 21:47:34 +00002043 png_info
2044 *end_info,
2045 *ping_info;
2046
2047 png_struct
2048 *ping;
2049
2050 png_textp
2051 text;
2052
glennrpfaa852b2010-03-30 12:17:00 +00002053 png_uint_32
2054 ping_height,
2055 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002056 x_resolution,
2057 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002058
cristy3ed852e2009-09-05 21:47:34 +00002059 QuantumInfo
2060 *quantum_info;
2061
2062 unsigned char
glennrpcf002022011-01-30 02:38:15 +00002063 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002064
cristybb503372010-05-27 20:51:26 +00002065 ssize_t
cristy756ae432011-11-19 02:18:25 +00002066 ping_rowbytes,
cristy3ed852e2009-09-05 21:47:34 +00002067 y;
2068
2069 register unsigned char
2070 *p;
2071
cristybb503372010-05-27 20:51:26 +00002072 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002073 i,
2074 x;
2075
cristy4c08aed2011-07-01 19:47:50 +00002076 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002077 *q;
2078
2079 size_t
glennrp39992b42010-11-14 00:03:43 +00002080 length,
cristy3ed852e2009-09-05 21:47:34 +00002081 row_offset;
2082
cristyeb3b22a2011-03-31 20:16:11 +00002083 ssize_t
2084 j;
2085
cristy3ed852e2009-09-05 21:47:34 +00002086#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2087 png_byte unused_chunks[]=
2088 {
2089 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2090 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2091 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2092 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2093 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2094 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2095 };
2096#endif
2097
2098 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002099 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002100
2101#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002102 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002103#endif
2104
glennrp25c1e2b2010-03-25 01:39:56 +00002105#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002106 if (image_info->verbose)
2107 printf("Your PNG library (libpng-%s) is rather old.\n",
2108 PNG_LIBPNG_VER_STRING);
2109#endif
2110
glennrp61b4c952009-11-10 20:40:41 +00002111#if (PNG_LIBPNG_VER >= 10400)
2112# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2113 if (image_info->verbose)
2114 {
2115 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2116 PNG_LIBPNG_VER_STRING);
2117 printf("Please update it.\n");
2118 }
2119# endif
2120#endif
2121
2122
cristyed552522009-10-16 14:04:35 +00002123 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002124 image=mng_info->image;
2125
glennrpa6a06632011-01-19 15:15:34 +00002126 if (logging != MagickFalse)
2127 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2128 " image->matte=%d",(int) image->matte);
2129
glennrp0e319732011-01-25 21:53:13 +00002130 /* Set to an out-of-range color unless tRNS chunk is present */
2131 transparent_color.red=65537;
2132 transparent_color.green=65537;
2133 transparent_color.blue=65537;
cristy4c08aed2011-07-01 19:47:50 +00002134 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002135
glennrpcb395ac2011-03-30 19:50:23 +00002136 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002137 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002138 num_raw_profiles = 0;
2139
cristy3ed852e2009-09-05 21:47:34 +00002140 /*
2141 Allocate the PNG structures
2142 */
2143#ifdef PNG_USER_MEM_SUPPORTED
cristyc82a27b2011-10-21 01:07:16 +00002144 error_info.image=image;
2145 error_info.exception=exception;
2146 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002147 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2148 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002149#else
cristyc82a27b2011-10-21 01:07:16 +00002150 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002151 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002152#endif
2153 if (ping == (png_struct *) NULL)
2154 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002155
cristy3ed852e2009-09-05 21:47:34 +00002156 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002157
cristy3ed852e2009-09-05 21:47:34 +00002158 if (ping_info == (png_info *) NULL)
2159 {
2160 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2161 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2162 }
glennrp0fe50b42010-11-16 03:52:51 +00002163
cristy3ed852e2009-09-05 21:47:34 +00002164 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002165
cristy3ed852e2009-09-05 21:47:34 +00002166 if (end_info == (png_info *) NULL)
2167 {
2168 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2169 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2170 }
glennrp0fe50b42010-11-16 03:52:51 +00002171
glennrpcf002022011-01-30 02:38:15 +00002172 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002173
glennrpfaa852b2010-03-30 12:17:00 +00002174 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002175 {
2176 /*
2177 PNG image is corrupt.
2178 */
2179 png_destroy_read_struct(&ping,&ping_info,&end_info);
2180#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002181 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002182#endif
2183 if (logging != MagickFalse)
2184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2185 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002186
cristy3ed852e2009-09-05 21:47:34 +00002187 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002188 {
cristyc82a27b2011-10-21 01:07:16 +00002189 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002190 image->columns=0;
2191 }
glennrp0fe50b42010-11-16 03:52:51 +00002192
cristy3ed852e2009-09-05 21:47:34 +00002193 return(GetFirstImageInList(image));
2194 }
2195 /*
2196 Prepare PNG for reading.
2197 */
glennrpfaa852b2010-03-30 12:17:00 +00002198
cristy3ed852e2009-09-05 21:47:34 +00002199 mng_info->image_found++;
2200 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002201
cristy3ed852e2009-09-05 21:47:34 +00002202 if (LocaleCompare(image_info->magick,"MNG") == 0)
2203 {
2204#if defined(PNG_MNG_FEATURES_SUPPORTED)
2205 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2206 png_set_read_fn(ping,image,png_get_data);
2207#else
2208#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2209 png_permit_empty_plte(ping,MagickTrue);
2210 png_set_read_fn(ping,image,png_get_data);
2211#else
2212 mng_info->image=image;
2213 mng_info->bytes_in_read_buffer=0;
2214 mng_info->found_empty_plte=MagickFalse;
2215 mng_info->have_saved_bkgd_index=MagickFalse;
2216 png_set_read_fn(ping,mng_info,mng_get_data);
2217#endif
2218#endif
2219 }
glennrp0fe50b42010-11-16 03:52:51 +00002220
cristy3ed852e2009-09-05 21:47:34 +00002221 else
2222 png_set_read_fn(ping,image,png_get_data);
2223
2224#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2225 /* Ignore unused chunks and all unknown chunks except for vpAg */
2226 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2227 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2228 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2229 (int)sizeof(unused_chunks)/5);
2230 /* Callback for other unknown chunks */
2231 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2232#endif
2233
glennrp991e92a2010-01-28 03:09:00 +00002234#if (PNG_LIBPNG_VER < 10400)
2235# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2236 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002237 /* Disable thread-unsafe features of pnggccrd */
2238 if (png_access_version_number() >= 10200)
2239 {
2240 png_uint_32 mmx_disable_mask=0;
2241 png_uint_32 asm_flags;
2242
2243 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2244 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2245 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2246 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2247 asm_flags=png_get_asm_flags(ping);
2248 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2249 }
glennrp991e92a2010-01-28 03:09:00 +00002250# endif
cristy3ed852e2009-09-05 21:47:34 +00002251#endif
2252
2253 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002254
2255 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2256 &ping_bit_depth,&ping_color_type,
2257 &ping_interlace_method,&ping_compression_method,
2258 &ping_filter_method);
2259
2260 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2261 &ping_trans_color);
2262
2263 (void) png_get_bKGD(ping, ping_info, &ping_background);
2264
2265 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002266 {
glennrpfaa852b2010-03-30 12:17:00 +00002267 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2268 {
2269 png_set_packing(ping);
2270 ping_bit_depth = 8;
2271 }
cristy3ed852e2009-09-05 21:47:34 +00002272 }
glennrpfaa852b2010-03-30 12:17:00 +00002273
2274 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002275 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002276 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00002277 if (logging != MagickFalse)
2278 {
2279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002280 " PNG width: %.20g, height: %.20g",
2281 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002282
cristy3ed852e2009-09-05 21:47:34 +00002283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2284 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002285 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002286
cristy3ed852e2009-09-05 21:47:34 +00002287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2288 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002289 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002290
cristy3ed852e2009-09-05 21:47:34 +00002291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2292 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002293 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002294 }
2295
glennrpfaa852b2010-03-30 12:17:00 +00002296#ifdef PNG_READ_iCCP_SUPPORTED
2297 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002298 {
2299 int
2300 compression;
2301
glennrpe4017e32011-01-08 17:16:09 +00002302#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002303 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002304 info;
2305#else
2306 png_bytep
2307 info;
2308#endif
2309
2310 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002311 name;
2312
2313 png_uint_32
2314 profile_length;
2315
2316 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2317 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002318
cristy3ed852e2009-09-05 21:47:34 +00002319 if (profile_length != 0)
2320 {
2321 StringInfo
2322 *profile;
2323
2324 if (logging != MagickFalse)
2325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2326 " Reading PNG iCCP chunk.");
cristye8f8f382011-09-01 13:32:37 +00002327 profile=BlobToStringInfo(info,profile_length);
2328 if (profile == (StringInfo *) NULL)
2329 {
cristyc82a27b2011-10-21 01:07:16 +00002330 (void) ThrowMagickException(exception,GetMagickModule(),
cristye8f8f382011-09-01 13:32:37 +00002331 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2332 "unable to copy profile");
cristyad5b0852011-09-08 02:01:21 +00002333 return((Image *) NULL);
cristye8f8f382011-09-01 13:32:37 +00002334 }
cristy3ed852e2009-09-05 21:47:34 +00002335 SetStringInfoDatum(profile,(const unsigned char *) info);
cristyd15e6592011-10-15 00:13:06 +00002336 (void) SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00002337 profile=DestroyStringInfo(profile);
2338 }
2339 }
2340#endif
2341#if defined(PNG_READ_sRGB_SUPPORTED)
2342 {
cristy3ed852e2009-09-05 21:47:34 +00002343 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00002344 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2345 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00002346
cristy3ed852e2009-09-05 21:47:34 +00002347 if (png_get_sRGB(ping,ping_info,&intent))
2348 {
glennrpcf002022011-01-30 02:38:15 +00002349 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2350 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002351
cristy3ed852e2009-09-05 21:47:34 +00002352 if (logging != MagickFalse)
2353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002354 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002355 }
2356 }
2357#endif
2358 {
glennrpfaa852b2010-03-30 12:17:00 +00002359 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2360 if (mng_info->have_global_gama)
2361 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002362
cristy3ed852e2009-09-05 21:47:34 +00002363 if (png_get_gAMA(ping,ping_info,&file_gamma))
2364 {
2365 image->gamma=(float) file_gamma;
2366 if (logging != MagickFalse)
2367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2368 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2369 }
2370 }
glennrpfaa852b2010-03-30 12:17:00 +00002371 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2372 {
2373 if (mng_info->have_global_chrm != MagickFalse)
2374 {
2375 (void) png_set_cHRM(ping,ping_info,
2376 mng_info->global_chrm.white_point.x,
2377 mng_info->global_chrm.white_point.y,
2378 mng_info->global_chrm.red_primary.x,
2379 mng_info->global_chrm.red_primary.y,
2380 mng_info->global_chrm.green_primary.x,
2381 mng_info->global_chrm.green_primary.y,
2382 mng_info->global_chrm.blue_primary.x,
2383 mng_info->global_chrm.blue_primary.y);
2384 }
2385 }
glennrp0fe50b42010-11-16 03:52:51 +00002386
glennrpfaa852b2010-03-30 12:17:00 +00002387 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002388 {
2389 (void) png_get_cHRM(ping,ping_info,
2390 &image->chromaticity.white_point.x,
2391 &image->chromaticity.white_point.y,
2392 &image->chromaticity.red_primary.x,
2393 &image->chromaticity.red_primary.y,
2394 &image->chromaticity.green_primary.x,
2395 &image->chromaticity.green_primary.y,
2396 &image->chromaticity.blue_primary.x,
2397 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002398
cristy3ed852e2009-09-05 21:47:34 +00002399 if (logging != MagickFalse)
2400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2401 " Reading PNG cHRM chunk.");
2402 }
glennrp0fe50b42010-11-16 03:52:51 +00002403
glennrpe610a072010-08-05 17:08:46 +00002404 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002405 {
glennrpe610a072010-08-05 17:08:46 +00002406 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002407 Magick_RenderingIntent_to_PNG_RenderingIntent
2408 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00002409 png_set_gAMA(ping,ping_info,0.45455f);
2410 png_set_cHRM(ping,ping_info,
2411 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2412 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002413 }
cristy3ed852e2009-09-05 21:47:34 +00002414#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002415 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002416 {
cristy905ef802011-02-23 00:29:18 +00002417 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2418 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002419
cristy3ed852e2009-09-05 21:47:34 +00002420 if (logging != MagickFalse)
2421 if (image->page.x || image->page.y)
2422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002423 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2424 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002425 }
2426#endif
2427#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002428 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2429 {
2430 if (mng_info->have_global_phys)
2431 {
2432 png_set_pHYs(ping,ping_info,
2433 mng_info->global_x_pixels_per_unit,
2434 mng_info->global_y_pixels_per_unit,
2435 mng_info->global_phys_unit_type);
2436 }
2437 }
2438
2439 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002440 {
cristy3ed852e2009-09-05 21:47:34 +00002441 /*
2442 Set image resolution.
2443 */
2444 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002445 &unit_type);
cristy2a11bef2011-10-28 18:33:11 +00002446 image->resolution.x=(double) x_resolution;
2447 image->resolution.y=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002448
cristy3ed852e2009-09-05 21:47:34 +00002449 if (unit_type == PNG_RESOLUTION_METER)
2450 {
2451 image->units=PixelsPerCentimeterResolution;
cristy2a11bef2011-10-28 18:33:11 +00002452 image->resolution.x=(double) x_resolution/100.0;
2453 image->resolution.y=(double) y_resolution/100.0;
cristy3ed852e2009-09-05 21:47:34 +00002454 }
glennrp0fe50b42010-11-16 03:52:51 +00002455
cristy3ed852e2009-09-05 21:47:34 +00002456 if (logging != MagickFalse)
2457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002458 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2459 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002460 }
cristy3ed852e2009-09-05 21:47:34 +00002461#endif
glennrp823b55c2011-03-14 18:46:46 +00002462
glennrpfaa852b2010-03-30 12:17:00 +00002463 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002464 {
2465 int
2466 number_colors;
2467
2468 png_colorp
2469 palette;
2470
2471 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002472
cristy3ed852e2009-09-05 21:47:34 +00002473 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002474 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002475 {
2476 if (mng_info->global_plte_length)
2477 {
2478 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2479 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002480
glennrpfaa852b2010-03-30 12:17:00 +00002481 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002482 if (mng_info->global_trns_length)
2483 {
2484 if (mng_info->global_trns_length >
2485 mng_info->global_plte_length)
cristyc82a27b2011-10-21 01:07:16 +00002486 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00002487 GetMagickModule(),CoderError,
2488 "global tRNS has more entries than global PLTE",
2489 "`%s'",image_info->filename);
2490 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2491 (int) mng_info->global_trns_length,NULL);
2492 }
glennrpbfd9e612011-04-22 14:02:20 +00002493#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002494 if (
2495#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2496 mng_info->have_saved_bkgd_index ||
2497#endif
glennrpfaa852b2010-03-30 12:17:00 +00002498 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002499 {
2500 png_color_16
2501 background;
2502
2503#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2504 if (mng_info->have_saved_bkgd_index)
2505 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002506#endif
glennrpfaa852b2010-03-30 12:17:00 +00002507 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2508 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002509
cristy3ed852e2009-09-05 21:47:34 +00002510 background.red=(png_uint_16)
2511 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002512
cristy3ed852e2009-09-05 21:47:34 +00002513 background.green=(png_uint_16)
2514 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002515
cristy3ed852e2009-09-05 21:47:34 +00002516 background.blue=(png_uint_16)
2517 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002518
glennrpc6c391a2011-04-27 02:23:56 +00002519 background.gray=(png_uint_16)
2520 mng_info->global_plte[background.index].green;
2521
cristy3ed852e2009-09-05 21:47:34 +00002522 png_set_bKGD(ping,ping_info,&background);
2523 }
2524#endif
2525 }
2526 else
cristyc82a27b2011-10-21 01:07:16 +00002527 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00002528 CoderError,"No global PLTE in file","`%s'",
2529 image_info->filename);
2530 }
2531 }
2532
glennrpbfd9e612011-04-22 14:02:20 +00002533#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002534 if (mng_info->have_global_bkgd &&
2535 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002536 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002537
glennrpfaa852b2010-03-30 12:17:00 +00002538 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002539 {
glennrpbfd9e612011-04-22 14:02:20 +00002540 unsigned int
2541 bkgd_scale;
2542
cristy3ed852e2009-09-05 21:47:34 +00002543 /*
2544 Set image background color.
2545 */
2546 if (logging != MagickFalse)
2547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2548 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002549
glennrpbfd9e612011-04-22 14:02:20 +00002550 /* Scale background components to 16-bit, then scale
2551 * to quantum depth
2552 */
2553 if (logging != MagickFalse)
2554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2555 " raw ping_background=(%d,%d,%d).",ping_background->red,
2556 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002557
glennrpbfd9e612011-04-22 14:02:20 +00002558 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002559
glennrpbfd9e612011-04-22 14:02:20 +00002560 if (ping_bit_depth == 1)
2561 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002562
glennrpbfd9e612011-04-22 14:02:20 +00002563 else if (ping_bit_depth == 2)
2564 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002565
glennrpbfd9e612011-04-22 14:02:20 +00002566 else if (ping_bit_depth == 4)
2567 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002568
glennrpbfd9e612011-04-22 14:02:20 +00002569 if (ping_bit_depth <= 8)
2570 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002571
glennrpbfd9e612011-04-22 14:02:20 +00002572 ping_background->red *= bkgd_scale;
2573 ping_background->green *= bkgd_scale;
2574 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002575
glennrpbfd9e612011-04-22 14:02:20 +00002576 if (logging != MagickFalse)
2577 {
glennrp2cbb4482010-06-02 04:37:24 +00002578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2579 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002580
glennrp2cbb4482010-06-02 04:37:24 +00002581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2582 " ping_background=(%d,%d,%d).",ping_background->red,
2583 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002584 }
glennrp2cbb4482010-06-02 04:37:24 +00002585
glennrpbfd9e612011-04-22 14:02:20 +00002586 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002587 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002588
glennrpbfd9e612011-04-22 14:02:20 +00002589 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002590 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002591
glennrpbfd9e612011-04-22 14:02:20 +00002592 image->background_color.blue=
2593 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002594
cristy4c08aed2011-07-01 19:47:50 +00002595 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002596
glennrpbfd9e612011-04-22 14:02:20 +00002597 if (logging != MagickFalse)
2598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2599 " image->background_color=(%.20g,%.20g,%.20g).",
2600 (double) image->background_color.red,
2601 (double) image->background_color.green,
2602 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002603 }
glennrpbfd9e612011-04-22 14:02:20 +00002604#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002605
glennrpfaa852b2010-03-30 12:17:00 +00002606 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002607 {
2608 /*
glennrpa6a06632011-01-19 15:15:34 +00002609 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002610 */
2611 int
2612 max_sample;
2613
cristy35ef8242010-06-03 16:24:13 +00002614 size_t
2615 one=1;
2616
cristy3ed852e2009-09-05 21:47:34 +00002617 if (logging != MagickFalse)
2618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2619 " Reading PNG tRNS chunk.");
2620
cristyf9cca6a2010-06-04 23:49:28 +00002621 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002622
glennrpfaa852b2010-03-30 12:17:00 +00002623 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2624 (int)ping_trans_color->gray > max_sample) ||
2625 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2626 ((int)ping_trans_color->red > max_sample ||
2627 (int)ping_trans_color->green > max_sample ||
2628 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002629 {
2630 if (logging != MagickFalse)
2631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2632 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002633 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002634 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002635 image->matte=MagickFalse;
2636 }
2637 else
2638 {
glennrpa6a06632011-01-19 15:15:34 +00002639 int
2640 scale_to_short;
2641
2642 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2643
2644 /* Scale transparent_color to short */
2645 transparent_color.red= scale_to_short*ping_trans_color->red;
2646 transparent_color.green= scale_to_short*ping_trans_color->green;
2647 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy4c08aed2011-07-01 19:47:50 +00002648 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002649
glennrpfaa852b2010-03-30 12:17:00 +00002650 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002651 {
glennrp0f111982010-07-07 20:18:33 +00002652 if (logging != MagickFalse)
2653 {
2654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2655 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002656
glennrp0f111982010-07-07 20:18:33 +00002657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy101ab702011-10-13 13:06:32 +00002658 " scaled graylevel is %.20g.",transparent_color.alpha);
glennrp0f111982010-07-07 20:18:33 +00002659 }
cristy4c08aed2011-07-01 19:47:50 +00002660 transparent_color.red=transparent_color.alpha;
2661 transparent_color.green=transparent_color.alpha;
2662 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002663 }
2664 }
2665 }
2666#if defined(PNG_READ_sBIT_SUPPORTED)
2667 if (mng_info->have_global_sbit)
2668 {
glennrpfaa852b2010-03-30 12:17:00 +00002669 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002670 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2671 }
2672#endif
2673 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002674
cristy3ed852e2009-09-05 21:47:34 +00002675 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002676
2677 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2678
cristy3ed852e2009-09-05 21:47:34 +00002679 /*
2680 Initialize image structure.
2681 */
2682 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002683 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002684 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002685 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002686 if (mng_info->mng_type == 0)
2687 {
glennrpfaa852b2010-03-30 12:17:00 +00002688 mng_info->mng_width=ping_width;
2689 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002690 mng_info->frame=mng_info->image_box;
2691 mng_info->clip=mng_info->image_box;
2692 }
glennrp0fe50b42010-11-16 03:52:51 +00002693
cristy3ed852e2009-09-05 21:47:34 +00002694 else
2695 {
2696 image->page.y=mng_info->y_off[mng_info->object_id];
2697 }
glennrp0fe50b42010-11-16 03:52:51 +00002698
cristy3ed852e2009-09-05 21:47:34 +00002699 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002700 image->columns=ping_width;
2701 image->rows=ping_height;
cristy897f2bf2012-01-05 02:03:44 +00002702 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2703 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2704 image->colorspace=GRAYColorspace;
glennrpfaa852b2010-03-30 12:17:00 +00002705 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002706 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002707 {
cristybefe4d22010-06-07 01:18:58 +00002708 size_t
2709 one;
2710
cristy3ed852e2009-09-05 21:47:34 +00002711 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002712 one=1;
2713 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002714#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2715 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002716 image->colors=256;
2717#else
2718 if (image->colors > 65536L)
2719 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002720#endif
glennrpfaa852b2010-03-30 12:17:00 +00002721 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002722 {
2723 int
2724 number_colors;
2725
2726 png_colorp
2727 palette;
2728
2729 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002730 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002731
cristy3ed852e2009-09-05 21:47:34 +00002732 if (logging != MagickFalse)
2733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2734 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2735 }
2736 }
2737
2738 if (image->storage_class == PseudoClass)
2739 {
2740 /*
2741 Initialize image colormap.
2742 */
cristy018f07f2011-09-04 21:15:19 +00002743 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002744 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002745
glennrpfaa852b2010-03-30 12:17:00 +00002746 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002747 {
2748 int
2749 number_colors;
2750
2751 png_colorp
2752 palette;
2753
2754 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002755
glennrp6af6cf12011-04-22 13:05:16 +00002756 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002757 {
2758 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2759 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2760 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2761 }
glennrp6af6cf12011-04-22 13:05:16 +00002762
glennrp67b9c1a2011-04-22 18:47:36 +00002763 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002764 {
2765 image->colormap[i].red=0;
2766 image->colormap[i].green=0;
2767 image->colormap[i].blue=0;
2768 }
cristy3ed852e2009-09-05 21:47:34 +00002769 }
glennrp0fe50b42010-11-16 03:52:51 +00002770
cristy3ed852e2009-09-05 21:47:34 +00002771 else
2772 {
cristybb503372010-05-27 20:51:26 +00002773 size_t
cristy3ed852e2009-09-05 21:47:34 +00002774 scale;
2775
glennrpfaa852b2010-03-30 12:17:00 +00002776 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002777
cristy3ed852e2009-09-05 21:47:34 +00002778 if (scale < 1)
2779 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002780
cristybb503372010-05-27 20:51:26 +00002781 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002782 {
2783 image->colormap[i].red=(Quantum) (i*scale);
2784 image->colormap[i].green=(Quantum) (i*scale);
2785 image->colormap[i].blue=(Quantum) (i*scale);
2786 }
2787 }
2788 }
glennrp147bc912011-03-30 18:47:21 +00002789
glennrpcb395ac2011-03-30 19:50:23 +00002790 /* Set some properties for reporting by "identify" */
2791 {
glennrp147bc912011-03-30 18:47:21 +00002792 char
2793 msg[MaxTextExtent];
2794
2795 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2796 ping_interlace_method in value */
2797
cristy3b6fd2e2011-05-20 12:53:50 +00002798 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002799 "%d, %d",(int) ping_width, (int) ping_height);
cristy5d6fc9c2011-12-27 03:10:42 +00002800 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002801
cristy3b6fd2e2011-05-20 12:53:50 +00002802 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
cristy5d6fc9c2011-12-27 03:10:42 +00002803 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002804
cristy3b6fd2e2011-05-20 12:53:50 +00002805 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
cristy5d6fc9c2011-12-27 03:10:42 +00002806 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002807
cristy3b6fd2e2011-05-20 12:53:50 +00002808 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002809 (int) ping_interlace_method);
cristy5d6fc9c2011-12-27 03:10:42 +00002810 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
glennrpcb395ac2011-03-30 19:50:23 +00002811 }
glennrp147bc912011-03-30 18:47:21 +00002812
cristy3ed852e2009-09-05 21:47:34 +00002813 /*
2814 Read image scanlines.
2815 */
2816 if (image->delay != 0)
2817 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002818
glennrp0ca69b12010-07-26 01:57:52 +00002819 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002820 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2821 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002822 {
2823 if (logging != MagickFalse)
2824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002825 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002826 mng_info->scenes_found-1);
2827 png_destroy_read_struct(&ping,&ping_info,&end_info);
2828#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002829 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002830#endif
2831 if (logging != MagickFalse)
2832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2833 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002834
cristy3ed852e2009-09-05 21:47:34 +00002835 return(image);
2836 }
glennrp0fe50b42010-11-16 03:52:51 +00002837
cristy3ed852e2009-09-05 21:47:34 +00002838 if (logging != MagickFalse)
2839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2840 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002841
cristy3ed852e2009-09-05 21:47:34 +00002842 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002843 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2844 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002845
cristy3ed852e2009-09-05 21:47:34 +00002846 else
glennrpcf002022011-01-30 02:38:15 +00002847 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2848 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002849
glennrpcf002022011-01-30 02:38:15 +00002850 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002851 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002852
cristy3ed852e2009-09-05 21:47:34 +00002853 if (logging != MagickFalse)
2854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2855 " Converting PNG pixels to pixel packets");
2856 /*
2857 Convert PNG pixels to pixel packets.
2858 */
glennrpfaa852b2010-03-30 12:17:00 +00002859 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002860 {
2861 /*
2862 PNG image is corrupt.
2863 */
2864 png_destroy_read_struct(&ping,&ping_info,&end_info);
2865#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002866 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002867#endif
2868 if (quantum_info != (QuantumInfo *) NULL)
2869 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002870
glennrpcf002022011-01-30 02:38:15 +00002871 if (ping_pixels != (unsigned char *) NULL)
2872 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002873
cristy3ed852e2009-09-05 21:47:34 +00002874 if (logging != MagickFalse)
2875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2876 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002877
cristy3ed852e2009-09-05 21:47:34 +00002878 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002879 {
cristyc82a27b2011-10-21 01:07:16 +00002880 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002881 image->columns=0;
2882 }
glennrp0fe50b42010-11-16 03:52:51 +00002883
cristy3ed852e2009-09-05 21:47:34 +00002884 return(GetFirstImageInList(image));
2885 }
glennrp0fe50b42010-11-16 03:52:51 +00002886
cristyed552522009-10-16 14:04:35 +00002887 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002888
cristyed552522009-10-16 14:04:35 +00002889 if (quantum_info == (QuantumInfo *) NULL)
2890 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002891
glennrpc8cbc5d2011-01-01 00:12:34 +00002892 {
2893
2894 MagickBooleanType
2895 found_transparent_pixel;
2896
2897 found_transparent_pixel=MagickFalse;
2898
cristy3ed852e2009-09-05 21:47:34 +00002899 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002900 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002901 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002902 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002903 /*
2904 Convert image to DirectClass pixel packets.
2905 */
glennrp67b9c1a2011-04-22 18:47:36 +00002906#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2907 int
2908 depth;
2909
2910 depth=(ssize_t) ping_bit_depth;
2911#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002912 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2913 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2914 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2915 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002916
glennrpc8cbc5d2011-01-01 00:12:34 +00002917 for (y=0; y < (ssize_t) image->rows; y++)
2918 {
2919 if (num_passes > 1)
2920 row_offset=ping_rowbytes*y;
2921
2922 else
2923 row_offset=0;
2924
glennrpcf002022011-01-30 02:38:15 +00002925 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002926 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2927
cristyacd2ed22011-08-30 01:44:23 +00002928 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00002929 break;
2930
glennrpc8cbc5d2011-01-01 00:12:34 +00002931 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2932 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002933 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002934
2935 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2936 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002937 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002938
2939 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2940 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002941 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002942
2943 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2944 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002945 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002946
2947 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2948 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002949 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002950
glennrpc8cbc5d2011-01-01 00:12:34 +00002951 if (found_transparent_pixel == MagickFalse)
2952 {
2953 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002954 if (y== 0 && logging != MagickFalse)
2955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2956 " Looking for cheap transparent pixel");
2957
glennrpc8cbc5d2011-01-01 00:12:34 +00002958 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2959 {
glennrp5aa37f62011-01-02 03:07:57 +00002960 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2961 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy4c08aed2011-07-01 19:47:50 +00002962 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00002963 {
glennrpa6a06632011-01-19 15:15:34 +00002964 if (logging != MagickFalse)
2965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2966 " ...got one.");
2967
glennrpc8cbc5d2011-01-01 00:12:34 +00002968 found_transparent_pixel = MagickTrue;
2969 break;
2970 }
glennrp4f25bd02011-01-01 18:51:28 +00002971 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2972 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp847370c2011-07-05 17:37:15 +00002973 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2974 transparent_color.red &&
2975 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2976 transparent_color.green &&
2977 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2978 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002979 {
glennrpa6a06632011-01-19 15:15:34 +00002980 if (logging != MagickFalse)
2981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2982 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002983 found_transparent_pixel = MagickTrue;
2984 break;
2985 }
cristyed231572011-07-14 02:18:59 +00002986 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00002987 }
2988 }
2989
2990 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2991 {
2992 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2993 image->rows);
2994
2995 if (status == MagickFalse)
2996 break;
2997 }
2998 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2999 break;
3000 }
3001
3002 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3003 {
3004 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00003005 if (status == MagickFalse)
3006 break;
3007 }
cristy3ed852e2009-09-05 21:47:34 +00003008 }
cristy3ed852e2009-09-05 21:47:34 +00003009 }
glennrp0fe50b42010-11-16 03:52:51 +00003010
cristy3ed852e2009-09-05 21:47:34 +00003011 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00003012
cristy3ed852e2009-09-05 21:47:34 +00003013 for (pass=0; pass < num_passes; pass++)
3014 {
3015 Quantum
3016 *quantum_scanline;
3017
3018 register Quantum
3019 *r;
3020
3021 /*
3022 Convert grayscale image to PseudoClass pixel packets.
3023 */
glennrpc17d96f2011-06-27 01:20:11 +00003024 if (logging != MagickFalse)
3025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3026 " Converting grayscale pixels to pixel packets");
cristy4c08aed2011-07-01 19:47:50 +00003027
glennrpfaa852b2010-03-30 12:17:00 +00003028 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00003029 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00003030
cristy3ed852e2009-09-05 21:47:34 +00003031 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3032 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003033
cristy3ed852e2009-09-05 21:47:34 +00003034 if (quantum_scanline == (Quantum *) NULL)
3035 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003036
cristybb503372010-05-27 20:51:26 +00003037 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003038 {
3039 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003040 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003041
cristy3ed852e2009-09-05 21:47:34 +00003042 else
3043 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003044
glennrpcf002022011-01-30 02:38:15 +00003045 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00003046 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003047
cristyacd2ed22011-08-30 01:44:23 +00003048 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003049 break;
glennrp0fe50b42010-11-16 03:52:51 +00003050
glennrpcf002022011-01-30 02:38:15 +00003051 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003052 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003053
glennrpfaa852b2010-03-30 12:17:00 +00003054 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003055 {
3056 case 1:
3057 {
cristybb503372010-05-27 20:51:26 +00003058 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003059 bit;
3060
cristybb503372010-05-27 20:51:26 +00003061 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003062 {
3063 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003064 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003065 p++;
3066 }
glennrp0fe50b42010-11-16 03:52:51 +00003067
cristy3ed852e2009-09-05 21:47:34 +00003068 if ((image->columns % 8) != 0)
3069 {
cristybb503372010-05-27 20:51:26 +00003070 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003071 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003072 }
glennrp0fe50b42010-11-16 03:52:51 +00003073
cristy3ed852e2009-09-05 21:47:34 +00003074 break;
3075 }
glennrp47b9dd52010-11-24 18:12:06 +00003076
cristy3ed852e2009-09-05 21:47:34 +00003077 case 2:
3078 {
cristybb503372010-05-27 20:51:26 +00003079 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003080 {
glennrpa18d5bc2011-04-23 14:51:34 +00003081 *r++=(*p >> 6) & 0x03;
3082 *r++=(*p >> 4) & 0x03;
3083 *r++=(*p >> 2) & 0x03;
3084 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003085 }
glennrp0fe50b42010-11-16 03:52:51 +00003086
cristy3ed852e2009-09-05 21:47:34 +00003087 if ((image->columns % 4) != 0)
3088 {
cristybb503372010-05-27 20:51:26 +00003089 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003090 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003091 }
glennrp0fe50b42010-11-16 03:52:51 +00003092
cristy3ed852e2009-09-05 21:47:34 +00003093 break;
3094 }
glennrp47b9dd52010-11-24 18:12:06 +00003095
cristy3ed852e2009-09-05 21:47:34 +00003096 case 4:
3097 {
cristybb503372010-05-27 20:51:26 +00003098 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003099 {
glennrpa18d5bc2011-04-23 14:51:34 +00003100 *r++=(*p >> 4) & 0x0f;
3101 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003102 }
glennrp0fe50b42010-11-16 03:52:51 +00003103
cristy3ed852e2009-09-05 21:47:34 +00003104 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003105 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003106
cristy3ed852e2009-09-05 21:47:34 +00003107 break;
3108 }
glennrp47b9dd52010-11-24 18:12:06 +00003109
cristy3ed852e2009-09-05 21:47:34 +00003110 case 8:
3111 {
glennrpfaa852b2010-03-30 12:17:00 +00003112 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003113 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003114 {
glennrpa18d5bc2011-04-23 14:51:34 +00003115 *r++=*p++;
cristy4c08aed2011-07-01 19:47:50 +00003116 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3117 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003118 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003119 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003120 }
glennrp0fe50b42010-11-16 03:52:51 +00003121
cristy3ed852e2009-09-05 21:47:34 +00003122 else
cristybb503372010-05-27 20:51:26 +00003123 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003124 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003125
cristy3ed852e2009-09-05 21:47:34 +00003126 break;
3127 }
glennrp47b9dd52010-11-24 18:12:06 +00003128
cristy3ed852e2009-09-05 21:47:34 +00003129 case 16:
3130 {
cristybb503372010-05-27 20:51:26 +00003131 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003132 {
glennrpc17d96f2011-06-27 01:20:11 +00003133#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003134 size_t
3135 quantum;
3136
3137 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003138 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003139
3140 else
glennrpc17d96f2011-06-27 01:20:11 +00003141 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003142
glennrp58f77c72011-04-23 14:09:09 +00003143 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003144 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003145 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003146
3147 if (ping_color_type == 4)
3148 {
glennrpc17d96f2011-06-27 01:20:11 +00003149 if (image->colors > 256)
3150 quantum=((*p++) << 8);
3151 else
3152 quantum=0;
3153
glennrp58f77c72011-04-23 14:09:09 +00003154 quantum|=(*p++);
cristy4c08aed2011-07-01 19:47:50 +00003155 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3156 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003157 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003158 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003159 }
glennrp58f77c72011-04-23 14:09:09 +00003160
3161#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3162 *r++=(*p++);
3163 p++; /* strip low byte */
3164
3165 if (ping_color_type == 4)
3166 {
cristy4c08aed2011-07-01 19:47:50 +00003167 SetPixelAlpha(image,*p++,q);
3168 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003169 found_transparent_pixel = MagickTrue;
3170 p++;
cristyed231572011-07-14 02:18:59 +00003171 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003172 }
cristy3ed852e2009-09-05 21:47:34 +00003173#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003174 }
glennrp47b9dd52010-11-24 18:12:06 +00003175
cristy3ed852e2009-09-05 21:47:34 +00003176 break;
3177 }
glennrp47b9dd52010-11-24 18:12:06 +00003178
cristy3ed852e2009-09-05 21:47:34 +00003179 default:
3180 break;
3181 }
glennrp3faa9a32011-04-23 14:00:25 +00003182
cristy3ed852e2009-09-05 21:47:34 +00003183 /*
3184 Transfer image scanline.
3185 */
3186 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003187
cristy4c08aed2011-07-01 19:47:50 +00003188 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3189
cristyacd2ed22011-08-30 01:44:23 +00003190 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00003191 break;
cristybb503372010-05-27 20:51:26 +00003192 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00003193 {
3194 SetPixelIndex(image,*r++,q);
cristyed231572011-07-14 02:18:59 +00003195 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00003196 }
glennrp0fe50b42010-11-16 03:52:51 +00003197
cristy3ed852e2009-09-05 21:47:34 +00003198 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3199 break;
glennrp0fe50b42010-11-16 03:52:51 +00003200
cristy7a287bf2010-02-14 02:18:09 +00003201 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3202 {
cristycee97112010-05-28 00:44:52 +00003203 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003204 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003205
cristy7a287bf2010-02-14 02:18:09 +00003206 if (status == MagickFalse)
3207 break;
3208 }
cristy3ed852e2009-09-05 21:47:34 +00003209 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003210
cristy7a287bf2010-02-14 02:18:09 +00003211 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003212 {
3213 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003214
cristy3ed852e2009-09-05 21:47:34 +00003215 if (status == MagickFalse)
3216 break;
3217 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003218
cristy3ed852e2009-09-05 21:47:34 +00003219 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3220 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003221
3222 image->matte=found_transparent_pixel;
3223
3224 if (logging != MagickFalse)
3225 {
3226 if (found_transparent_pixel != MagickFalse)
3227 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3228 " Found transparent pixel");
3229 else
glennrp5aa37f62011-01-02 03:07:57 +00003230 {
3231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3232 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003233
glennrp5aa37f62011-01-02 03:07:57 +00003234 ping_color_type&=0x03;
3235 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003236 }
3237 }
3238
cristyb32b90a2009-09-07 21:45:48 +00003239 if (quantum_info != (QuantumInfo *) NULL)
3240 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00003241
cristy5c6f7892010-05-05 22:53:29 +00003242 if (image->storage_class == PseudoClass)
3243 {
cristyaeb2cbc2010-05-07 13:28:58 +00003244 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003245 matte;
3246
3247 matte=image->matte;
3248 image->matte=MagickFalse;
cristyea1a8aa2011-10-20 13:24:06 +00003249 (void) SyncImage(image,exception);
cristyaeb2cbc2010-05-07 13:28:58 +00003250 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003251 }
glennrp47b9dd52010-11-24 18:12:06 +00003252
glennrp4eb39312011-03-30 21:34:55 +00003253 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003254
3255 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003256 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003257 {
3258 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003259 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003260 image->colors=2;
cristyea1a8aa2011-10-20 13:24:06 +00003261 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003262#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003263 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003264#endif
3265 if (logging != MagickFalse)
3266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3267 " exit ReadOnePNGImage() early.");
3268 return(image);
3269 }
glennrp47b9dd52010-11-24 18:12:06 +00003270
glennrpfaa852b2010-03-30 12:17:00 +00003271 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003272 {
3273 ClassType
3274 storage_class;
3275
3276 /*
3277 Image has a transparent background.
3278 */
3279 storage_class=image->storage_class;
3280 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003281
glennrp3c218112010-11-27 15:31:26 +00003282/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003283
glennrp0fe50b42010-11-16 03:52:51 +00003284 if (storage_class == PseudoClass)
3285 {
3286 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003287 {
glennrp0fe50b42010-11-16 03:52:51 +00003288 for (x=0; x < ping_num_trans; x++)
3289 {
cristy6b7677c2012-01-01 20:59:57 +00003290 image->colormap[x].matte=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00003291 image->colormap[x].alpha =
3292 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003293 }
glennrpc11cf6a2010-03-20 16:46:19 +00003294 }
glennrp47b9dd52010-11-24 18:12:06 +00003295
glennrp0fe50b42010-11-16 03:52:51 +00003296 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3297 {
3298 for (x=0; x < (int) image->colors; x++)
3299 {
3300 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy4c08aed2011-07-01 19:47:50 +00003301 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003302 {
cristy6b7677c2012-01-01 20:59:57 +00003303 image->colormap[x].matte=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00003304 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003305 }
3306 }
3307 }
cristyea1a8aa2011-10-20 13:24:06 +00003308 (void) SyncImage(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003309 }
glennrp47b9dd52010-11-24 18:12:06 +00003310
glennrpa6a06632011-01-19 15:15:34 +00003311#if 1 /* Should have already been done above, but glennrp problem P10
3312 * needs this.
3313 */
glennrp0fe50b42010-11-16 03:52:51 +00003314 else
3315 {
3316 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003317 {
glennrp0fe50b42010-11-16 03:52:51 +00003318 image->storage_class=storage_class;
3319 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3320
cristyacd2ed22011-08-30 01:44:23 +00003321 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003322 break;
3323
glennrp0fe50b42010-11-16 03:52:51 +00003324
glennrpa6a06632011-01-19 15:15:34 +00003325 /* Caution: on a Q8 build, this does not distinguish between
3326 * 16-bit colors that differ only in the low byte
3327 */
glennrp0fe50b42010-11-16 03:52:51 +00003328 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3329 {
glennrp847370c2011-07-05 17:37:15 +00003330 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3331 transparent_color.red &&
3332 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3333 transparent_color.green &&
3334 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3335 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003336 {
cristy4c08aed2011-07-01 19:47:50 +00003337 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003338 }
glennrp0fe50b42010-11-16 03:52:51 +00003339
glennrp67b9c1a2011-04-22 18:47:36 +00003340#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003341 else
glennrp4f25bd02011-01-01 18:51:28 +00003342 {
cristy4c08aed2011-07-01 19:47:50 +00003343 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003344 }
glennrpa6a06632011-01-19 15:15:34 +00003345#endif
glennrp0fe50b42010-11-16 03:52:51 +00003346
cristyed231572011-07-14 02:18:59 +00003347 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003348 }
3349
3350 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3351 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003352 }
glennrp0fe50b42010-11-16 03:52:51 +00003353 }
glennrpa6a06632011-01-19 15:15:34 +00003354#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003355
cristy3ed852e2009-09-05 21:47:34 +00003356 image->storage_class=DirectClass;
3357 }
glennrp3c218112010-11-27 15:31:26 +00003358
cristyb40fc462010-08-08 00:49:49 +00003359 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3360 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3361 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00003362
cristyeb3b22a2011-03-31 20:16:11 +00003363 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003364 {
3365 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003366 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3367 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003368 else
glennrpa0ed0092011-04-18 16:36:29 +00003369 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3370 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003371
glennrp4eb39312011-03-30 21:34:55 +00003372 if (status != MagickFalse)
3373 for (i=0; i < (ssize_t) num_text; i++)
3374 {
3375 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003376
glennrp4eb39312011-03-30 21:34:55 +00003377 if (logging != MagickFalse)
3378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3379 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003380
glennrp4eb39312011-03-30 21:34:55 +00003381 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003382 {
cristyd15e6592011-10-15 00:13:06 +00003383 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i,
3384 exception);
glennrp4eb39312011-03-30 21:34:55 +00003385 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003386 }
glennrp0fe50b42010-11-16 03:52:51 +00003387
glennrp4eb39312011-03-30 21:34:55 +00003388 else
3389 {
3390 char
3391 *value;
3392
3393 length=text[i].text_length;
3394 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3395 sizeof(*value));
3396 if (value == (char *) NULL)
3397 {
cristyc82a27b2011-10-21 01:07:16 +00003398 (void) ThrowMagickException(exception,GetMagickModule(),
glennrp4eb39312011-03-30 21:34:55 +00003399 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3400 image->filename);
3401 break;
3402 }
3403 *value='\0';
3404 (void) ConcatenateMagickString(value,text[i].text,length+2);
3405
3406 /* Don't save "density" or "units" property if we have a pHYs
3407 * chunk
3408 */
3409 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3410 (LocaleCompare(text[i].key,"density") != 0 &&
3411 LocaleCompare(text[i].key,"units") != 0))
cristyd15e6592011-10-15 00:13:06 +00003412 (void) SetImageProperty(image,text[i].key,value,exception);
glennrp4eb39312011-03-30 21:34:55 +00003413
3414 if (logging != MagickFalse)
3415 {
3416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3417 " length: %lu",(unsigned long) length);
3418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3419 " Keyword: %s",text[i].key);
3420 }
3421
3422 value=DestroyString(value);
3423 }
3424 }
3425 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003426 }
glennrp3c218112010-11-27 15:31:26 +00003427
cristy3ed852e2009-09-05 21:47:34 +00003428#ifdef MNG_OBJECT_BUFFERS
3429 /*
3430 Store the object if necessary.
3431 */
3432 if (object_id && !mng_info->frozen[object_id])
3433 {
3434 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3435 {
3436 /*
3437 create a new object buffer.
3438 */
3439 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003440 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003441
cristy3ed852e2009-09-05 21:47:34 +00003442 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3443 {
3444 mng_info->ob[object_id]->image=(Image *) NULL;
3445 mng_info->ob[object_id]->reference_count=1;
3446 }
3447 }
glennrp47b9dd52010-11-24 18:12:06 +00003448
cristy3ed852e2009-09-05 21:47:34 +00003449 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3450 mng_info->ob[object_id]->frozen)
3451 {
3452 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
cristyc82a27b2011-10-21 01:07:16 +00003453 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003454 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3455 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003456
cristy3ed852e2009-09-05 21:47:34 +00003457 if (mng_info->ob[object_id]->frozen)
cristyc82a27b2011-10-21 01:07:16 +00003458 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003459 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3460 "`%s'",image->filename);
3461 }
glennrp0fe50b42010-11-16 03:52:51 +00003462
cristy3ed852e2009-09-05 21:47:34 +00003463 else
3464 {
cristy3ed852e2009-09-05 21:47:34 +00003465
3466 if (mng_info->ob[object_id]->image != (Image *) NULL)
3467 mng_info->ob[object_id]->image=DestroyImage
3468 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003469
cristy3ed852e2009-09-05 21:47:34 +00003470 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
cristyc82a27b2011-10-21 01:07:16 +00003471 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003472
cristy3ed852e2009-09-05 21:47:34 +00003473 if (mng_info->ob[object_id]->image != (Image *) NULL)
3474 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003475
cristy3ed852e2009-09-05 21:47:34 +00003476 else
cristyc82a27b2011-10-21 01:07:16 +00003477 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003478 ResourceLimitError,"Cloning image for object buffer failed",
3479 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003480
glennrpfaa852b2010-03-30 12:17:00 +00003481 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003482 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003483
glennrpfaa852b2010-03-30 12:17:00 +00003484 mng_info->ob[object_id]->width=ping_width;
3485 mng_info->ob[object_id]->height=ping_height;
3486 mng_info->ob[object_id]->color_type=ping_color_type;
3487 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3488 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3489 mng_info->ob[object_id]->compression_method=
3490 ping_compression_method;
3491 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003492
glennrpfaa852b2010-03-30 12:17:00 +00003493 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003494 {
3495 int
3496 number_colors;
3497
3498 png_colorp
3499 plte;
3500
3501 /*
3502 Copy the PLTE to the object buffer.
3503 */
3504 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3505 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003506
cristy3ed852e2009-09-05 21:47:34 +00003507 for (i=0; i < number_colors; i++)
3508 {
3509 mng_info->ob[object_id]->plte[i]=plte[i];
3510 }
3511 }
glennrp47b9dd52010-11-24 18:12:06 +00003512
cristy3ed852e2009-09-05 21:47:34 +00003513 else
3514 mng_info->ob[object_id]->plte_length=0;
3515 }
3516 }
3517#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003518
3519 /* Set image->matte to MagickTrue if the input colortype supports
3520 * alpha or if a valid tRNS chunk is present, no matter whether there
3521 * is actual transparency present.
3522 */
3523 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3524 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3525 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3526 MagickTrue : MagickFalse;
3527
glennrpcb395ac2011-03-30 19:50:23 +00003528 /* Set more properties for identify to retrieve */
3529 {
3530 char
3531 msg[MaxTextExtent];
3532
glennrp4eb39312011-03-30 21:34:55 +00003533 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003534 {
3535 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003536 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003537 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
cristy5d6fc9c2011-12-27 03:10:42 +00003538 (void) SetImageProperty(image,"png:text ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003539 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003540 }
3541
3542 if (num_raw_profiles != 0)
3543 {
cristy3b6fd2e2011-05-20 12:53:50 +00003544 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003545 "%d were found", num_raw_profiles);
cristy5d6fc9c2011-12-27 03:10:42 +00003546 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003547 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003548 }
3549
glennrpcb395ac2011-03-30 19:50:23 +00003550 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003551 {
cristy3b6fd2e2011-05-20 12:53:50 +00003552 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003553 "chunk was found (see Chromaticity, above)");
cristy5d6fc9c2011-12-27 03:10:42 +00003554 (void) SetImageProperty(image,"png:cHRM ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003555 exception);
glennrp59612252011-03-30 21:45:21 +00003556 }
glennrpcb395ac2011-03-30 19:50:23 +00003557
3558 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003559 {
cristy3b6fd2e2011-05-20 12:53:50 +00003560 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003561 "chunk was found (see Background color, above)");
cristy5d6fc9c2011-12-27 03:10:42 +00003562 (void) SetImageProperty(image,"png:bKGD ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003563 exception);
glennrp59612252011-03-30 21:45:21 +00003564 }
3565
cristy3b6fd2e2011-05-20 12:53:50 +00003566 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003567 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003568
3569 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy5d6fc9c2011-12-27 03:10:42 +00003570 (void) SetImageProperty(image,"png:iCCP ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003571 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003572
glennrpcb395ac2011-03-30 19:50:23 +00003573 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy5d6fc9c2011-12-27 03:10:42 +00003574 (void) SetImageProperty(image,"png:tRNS ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003575 exception);
glennrp4eb39312011-03-30 21:34:55 +00003576
3577#if defined(PNG_sRGB_SUPPORTED)
3578 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3579 {
cristy3b6fd2e2011-05-20 12:53:50 +00003580 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003581 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003582 (int) intent);
cristy5d6fc9c2011-12-27 03:10:42 +00003583 (void) SetImageProperty(image,"png:sRGB ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003584 exception);
glennrp4eb39312011-03-30 21:34:55 +00003585 }
3586#endif
3587
3588 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3589 {
cristy3b6fd2e2011-05-20 12:53:50 +00003590 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003591 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003592 file_gamma);
cristy5d6fc9c2011-12-27 03:10:42 +00003593 (void) SetImageProperty(image,"png:gAMA ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003594 exception);
glennrp4eb39312011-03-30 21:34:55 +00003595 }
3596
3597#if defined(PNG_pHYs_SUPPORTED)
3598 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3599 {
cristy3b6fd2e2011-05-20 12:53:50 +00003600 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003601 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003602 (double) x_resolution,(double) y_resolution, unit_type);
cristy5d6fc9c2011-12-27 03:10:42 +00003603 (void) SetImageProperty(image,"png:pHYs ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003604 exception);
glennrp4eb39312011-03-30 21:34:55 +00003605 }
3606#endif
3607
3608#if defined(PNG_oFFs_SUPPORTED)
3609 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3610 {
cristy3b6fd2e2011-05-20 12:53:50 +00003611 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003612 (double) image->page.x,(double) image->page.y);
cristy5d6fc9c2011-12-27 03:10:42 +00003613 (void) SetImageProperty(image,"png:oFFs ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003614 exception);
glennrp4eb39312011-03-30 21:34:55 +00003615 }
3616#endif
3617
glennrp07523c72011-03-31 18:12:10 +00003618 if ((image->page.width != 0 && image->page.width != image->columns) ||
3619 (image->page.height != 0 && image->page.height != image->rows))
3620 {
cristy3b6fd2e2011-05-20 12:53:50 +00003621 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003622 "width=%.20g, height=%.20g",
3623 (double) image->page.width,(double) image->page.height);
cristy5d6fc9c2011-12-27 03:10:42 +00003624 (void) SetImageProperty(image,"png:vpAg ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003625 exception);
glennrp07523c72011-03-31 18:12:10 +00003626 }
glennrpcb395ac2011-03-30 19:50:23 +00003627 }
3628
cristy3ed852e2009-09-05 21:47:34 +00003629 /*
3630 Relinquish resources.
3631 */
3632 png_destroy_read_struct(&ping,&ping_info,&end_info);
3633
glennrpcf002022011-01-30 02:38:15 +00003634 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003635#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003636 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003637#endif
3638
3639 if (logging != MagickFalse)
3640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3641 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003642
cristy3ed852e2009-09-05 21:47:34 +00003643 return(image);
3644
3645/* end of reading one PNG image */
3646}
3647
3648static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3649{
3650 Image
3651 *image,
3652 *previous;
3653
3654 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003655 have_mng_structure,
3656 logging,
cristy3ed852e2009-09-05 21:47:34 +00003657 status;
3658
3659 MngInfo
3660 *mng_info;
3661
3662 char
3663 magic_number[MaxTextExtent];
3664
cristy3ed852e2009-09-05 21:47:34 +00003665 ssize_t
3666 count;
3667
3668 /*
3669 Open image file.
3670 */
3671 assert(image_info != (const ImageInfo *) NULL);
3672 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003673
cristy3ed852e2009-09-05 21:47:34 +00003674 if (image_info->debug != MagickFalse)
3675 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3676 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003677
cristy3ed852e2009-09-05 21:47:34 +00003678 assert(exception != (ExceptionInfo *) NULL);
3679 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003680 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy9950d572011-10-01 18:22:35 +00003681 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003682 mng_info=(MngInfo *) NULL;
3683 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003684
cristy3ed852e2009-09-05 21:47:34 +00003685 if (status == MagickFalse)
3686 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003687
cristy3ed852e2009-09-05 21:47:34 +00003688 /*
3689 Verify PNG signature.
3690 */
3691 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003692
glennrpdde35db2011-02-21 12:06:32 +00003693 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003694 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003695
cristy3ed852e2009-09-05 21:47:34 +00003696 /*
3697 Allocate a MngInfo structure.
3698 */
3699 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003700 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003701
cristy3ed852e2009-09-05 21:47:34 +00003702 if (mng_info == (MngInfo *) NULL)
3703 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003704
cristy3ed852e2009-09-05 21:47:34 +00003705 /*
3706 Initialize members of the MngInfo structure.
3707 */
3708 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3709 mng_info->image=image;
3710 have_mng_structure=MagickTrue;
3711
3712 previous=image;
3713 image=ReadOnePNGImage(mng_info,image_info,exception);
3714 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003715
cristy3ed852e2009-09-05 21:47:34 +00003716 if (image == (Image *) NULL)
3717 {
3718 if (previous != (Image *) NULL)
3719 {
3720 if (previous->signature != MagickSignature)
3721 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003722
cristy3ed852e2009-09-05 21:47:34 +00003723 (void) CloseBlob(previous);
3724 (void) DestroyImageList(previous);
3725 }
glennrp0fe50b42010-11-16 03:52:51 +00003726
cristy3ed852e2009-09-05 21:47:34 +00003727 if (logging != MagickFalse)
3728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3729 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003730
cristy3ed852e2009-09-05 21:47:34 +00003731 return((Image *) NULL);
3732 }
glennrp47b9dd52010-11-24 18:12:06 +00003733
cristy3ed852e2009-09-05 21:47:34 +00003734 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003735
cristy3ed852e2009-09-05 21:47:34 +00003736 if ((image->columns == 0) || (image->rows == 0))
3737 {
3738 if (logging != MagickFalse)
3739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3740 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003741
cristy3ed852e2009-09-05 21:47:34 +00003742 ThrowReaderException(CorruptImageError,"CorruptImage");
3743 }
glennrp47b9dd52010-11-24 18:12:06 +00003744
cristy3ed852e2009-09-05 21:47:34 +00003745 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3746 {
cristy018f07f2011-09-04 21:15:19 +00003747 (void) SetImageType(image,TrueColorType,exception);
cristy3ed852e2009-09-05 21:47:34 +00003748 image->matte=MagickFalse;
3749 }
glennrp0fe50b42010-11-16 03:52:51 +00003750
cristy3ed852e2009-09-05 21:47:34 +00003751 if (LocaleCompare(image_info->magick,"PNG32") == 0)
cristy018f07f2011-09-04 21:15:19 +00003752 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003753
cristy3ed852e2009-09-05 21:47:34 +00003754 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3756 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3757 (double) image->page.width,(double) image->page.height,
3758 (double) image->page.x,(double) image->page.y);
3759
3760 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003762
cristy3ed852e2009-09-05 21:47:34 +00003763 return(image);
3764}
3765
3766
3767
3768#if defined(JNG_SUPPORTED)
3769/*
3770%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3771% %
3772% %
3773% %
3774% R e a d O n e J N G I m a g e %
3775% %
3776% %
3777% %
3778%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3779%
3780% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3781% (minus the 8-byte signature) and returns it. It allocates the memory
3782% necessary for the new Image structure and returns a pointer to the new
3783% image.
3784%
3785% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3786%
3787% The format of the ReadOneJNGImage method is:
3788%
3789% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3790% ExceptionInfo *exception)
3791%
3792% A description of each parameter follows:
3793%
3794% o mng_info: Specifies a pointer to a MngInfo structure.
3795%
3796% o image_info: the image info.
3797%
3798% o exception: return any errors or warnings in this structure.
3799%
3800*/
3801static Image *ReadOneJNGImage(MngInfo *mng_info,
3802 const ImageInfo *image_info, ExceptionInfo *exception)
3803{
3804 Image
3805 *alpha_image,
3806 *color_image,
3807 *image,
3808 *jng_image;
3809
3810 ImageInfo
3811 *alpha_image_info,
3812 *color_image_info;
3813
cristy4383ec82011-01-05 15:42:32 +00003814 MagickBooleanType
3815 logging;
3816
cristybb503372010-05-27 20:51:26 +00003817 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003818 y;
3819
3820 MagickBooleanType
3821 status;
3822
3823 png_uint_32
3824 jng_height,
3825 jng_width;
3826
3827 png_byte
3828 jng_color_type,
3829 jng_image_sample_depth,
3830 jng_image_compression_method,
3831 jng_image_interlace_method,
3832 jng_alpha_sample_depth,
3833 jng_alpha_compression_method,
3834 jng_alpha_filter_method,
3835 jng_alpha_interlace_method;
3836
cristy4c08aed2011-07-01 19:47:50 +00003837 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003838 *s;
3839
cristybb503372010-05-27 20:51:26 +00003840 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003841 i,
3842 x;
3843
cristy4c08aed2011-07-01 19:47:50 +00003844 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003845 *q;
3846
3847 register unsigned char
3848 *p;
3849
3850 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003851 read_JSEP,
3852 reading_idat,
3853 skip_to_iend;
3854
cristybb503372010-05-27 20:51:26 +00003855 size_t
cristy3ed852e2009-09-05 21:47:34 +00003856 length;
3857
3858 jng_alpha_compression_method=0;
3859 jng_alpha_sample_depth=8;
3860 jng_color_type=0;
3861 jng_height=0;
3862 jng_width=0;
3863 alpha_image=(Image *) NULL;
3864 color_image=(Image *) NULL;
3865 alpha_image_info=(ImageInfo *) NULL;
3866 color_image_info=(ImageInfo *) NULL;
3867
3868 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003869 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003870
3871 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003872
cristy4c08aed2011-07-01 19:47:50 +00003873 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003874 {
3875 /*
3876 Allocate next image structure.
3877 */
3878 if (logging != MagickFalse)
3879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3880 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003881
cristy9950d572011-10-01 18:22:35 +00003882 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003883
cristy3ed852e2009-09-05 21:47:34 +00003884 if (GetNextImageInList(image) == (Image *) NULL)
3885 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003886
cristy3ed852e2009-09-05 21:47:34 +00003887 image=SyncNextImageInList(image);
3888 }
3889 mng_info->image=image;
3890
3891 /*
3892 Signature bytes have already been read.
3893 */
3894
3895 read_JSEP=MagickFalse;
3896 reading_idat=MagickFalse;
3897 skip_to_iend=MagickFalse;
3898 for (;;)
3899 {
3900 char
3901 type[MaxTextExtent];
3902
3903 unsigned char
3904 *chunk;
3905
3906 unsigned int
3907 count;
3908
3909 /*
3910 Read a new JNG chunk.
3911 */
3912 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3913 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003914
cristy3ed852e2009-09-05 21:47:34 +00003915 if (status == MagickFalse)
3916 break;
glennrp0fe50b42010-11-16 03:52:51 +00003917
cristy3ed852e2009-09-05 21:47:34 +00003918 type[0]='\0';
3919 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3920 length=ReadBlobMSBLong(image);
3921 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3922
3923 if (logging != MagickFalse)
3924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003925 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3926 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003927
3928 if (length > PNG_UINT_31_MAX || count == 0)
3929 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003930
cristy3ed852e2009-09-05 21:47:34 +00003931 p=NULL;
3932 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003933
cristy3ed852e2009-09-05 21:47:34 +00003934 if (length)
3935 {
3936 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003937
cristy3ed852e2009-09-05 21:47:34 +00003938 if (chunk == (unsigned char *) NULL)
3939 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003940
cristybb503372010-05-27 20:51:26 +00003941 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003942 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003943
cristy3ed852e2009-09-05 21:47:34 +00003944 p=chunk;
3945 }
glennrp47b9dd52010-11-24 18:12:06 +00003946
cristy3ed852e2009-09-05 21:47:34 +00003947 (void) ReadBlobMSBLong(image); /* read crc word */
3948
3949 if (skip_to_iend)
3950 {
3951 if (length)
3952 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003953
cristy3ed852e2009-09-05 21:47:34 +00003954 continue;
3955 }
3956
3957 if (memcmp(type,mng_JHDR,4) == 0)
3958 {
3959 if (length == 16)
3960 {
cristybb503372010-05-27 20:51:26 +00003961 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003962 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003963 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003964 (p[6] << 8) | p[7]);
3965 jng_color_type=p[8];
3966 jng_image_sample_depth=p[9];
3967 jng_image_compression_method=p[10];
3968 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003969
cristy3ed852e2009-09-05 21:47:34 +00003970 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3971 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003972
cristy3ed852e2009-09-05 21:47:34 +00003973 jng_alpha_sample_depth=p[12];
3974 jng_alpha_compression_method=p[13];
3975 jng_alpha_filter_method=p[14];
3976 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003977
cristy3ed852e2009-09-05 21:47:34 +00003978 if (logging != MagickFalse)
3979 {
3980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003981 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003982
cristy3ed852e2009-09-05 21:47:34 +00003983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003984 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003985
cristy3ed852e2009-09-05 21:47:34 +00003986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3987 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003988
cristy3ed852e2009-09-05 21:47:34 +00003989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3990 " jng_image_sample_depth: %3d",
3991 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003992
cristy3ed852e2009-09-05 21:47:34 +00003993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3994 " jng_image_compression_method:%3d",
3995 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003996
cristy3ed852e2009-09-05 21:47:34 +00003997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3998 " jng_image_interlace_method: %3d",
3999 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00004000
cristy3ed852e2009-09-05 21:47:34 +00004001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4002 " jng_alpha_sample_depth: %3d",
4003 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00004004
cristy3ed852e2009-09-05 21:47:34 +00004005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4006 " jng_alpha_compression_method:%3d",
4007 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004008
cristy3ed852e2009-09-05 21:47:34 +00004009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4010 " jng_alpha_filter_method: %3d",
4011 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00004012
cristy3ed852e2009-09-05 21:47:34 +00004013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4014 " jng_alpha_interlace_method: %3d",
4015 jng_alpha_interlace_method);
4016 }
4017 }
glennrp47b9dd52010-11-24 18:12:06 +00004018
cristy3ed852e2009-09-05 21:47:34 +00004019 if (length)
4020 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004021
cristy3ed852e2009-09-05 21:47:34 +00004022 continue;
4023 }
4024
4025
4026 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4027 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4028 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4029 {
4030 /*
4031 o create color_image
4032 o open color_blob, attached to color_image
4033 o if (color type has alpha)
4034 open alpha_blob, attached to alpha_image
4035 */
4036
cristy73bd4a52010-10-05 11:24:23 +00004037 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004038
cristy3ed852e2009-09-05 21:47:34 +00004039 if (color_image_info == (ImageInfo *) NULL)
4040 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004041
cristy3ed852e2009-09-05 21:47:34 +00004042 GetImageInfo(color_image_info);
cristy9950d572011-10-01 18:22:35 +00004043 color_image=AcquireImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004044
cristy3ed852e2009-09-05 21:47:34 +00004045 if (color_image == (Image *) NULL)
4046 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4047
4048 if (logging != MagickFalse)
4049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4050 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004051
cristy3ed852e2009-09-05 21:47:34 +00004052 (void) AcquireUniqueFilename(color_image->filename);
4053 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4054 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004055
cristy3ed852e2009-09-05 21:47:34 +00004056 if (status == MagickFalse)
4057 return((Image *) NULL);
4058
4059 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4060 {
4061 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004062 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004063
cristy3ed852e2009-09-05 21:47:34 +00004064 if (alpha_image_info == (ImageInfo *) NULL)
4065 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004066
cristy3ed852e2009-09-05 21:47:34 +00004067 GetImageInfo(alpha_image_info);
cristy9950d572011-10-01 18:22:35 +00004068 alpha_image=AcquireImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004069
cristy3ed852e2009-09-05 21:47:34 +00004070 if (alpha_image == (Image *) NULL)
4071 {
4072 alpha_image=DestroyImage(alpha_image);
4073 ThrowReaderException(ResourceLimitError,
4074 "MemoryAllocationFailed");
4075 }
glennrp0fe50b42010-11-16 03:52:51 +00004076
cristy3ed852e2009-09-05 21:47:34 +00004077 if (logging != MagickFalse)
4078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4079 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004080
cristy3ed852e2009-09-05 21:47:34 +00004081 (void) AcquireUniqueFilename(alpha_image->filename);
4082 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4083 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004084
cristy3ed852e2009-09-05 21:47:34 +00004085 if (status == MagickFalse)
4086 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004087
cristy3ed852e2009-09-05 21:47:34 +00004088 if (jng_alpha_compression_method == 0)
4089 {
4090 unsigned char
4091 data[18];
4092
4093 if (logging != MagickFalse)
4094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4095 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004096
cristy3ed852e2009-09-05 21:47:34 +00004097 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4098 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004099
cristy3ed852e2009-09-05 21:47:34 +00004100 (void) WriteBlobMSBULong(alpha_image,13L);
4101 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004102 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004103 PNGLong(data+4,jng_width);
4104 PNGLong(data+8,jng_height);
4105 data[12]=jng_alpha_sample_depth;
4106 data[13]=0; /* color_type gray */
4107 data[14]=0; /* compression method 0 */
4108 data[15]=0; /* filter_method 0 */
4109 data[16]=0; /* interlace_method 0 */
4110 (void) WriteBlob(alpha_image,17,data);
4111 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4112 }
4113 }
4114 reading_idat=MagickTrue;
4115 }
4116
4117 if (memcmp(type,mng_JDAT,4) == 0)
4118 {
glennrp47b9dd52010-11-24 18:12:06 +00004119 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004120
4121 if (logging != MagickFalse)
4122 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4123 " Copying JDAT chunk data to color_blob.");
4124
4125 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004126
cristy3ed852e2009-09-05 21:47:34 +00004127 if (length)
4128 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004129
cristy3ed852e2009-09-05 21:47:34 +00004130 continue;
4131 }
4132
4133 if (memcmp(type,mng_IDAT,4) == 0)
4134 {
4135 png_byte
4136 data[5];
4137
glennrp47b9dd52010-11-24 18:12:06 +00004138 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004139
4140 if (image_info->ping == MagickFalse)
4141 {
4142 if (logging != MagickFalse)
4143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4144 " Copying IDAT chunk data to alpha_blob.");
4145
cristybb503372010-05-27 20:51:26 +00004146 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004147 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004148 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004149 (void) WriteBlob(alpha_image,4,data);
4150 (void) WriteBlob(alpha_image,length,chunk);
4151 (void) WriteBlobMSBULong(alpha_image,
4152 crc32(crc32(0,data,4),chunk,(uInt) length));
4153 }
glennrp0fe50b42010-11-16 03:52:51 +00004154
cristy3ed852e2009-09-05 21:47:34 +00004155 if (length)
4156 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004157
cristy3ed852e2009-09-05 21:47:34 +00004158 continue;
4159 }
4160
4161 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4162 {
glennrp47b9dd52010-11-24 18:12:06 +00004163 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004164
4165 if (image_info->ping == MagickFalse)
4166 {
4167 if (logging != MagickFalse)
4168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4169 " Copying JDAA chunk data to alpha_blob.");
4170
4171 (void) WriteBlob(alpha_image,length,chunk);
4172 }
glennrp0fe50b42010-11-16 03:52:51 +00004173
cristy3ed852e2009-09-05 21:47:34 +00004174 if (length)
4175 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004176
cristy3ed852e2009-09-05 21:47:34 +00004177 continue;
4178 }
4179
4180 if (memcmp(type,mng_JSEP,4) == 0)
4181 {
4182 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004183
cristy3ed852e2009-09-05 21:47:34 +00004184 if (length)
4185 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004186
cristy3ed852e2009-09-05 21:47:34 +00004187 continue;
4188 }
4189
4190 if (memcmp(type,mng_bKGD,4) == 0)
4191 {
4192 if (length == 2)
4193 {
4194 image->background_color.red=ScaleCharToQuantum(p[1]);
4195 image->background_color.green=image->background_color.red;
4196 image->background_color.blue=image->background_color.red;
4197 }
glennrp0fe50b42010-11-16 03:52:51 +00004198
cristy3ed852e2009-09-05 21:47:34 +00004199 if (length == 6)
4200 {
4201 image->background_color.red=ScaleCharToQuantum(p[1]);
4202 image->background_color.green=ScaleCharToQuantum(p[3]);
4203 image->background_color.blue=ScaleCharToQuantum(p[5]);
4204 }
glennrp0fe50b42010-11-16 03:52:51 +00004205
cristy3ed852e2009-09-05 21:47:34 +00004206 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4207 continue;
4208 }
4209
4210 if (memcmp(type,mng_gAMA,4) == 0)
4211 {
4212 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004213 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004214
cristy3ed852e2009-09-05 21:47:34 +00004215 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4216 continue;
4217 }
4218
4219 if (memcmp(type,mng_cHRM,4) == 0)
4220 {
4221 if (length == 32)
4222 {
cristy8182b072010-05-30 20:10:53 +00004223 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4224 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4225 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4226 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4227 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4228 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4229 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4230 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004231 }
glennrp47b9dd52010-11-24 18:12:06 +00004232
cristy3ed852e2009-09-05 21:47:34 +00004233 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4234 continue;
4235 }
4236
4237 if (memcmp(type,mng_sRGB,4) == 0)
4238 {
4239 if (length == 1)
4240 {
glennrpe610a072010-08-05 17:08:46 +00004241 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004242 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004243 image->gamma=0.45455f;
4244 image->chromaticity.red_primary.x=0.6400f;
4245 image->chromaticity.red_primary.y=0.3300f;
4246 image->chromaticity.green_primary.x=0.3000f;
4247 image->chromaticity.green_primary.y=0.6000f;
4248 image->chromaticity.blue_primary.x=0.1500f;
4249 image->chromaticity.blue_primary.y=0.0600f;
4250 image->chromaticity.white_point.x=0.3127f;
4251 image->chromaticity.white_point.y=0.3290f;
4252 }
glennrp47b9dd52010-11-24 18:12:06 +00004253
cristy3ed852e2009-09-05 21:47:34 +00004254 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4255 continue;
4256 }
4257
4258 if (memcmp(type,mng_oFFs,4) == 0)
4259 {
4260 if (length > 8)
4261 {
glennrp5eae7602011-02-22 15:21:32 +00004262 image->page.x=(ssize_t) mng_get_long(p);
4263 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004264
cristy3ed852e2009-09-05 21:47:34 +00004265 if ((int) p[8] != 0)
4266 {
4267 image->page.x/=10000;
4268 image->page.y/=10000;
4269 }
4270 }
glennrp47b9dd52010-11-24 18:12:06 +00004271
cristy3ed852e2009-09-05 21:47:34 +00004272 if (length)
4273 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004274
cristy3ed852e2009-09-05 21:47:34 +00004275 continue;
4276 }
4277
4278 if (memcmp(type,mng_pHYs,4) == 0)
4279 {
4280 if (length > 8)
4281 {
cristy2a11bef2011-10-28 18:33:11 +00004282 image->resolution.x=(double) mng_get_long(p);
4283 image->resolution.y=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004284 if ((int) p[8] == PNG_RESOLUTION_METER)
4285 {
4286 image->units=PixelsPerCentimeterResolution;
cristy2a11bef2011-10-28 18:33:11 +00004287 image->resolution.x=image->resolution.x/100.0f;
4288 image->resolution.y=image->resolution.y/100.0f;
cristy3ed852e2009-09-05 21:47:34 +00004289 }
4290 }
glennrp0fe50b42010-11-16 03:52:51 +00004291
cristy3ed852e2009-09-05 21:47:34 +00004292 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4293 continue;
4294 }
4295
4296#if 0
4297 if (memcmp(type,mng_iCCP,4) == 0)
4298 {
glennrpfd05d622011-02-25 04:10:33 +00004299 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004300 if (length)
4301 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004302
cristy3ed852e2009-09-05 21:47:34 +00004303 continue;
4304 }
4305#endif
4306
4307 if (length)
4308 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4309
4310 if (memcmp(type,mng_IEND,4))
4311 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004312
cristy3ed852e2009-09-05 21:47:34 +00004313 break;
4314 }
4315
4316
4317 /* IEND found */
4318
4319 /*
4320 Finish up reading image data:
4321
4322 o read main image from color_blob.
4323
4324 o close color_blob.
4325
4326 o if (color_type has alpha)
4327 if alpha_encoding is PNG
4328 read secondary image from alpha_blob via ReadPNG
4329 if alpha_encoding is JPEG
4330 read secondary image from alpha_blob via ReadJPEG
4331
4332 o close alpha_blob.
4333
4334 o copy intensity of secondary image into
cristy4c08aed2011-07-01 19:47:50 +00004335 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004336
4337 o destroy the secondary image.
4338 */
4339
4340 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004341
cristy3ed852e2009-09-05 21:47:34 +00004342 if (logging != MagickFalse)
4343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4344 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004345
cristy3b6fd2e2011-05-20 12:53:50 +00004346 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004347 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004348
cristy3ed852e2009-09-05 21:47:34 +00004349 color_image_info->ping=MagickFalse; /* To do: avoid this */
4350 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004351
cristy3ed852e2009-09-05 21:47:34 +00004352 if (jng_image == (Image *) NULL)
4353 return((Image *) NULL);
4354
4355 (void) RelinquishUniqueFileResource(color_image->filename);
4356 color_image=DestroyImage(color_image);
4357 color_image_info=DestroyImageInfo(color_image_info);
4358
4359 if (jng_image == (Image *) NULL)
4360 return((Image *) NULL);
4361
4362 if (logging != MagickFalse)
4363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4364 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004365
cristy3ed852e2009-09-05 21:47:34 +00004366 image->rows=jng_height;
4367 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004368
cristybb503372010-05-27 20:51:26 +00004369 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004370 {
cristyc82a27b2011-10-21 01:07:16 +00004371 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00004372 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004373 for (x=(ssize_t) image->columns; x != 0; x--)
4374 {
4375 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4376 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4377 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004378 q+=GetPixelChannels(image);
4379 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004380 }
glennrp47b9dd52010-11-24 18:12:06 +00004381
cristy3ed852e2009-09-05 21:47:34 +00004382 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4383 break;
4384 }
glennrp0fe50b42010-11-16 03:52:51 +00004385
cristy3ed852e2009-09-05 21:47:34 +00004386 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004387
cristy3ed852e2009-09-05 21:47:34 +00004388 if (image_info->ping == MagickFalse)
4389 {
4390 if (jng_color_type >= 12)
4391 {
4392 if (jng_alpha_compression_method == 0)
4393 {
4394 png_byte
4395 data[5];
4396 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4397 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004398 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004399 (void) WriteBlob(alpha_image,4,data);
4400 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4401 }
glennrp0fe50b42010-11-16 03:52:51 +00004402
cristy3ed852e2009-09-05 21:47:34 +00004403 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004404
cristy3ed852e2009-09-05 21:47:34 +00004405 if (logging != MagickFalse)
4406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00004407 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004408
cristy3b6fd2e2011-05-20 12:53:50 +00004409 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004410 "%s",alpha_image->filename);
4411
4412 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004413
cristy3ed852e2009-09-05 21:47:34 +00004414 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004415 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004416 {
4417 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristyc82a27b2011-10-21 01:07:16 +00004418 exception);
cristy3ed852e2009-09-05 21:47:34 +00004419 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004420
cristy3ed852e2009-09-05 21:47:34 +00004421 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00004422 for (x=(ssize_t) image->columns; x != 0; x--)
4423 {
4424 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004425 q+=GetPixelChannels(image);
4426 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004427 }
glennrp0fe50b42010-11-16 03:52:51 +00004428
cristy3ed852e2009-09-05 21:47:34 +00004429 else
cristy4c08aed2011-07-01 19:47:50 +00004430 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004431 {
cristy4c08aed2011-07-01 19:47:50 +00004432 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4433 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004434 image->matte=MagickTrue;
cristyed231572011-07-14 02:18:59 +00004435 q+=GetPixelChannels(image);
4436 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004437 }
glennrp0fe50b42010-11-16 03:52:51 +00004438
cristy3ed852e2009-09-05 21:47:34 +00004439 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4440 break;
4441 }
4442 (void) RelinquishUniqueFileResource(alpha_image->filename);
4443 alpha_image=DestroyImage(alpha_image);
4444 alpha_image_info=DestroyImageInfo(alpha_image_info);
4445 if (jng_image != (Image *) NULL)
4446 jng_image=DestroyImage(jng_image);
4447 }
4448 }
4449
glennrp47b9dd52010-11-24 18:12:06 +00004450 /* Read the JNG image. */
4451
cristy3ed852e2009-09-05 21:47:34 +00004452 if (mng_info->mng_type == 0)
4453 {
4454 mng_info->mng_width=jng_width;
4455 mng_info->mng_height=jng_height;
4456 }
glennrp0fe50b42010-11-16 03:52:51 +00004457
cristy3ed852e2009-09-05 21:47:34 +00004458 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004459 {
4460 image->page.width=jng_width;
4461 image->page.height=jng_height;
4462 }
4463
cristy3ed852e2009-09-05 21:47:34 +00004464 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004465 {
4466 image->page.x=mng_info->x_off[mng_info->object_id];
4467 image->page.y=mng_info->y_off[mng_info->object_id];
4468 }
4469
cristy3ed852e2009-09-05 21:47:34 +00004470 else
glennrp0fe50b42010-11-16 03:52:51 +00004471 {
4472 image->page.y=mng_info->y_off[mng_info->object_id];
4473 }
4474
cristy3ed852e2009-09-05 21:47:34 +00004475 mng_info->image_found++;
4476 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4477 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004478
cristy3ed852e2009-09-05 21:47:34 +00004479 if (logging != MagickFalse)
4480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4481 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004482
cristy3ed852e2009-09-05 21:47:34 +00004483 return(image);
4484}
4485
4486/*
4487%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4488% %
4489% %
4490% %
4491% R e a d J N G I m a g e %
4492% %
4493% %
4494% %
4495%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4496%
4497% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4498% (including the 8-byte signature) and returns it. It allocates the memory
4499% necessary for the new Image structure and returns a pointer to the new
4500% image.
4501%
4502% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4503%
4504% The format of the ReadJNGImage method is:
4505%
4506% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4507% *exception)
4508%
4509% A description of each parameter follows:
4510%
4511% o image_info: the image info.
4512%
4513% o exception: return any errors or warnings in this structure.
4514%
4515*/
4516
4517static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4518{
4519 Image
4520 *image,
4521 *previous;
4522
4523 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004524 have_mng_structure,
4525 logging,
cristy3ed852e2009-09-05 21:47:34 +00004526 status;
4527
4528 MngInfo
4529 *mng_info;
4530
4531 char
4532 magic_number[MaxTextExtent];
4533
cristy3ed852e2009-09-05 21:47:34 +00004534 size_t
4535 count;
4536
4537 /*
4538 Open image file.
4539 */
4540 assert(image_info != (const ImageInfo *) NULL);
4541 assert(image_info->signature == MagickSignature);
4542 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4543 assert(exception != (ExceptionInfo *) NULL);
4544 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004545 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy9950d572011-10-01 18:22:35 +00004546 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004547 mng_info=(MngInfo *) NULL;
4548 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004549
cristy3ed852e2009-09-05 21:47:34 +00004550 if (status == MagickFalse)
4551 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004552
cristy3ed852e2009-09-05 21:47:34 +00004553 if (LocaleCompare(image_info->magick,"JNG") != 0)
4554 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004555
glennrp47b9dd52010-11-24 18:12:06 +00004556 /* Verify JNG signature. */
4557
cristy3ed852e2009-09-05 21:47:34 +00004558 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004559
glennrp3b8763e2011-02-21 12:08:18 +00004560 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004561 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004562
glennrp47b9dd52010-11-24 18:12:06 +00004563 /* Allocate a MngInfo structure. */
4564
cristy3ed852e2009-09-05 21:47:34 +00004565 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004566 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004567
cristy3ed852e2009-09-05 21:47:34 +00004568 if (mng_info == (MngInfo *) NULL)
4569 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004570
glennrp47b9dd52010-11-24 18:12:06 +00004571 /* Initialize members of the MngInfo structure. */
4572
cristy3ed852e2009-09-05 21:47:34 +00004573 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4574 have_mng_structure=MagickTrue;
4575
4576 mng_info->image=image;
4577 previous=image;
4578 image=ReadOneJNGImage(mng_info,image_info,exception);
4579 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004580
cristy3ed852e2009-09-05 21:47:34 +00004581 if (image == (Image *) NULL)
4582 {
4583 if (IsImageObject(previous) != MagickFalse)
4584 {
4585 (void) CloseBlob(previous);
4586 (void) DestroyImageList(previous);
4587 }
glennrp0fe50b42010-11-16 03:52:51 +00004588
cristy3ed852e2009-09-05 21:47:34 +00004589 if (logging != MagickFalse)
4590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4591 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004592
cristy3ed852e2009-09-05 21:47:34 +00004593 return((Image *) NULL);
4594 }
4595 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004596
cristy3ed852e2009-09-05 21:47:34 +00004597 if (image->columns == 0 || image->rows == 0)
4598 {
4599 if (logging != MagickFalse)
4600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4601 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004602
cristy3ed852e2009-09-05 21:47:34 +00004603 ThrowReaderException(CorruptImageError,"CorruptImage");
4604 }
glennrp0fe50b42010-11-16 03:52:51 +00004605
cristy3ed852e2009-09-05 21:47:34 +00004606 if (logging != MagickFalse)
4607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004608
cristy3ed852e2009-09-05 21:47:34 +00004609 return(image);
4610}
4611#endif
4612
4613static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4614{
4615 char
4616 page_geometry[MaxTextExtent];
4617
4618 Image
4619 *image,
4620 *previous;
4621
cristy4383ec82011-01-05 15:42:32 +00004622 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004623 logging,
4624 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004625
cristy3ed852e2009-09-05 21:47:34 +00004626 volatile int
4627 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004628 object_id,
4629 term_chunk_found,
4630 skip_to_iend;
4631
cristybb503372010-05-27 20:51:26 +00004632 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004633 image_count=0;
4634
4635 MagickBooleanType
4636 status;
4637
4638 MagickOffsetType
4639 offset;
4640
4641 MngInfo
4642 *mng_info;
4643
4644 MngBox
4645 default_fb,
4646 fb,
4647 previous_fb;
4648
4649#if defined(MNG_INSERT_LAYERS)
cristy101ab702011-10-13 13:06:32 +00004650 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004651 mng_background_color;
4652#endif
4653
4654 register unsigned char
4655 *p;
4656
cristybb503372010-05-27 20:51:26 +00004657 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004658 i;
4659
4660 size_t
4661 count;
4662
cristybb503372010-05-27 20:51:26 +00004663 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004664 loop_level;
4665
4666 volatile short
4667 skipping_loop;
4668
4669#if defined(MNG_INSERT_LAYERS)
4670 unsigned int
4671 mandatory_back=0;
4672#endif
4673
4674 volatile unsigned int
4675#ifdef MNG_OBJECT_BUFFERS
4676 mng_background_object=0,
4677#endif
4678 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4679
cristybb503372010-05-27 20:51:26 +00004680 size_t
cristy3ed852e2009-09-05 21:47:34 +00004681 default_frame_timeout,
4682 frame_timeout,
4683#if defined(MNG_INSERT_LAYERS)
4684 image_height,
4685 image_width,
4686#endif
4687 length;
4688
glennrp38ea0832010-06-02 18:50:28 +00004689 /* These delays are all measured in image ticks_per_second,
4690 * not in MNG ticks_per_second
4691 */
cristybb503372010-05-27 20:51:26 +00004692 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004693 default_frame_delay,
4694 final_delay,
4695 final_image_delay,
4696 frame_delay,
4697#if defined(MNG_INSERT_LAYERS)
4698 insert_layers,
4699#endif
4700 mng_iterations=1,
4701 simplicity=0,
4702 subframe_height=0,
4703 subframe_width=0;
4704
4705 previous_fb.top=0;
4706 previous_fb.bottom=0;
4707 previous_fb.left=0;
4708 previous_fb.right=0;
4709 default_fb.top=0;
4710 default_fb.bottom=0;
4711 default_fb.left=0;
4712 default_fb.right=0;
4713
glennrp47b9dd52010-11-24 18:12:06 +00004714 /* Open image file. */
4715
cristy3ed852e2009-09-05 21:47:34 +00004716 assert(image_info != (const ImageInfo *) NULL);
4717 assert(image_info->signature == MagickSignature);
4718 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4719 assert(exception != (ExceptionInfo *) NULL);
4720 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004721 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy9950d572011-10-01 18:22:35 +00004722 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004723 mng_info=(MngInfo *) NULL;
4724 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004725
cristy3ed852e2009-09-05 21:47:34 +00004726 if (status == MagickFalse)
4727 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004728
cristy3ed852e2009-09-05 21:47:34 +00004729 first_mng_object=MagickFalse;
4730 skipping_loop=(-1);
4731 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004732
4733 /* Allocate a MngInfo structure. */
4734
cristy73bd4a52010-10-05 11:24:23 +00004735 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004736
cristy3ed852e2009-09-05 21:47:34 +00004737 if (mng_info == (MngInfo *) NULL)
4738 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004739
glennrp47b9dd52010-11-24 18:12:06 +00004740 /* Initialize members of the MngInfo structure. */
4741
cristy3ed852e2009-09-05 21:47:34 +00004742 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4743 mng_info->image=image;
4744 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004745
4746 if (LocaleCompare(image_info->magick,"MNG") == 0)
4747 {
4748 char
4749 magic_number[MaxTextExtent];
4750
glennrp47b9dd52010-11-24 18:12:06 +00004751 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004752 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4753 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4754 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004755
4756 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004757 for (i=0; i < MNG_MAX_OBJECTS; i++)
4758 {
cristybb503372010-05-27 20:51:26 +00004759 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4760 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004761 }
4762 mng_info->exists[0]=MagickTrue;
4763 }
glennrp47b9dd52010-11-24 18:12:06 +00004764
cristy3ed852e2009-09-05 21:47:34 +00004765 first_mng_object=MagickTrue;
4766 mng_type=0;
4767#if defined(MNG_INSERT_LAYERS)
4768 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4769#endif
4770 default_frame_delay=0;
4771 default_frame_timeout=0;
4772 frame_delay=0;
4773 final_delay=1;
4774 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4775 object_id=0;
4776 skip_to_iend=MagickFalse;
4777 term_chunk_found=MagickFalse;
4778 mng_info->framing_mode=1;
4779#if defined(MNG_INSERT_LAYERS)
4780 mandatory_back=MagickFalse;
4781#endif
4782#if defined(MNG_INSERT_LAYERS)
4783 mng_background_color=image->background_color;
4784#endif
4785 default_fb=mng_info->frame;
4786 previous_fb=mng_info->frame;
4787 do
4788 {
4789 char
4790 type[MaxTextExtent];
4791
4792 if (LocaleCompare(image_info->magick,"MNG") == 0)
4793 {
4794 unsigned char
4795 *chunk;
4796
4797 /*
4798 Read a new chunk.
4799 */
4800 type[0]='\0';
4801 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4802 length=ReadBlobMSBLong(image);
4803 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4804
4805 if (logging != MagickFalse)
4806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004807 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4808 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004809
4810 if (length > PNG_UINT_31_MAX)
4811 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004812
cristy3ed852e2009-09-05 21:47:34 +00004813 if (count == 0)
4814 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004815
cristy3ed852e2009-09-05 21:47:34 +00004816 p=NULL;
4817 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004818
cristy3ed852e2009-09-05 21:47:34 +00004819 if (length)
4820 {
4821 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004822
cristy3ed852e2009-09-05 21:47:34 +00004823 if (chunk == (unsigned char *) NULL)
4824 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004825
cristybb503372010-05-27 20:51:26 +00004826 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004827 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004828
cristy3ed852e2009-09-05 21:47:34 +00004829 p=chunk;
4830 }
glennrp0fe50b42010-11-16 03:52:51 +00004831
cristy3ed852e2009-09-05 21:47:34 +00004832 (void) ReadBlobMSBLong(image); /* read crc word */
4833
4834#if !defined(JNG_SUPPORTED)
4835 if (memcmp(type,mng_JHDR,4) == 0)
4836 {
4837 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004838
cristy3ed852e2009-09-05 21:47:34 +00004839 if (mng_info->jhdr_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00004840 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004841 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004842
cristy3ed852e2009-09-05 21:47:34 +00004843 mng_info->jhdr_warning++;
4844 }
4845#endif
4846 if (memcmp(type,mng_DHDR,4) == 0)
4847 {
4848 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004849
cristy3ed852e2009-09-05 21:47:34 +00004850 if (mng_info->dhdr_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00004851 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004852 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004853
cristy3ed852e2009-09-05 21:47:34 +00004854 mng_info->dhdr_warning++;
4855 }
4856 if (memcmp(type,mng_MEND,4) == 0)
4857 break;
glennrp47b9dd52010-11-24 18:12:06 +00004858
cristy3ed852e2009-09-05 21:47:34 +00004859 if (skip_to_iend)
4860 {
4861 if (memcmp(type,mng_IEND,4) == 0)
4862 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004863
cristy3ed852e2009-09-05 21:47:34 +00004864 if (length)
4865 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004866
cristy3ed852e2009-09-05 21:47:34 +00004867 if (logging != MagickFalse)
4868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4869 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004870
cristy3ed852e2009-09-05 21:47:34 +00004871 continue;
4872 }
glennrp0fe50b42010-11-16 03:52:51 +00004873
cristy3ed852e2009-09-05 21:47:34 +00004874 if (memcmp(type,mng_MHDR,4) == 0)
4875 {
cristybb503372010-05-27 20:51:26 +00004876 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004877 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004878
cristybb503372010-05-27 20:51:26 +00004879 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004880 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004881
cristy3ed852e2009-09-05 21:47:34 +00004882 if (logging != MagickFalse)
4883 {
4884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004885 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004887 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004888 }
glennrp0fe50b42010-11-16 03:52:51 +00004889
cristy3ed852e2009-09-05 21:47:34 +00004890 p+=8;
cristy8182b072010-05-30 20:10:53 +00004891 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004892
cristy3ed852e2009-09-05 21:47:34 +00004893 if (mng_info->ticks_per_second == 0)
4894 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004895
cristy3ed852e2009-09-05 21:47:34 +00004896 else
4897 default_frame_delay=1UL*image->ticks_per_second/
4898 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004899
cristy3ed852e2009-09-05 21:47:34 +00004900 frame_delay=default_frame_delay;
4901 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004902
cristy3ed852e2009-09-05 21:47:34 +00004903 if (length > 16)
4904 {
4905 p+=16;
cristy8182b072010-05-30 20:10:53 +00004906 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004907 }
glennrp0fe50b42010-11-16 03:52:51 +00004908
cristy3ed852e2009-09-05 21:47:34 +00004909 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004910
cristy3ed852e2009-09-05 21:47:34 +00004911 if ((simplicity != 0) && ((simplicity | 11) == 11))
4912 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004913
cristy3ed852e2009-09-05 21:47:34 +00004914 if ((simplicity != 0) && ((simplicity | 9) == 9))
4915 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004916
cristy3ed852e2009-09-05 21:47:34 +00004917#if defined(MNG_INSERT_LAYERS)
4918 if (mng_type != 3)
4919 insert_layers=MagickTrue;
4920#endif
cristy4c08aed2011-07-01 19:47:50 +00004921 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004922 {
glennrp47b9dd52010-11-24 18:12:06 +00004923 /* Allocate next image structure. */
cristy9950d572011-10-01 18:22:35 +00004924 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004925
cristy3ed852e2009-09-05 21:47:34 +00004926 if (GetNextImageInList(image) == (Image *) NULL)
4927 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004928
cristy3ed852e2009-09-05 21:47:34 +00004929 image=SyncNextImageInList(image);
4930 mng_info->image=image;
4931 }
4932
4933 if ((mng_info->mng_width > 65535L) ||
4934 (mng_info->mng_height > 65535L))
4935 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004936
cristy3b6fd2e2011-05-20 12:53:50 +00004937 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004938 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004939 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004940
cristy3ed852e2009-09-05 21:47:34 +00004941 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004942 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004943 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004944 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004945 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004946
cristy3ed852e2009-09-05 21:47:34 +00004947 for (i=0; i < MNG_MAX_OBJECTS; i++)
4948 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004949
cristy3ed852e2009-09-05 21:47:34 +00004950 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4951 continue;
4952 }
4953
4954 if (memcmp(type,mng_TERM,4) == 0)
4955 {
4956 int
4957 repeat=0;
4958
4959
4960 if (length)
4961 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004962
cristy3ed852e2009-09-05 21:47:34 +00004963 if (repeat == 3)
4964 {
cristy8182b072010-05-30 20:10:53 +00004965 final_delay=(png_uint_32) mng_get_long(&p[2]);
4966 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004967
cristy3ed852e2009-09-05 21:47:34 +00004968 if (mng_iterations == PNG_UINT_31_MAX)
4969 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004970
cristy3ed852e2009-09-05 21:47:34 +00004971 image->iterations=mng_iterations;
4972 term_chunk_found=MagickTrue;
4973 }
glennrp0fe50b42010-11-16 03:52:51 +00004974
cristy3ed852e2009-09-05 21:47:34 +00004975 if (logging != MagickFalse)
4976 {
4977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4978 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004979
cristy3ed852e2009-09-05 21:47:34 +00004980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004981 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004982
cristy3ed852e2009-09-05 21:47:34 +00004983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004984 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004985 }
glennrp0fe50b42010-11-16 03:52:51 +00004986
cristy3ed852e2009-09-05 21:47:34 +00004987 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4988 continue;
4989 }
4990 if (memcmp(type,mng_DEFI,4) == 0)
4991 {
4992 if (mng_type == 3)
cristyc82a27b2011-10-21 01:07:16 +00004993 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004994 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4995 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004996
cristy3ed852e2009-09-05 21:47:34 +00004997 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004998
cristy3ed852e2009-09-05 21:47:34 +00004999 if (mng_type == 2 && object_id != 0)
cristyc82a27b2011-10-21 01:07:16 +00005000 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005001 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5002 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005003
cristy3ed852e2009-09-05 21:47:34 +00005004 if (object_id > MNG_MAX_OBJECTS)
5005 {
5006 /*
5007 Instead ofsuing a warning we should allocate a larger
5008 MngInfo structure and continue.
5009 */
cristyc82a27b2011-10-21 01:07:16 +00005010 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005011 CoderError,"object id too large","`%s'",image->filename);
5012 object_id=MNG_MAX_OBJECTS;
5013 }
glennrp0fe50b42010-11-16 03:52:51 +00005014
cristy3ed852e2009-09-05 21:47:34 +00005015 if (mng_info->exists[object_id])
5016 if (mng_info->frozen[object_id])
5017 {
5018 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
cristyc82a27b2011-10-21 01:07:16 +00005019 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005020 GetMagickModule(),CoderError,
5021 "DEFI cannot redefine a frozen MNG object","`%s'",
5022 image->filename);
5023 continue;
5024 }
glennrp0fe50b42010-11-16 03:52:51 +00005025
cristy3ed852e2009-09-05 21:47:34 +00005026 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005027
cristy3ed852e2009-09-05 21:47:34 +00005028 if (length > 2)
5029 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00005030
cristy3ed852e2009-09-05 21:47:34 +00005031 /*
5032 Extract object offset info.
5033 */
5034 if (length > 11)
5035 {
glennrp0fe50b42010-11-16 03:52:51 +00005036 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5037 (p[5] << 16) | (p[6] << 8) | p[7]);
5038
5039 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5040 (p[9] << 16) | (p[10] << 8) | p[11]);
5041
cristy3ed852e2009-09-05 21:47:34 +00005042 if (logging != MagickFalse)
5043 {
5044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005045 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005046 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005047
cristy3ed852e2009-09-05 21:47:34 +00005048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005049 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005050 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005051 }
5052 }
glennrp0fe50b42010-11-16 03:52:51 +00005053
cristy3ed852e2009-09-05 21:47:34 +00005054 /*
5055 Extract object clipping info.
5056 */
5057 if (length > 27)
5058 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5059 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005060
cristy3ed852e2009-09-05 21:47:34 +00005061 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5062 continue;
5063 }
5064 if (memcmp(type,mng_bKGD,4) == 0)
5065 {
5066 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005067
cristy3ed852e2009-09-05 21:47:34 +00005068 if (length > 5)
5069 {
5070 mng_info->mng_global_bkgd.red=
5071 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005072
cristy3ed852e2009-09-05 21:47:34 +00005073 mng_info->mng_global_bkgd.green=
5074 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005075
cristy3ed852e2009-09-05 21:47:34 +00005076 mng_info->mng_global_bkgd.blue=
5077 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005078
cristy3ed852e2009-09-05 21:47:34 +00005079 mng_info->have_global_bkgd=MagickTrue;
5080 }
glennrp0fe50b42010-11-16 03:52:51 +00005081
cristy3ed852e2009-09-05 21:47:34 +00005082 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5083 continue;
5084 }
5085 if (memcmp(type,mng_BACK,4) == 0)
5086 {
5087#if defined(MNG_INSERT_LAYERS)
5088 if (length > 6)
5089 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005090
cristy3ed852e2009-09-05 21:47:34 +00005091 else
5092 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005093
cristy3ed852e2009-09-05 21:47:34 +00005094 if (mandatory_back && length > 5)
5095 {
5096 mng_background_color.red=
5097 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005098
cristy3ed852e2009-09-05 21:47:34 +00005099 mng_background_color.green=
5100 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005101
cristy3ed852e2009-09-05 21:47:34 +00005102 mng_background_color.blue=
5103 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005104
cristy4c08aed2011-07-01 19:47:50 +00005105 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005106 }
glennrp0fe50b42010-11-16 03:52:51 +00005107
cristy3ed852e2009-09-05 21:47:34 +00005108#ifdef MNG_OBJECT_BUFFERS
5109 if (length > 8)
5110 mng_background_object=(p[7] << 8) | p[8];
5111#endif
5112#endif
5113 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5114 continue;
5115 }
glennrp47b9dd52010-11-24 18:12:06 +00005116
cristy3ed852e2009-09-05 21:47:34 +00005117 if (memcmp(type,mng_PLTE,4) == 0)
5118 {
glennrp47b9dd52010-11-24 18:12:06 +00005119 /* Read global PLTE. */
5120
cristy3ed852e2009-09-05 21:47:34 +00005121 if (length && (length < 769))
5122 {
5123 if (mng_info->global_plte == (png_colorp) NULL)
5124 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5125 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005126
cristybb503372010-05-27 20:51:26 +00005127 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005128 {
5129 mng_info->global_plte[i].red=p[3*i];
5130 mng_info->global_plte[i].green=p[3*i+1];
5131 mng_info->global_plte[i].blue=p[3*i+2];
5132 }
glennrp0fe50b42010-11-16 03:52:51 +00005133
cristy35ef8242010-06-03 16:24:13 +00005134 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005135 }
5136#ifdef MNG_LOOSE
5137 for ( ; i < 256; i++)
5138 {
5139 mng_info->global_plte[i].red=i;
5140 mng_info->global_plte[i].green=i;
5141 mng_info->global_plte[i].blue=i;
5142 }
glennrp0fe50b42010-11-16 03:52:51 +00005143
cristy3ed852e2009-09-05 21:47:34 +00005144 if (length)
5145 mng_info->global_plte_length=256;
5146#endif
5147 else
5148 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005149
cristy3ed852e2009-09-05 21:47:34 +00005150 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5151 continue;
5152 }
glennrp47b9dd52010-11-24 18:12:06 +00005153
cristy3ed852e2009-09-05 21:47:34 +00005154 if (memcmp(type,mng_tRNS,4) == 0)
5155 {
5156 /* read global tRNS */
5157
5158 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005159 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005160 mng_info->global_trns[i]=p[i];
5161
5162#ifdef MNG_LOOSE
5163 for ( ; i < 256; i++)
5164 mng_info->global_trns[i]=255;
5165#endif
cristy12560f32010-06-03 16:51:08 +00005166 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005167 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5168 continue;
5169 }
5170 if (memcmp(type,mng_gAMA,4) == 0)
5171 {
5172 if (length == 4)
5173 {
cristybb503372010-05-27 20:51:26 +00005174 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005175 igamma;
5176
cristy8182b072010-05-30 20:10:53 +00005177 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005178 mng_info->global_gamma=((float) igamma)*0.00001;
5179 mng_info->have_global_gama=MagickTrue;
5180 }
glennrp0fe50b42010-11-16 03:52:51 +00005181
cristy3ed852e2009-09-05 21:47:34 +00005182 else
5183 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005184
cristy3ed852e2009-09-05 21:47:34 +00005185 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5186 continue;
5187 }
5188
5189 if (memcmp(type,mng_cHRM,4) == 0)
5190 {
glennrp47b9dd52010-11-24 18:12:06 +00005191 /* Read global cHRM */
5192
cristy3ed852e2009-09-05 21:47:34 +00005193 if (length == 32)
5194 {
cristy8182b072010-05-30 20:10:53 +00005195 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5196 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5197 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005198 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005199 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005200 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005201 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005202 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005203 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005204 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005205 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005206 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005207 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005208 mng_info->have_global_chrm=MagickTrue;
5209 }
5210 else
5211 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005212
cristy3ed852e2009-09-05 21:47:34 +00005213 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5214 continue;
5215 }
glennrp47b9dd52010-11-24 18:12:06 +00005216
cristy3ed852e2009-09-05 21:47:34 +00005217 if (memcmp(type,mng_sRGB,4) == 0)
5218 {
5219 /*
5220 Read global sRGB.
5221 */
5222 if (length)
5223 {
glennrpe610a072010-08-05 17:08:46 +00005224 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005225 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005226 mng_info->have_global_srgb=MagickTrue;
5227 }
5228 else
5229 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005230
cristy3ed852e2009-09-05 21:47:34 +00005231 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5232 continue;
5233 }
glennrp47b9dd52010-11-24 18:12:06 +00005234
cristy3ed852e2009-09-05 21:47:34 +00005235 if (memcmp(type,mng_iCCP,4) == 0)
5236 {
glennrpfd05d622011-02-25 04:10:33 +00005237 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005238
5239 /*
5240 Read global iCCP.
5241 */
5242 if (length)
5243 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005244
cristy3ed852e2009-09-05 21:47:34 +00005245 continue;
5246 }
glennrp47b9dd52010-11-24 18:12:06 +00005247
cristy3ed852e2009-09-05 21:47:34 +00005248 if (memcmp(type,mng_FRAM,4) == 0)
5249 {
5250 if (mng_type == 3)
cristyc82a27b2011-10-21 01:07:16 +00005251 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005252 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5253 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005254
cristy3ed852e2009-09-05 21:47:34 +00005255 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5256 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005257
cristy3ed852e2009-09-05 21:47:34 +00005258 frame_delay=default_frame_delay;
5259 frame_timeout=default_frame_timeout;
5260 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005261
cristy3ed852e2009-09-05 21:47:34 +00005262 if (length)
5263 if (p[0])
5264 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005265
cristy3ed852e2009-09-05 21:47:34 +00005266 if (logging != MagickFalse)
5267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5268 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005269
cristy3ed852e2009-09-05 21:47:34 +00005270 if (length > 6)
5271 {
glennrp47b9dd52010-11-24 18:12:06 +00005272 /* Note the delay and frame clipping boundaries. */
5273
cristy3ed852e2009-09-05 21:47:34 +00005274 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005275
cristybb503372010-05-27 20:51:26 +00005276 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005277 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005278
cristy3ed852e2009-09-05 21:47:34 +00005279 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005280
cristybb503372010-05-27 20:51:26 +00005281 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005282 {
5283 int
5284 change_delay,
5285 change_timeout,
5286 change_clipping;
5287
5288 change_delay=(*p++);
5289 change_timeout=(*p++);
5290 change_clipping=(*p++);
5291 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005292
cristy3ed852e2009-09-05 21:47:34 +00005293 if (change_delay)
5294 {
cristy8182b072010-05-30 20:10:53 +00005295 frame_delay=1UL*image->ticks_per_second*
5296 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005297
cristy8182b072010-05-30 20:10:53 +00005298 if (mng_info->ticks_per_second != 0)
5299 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005300
glennrpbb010dd2010-06-01 13:07:15 +00005301 else
5302 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005303
cristy3ed852e2009-09-05 21:47:34 +00005304 if (change_delay == 2)
5305 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005306
cristy3ed852e2009-09-05 21:47:34 +00005307 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005308
cristy3ed852e2009-09-05 21:47:34 +00005309 if (logging != MagickFalse)
5310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005311 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005312 }
glennrp47b9dd52010-11-24 18:12:06 +00005313
cristy3ed852e2009-09-05 21:47:34 +00005314 if (change_timeout)
5315 {
glennrpbb010dd2010-06-01 13:07:15 +00005316 frame_timeout=1UL*image->ticks_per_second*
5317 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005318
glennrpbb010dd2010-06-01 13:07:15 +00005319 if (mng_info->ticks_per_second != 0)
5320 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005321
glennrpbb010dd2010-06-01 13:07:15 +00005322 else
5323 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005324
cristy3ed852e2009-09-05 21:47:34 +00005325 if (change_delay == 2)
5326 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005327
cristy3ed852e2009-09-05 21:47:34 +00005328 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005329
cristy3ed852e2009-09-05 21:47:34 +00005330 if (logging != MagickFalse)
5331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005332 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005333 }
glennrp47b9dd52010-11-24 18:12:06 +00005334
cristy3ed852e2009-09-05 21:47:34 +00005335 if (change_clipping)
5336 {
5337 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5338 p+=17;
5339 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005340
cristy3ed852e2009-09-05 21:47:34 +00005341 if (logging != MagickFalse)
5342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005343 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005344 (double) fb.left,(double) fb.right,(double) fb.top,
5345 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005346
cristy3ed852e2009-09-05 21:47:34 +00005347 if (change_clipping == 2)
5348 default_fb=fb;
5349 }
5350 }
5351 }
5352 mng_info->clip=fb;
5353 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005354
cristybb503372010-05-27 20:51:26 +00005355 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005356 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005357
cristybb503372010-05-27 20:51:26 +00005358 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005359 -mng_info->clip.top);
5360 /*
5361 Insert a background layer behind the frame if framing_mode is 4.
5362 */
5363#if defined(MNG_INSERT_LAYERS)
5364 if (logging != MagickFalse)
5365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005366 " subframe_width=%.20g, subframe_height=%.20g",(double)
5367 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005368
cristy3ed852e2009-09-05 21:47:34 +00005369 if (insert_layers && (mng_info->framing_mode == 4) &&
5370 (subframe_width) && (subframe_height))
5371 {
glennrp47b9dd52010-11-24 18:12:06 +00005372 /* Allocate next image structure. */
cristy4c08aed2011-07-01 19:47:50 +00005373 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005374 {
cristy9950d572011-10-01 18:22:35 +00005375 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005376
cristy3ed852e2009-09-05 21:47:34 +00005377 if (GetNextImageInList(image) == (Image *) NULL)
5378 {
5379 image=DestroyImageList(image);
5380 MngInfoFreeStruct(mng_info,&have_mng_structure);
5381 return((Image *) NULL);
5382 }
glennrp47b9dd52010-11-24 18:12:06 +00005383
cristy3ed852e2009-09-05 21:47:34 +00005384 image=SyncNextImageInList(image);
5385 }
glennrp0fe50b42010-11-16 03:52:51 +00005386
cristy3ed852e2009-09-05 21:47:34 +00005387 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005388
cristy3ed852e2009-09-05 21:47:34 +00005389 if (term_chunk_found)
5390 {
5391 image->start_loop=MagickTrue;
5392 image->iterations=mng_iterations;
5393 term_chunk_found=MagickFalse;
5394 }
glennrp0fe50b42010-11-16 03:52:51 +00005395
cristy3ed852e2009-09-05 21:47:34 +00005396 else
5397 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005398
cristy3ed852e2009-09-05 21:47:34 +00005399 image->columns=subframe_width;
5400 image->rows=subframe_height;
5401 image->page.width=subframe_width;
5402 image->page.height=subframe_height;
5403 image->page.x=mng_info->clip.left;
5404 image->page.y=mng_info->clip.top;
5405 image->background_color=mng_background_color;
5406 image->matte=MagickFalse;
5407 image->delay=0;
cristyea1a8aa2011-10-20 13:24:06 +00005408 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005409
cristy3ed852e2009-09-05 21:47:34 +00005410 if (logging != MagickFalse)
5411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005412 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005413 (double) mng_info->clip.left,(double) mng_info->clip.right,
5414 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005415 }
5416#endif
5417 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5418 continue;
5419 }
5420 if (memcmp(type,mng_CLIP,4) == 0)
5421 {
5422 unsigned int
5423 first_object,
5424 last_object;
5425
5426 /*
5427 Read CLIP.
5428 */
5429 first_object=(p[0] << 8) | p[1];
5430 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005431
cristy3ed852e2009-09-05 21:47:34 +00005432 for (i=(int) first_object; i <= (int) last_object; i++)
5433 {
5434 if (mng_info->exists[i] && !mng_info->frozen[i])
5435 {
5436 MngBox
5437 box;
5438
5439 box=mng_info->object_clip[i];
5440 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5441 }
5442 }
glennrp47b9dd52010-11-24 18:12:06 +00005443
cristy3ed852e2009-09-05 21:47:34 +00005444 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5445 continue;
5446 }
5447 if (memcmp(type,mng_SAVE,4) == 0)
5448 {
5449 for (i=1; i < MNG_MAX_OBJECTS; i++)
5450 if (mng_info->exists[i])
5451 {
5452 mng_info->frozen[i]=MagickTrue;
5453#ifdef MNG_OBJECT_BUFFERS
5454 if (mng_info->ob[i] != (MngBuffer *) NULL)
5455 mng_info->ob[i]->frozen=MagickTrue;
5456#endif
5457 }
glennrp0fe50b42010-11-16 03:52:51 +00005458
cristy3ed852e2009-09-05 21:47:34 +00005459 if (length)
5460 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005461
cristy3ed852e2009-09-05 21:47:34 +00005462 continue;
5463 }
5464
5465 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5466 {
glennrp47b9dd52010-11-24 18:12:06 +00005467 /* Read DISC or SEEK. */
5468
cristy3ed852e2009-09-05 21:47:34 +00005469 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5470 {
5471 for (i=1; i < MNG_MAX_OBJECTS; i++)
5472 MngInfoDiscardObject(mng_info,i);
5473 }
glennrp0fe50b42010-11-16 03:52:51 +00005474
cristy3ed852e2009-09-05 21:47:34 +00005475 else
5476 {
cristybb503372010-05-27 20:51:26 +00005477 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005478 j;
5479
cristybb503372010-05-27 20:51:26 +00005480 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005481 {
5482 i=p[j] << 8 | p[j+1];
5483 MngInfoDiscardObject(mng_info,i);
5484 }
5485 }
glennrp0fe50b42010-11-16 03:52:51 +00005486
cristy3ed852e2009-09-05 21:47:34 +00005487 if (length)
5488 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005489
cristy3ed852e2009-09-05 21:47:34 +00005490 continue;
5491 }
glennrp47b9dd52010-11-24 18:12:06 +00005492
cristy3ed852e2009-09-05 21:47:34 +00005493 if (memcmp(type,mng_MOVE,4) == 0)
5494 {
cristybb503372010-05-27 20:51:26 +00005495 size_t
cristy3ed852e2009-09-05 21:47:34 +00005496 first_object,
5497 last_object;
5498
glennrp47b9dd52010-11-24 18:12:06 +00005499 /* read MOVE */
5500
cristy3ed852e2009-09-05 21:47:34 +00005501 first_object=(p[0] << 8) | p[1];
5502 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005503 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005504 {
5505 if (mng_info->exists[i] && !mng_info->frozen[i])
5506 {
5507 MngPair
5508 new_pair;
5509
5510 MngPair
5511 old_pair;
5512
5513 old_pair.a=mng_info->x_off[i];
5514 old_pair.b=mng_info->y_off[i];
5515 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5516 mng_info->x_off[i]=new_pair.a;
5517 mng_info->y_off[i]=new_pair.b;
5518 }
5519 }
glennrp47b9dd52010-11-24 18:12:06 +00005520
cristy3ed852e2009-09-05 21:47:34 +00005521 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5522 continue;
5523 }
5524
5525 if (memcmp(type,mng_LOOP,4) == 0)
5526 {
cristybb503372010-05-27 20:51:26 +00005527 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005528 loop_level=chunk[0];
5529 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005530
5531 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005532 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005533
cristy3ed852e2009-09-05 21:47:34 +00005534 if (logging != MagickFalse)
5535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005536 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5537 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005538
cristy3ed852e2009-09-05 21:47:34 +00005539 if (loop_iters == 0)
5540 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005541
cristy3ed852e2009-09-05 21:47:34 +00005542 else
5543 {
5544 mng_info->loop_jump[loop_level]=TellBlob(image);
5545 mng_info->loop_count[loop_level]=loop_iters;
5546 }
glennrp0fe50b42010-11-16 03:52:51 +00005547
cristy3ed852e2009-09-05 21:47:34 +00005548 mng_info->loop_iteration[loop_level]=0;
5549 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5550 continue;
5551 }
glennrp47b9dd52010-11-24 18:12:06 +00005552
cristy3ed852e2009-09-05 21:47:34 +00005553 if (memcmp(type,mng_ENDL,4) == 0)
5554 {
5555 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005556
cristy3ed852e2009-09-05 21:47:34 +00005557 if (skipping_loop > 0)
5558 {
5559 if (skipping_loop == loop_level)
5560 {
5561 /*
5562 Found end of zero-iteration loop.
5563 */
5564 skipping_loop=(-1);
5565 mng_info->loop_active[loop_level]=0;
5566 }
5567 }
glennrp47b9dd52010-11-24 18:12:06 +00005568
cristy3ed852e2009-09-05 21:47:34 +00005569 else
5570 {
5571 if (mng_info->loop_active[loop_level] == 1)
5572 {
5573 mng_info->loop_count[loop_level]--;
5574 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005575
cristy3ed852e2009-09-05 21:47:34 +00005576 if (logging != MagickFalse)
5577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005578 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005579 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005580 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005581
cristy3ed852e2009-09-05 21:47:34 +00005582 if (mng_info->loop_count[loop_level] != 0)
5583 {
5584 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5585 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005586
cristy3ed852e2009-09-05 21:47:34 +00005587 if (offset < 0)
5588 ThrowReaderException(CorruptImageError,
5589 "ImproperImageHeader");
5590 }
glennrp47b9dd52010-11-24 18:12:06 +00005591
cristy3ed852e2009-09-05 21:47:34 +00005592 else
5593 {
5594 short
5595 last_level;
5596
5597 /*
5598 Finished loop.
5599 */
5600 mng_info->loop_active[loop_level]=0;
5601 last_level=(-1);
5602 for (i=0; i < loop_level; i++)
5603 if (mng_info->loop_active[i] == 1)
5604 last_level=(short) i;
5605 loop_level=last_level;
5606 }
5607 }
5608 }
glennrp47b9dd52010-11-24 18:12:06 +00005609
cristy3ed852e2009-09-05 21:47:34 +00005610 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5611 continue;
5612 }
glennrp47b9dd52010-11-24 18:12:06 +00005613
cristy3ed852e2009-09-05 21:47:34 +00005614 if (memcmp(type,mng_CLON,4) == 0)
5615 {
5616 if (mng_info->clon_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005617 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005618 CoderError,"CLON is not implemented yet","`%s'",
5619 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005620
cristy3ed852e2009-09-05 21:47:34 +00005621 mng_info->clon_warning++;
5622 }
glennrp47b9dd52010-11-24 18:12:06 +00005623
cristy3ed852e2009-09-05 21:47:34 +00005624 if (memcmp(type,mng_MAGN,4) == 0)
5625 {
5626 png_uint_16
5627 magn_first,
5628 magn_last,
5629 magn_mb,
5630 magn_ml,
5631 magn_mr,
5632 magn_mt,
5633 magn_mx,
5634 magn_my,
5635 magn_methx,
5636 magn_methy;
5637
5638 if (length > 1)
5639 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005640
cristy3ed852e2009-09-05 21:47:34 +00005641 else
5642 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005643
cristy3ed852e2009-09-05 21:47:34 +00005644 if (length > 3)
5645 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005646
cristy3ed852e2009-09-05 21:47:34 +00005647 else
5648 magn_last=magn_first;
5649#ifndef MNG_OBJECT_BUFFERS
5650 if (magn_first || magn_last)
5651 if (mng_info->magn_warning == 0)
5652 {
cristyc82a27b2011-10-21 01:07:16 +00005653 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005654 GetMagickModule(),CoderError,
5655 "MAGN is not implemented yet for nonzero objects",
5656 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005657
cristy3ed852e2009-09-05 21:47:34 +00005658 mng_info->magn_warning++;
5659 }
5660#endif
5661 if (length > 4)
5662 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005663
cristy3ed852e2009-09-05 21:47:34 +00005664 else
5665 magn_methx=0;
5666
5667 if (length > 6)
5668 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005669
cristy3ed852e2009-09-05 21:47:34 +00005670 else
5671 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005672
cristy3ed852e2009-09-05 21:47:34 +00005673 if (magn_mx == 0)
5674 magn_mx=1;
5675
5676 if (length > 8)
5677 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005678
cristy3ed852e2009-09-05 21:47:34 +00005679 else
5680 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005681
cristy3ed852e2009-09-05 21:47:34 +00005682 if (magn_my == 0)
5683 magn_my=1;
5684
5685 if (length > 10)
5686 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005687
cristy3ed852e2009-09-05 21:47:34 +00005688 else
5689 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005690
cristy3ed852e2009-09-05 21:47:34 +00005691 if (magn_ml == 0)
5692 magn_ml=1;
5693
5694 if (length > 12)
5695 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005696
cristy3ed852e2009-09-05 21:47:34 +00005697 else
5698 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005699
cristy3ed852e2009-09-05 21:47:34 +00005700 if (magn_mr == 0)
5701 magn_mr=1;
5702
5703 if (length > 14)
5704 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005705
cristy3ed852e2009-09-05 21:47:34 +00005706 else
5707 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005708
cristy3ed852e2009-09-05 21:47:34 +00005709 if (magn_mt == 0)
5710 magn_mt=1;
5711
5712 if (length > 16)
5713 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005714
cristy3ed852e2009-09-05 21:47:34 +00005715 else
5716 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005717
cristy3ed852e2009-09-05 21:47:34 +00005718 if (magn_mb == 0)
5719 magn_mb=1;
5720
5721 if (length > 17)
5722 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005723
cristy3ed852e2009-09-05 21:47:34 +00005724 else
5725 magn_methy=magn_methx;
5726
glennrp47b9dd52010-11-24 18:12:06 +00005727
cristy3ed852e2009-09-05 21:47:34 +00005728 if (magn_methx > 5 || magn_methy > 5)
5729 if (mng_info->magn_warning == 0)
5730 {
cristyc82a27b2011-10-21 01:07:16 +00005731 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005732 GetMagickModule(),CoderError,
5733 "Unknown MAGN method in MNG datastream","`%s'",
5734 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005735
cristy3ed852e2009-09-05 21:47:34 +00005736 mng_info->magn_warning++;
5737 }
5738#ifdef MNG_OBJECT_BUFFERS
5739 /* Magnify existing objects in the range magn_first to magn_last */
5740#endif
5741 if (magn_first == 0 || magn_last == 0)
5742 {
5743 /* Save the magnification factors for object 0 */
5744 mng_info->magn_mb=magn_mb;
5745 mng_info->magn_ml=magn_ml;
5746 mng_info->magn_mr=magn_mr;
5747 mng_info->magn_mt=magn_mt;
5748 mng_info->magn_mx=magn_mx;
5749 mng_info->magn_my=magn_my;
5750 mng_info->magn_methx=magn_methx;
5751 mng_info->magn_methy=magn_methy;
5752 }
5753 }
glennrp47b9dd52010-11-24 18:12:06 +00005754
cristy3ed852e2009-09-05 21:47:34 +00005755 if (memcmp(type,mng_PAST,4) == 0)
5756 {
5757 if (mng_info->past_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005758 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005759 CoderError,"PAST is not implemented yet","`%s'",
5760 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005761
cristy3ed852e2009-09-05 21:47:34 +00005762 mng_info->past_warning++;
5763 }
glennrp47b9dd52010-11-24 18:12:06 +00005764
cristy3ed852e2009-09-05 21:47:34 +00005765 if (memcmp(type,mng_SHOW,4) == 0)
5766 {
5767 if (mng_info->show_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005768 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005769 CoderError,"SHOW is not implemented yet","`%s'",
5770 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005771
cristy3ed852e2009-09-05 21:47:34 +00005772 mng_info->show_warning++;
5773 }
glennrp47b9dd52010-11-24 18:12:06 +00005774
cristy3ed852e2009-09-05 21:47:34 +00005775 if (memcmp(type,mng_sBIT,4) == 0)
5776 {
5777 if (length < 4)
5778 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005779
cristy3ed852e2009-09-05 21:47:34 +00005780 else
5781 {
5782 mng_info->global_sbit.gray=p[0];
5783 mng_info->global_sbit.red=p[0];
5784 mng_info->global_sbit.green=p[1];
5785 mng_info->global_sbit.blue=p[2];
5786 mng_info->global_sbit.alpha=p[3];
5787 mng_info->have_global_sbit=MagickTrue;
5788 }
5789 }
5790 if (memcmp(type,mng_pHYs,4) == 0)
5791 {
5792 if (length > 8)
5793 {
5794 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005795 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005796 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005797 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005798 mng_info->global_phys_unit_type=p[8];
5799 mng_info->have_global_phys=MagickTrue;
5800 }
glennrp47b9dd52010-11-24 18:12:06 +00005801
cristy3ed852e2009-09-05 21:47:34 +00005802 else
5803 mng_info->have_global_phys=MagickFalse;
5804 }
5805 if (memcmp(type,mng_pHYg,4) == 0)
5806 {
5807 if (mng_info->phyg_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005808 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005809 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005810
cristy3ed852e2009-09-05 21:47:34 +00005811 mng_info->phyg_warning++;
5812 }
5813 if (memcmp(type,mng_BASI,4) == 0)
5814 {
5815 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005816
cristy3ed852e2009-09-05 21:47:34 +00005817 if (mng_info->basi_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005818 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005819 CoderError,"BASI is not implemented yet","`%s'",
5820 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005821
cristy3ed852e2009-09-05 21:47:34 +00005822 mng_info->basi_warning++;
5823#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005824 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005825 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005826 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005827 (p[6] << 8) | p[7]);
5828 basi_color_type=p[8];
5829 basi_compression_method=p[9];
5830 basi_filter_type=p[10];
5831 basi_interlace_method=p[11];
5832 if (length > 11)
5833 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005834
cristy3ed852e2009-09-05 21:47:34 +00005835 else
5836 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005837
cristy3ed852e2009-09-05 21:47:34 +00005838 if (length > 13)
5839 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005840
cristy3ed852e2009-09-05 21:47:34 +00005841 else
5842 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005843
cristy3ed852e2009-09-05 21:47:34 +00005844 if (length > 15)
5845 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005846
cristy3ed852e2009-09-05 21:47:34 +00005847 else
5848 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005849
cristy3ed852e2009-09-05 21:47:34 +00005850 if (length > 17)
5851 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005852
cristy3ed852e2009-09-05 21:47:34 +00005853 else
5854 {
5855 if (basi_sample_depth == 16)
5856 basi_alpha=65535L;
5857 else
5858 basi_alpha=255;
5859 }
glennrp47b9dd52010-11-24 18:12:06 +00005860
cristy3ed852e2009-09-05 21:47:34 +00005861 if (length > 19)
5862 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005863
cristy3ed852e2009-09-05 21:47:34 +00005864 else
5865 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005866
cristy3ed852e2009-09-05 21:47:34 +00005867#endif
5868 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5869 continue;
5870 }
glennrp47b9dd52010-11-24 18:12:06 +00005871
cristy3ed852e2009-09-05 21:47:34 +00005872 if (memcmp(type,mng_IHDR,4)
5873#if defined(JNG_SUPPORTED)
5874 && memcmp(type,mng_JHDR,4)
5875#endif
5876 )
5877 {
5878 /* Not an IHDR or JHDR chunk */
5879 if (length)
5880 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005881
cristy3ed852e2009-09-05 21:47:34 +00005882 continue;
5883 }
5884/* Process IHDR */
5885 if (logging != MagickFalse)
5886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5887 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005888
cristy3ed852e2009-09-05 21:47:34 +00005889 mng_info->exists[object_id]=MagickTrue;
5890 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005891
cristy3ed852e2009-09-05 21:47:34 +00005892 if (mng_info->invisible[object_id])
5893 {
5894 if (logging != MagickFalse)
5895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5896 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005897
cristy3ed852e2009-09-05 21:47:34 +00005898 skip_to_iend=MagickTrue;
5899 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5900 continue;
5901 }
5902#if defined(MNG_INSERT_LAYERS)
5903 if (length < 8)
5904 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005905
cristy8182b072010-05-30 20:10:53 +00005906 image_width=(size_t) mng_get_long(p);
5907 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005908#endif
5909 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5910
5911 /*
5912 Insert a transparent background layer behind the entire animation
5913 if it is not full screen.
5914 */
5915#if defined(MNG_INSERT_LAYERS)
5916 if (insert_layers && mng_type && first_mng_object)
5917 {
5918 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5919 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005920 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005921 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005922 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005923 {
cristy4c08aed2011-07-01 19:47:50 +00005924 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005925 {
5926 /*
5927 Allocate next image structure.
5928 */
cristy9950d572011-10-01 18:22:35 +00005929 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005930
cristy3ed852e2009-09-05 21:47:34 +00005931 if (GetNextImageInList(image) == (Image *) NULL)
5932 {
5933 image=DestroyImageList(image);
5934 MngInfoFreeStruct(mng_info,&have_mng_structure);
5935 return((Image *) NULL);
5936 }
glennrp47b9dd52010-11-24 18:12:06 +00005937
cristy3ed852e2009-09-05 21:47:34 +00005938 image=SyncNextImageInList(image);
5939 }
5940 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005941
cristy3ed852e2009-09-05 21:47:34 +00005942 if (term_chunk_found)
5943 {
5944 image->start_loop=MagickTrue;
5945 image->iterations=mng_iterations;
5946 term_chunk_found=MagickFalse;
5947 }
glennrp47b9dd52010-11-24 18:12:06 +00005948
cristy3ed852e2009-09-05 21:47:34 +00005949 else
5950 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005951
5952 /* Make a background rectangle. */
5953
cristy3ed852e2009-09-05 21:47:34 +00005954 image->delay=0;
5955 image->columns=mng_info->mng_width;
5956 image->rows=mng_info->mng_height;
5957 image->page.width=mng_info->mng_width;
5958 image->page.height=mng_info->mng_height;
5959 image->page.x=0;
5960 image->page.y=0;
5961 image->background_color=mng_background_color;
cristyea1a8aa2011-10-20 13:24:06 +00005962 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005963 if (logging != MagickFalse)
5964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005965 " Inserted transparent background layer, W=%.20g, H=%.20g",
5966 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005967 }
5968 }
5969 /*
5970 Insert a background layer behind the upcoming image if
5971 framing_mode is 3, and we haven't already inserted one.
5972 */
5973 if (insert_layers && (mng_info->framing_mode == 3) &&
5974 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5975 (simplicity & 0x08)))
5976 {
cristy4c08aed2011-07-01 19:47:50 +00005977 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005978 {
5979 /*
5980 Allocate next image structure.
5981 */
cristy9950d572011-10-01 18:22:35 +00005982 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005983
cristy3ed852e2009-09-05 21:47:34 +00005984 if (GetNextImageInList(image) == (Image *) NULL)
5985 {
5986 image=DestroyImageList(image);
5987 MngInfoFreeStruct(mng_info,&have_mng_structure);
5988 return((Image *) NULL);
5989 }
glennrp47b9dd52010-11-24 18:12:06 +00005990
cristy3ed852e2009-09-05 21:47:34 +00005991 image=SyncNextImageInList(image);
5992 }
glennrp0fe50b42010-11-16 03:52:51 +00005993
cristy3ed852e2009-09-05 21:47:34 +00005994 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005995
cristy3ed852e2009-09-05 21:47:34 +00005996 if (term_chunk_found)
5997 {
5998 image->start_loop=MagickTrue;
5999 image->iterations=mng_iterations;
6000 term_chunk_found=MagickFalse;
6001 }
glennrp0fe50b42010-11-16 03:52:51 +00006002
cristy3ed852e2009-09-05 21:47:34 +00006003 else
6004 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006005
cristy3ed852e2009-09-05 21:47:34 +00006006 image->delay=0;
6007 image->columns=subframe_width;
6008 image->rows=subframe_height;
6009 image->page.width=subframe_width;
6010 image->page.height=subframe_height;
6011 image->page.x=mng_info->clip.left;
6012 image->page.y=mng_info->clip.top;
6013 image->background_color=mng_background_color;
6014 image->matte=MagickFalse;
cristyea1a8aa2011-10-20 13:24:06 +00006015 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006016
cristy3ed852e2009-09-05 21:47:34 +00006017 if (logging != MagickFalse)
6018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00006019 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00006020 (double) mng_info->clip.left,(double) mng_info->clip.right,
6021 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00006022 }
6023#endif /* MNG_INSERT_LAYERS */
6024 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006025
cristy4c08aed2011-07-01 19:47:50 +00006026 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006027 {
6028 /*
6029 Allocate next image structure.
6030 */
cristy9950d572011-10-01 18:22:35 +00006031 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006032
cristy3ed852e2009-09-05 21:47:34 +00006033 if (GetNextImageInList(image) == (Image *) NULL)
6034 {
6035 image=DestroyImageList(image);
6036 MngInfoFreeStruct(mng_info,&have_mng_structure);
6037 return((Image *) NULL);
6038 }
glennrp47b9dd52010-11-24 18:12:06 +00006039
cristy3ed852e2009-09-05 21:47:34 +00006040 image=SyncNextImageInList(image);
6041 }
6042 mng_info->image=image;
6043 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6044 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006045
cristy3ed852e2009-09-05 21:47:34 +00006046 if (status == MagickFalse)
6047 break;
glennrp0fe50b42010-11-16 03:52:51 +00006048
cristy3ed852e2009-09-05 21:47:34 +00006049 if (term_chunk_found)
6050 {
6051 image->start_loop=MagickTrue;
6052 term_chunk_found=MagickFalse;
6053 }
glennrp0fe50b42010-11-16 03:52:51 +00006054
cristy3ed852e2009-09-05 21:47:34 +00006055 else
6056 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006057
cristy3ed852e2009-09-05 21:47:34 +00006058 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6059 {
6060 image->delay=frame_delay;
6061 frame_delay=default_frame_delay;
6062 }
glennrp0fe50b42010-11-16 03:52:51 +00006063
cristy3ed852e2009-09-05 21:47:34 +00006064 else
6065 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006066
cristy3ed852e2009-09-05 21:47:34 +00006067 image->page.width=mng_info->mng_width;
6068 image->page.height=mng_info->mng_height;
6069 image->page.x=mng_info->x_off[object_id];
6070 image->page.y=mng_info->y_off[object_id];
6071 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006072
cristy3ed852e2009-09-05 21:47:34 +00006073 /*
6074 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6075 */
glennrp47b9dd52010-11-24 18:12:06 +00006076
cristy3ed852e2009-09-05 21:47:34 +00006077 if (logging != MagickFalse)
6078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6079 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6080 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006081
cristybb503372010-05-27 20:51:26 +00006082 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006083
cristy3ed852e2009-09-05 21:47:34 +00006084 if (offset < 0)
6085 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6086 }
6087
6088 previous=image;
6089 mng_info->image=image;
6090 mng_info->mng_type=mng_type;
6091 mng_info->object_id=object_id;
6092
6093 if (memcmp(type,mng_IHDR,4) == 0)
6094 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006095
cristy3ed852e2009-09-05 21:47:34 +00006096#if defined(JNG_SUPPORTED)
6097 else
6098 image=ReadOneJNGImage(mng_info,image_info,exception);
6099#endif
6100
6101 if (image == (Image *) NULL)
6102 {
6103 if (IsImageObject(previous) != MagickFalse)
6104 {
6105 (void) DestroyImageList(previous);
6106 (void) CloseBlob(previous);
6107 }
glennrp47b9dd52010-11-24 18:12:06 +00006108
cristy3ed852e2009-09-05 21:47:34 +00006109 MngInfoFreeStruct(mng_info,&have_mng_structure);
6110 return((Image *) NULL);
6111 }
glennrp0fe50b42010-11-16 03:52:51 +00006112
cristy3ed852e2009-09-05 21:47:34 +00006113 if (image->columns == 0 || image->rows == 0)
6114 {
6115 (void) CloseBlob(image);
6116 image=DestroyImageList(image);
6117 MngInfoFreeStruct(mng_info,&have_mng_structure);
6118 return((Image *) NULL);
6119 }
glennrp0fe50b42010-11-16 03:52:51 +00006120
cristy3ed852e2009-09-05 21:47:34 +00006121 mng_info->image=image;
6122
6123 if (mng_type)
6124 {
6125 MngBox
6126 crop_box;
6127
6128 if (mng_info->magn_methx || mng_info->magn_methy)
6129 {
6130 png_uint_32
6131 magnified_height,
6132 magnified_width;
6133
6134 if (logging != MagickFalse)
6135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6136 " Processing MNG MAGN chunk");
6137
6138 if (mng_info->magn_methx == 1)
6139 {
6140 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006141
cristy3ed852e2009-09-05 21:47:34 +00006142 if (image->columns > 1)
6143 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006144
cristy3ed852e2009-09-05 21:47:34 +00006145 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006146 magnified_width += (png_uint_32)
6147 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006148 }
glennrp47b9dd52010-11-24 18:12:06 +00006149
cristy3ed852e2009-09-05 21:47:34 +00006150 else
6151 {
cristy4e5bc842010-06-09 13:56:01 +00006152 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006153
cristy3ed852e2009-09-05 21:47:34 +00006154 if (image->columns > 1)
6155 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006156
cristy3ed852e2009-09-05 21:47:34 +00006157 if (image->columns > 2)
6158 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006159
cristy3ed852e2009-09-05 21:47:34 +00006160 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006161 magnified_width += (png_uint_32)
6162 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006163 }
glennrp47b9dd52010-11-24 18:12:06 +00006164
cristy3ed852e2009-09-05 21:47:34 +00006165 if (mng_info->magn_methy == 1)
6166 {
6167 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006168
cristy3ed852e2009-09-05 21:47:34 +00006169 if (image->rows > 1)
6170 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006171
cristy3ed852e2009-09-05 21:47:34 +00006172 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006173 magnified_height += (png_uint_32)
6174 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006175 }
glennrp47b9dd52010-11-24 18:12:06 +00006176
cristy3ed852e2009-09-05 21:47:34 +00006177 else
6178 {
cristy4e5bc842010-06-09 13:56:01 +00006179 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006180
cristy3ed852e2009-09-05 21:47:34 +00006181 if (image->rows > 1)
6182 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006183
cristy3ed852e2009-09-05 21:47:34 +00006184 if (image->rows > 2)
6185 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006186
cristy3ed852e2009-09-05 21:47:34 +00006187 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006188 magnified_height += (png_uint_32)
6189 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006190 }
glennrp47b9dd52010-11-24 18:12:06 +00006191
cristy3ed852e2009-09-05 21:47:34 +00006192 if (magnified_height > image->rows ||
6193 magnified_width > image->columns)
6194 {
6195 Image
6196 *large_image;
6197
6198 int
6199 yy;
6200
cristy4c08aed2011-07-01 19:47:50 +00006201 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006202 *next,
6203 *prev;
6204
6205 png_uint_16
6206 magn_methx,
6207 magn_methy;
6208
cristy4c08aed2011-07-01 19:47:50 +00006209 ssize_t
6210 m,
6211 y;
6212
6213 register Quantum
6214 *n,
6215 *q;
6216
6217 register ssize_t
6218 x;
6219
glennrp47b9dd52010-11-24 18:12:06 +00006220 /* Allocate next image structure. */
6221
cristy3ed852e2009-09-05 21:47:34 +00006222 if (logging != MagickFalse)
6223 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6224 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006225
cristy9950d572011-10-01 18:22:35 +00006226 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006227
cristy3ed852e2009-09-05 21:47:34 +00006228 if (GetNextImageInList(image) == (Image *) NULL)
6229 {
6230 image=DestroyImageList(image);
6231 MngInfoFreeStruct(mng_info,&have_mng_structure);
6232 return((Image *) NULL);
6233 }
6234
6235 large_image=SyncNextImageInList(image);
6236
6237 large_image->columns=magnified_width;
6238 large_image->rows=magnified_height;
6239
6240 magn_methx=mng_info->magn_methx;
6241 magn_methy=mng_info->magn_methy;
6242
glennrp3faa9a32011-04-23 14:00:25 +00006243#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006244#define QM unsigned short
6245 if (magn_methx != 1 || magn_methy != 1)
6246 {
6247 /*
6248 Scale pixels to unsigned shorts to prevent
6249 overflow of intermediate values of interpolations
6250 */
cristybb503372010-05-27 20:51:26 +00006251 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006252 {
6253 q=GetAuthenticPixels(image,0,y,image->columns,1,
6254 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006255
cristybb503372010-05-27 20:51:26 +00006256 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006257 {
cristy4c08aed2011-07-01 19:47:50 +00006258 SetPixelRed(image,ScaleQuantumToShort(
6259 GetPixelRed(image,q)),q);
6260 SetPixelGreen(image,ScaleQuantumToShort(
6261 GetPixelGreen(image,q)),q);
6262 SetPixelBlue(image,ScaleQuantumToShort(
6263 GetPixelBlue(image,q)),q);
6264 SetPixelAlpha(image,ScaleQuantumToShort(
6265 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006266 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006267 }
glennrp47b9dd52010-11-24 18:12:06 +00006268
cristy3ed852e2009-09-05 21:47:34 +00006269 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6270 break;
6271 }
6272 }
6273#else
6274#define QM Quantum
6275#endif
6276
6277 if (image->matte != MagickFalse)
cristyea1a8aa2011-10-20 13:24:06 +00006278 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006279
cristy3ed852e2009-09-05 21:47:34 +00006280 else
6281 {
cristy4c08aed2011-07-01 19:47:50 +00006282 large_image->background_color.alpha=OpaqueAlpha;
cristyea1a8aa2011-10-20 13:24:06 +00006283 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006284
cristy3ed852e2009-09-05 21:47:34 +00006285 if (magn_methx == 4)
6286 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006287
cristy3ed852e2009-09-05 21:47:34 +00006288 if (magn_methx == 5)
6289 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006290
cristy3ed852e2009-09-05 21:47:34 +00006291 if (magn_methy == 4)
6292 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006293
cristy3ed852e2009-09-05 21:47:34 +00006294 if (magn_methy == 5)
6295 magn_methy=3;
6296 }
6297
6298 /* magnify the rows into the right side of the large image */
6299
6300 if (logging != MagickFalse)
6301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006302 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006303 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006304 yy=0;
cristy8a20fa02011-12-27 15:54:31 +00006305 length=(size_t) image->columns*GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00006306 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6307 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006308
cristy4c08aed2011-07-01 19:47:50 +00006309 if ((prev == (Quantum *) NULL) ||
6310 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006311 {
6312 image=DestroyImageList(image);
6313 MngInfoFreeStruct(mng_info,&have_mng_structure);
6314 ThrowReaderException(ResourceLimitError,
6315 "MemoryAllocationFailed");
6316 }
glennrp47b9dd52010-11-24 18:12:06 +00006317
cristy3ed852e2009-09-05 21:47:34 +00006318 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6319 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006320
cristybb503372010-05-27 20:51:26 +00006321 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006322 {
6323 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006324 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006325
cristybb503372010-05-27 20:51:26 +00006326 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6327 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006328
cristybb503372010-05-27 20:51:26 +00006329 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6330 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006331
cristybb503372010-05-27 20:51:26 +00006332 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006333 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006334
cristy3ed852e2009-09-05 21:47:34 +00006335 else
cristybb503372010-05-27 20:51:26 +00006336 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006337
cristy3ed852e2009-09-05 21:47:34 +00006338 n=prev;
6339 prev=next;
6340 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006341
cristybb503372010-05-27 20:51:26 +00006342 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006343 {
6344 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6345 exception);
6346 (void) CopyMagickMemory(next,n,length);
6347 }
glennrp47b9dd52010-11-24 18:12:06 +00006348
cristy3ed852e2009-09-05 21:47:34 +00006349 for (i=0; i < m; i++, yy++)
6350 {
cristy4c08aed2011-07-01 19:47:50 +00006351 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006352 *pixels;
6353
cristybb503372010-05-27 20:51:26 +00006354 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006355 pixels=prev;
6356 n=next;
6357 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006358 1,exception);
cristy97707062011-12-27 18:25:00 +00006359 q+=(large_image->columns-image->columns)*
6360 GetPixelChannels(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006361
cristybb503372010-05-27 20:51:26 +00006362 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006363 {
glennrpfd05d622011-02-25 04:10:33 +00006364 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006365 /*
6366 if (image->storage_class == PseudoClass)
6367 {
6368 }
6369 */
6370
6371 if (magn_methy <= 1)
6372 {
glennrpbb4f99d2011-05-22 11:13:17 +00006373 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006374 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
glennrp847370c2011-07-05 17:37:15 +00006375 SetPixelGreen(large_image,GetPixelGreen(image,
6376 pixels),q);
6377 SetPixelBlue(large_image,GetPixelBlue(image,
6378 pixels),q);
6379 SetPixelAlpha(large_image,GetPixelAlpha(image,
6380 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006381 }
glennrp47b9dd52010-11-24 18:12:06 +00006382
cristy3ed852e2009-09-05 21:47:34 +00006383 else if (magn_methy == 2 || magn_methy == 4)
6384 {
6385 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006386 {
glennrp847370c2011-07-05 17:37:15 +00006387 SetPixelRed(large_image,GetPixelRed(image,
6388 pixels),q);
6389 SetPixelGreen(large_image,GetPixelGreen(image,
6390 pixels),q);
6391 SetPixelBlue(large_image,GetPixelBlue(image,
6392 pixels),q);
6393 SetPixelAlpha(large_image,GetPixelAlpha(image,
6394 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006395 }
glennrp47b9dd52010-11-24 18:12:06 +00006396
cristy3ed852e2009-09-05 21:47:34 +00006397 else
6398 {
6399 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006400 SetPixelRed(large_image,((QM) (((ssize_t)
6401 (2*i*(GetPixelRed(image,n)
6402 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006403 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006404 +GetPixelRed(image,pixels)))),q);
6405 SetPixelGreen(large_image,((QM) (((ssize_t)
6406 (2*i*(GetPixelGreen(image,n)
6407 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006408 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006409 +GetPixelGreen(image,pixels)))),q);
6410 SetPixelBlue(large_image,((QM) (((ssize_t)
6411 (2*i*(GetPixelBlue(image,n)
6412 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006413 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006414 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006415
cristy3ed852e2009-09-05 21:47:34 +00006416 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006417 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6418 (2*i*(GetPixelAlpha(image,n)
6419 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006420 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006421 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006422 }
glennrp47b9dd52010-11-24 18:12:06 +00006423
cristy3ed852e2009-09-05 21:47:34 +00006424 if (magn_methy == 4)
6425 {
6426 /* Replicate nearest */
6427 if (i <= ((m+1) << 1))
glennrp847370c2011-07-05 17:37:15 +00006428 SetPixelAlpha(large_image,GetPixelAlpha(image,
6429 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006430 else
glennrp847370c2011-07-05 17:37:15 +00006431 SetPixelAlpha(large_image,GetPixelAlpha(image,
6432 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006433 }
6434 }
glennrp47b9dd52010-11-24 18:12:06 +00006435
cristy3ed852e2009-09-05 21:47:34 +00006436 else /* if (magn_methy == 3 || magn_methy == 5) */
6437 {
6438 /* Replicate nearest */
6439 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006440 {
glennrp847370c2011-07-05 17:37:15 +00006441 SetPixelRed(large_image,GetPixelRed(image,
6442 pixels),q);
6443 SetPixelGreen(large_image,GetPixelGreen(image,
6444 pixels),q);
6445 SetPixelBlue(large_image,GetPixelBlue(image,
6446 pixels),q);
6447 SetPixelAlpha(large_image,GetPixelAlpha(image,
6448 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006449 }
glennrp47b9dd52010-11-24 18:12:06 +00006450
cristy3ed852e2009-09-05 21:47:34 +00006451 else
glennrpbb4f99d2011-05-22 11:13:17 +00006452 {
cristy4c08aed2011-07-01 19:47:50 +00006453 SetPixelRed(large_image,GetPixelRed(image,n),q);
glennrp847370c2011-07-05 17:37:15 +00006454 SetPixelGreen(large_image,GetPixelGreen(image,n),
6455 q);
6456 SetPixelBlue(large_image,GetPixelBlue(image,n),
6457 q);
6458 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6459 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006460 }
glennrp47b9dd52010-11-24 18:12:06 +00006461
cristy3ed852e2009-09-05 21:47:34 +00006462 if (magn_methy == 5)
6463 {
cristy4c08aed2011-07-01 19:47:50 +00006464 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6465 (GetPixelAlpha(image,n)
6466 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006467 +m))/((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006468 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006469 }
6470 }
cristyed231572011-07-14 02:18:59 +00006471 n+=GetPixelChannels(image);
6472 q+=GetPixelChannels(large_image);
6473 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006474 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006475
cristy3ed852e2009-09-05 21:47:34 +00006476 if (SyncAuthenticPixels(large_image,exception) == 0)
6477 break;
glennrp47b9dd52010-11-24 18:12:06 +00006478
cristy3ed852e2009-09-05 21:47:34 +00006479 } /* i */
6480 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006481
cristy4c08aed2011-07-01 19:47:50 +00006482 prev=(Quantum *) RelinquishMagickMemory(prev);
6483 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006484
6485 length=image->columns;
6486
6487 if (logging != MagickFalse)
6488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6489 " Delete original image");
6490
6491 DeleteImageFromList(&image);
6492
6493 image=large_image;
6494
6495 mng_info->image=image;
6496
6497 /* magnify the columns */
6498 if (logging != MagickFalse)
6499 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006500 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006501
cristybb503372010-05-27 20:51:26 +00006502 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006503 {
cristy4c08aed2011-07-01 19:47:50 +00006504 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006505 *pixels;
6506
6507 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristyed231572011-07-14 02:18:59 +00006508 pixels=q+(image->columns-length)*GetPixelChannels(image);
6509 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006510
cristybb503372010-05-27 20:51:26 +00006511 for (x=(ssize_t) (image->columns-length);
6512 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006513 {
cristyed231572011-07-14 02:18:59 +00006514 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006515
cristybb503372010-05-27 20:51:26 +00006516 if (x == (ssize_t) (image->columns-length))
6517 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006518
cristybb503372010-05-27 20:51:26 +00006519 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6520 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006521
cristybb503372010-05-27 20:51:26 +00006522 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6523 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006524
cristybb503372010-05-27 20:51:26 +00006525 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006526 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006527
cristy3ed852e2009-09-05 21:47:34 +00006528 else
cristybb503372010-05-27 20:51:26 +00006529 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006530
cristy3ed852e2009-09-05 21:47:34 +00006531 for (i=0; i < m; i++)
6532 {
6533 if (magn_methx <= 1)
6534 {
6535 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006536 SetPixelRed(image,GetPixelRed(image,pixels),q);
6537 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6538 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6539 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006540 }
glennrp47b9dd52010-11-24 18:12:06 +00006541
cristy3ed852e2009-09-05 21:47:34 +00006542 else if (magn_methx == 2 || magn_methx == 4)
6543 {
6544 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006545 {
cristy4c08aed2011-07-01 19:47:50 +00006546 SetPixelRed(image,GetPixelRed(image,pixels),q);
6547 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6548 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6549 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006550 }
glennrp47b9dd52010-11-24 18:12:06 +00006551
cristyed231572011-07-14 02:18:59 +00006552 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006553 else
6554 {
6555 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006556 SetPixelRed(image,(QM) ((2*i*(
6557 GetPixelRed(image,n)
6558 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006559 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006560 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006561
cristy4c08aed2011-07-01 19:47:50 +00006562 SetPixelGreen(image,(QM) ((2*i*(
6563 GetPixelGreen(image,n)
6564 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006565 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006566 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006567
cristy4c08aed2011-07-01 19:47:50 +00006568 SetPixelBlue(image,(QM) ((2*i*(
6569 GetPixelBlue(image,n)
6570 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006571 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006572 GetPixelBlue(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006573 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006574 SetPixelAlpha(image,(QM) ((2*i*(
6575 GetPixelAlpha(image,n)
6576 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006577 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006578 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006579 }
glennrp47b9dd52010-11-24 18:12:06 +00006580
cristy3ed852e2009-09-05 21:47:34 +00006581 if (magn_methx == 4)
6582 {
6583 /* Replicate nearest */
6584 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006585 {
cristy4c08aed2011-07-01 19:47:50 +00006586 SetPixelAlpha(image,
6587 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006588 }
cristy3ed852e2009-09-05 21:47:34 +00006589 else
glennrpbb4f99d2011-05-22 11:13:17 +00006590 {
cristy4c08aed2011-07-01 19:47:50 +00006591 SetPixelAlpha(image,
6592 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006593 }
cristy3ed852e2009-09-05 21:47:34 +00006594 }
6595 }
glennrp47b9dd52010-11-24 18:12:06 +00006596
cristy3ed852e2009-09-05 21:47:34 +00006597 else /* if (magn_methx == 3 || magn_methx == 5) */
6598 {
6599 /* Replicate nearest */
6600 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006601 {
cristy4c08aed2011-07-01 19:47:50 +00006602 SetPixelRed(image,GetPixelRed(image,pixels),q);
6603 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6604 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6605 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006606 }
glennrp47b9dd52010-11-24 18:12:06 +00006607
cristy3ed852e2009-09-05 21:47:34 +00006608 else
glennrpbb4f99d2011-05-22 11:13:17 +00006609 {
cristy4c08aed2011-07-01 19:47:50 +00006610 SetPixelRed(image,GetPixelRed(image,n),q);
6611 SetPixelGreen(image,GetPixelGreen(image,n),q);
6612 SetPixelBlue(image,GetPixelBlue(image,n),q);
6613 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006614 }
glennrp47b9dd52010-11-24 18:12:06 +00006615
cristy3ed852e2009-09-05 21:47:34 +00006616 if (magn_methx == 5)
6617 {
6618 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006619 SetPixelAlpha(image,
6620 (QM) ((2*i*( GetPixelAlpha(image,n)
6621 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006622 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006623 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006624 }
6625 }
cristyed231572011-07-14 02:18:59 +00006626 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006627 }
cristyed231572011-07-14 02:18:59 +00006628 n+=GetPixelChannels(image);
6629 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006630 }
glennrp47b9dd52010-11-24 18:12:06 +00006631
cristy3ed852e2009-09-05 21:47:34 +00006632 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6633 break;
6634 }
glennrp3faa9a32011-04-23 14:00:25 +00006635#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006636 if (magn_methx != 1 || magn_methy != 1)
6637 {
6638 /*
6639 Rescale pixels to Quantum
6640 */
cristybb503372010-05-27 20:51:26 +00006641 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006642 {
6643 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006644
cristybb503372010-05-27 20:51:26 +00006645 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006646 {
cristy4c08aed2011-07-01 19:47:50 +00006647 SetPixelRed(image,ScaleShortToQuantum(
6648 GetPixelRed(image,q)),q);
6649 SetPixelGreen(image,ScaleShortToQuantum(
6650 GetPixelGreen(image,q)),q);
6651 SetPixelBlue(image,ScaleShortToQuantum(
6652 GetPixelBlue(image,q)),q);
6653 SetPixelAlpha(image,ScaleShortToQuantum(
6654 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006655 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006656 }
glennrp47b9dd52010-11-24 18:12:06 +00006657
cristy3ed852e2009-09-05 21:47:34 +00006658 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6659 break;
6660 }
6661 }
6662#endif
6663 if (logging != MagickFalse)
6664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6665 " Finished MAGN processing");
6666 }
6667 }
6668
6669 /*
6670 Crop_box is with respect to the upper left corner of the MNG.
6671 */
6672 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6673 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6674 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6675 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6676 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6677 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6678 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6679 if ((crop_box.left != (mng_info->image_box.left
6680 +mng_info->x_off[object_id])) ||
6681 (crop_box.right != (mng_info->image_box.right
6682 +mng_info->x_off[object_id])) ||
6683 (crop_box.top != (mng_info->image_box.top
6684 +mng_info->y_off[object_id])) ||
6685 (crop_box.bottom != (mng_info->image_box.bottom
6686 +mng_info->y_off[object_id])))
6687 {
6688 if (logging != MagickFalse)
6689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6690 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006691
cristy3ed852e2009-09-05 21:47:34 +00006692 if ((crop_box.left < crop_box.right) &&
6693 (crop_box.top < crop_box.bottom))
6694 {
6695 Image
6696 *im;
6697
6698 RectangleInfo
6699 crop_info;
6700
6701 /*
6702 Crop_info is with respect to the upper left corner of
6703 the image.
6704 */
6705 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6706 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006707 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6708 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006709 image->page.width=image->columns;
6710 image->page.height=image->rows;
6711 image->page.x=0;
6712 image->page.y=0;
6713 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006714
cristy3ed852e2009-09-05 21:47:34 +00006715 if (im != (Image *) NULL)
6716 {
6717 image->columns=im->columns;
6718 image->rows=im->rows;
6719 im=DestroyImage(im);
6720 image->page.width=image->columns;
6721 image->page.height=image->rows;
6722 image->page.x=crop_box.left;
6723 image->page.y=crop_box.top;
6724 }
6725 }
glennrp47b9dd52010-11-24 18:12:06 +00006726
cristy3ed852e2009-09-05 21:47:34 +00006727 else
6728 {
6729 /*
6730 No pixels in crop area. The MNG spec still requires
6731 a layer, though, so make a single transparent pixel in
6732 the top left corner.
6733 */
6734 image->columns=1;
6735 image->rows=1;
6736 image->colors=2;
cristyea1a8aa2011-10-20 13:24:06 +00006737 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006738 image->page.width=1;
6739 image->page.height=1;
6740 image->page.x=0;
6741 image->page.y=0;
6742 }
6743 }
6744#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6745 image=mng_info->image;
6746#endif
6747 }
6748
glennrp2b013e42010-11-24 16:55:50 +00006749#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6750 /* PNG does not handle depths greater than 16 so reduce it even
glennrpcc5d45b2012-01-06 04:06:10 +00006751 * if lossy.
glennrp2b013e42010-11-24 16:55:50 +00006752 */
6753 if (image->depth > 16)
6754 image->depth=16;
6755#endif
6756
glennrp3faa9a32011-04-23 14:00:25 +00006757#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00006758 if (image->depth > 8)
6759 {
6760 /* To do: fill low byte properly */
6761 image->depth=16;
6762 }
6763
cristyc82a27b2011-10-21 01:07:16 +00006764 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00006765 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006766#endif
glennrpd6afd542010-11-19 01:53:05 +00006767
cristy3ed852e2009-09-05 21:47:34 +00006768 if (image_info->number_scenes != 0)
6769 {
6770 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006771 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006772 break;
6773 }
glennrpd6afd542010-11-19 01:53:05 +00006774
cristy3ed852e2009-09-05 21:47:34 +00006775 if (logging != MagickFalse)
6776 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6777 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006778
cristy3ed852e2009-09-05 21:47:34 +00006779 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006780
cristy3ed852e2009-09-05 21:47:34 +00006781 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006782
cristy3ed852e2009-09-05 21:47:34 +00006783 if (logging != MagickFalse)
6784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6785 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006786
cristy3ed852e2009-09-05 21:47:34 +00006787#if defined(MNG_INSERT_LAYERS)
6788 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6789 (mng_info->mng_height))
6790 {
6791 /*
6792 Insert a background layer if nothing else was found.
6793 */
6794 if (logging != MagickFalse)
6795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6796 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006797
cristy4c08aed2011-07-01 19:47:50 +00006798 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006799 {
6800 /*
6801 Allocate next image structure.
6802 */
cristy9950d572011-10-01 18:22:35 +00006803 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006804 if (GetNextImageInList(image) == (Image *) NULL)
6805 {
6806 image=DestroyImageList(image);
6807 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006808
cristy3ed852e2009-09-05 21:47:34 +00006809 if (logging != MagickFalse)
6810 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6811 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006812
cristy3ed852e2009-09-05 21:47:34 +00006813 return((Image *) NULL);
6814 }
6815 image=SyncNextImageInList(image);
6816 }
6817 image->columns=mng_info->mng_width;
6818 image->rows=mng_info->mng_height;
6819 image->page.width=mng_info->mng_width;
6820 image->page.height=mng_info->mng_height;
6821 image->page.x=0;
6822 image->page.y=0;
6823 image->background_color=mng_background_color;
6824 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006825
cristy3ed852e2009-09-05 21:47:34 +00006826 if (image_info->ping == MagickFalse)
cristyea1a8aa2011-10-20 13:24:06 +00006827 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006828
cristy3ed852e2009-09-05 21:47:34 +00006829 mng_info->image_found++;
6830 }
6831#endif
6832 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006833
cristy3ed852e2009-09-05 21:47:34 +00006834 if (mng_iterations == 1)
6835 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006836
cristy3ed852e2009-09-05 21:47:34 +00006837 while (GetPreviousImageInList(image) != (Image *) NULL)
6838 {
6839 image_count++;
6840 if (image_count > 10*mng_info->image_found)
6841 {
6842 if (logging != MagickFalse)
6843 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006844
cristyc82a27b2011-10-21 01:07:16 +00006845 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006846 CoderError,"Linked list is corrupted, beginning of list not found",
6847 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006848
cristy3ed852e2009-09-05 21:47:34 +00006849 return((Image *) NULL);
6850 }
glennrp0fe50b42010-11-16 03:52:51 +00006851
cristy3ed852e2009-09-05 21:47:34 +00006852 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006853
cristy3ed852e2009-09-05 21:47:34 +00006854 if (GetNextImageInList(image) == (Image *) NULL)
6855 {
6856 if (logging != MagickFalse)
6857 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006858
cristyc82a27b2011-10-21 01:07:16 +00006859 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006860 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6861 image_info->filename);
6862 }
6863 }
glennrp47b9dd52010-11-24 18:12:06 +00006864
cristy3ed852e2009-09-05 21:47:34 +00006865 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6866 GetNextImageInList(image) ==
6867 (Image *) NULL)
6868 {
6869 if (logging != MagickFalse)
6870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6871 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006872
cristyc82a27b2011-10-21 01:07:16 +00006873 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006874 CoderError,"image->next for first image is NULL but shouldn't be.",
6875 "`%s'",image_info->filename);
6876 }
glennrp47b9dd52010-11-24 18:12:06 +00006877
cristy3ed852e2009-09-05 21:47:34 +00006878 if (mng_info->image_found == 0)
6879 {
6880 if (logging != MagickFalse)
6881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6882 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006883
cristyc82a27b2011-10-21 01:07:16 +00006884 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006885 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006886
cristy3ed852e2009-09-05 21:47:34 +00006887 if (image != (Image *) NULL)
6888 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006889
cristy3ed852e2009-09-05 21:47:34 +00006890 MngInfoFreeStruct(mng_info,&have_mng_structure);
6891 return((Image *) NULL);
6892 }
6893
6894 if (mng_info->ticks_per_second)
6895 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6896 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006897
cristy3ed852e2009-09-05 21:47:34 +00006898 else
6899 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006900
cristy3ed852e2009-09-05 21:47:34 +00006901 /* Find final nonzero image delay */
6902 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006903
cristy3ed852e2009-09-05 21:47:34 +00006904 while (GetNextImageInList(image) != (Image *) NULL)
6905 {
6906 if (image->delay)
6907 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006908
cristy3ed852e2009-09-05 21:47:34 +00006909 image=GetNextImageInList(image);
6910 }
glennrp0fe50b42010-11-16 03:52:51 +00006911
cristy3ed852e2009-09-05 21:47:34 +00006912 if (final_delay < final_image_delay)
6913 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006914
cristy3ed852e2009-09-05 21:47:34 +00006915 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006916
cristy3ed852e2009-09-05 21:47:34 +00006917 if (logging != MagickFalse)
6918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006919 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6920 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006921
cristy3ed852e2009-09-05 21:47:34 +00006922 if (logging != MagickFalse)
6923 {
6924 int
6925 scene;
6926
6927 scene=0;
6928 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006929
cristy3ed852e2009-09-05 21:47:34 +00006930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6931 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006932
cristy3ed852e2009-09-05 21:47:34 +00006933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006934 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006935
cristy3ed852e2009-09-05 21:47:34 +00006936 while (GetNextImageInList(image) != (Image *) NULL)
6937 {
6938 image=GetNextImageInList(image);
6939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006940 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006941 }
6942 }
6943
6944 image=GetFirstImageInList(image);
6945#ifdef MNG_COALESCE_LAYERS
6946 if (insert_layers)
6947 {
6948 Image
6949 *next_image,
6950 *next;
6951
cristybb503372010-05-27 20:51:26 +00006952 size_t
cristy3ed852e2009-09-05 21:47:34 +00006953 scene;
6954
6955 if (logging != MagickFalse)
6956 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006957
cristy3ed852e2009-09-05 21:47:34 +00006958 scene=image->scene;
cristyc82a27b2011-10-21 01:07:16 +00006959 next_image=CoalesceImages(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006960
cristy3ed852e2009-09-05 21:47:34 +00006961 if (next_image == (Image *) NULL)
6962 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006963
cristy3ed852e2009-09-05 21:47:34 +00006964 image=DestroyImageList(image);
6965 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006966
cristy3ed852e2009-09-05 21:47:34 +00006967 for (next=image; next != (Image *) NULL; next=next_image)
6968 {
6969 next->page.width=mng_info->mng_width;
6970 next->page.height=mng_info->mng_height;
6971 next->page.x=0;
6972 next->page.y=0;
6973 next->scene=scene++;
6974 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006975
cristy3ed852e2009-09-05 21:47:34 +00006976 if (next_image == (Image *) NULL)
6977 break;
glennrp47b9dd52010-11-24 18:12:06 +00006978
cristy3ed852e2009-09-05 21:47:34 +00006979 if (next->delay == 0)
6980 {
6981 scene--;
6982 next_image->previous=GetPreviousImageInList(next);
6983 if (GetPreviousImageInList(next) == (Image *) NULL)
6984 image=next_image;
6985 else
6986 next->previous->next=next_image;
6987 next=DestroyImage(next);
6988 }
6989 }
6990 }
6991#endif
6992
6993 while (GetNextImageInList(image) != (Image *) NULL)
6994 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006995
cristy3ed852e2009-09-05 21:47:34 +00006996 image->dispose=BackgroundDispose;
6997
6998 if (logging != MagickFalse)
6999 {
7000 int
7001 scene;
7002
7003 scene=0;
7004 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007005
cristy3ed852e2009-09-05 21:47:34 +00007006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7007 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00007008
cristy3ed852e2009-09-05 21:47:34 +00007009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007010 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7011 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00007012
cristy3ed852e2009-09-05 21:47:34 +00007013 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00007014 {
7015 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007016
cristyf2faecf2010-05-28 19:19:36 +00007017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007018 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7019 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00007020 }
7021 }
glennrp47b9dd52010-11-24 18:12:06 +00007022
cristy3ed852e2009-09-05 21:47:34 +00007023 image=GetFirstImageInList(image);
7024 MngInfoFreeStruct(mng_info,&have_mng_structure);
7025 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00007026
cristy3ed852e2009-09-05 21:47:34 +00007027 if (logging != MagickFalse)
7028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00007029
cristy3ed852e2009-09-05 21:47:34 +00007030 return(GetFirstImageInList(image));
7031}
glennrp25c1e2b2010-03-25 01:39:56 +00007032#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007033static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7034{
7035 printf("Your PNG library is too old: You have libpng-%s\n",
7036 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00007037
cristy3ed852e2009-09-05 21:47:34 +00007038 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7039 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007040
cristy3ed852e2009-09-05 21:47:34 +00007041 return(Image *) NULL;
7042}
glennrp47b9dd52010-11-24 18:12:06 +00007043
cristy3ed852e2009-09-05 21:47:34 +00007044static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7045{
7046 return(ReadPNGImage(image_info,exception));
7047}
glennrp25c1e2b2010-03-25 01:39:56 +00007048#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007049#endif
7050
7051/*
7052%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7053% %
7054% %
7055% %
7056% R e g i s t e r P N G I m a g e %
7057% %
7058% %
7059% %
7060%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7061%
7062% RegisterPNGImage() adds properties for the PNG image format to
7063% the list of supported formats. The properties include the image format
7064% tag, a method to read and/or write the format, whether the format
7065% supports the saving of more than one frame to the same file or blob,
7066% whether the format supports native in-memory I/O, and a brief
7067% description of the format.
7068%
7069% The format of the RegisterPNGImage method is:
7070%
cristybb503372010-05-27 20:51:26 +00007071% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007072%
7073*/
cristybb503372010-05-27 20:51:26 +00007074ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007075{
7076 char
7077 version[MaxTextExtent];
7078
7079 MagickInfo
7080 *entry;
7081
7082 static const char
7083 *PNGNote=
7084 {
7085 "See http://www.libpng.org/ for details about the PNG format."
7086 },
glennrp47b9dd52010-11-24 18:12:06 +00007087
cristy3ed852e2009-09-05 21:47:34 +00007088 *JNGNote=
7089 {
7090 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7091 "format."
7092 },
glennrp47b9dd52010-11-24 18:12:06 +00007093
cristy3ed852e2009-09-05 21:47:34 +00007094 *MNGNote=
7095 {
7096 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7097 "format."
7098 };
7099
7100 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007101
cristy3ed852e2009-09-05 21:47:34 +00007102#if defined(PNG_LIBPNG_VER_STRING)
7103 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7104 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007105
cristy3ed852e2009-09-05 21:47:34 +00007106 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7107 {
7108 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7109 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7110 MaxTextExtent);
7111 }
7112#endif
glennrp47b9dd52010-11-24 18:12:06 +00007113
cristy3ed852e2009-09-05 21:47:34 +00007114 entry=SetMagickInfo("MNG");
7115 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007116
cristy3ed852e2009-09-05 21:47:34 +00007117#if defined(MAGICKCORE_PNG_DELEGATE)
7118 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7119 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7120#endif
glennrp47b9dd52010-11-24 18:12:06 +00007121
cristy3ed852e2009-09-05 21:47:34 +00007122 entry->magick=(IsImageFormatHandler *) IsMNG;
7123 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007124
cristy3ed852e2009-09-05 21:47:34 +00007125 if (*version != '\0')
7126 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007127
cristy3ed852e2009-09-05 21:47:34 +00007128 entry->module=ConstantString("PNG");
7129 entry->note=ConstantString(MNGNote);
7130 (void) RegisterMagickInfo(entry);
7131
7132 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007133
cristy3ed852e2009-09-05 21:47:34 +00007134#if defined(MAGICKCORE_PNG_DELEGATE)
7135 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7136 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7137#endif
glennrp47b9dd52010-11-24 18:12:06 +00007138
cristy3ed852e2009-09-05 21:47:34 +00007139 entry->magick=(IsImageFormatHandler *) IsPNG;
7140 entry->adjoin=MagickFalse;
7141 entry->description=ConstantString("Portable Network Graphics");
7142 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007143
cristy3ed852e2009-09-05 21:47:34 +00007144 if (*version != '\0')
7145 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007146
cristy3ed852e2009-09-05 21:47:34 +00007147 entry->note=ConstantString(PNGNote);
7148 (void) RegisterMagickInfo(entry);
7149
7150 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007151
cristy3ed852e2009-09-05 21:47:34 +00007152#if defined(MAGICKCORE_PNG_DELEGATE)
7153 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7154 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7155#endif
glennrp47b9dd52010-11-24 18:12:06 +00007156
cristy3ed852e2009-09-05 21:47:34 +00007157 entry->magick=(IsImageFormatHandler *) IsPNG;
7158 entry->adjoin=MagickFalse;
7159 entry->description=ConstantString(
7160 "8-bit indexed with optional binary transparency");
7161 entry->module=ConstantString("PNG");
7162 (void) RegisterMagickInfo(entry);
7163
7164 entry=SetMagickInfo("PNG24");
7165 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007166
cristy3ed852e2009-09-05 21:47:34 +00007167#if defined(ZLIB_VERSION)
7168 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7169 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007170
cristy3ed852e2009-09-05 21:47:34 +00007171 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7172 {
7173 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7174 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7175 }
7176#endif
glennrp47b9dd52010-11-24 18:12:06 +00007177
cristy3ed852e2009-09-05 21:47:34 +00007178 if (*version != '\0')
7179 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007180
cristy3ed852e2009-09-05 21:47:34 +00007181#if defined(MAGICKCORE_PNG_DELEGATE)
7182 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7183 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7184#endif
glennrp47b9dd52010-11-24 18:12:06 +00007185
cristy3ed852e2009-09-05 21:47:34 +00007186 entry->magick=(IsImageFormatHandler *) IsPNG;
7187 entry->adjoin=MagickFalse;
7188 entry->description=ConstantString("opaque 24-bit RGB");
7189 entry->module=ConstantString("PNG");
7190 (void) RegisterMagickInfo(entry);
7191
7192 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007193
cristy3ed852e2009-09-05 21:47:34 +00007194#if defined(MAGICKCORE_PNG_DELEGATE)
7195 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7196 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7197#endif
glennrp47b9dd52010-11-24 18:12:06 +00007198
cristy3ed852e2009-09-05 21:47:34 +00007199 entry->magick=(IsImageFormatHandler *) IsPNG;
7200 entry->adjoin=MagickFalse;
7201 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7202 entry->module=ConstantString("PNG");
7203 (void) RegisterMagickInfo(entry);
7204
7205 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007206
cristy3ed852e2009-09-05 21:47:34 +00007207#if defined(JNG_SUPPORTED)
7208#if defined(MAGICKCORE_PNG_DELEGATE)
7209 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7210 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7211#endif
7212#endif
glennrp47b9dd52010-11-24 18:12:06 +00007213
cristy3ed852e2009-09-05 21:47:34 +00007214 entry->magick=(IsImageFormatHandler *) IsJNG;
7215 entry->adjoin=MagickFalse;
7216 entry->description=ConstantString("JPEG Network Graphics");
7217 entry->module=ConstantString("PNG");
7218 entry->note=ConstantString(JNGNote);
7219 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007220
cristy18b17442009-10-25 18:36:48 +00007221#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007222 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007223#endif
glennrp47b9dd52010-11-24 18:12:06 +00007224
cristy3ed852e2009-09-05 21:47:34 +00007225 return(MagickImageCoderSignature);
7226}
7227
7228/*
7229%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7230% %
7231% %
7232% %
7233% U n r e g i s t e r P N G I m a g e %
7234% %
7235% %
7236% %
7237%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7238%
7239% UnregisterPNGImage() removes format registrations made by the
7240% PNG module from the list of supported formats.
7241%
7242% The format of the UnregisterPNGImage method is:
7243%
7244% UnregisterPNGImage(void)
7245%
7246*/
7247ModuleExport void UnregisterPNGImage(void)
7248{
7249 (void) UnregisterMagickInfo("MNG");
7250 (void) UnregisterMagickInfo("PNG");
7251 (void) UnregisterMagickInfo("PNG8");
7252 (void) UnregisterMagickInfo("PNG24");
7253 (void) UnregisterMagickInfo("PNG32");
7254 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007255
cristy3ed852e2009-09-05 21:47:34 +00007256#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007257 if (ping_semaphore != (SemaphoreInfo *) NULL)
7258 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007259#endif
7260}
7261
7262#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007263#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007264/*
7265%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7266% %
7267% %
7268% %
7269% W r i t e M N G I m a g e %
7270% %
7271% %
7272% %
7273%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7274%
7275% WriteMNGImage() writes an image in the Portable Network Graphics
7276% Group's "Multiple-image Network Graphics" encoded image format.
7277%
7278% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7279%
7280% The format of the WriteMNGImage method is:
7281%
cristy1e178e72011-08-28 19:44:34 +00007282% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7283% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007284%
7285% A description of each parameter follows.
7286%
7287% o image_info: the image info.
7288%
7289% o image: The image.
7290%
cristy1e178e72011-08-28 19:44:34 +00007291% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007292%
7293% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7294% "To do" under ReadPNGImage):
7295%
cristy3ed852e2009-09-05 21:47:34 +00007296% Preserve all unknown and not-yet-handled known chunks found in input
7297% PNG file and copy them into output PNG files according to the PNG
7298% copying rules.
7299%
7300% Write the iCCP chunk at MNG level when (icc profile length > 0)
7301%
7302% Improve selection of color type (use indexed-colour or indexed-colour
7303% with tRNS when 256 or fewer unique RGBA values are present).
7304%
7305% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7306% This will be complicated if we limit ourselves to generating MNG-LC
7307% files. For now we ignore disposal method 3 and simply overlay the next
7308% image on it.
7309%
7310% Check for identical PLTE's or PLTE/tRNS combinations and use a
7311% global MNG PLTE or PLTE/tRNS combination when appropriate.
7312% [mostly done 15 June 1999 but still need to take care of tRNS]
7313%
7314% Check for identical sRGB and replace with a global sRGB (and remove
7315% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7316% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7317% local gAMA/cHRM with local sRGB if appropriate).
7318%
7319% Check for identical sBIT chunks and write global ones.
7320%
7321% Provide option to skip writing the signature tEXt chunks.
7322%
7323% Use signatures to detect identical objects and reuse the first
7324% instance of such objects instead of writing duplicate objects.
7325%
7326% Use a smaller-than-32k value of compression window size when
7327% appropriate.
7328%
7329% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7330% ancillary text chunks and save profiles.
7331%
7332% Provide an option to force LC files (to ensure exact framing rate)
7333% instead of VLC.
7334%
7335% Provide an option to force VLC files instead of LC, even when offsets
7336% are present. This will involve expanding the embedded images with a
7337% transparent region at the top and/or left.
7338*/
7339
cristy3ed852e2009-09-05 21:47:34 +00007340static void
glennrpcf002022011-01-30 02:38:15 +00007341Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007342 png_info *ping_info, unsigned char *profile_type, unsigned char
7343 *profile_description, unsigned char *profile_data, png_uint_32 length)
7344{
cristy3ed852e2009-09-05 21:47:34 +00007345 png_textp
7346 text;
7347
cristybb503372010-05-27 20:51:26 +00007348 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007349 i;
7350
7351 unsigned char
7352 *sp;
7353
7354 png_charp
7355 dp;
7356
7357 png_uint_32
7358 allocated_length,
7359 description_length;
7360
7361 unsigned char
7362 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007363
cristy3ed852e2009-09-05 21:47:34 +00007364 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7365 return;
7366
7367 if (image_info->verbose)
7368 {
glennrp0fe50b42010-11-16 03:52:51 +00007369 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7370 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007371 }
glennrp0fe50b42010-11-16 03:52:51 +00007372
cristy3ed852e2009-09-05 21:47:34 +00007373 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7374 description_length=(png_uint_32) strlen((const char *) profile_description);
7375 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7376 + description_length);
7377 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7378 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7379 text[0].key[0]='\0';
7380 (void) ConcatenateMagickString(text[0].key,
7381 "Raw profile type ",MaxTextExtent);
7382 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7383 sp=profile_data;
7384 dp=text[0].text;
7385 *dp++='\n';
7386 (void) CopyMagickString(dp,(const char *) profile_description,
7387 allocated_length);
7388 dp+=description_length;
7389 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007390 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007391 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007392 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007393
cristybb503372010-05-27 20:51:26 +00007394 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007395 {
7396 if (i%36 == 0)
7397 *dp++='\n';
7398 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7399 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7400 }
glennrp47b9dd52010-11-24 18:12:06 +00007401
cristy3ed852e2009-09-05 21:47:34 +00007402 *dp++='\n';
7403 *dp='\0';
7404 text[0].text_length=(png_size_t) (dp-text[0].text);
7405 text[0].compression=image_info->compression == NoCompression ||
7406 (image_info->compression == UndefinedCompression &&
7407 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007408
cristy3ed852e2009-09-05 21:47:34 +00007409 if (text[0].text_length <= allocated_length)
7410 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007411
cristy3ed852e2009-09-05 21:47:34 +00007412 png_free(ping,text[0].text);
7413 png_free(ping,text[0].key);
7414 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007415}
7416
glennrpcf002022011-01-30 02:38:15 +00007417static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007418 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007419{
7420 char
7421 *name;
7422
7423 const StringInfo
7424 *profile;
7425
7426 unsigned char
7427 *data;
7428
7429 png_uint_32 length;
7430
7431 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007432
7433 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7434 {
cristy3ed852e2009-09-05 21:47:34 +00007435 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007436
cristy3ed852e2009-09-05 21:47:34 +00007437 if (profile != (const StringInfo *) NULL)
7438 {
7439 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007440 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007441
glennrp47b9dd52010-11-24 18:12:06 +00007442 if (LocaleNCompare(name,string,11) == 0)
7443 {
7444 if (logging != MagickFalse)
7445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7446 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007447
glennrpcf002022011-01-30 02:38:15 +00007448 ping_profile=CloneStringInfo(profile);
7449 data=GetStringInfoDatum(ping_profile),
7450 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007451 data[4]=data[3];
7452 data[3]=data[2];
7453 data[2]=data[1];
7454 data[1]=data[0];
7455 (void) WriteBlobMSBULong(image,length-5); /* data length */
7456 (void) WriteBlob(image,length-1,data+1);
7457 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007458 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007459 }
cristy3ed852e2009-09-05 21:47:34 +00007460 }
glennrp47b9dd52010-11-24 18:12:06 +00007461
cristy3ed852e2009-09-05 21:47:34 +00007462 name=GetNextImageProfile(image);
7463 }
glennrp47b9dd52010-11-24 18:12:06 +00007464
cristy3ed852e2009-09-05 21:47:34 +00007465 return(MagickTrue);
7466}
7467
glennrpb9cfe272010-12-21 15:08:06 +00007468
cristy3ed852e2009-09-05 21:47:34 +00007469/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007470static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +00007471 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007472{
7473 Image
7474 *image;
7475
7476 ImageInfo
7477 *image_info;
7478
cristy3ed852e2009-09-05 21:47:34 +00007479 char
7480 s[2];
7481
7482 const char
7483 *name,
7484 *property,
7485 *value;
7486
7487 const StringInfo
7488 *profile;
7489
cristy3ed852e2009-09-05 21:47:34 +00007490 int
cristy3ed852e2009-09-05 21:47:34 +00007491 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007492 pass;
7493
glennrpe9c26dc2010-05-30 01:56:35 +00007494 png_byte
7495 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007496
glennrp39992b42010-11-14 00:03:43 +00007497 png_color
7498 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007499
glennrp5af765f2010-03-30 11:12:18 +00007500 png_color_16
7501 ping_background,
7502 ping_trans_color;
7503
cristy3ed852e2009-09-05 21:47:34 +00007504 png_info
7505 *ping_info;
7506
7507 png_struct
7508 *ping;
7509
glennrp5af765f2010-03-30 11:12:18 +00007510 png_uint_32
7511 ping_height,
7512 ping_width;
7513
cristybb503372010-05-27 20:51:26 +00007514 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007515 y;
7516
7517 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007518 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007519 logging,
glennrp58e01762011-01-07 15:28:54 +00007520 matte,
7521
glennrpda8f3a72011-02-27 23:54:12 +00007522 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007523 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007524 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007525 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007526 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007527 ping_have_bKGD,
7528 ping_have_pHYs,
7529 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007530
7531 ping_exclude_bKGD,
7532 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007533 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007534 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007535 ping_exclude_gAMA,
7536 ping_exclude_iCCP,
7537 /* ping_exclude_iTXt, */
7538 ping_exclude_oFFs,
7539 ping_exclude_pHYs,
7540 ping_exclude_sRGB,
7541 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007542 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007543 ping_exclude_vpAg,
7544 ping_exclude_zCCP, /* hex-encoded iCCP */
7545 ping_exclude_zTXt,
7546
glennrp8d3d6e52011-04-19 04:39:51 +00007547 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007548 ping_need_colortype_warning,
7549
glennrp82b3c532011-03-22 19:20:54 +00007550 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007551 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007552 tried_333,
7553 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007554
7555 QuantumInfo
7556 *quantum_info;
7557
cristyc82a27b2011-10-21 01:07:16 +00007558 PNGErrorInfo
7559 error_info;
7560
cristybb503372010-05-27 20:51:26 +00007561 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007562 i,
7563 x;
7564
7565 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007566 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007567
glennrp5af765f2010-03-30 11:12:18 +00007568 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007569 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007570 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007571 ping_color_type,
7572 ping_interlace_method,
7573 ping_compression_method,
7574 ping_filter_method,
7575 ping_num_trans;
7576
cristybb503372010-05-27 20:51:26 +00007577 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007578 image_depth,
7579 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007580
cristybb503372010-05-27 20:51:26 +00007581 size_t
cristy3ed852e2009-09-05 21:47:34 +00007582 quality,
7583 rowbytes,
7584 save_image_depth;
7585
glennrpdfd70802010-11-14 01:23:35 +00007586 int
glennrpfd05d622011-02-25 04:10:33 +00007587 j,
glennrpf09bded2011-01-08 01:15:59 +00007588 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007589 number_opaque,
7590 number_semitransparent,
7591 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007592 ping_pHYs_unit_type;
7593
7594 png_uint_32
7595 ping_pHYs_x_resolution,
7596 ping_pHYs_y_resolution;
7597
cristy3ed852e2009-09-05 21:47:34 +00007598 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007599 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007600
cristyc82a27b2011-10-21 01:07:16 +00007601 image = CloneImage(IMimage,0,0,MagickFalse,exception);
glennrpb9cfe272010-12-21 15:08:06 +00007602 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007603 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007604 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007605
cristy3ed852e2009-09-05 21:47:34 +00007606#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007607 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007608#endif
7609
glennrp5af765f2010-03-30 11:12:18 +00007610 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007611 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007612 ping_color_type=0,
7613 ping_interlace_method=0,
7614 ping_compression_method=0,
7615 ping_filter_method=0,
7616 ping_num_trans = 0;
7617
7618 ping_background.red = 0;
7619 ping_background.green = 0;
7620 ping_background.blue = 0;
7621 ping_background.gray = 0;
7622 ping_background.index = 0;
7623
7624 ping_trans_color.red=0;
7625 ping_trans_color.green=0;
7626 ping_trans_color.blue=0;
7627 ping_trans_color.gray=0;
7628
glennrpdfd70802010-11-14 01:23:35 +00007629 ping_pHYs_unit_type = 0;
7630 ping_pHYs_x_resolution = 0;
7631 ping_pHYs_y_resolution = 0;
7632
glennrpda8f3a72011-02-27 23:54:12 +00007633 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007634 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007635 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007636 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007637 ping_have_bKGD=MagickFalse;
7638 ping_have_pHYs=MagickFalse;
7639 ping_have_tRNS=MagickFalse;
7640
glennrp0e8ea192010-12-24 18:00:33 +00007641 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7642 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007643 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007644 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007645 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007646 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7647 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7648 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7649 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7650 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7651 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007652 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007653 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7654 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7655 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7656
glennrp8d3d6e52011-04-19 04:39:51 +00007657 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007658 ping_need_colortype_warning = MagickFalse;
7659
cristy0d57eec2011-09-04 22:13:56 +00007660 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7661 * i.e., eliminate the ICC profile and set image->rendering_intent.
7662 * Note that this will not involve any changes to the actual pixels
7663 * but merely passes information to applications that read the resulting
7664 * PNG image.
7665 */
7666 if (ping_exclude_sRGB == MagickFalse)
7667 {
7668 char
7669 *name;
7670
7671 const StringInfo
7672 *profile;
7673
7674 ResetImageProfileIterator(image);
7675 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7676 {
7677 profile=GetImageProfile(image,name);
7678
7679 if (profile != (StringInfo *) NULL)
7680 {
7681 if ((LocaleCompare(name,"ICC") == 0) ||
glennrp29a106e2011-09-06 17:11:42 +00007682 (LocaleCompare(name,"ICM") == 0))
7683 {
glennrpee7b4c02011-10-04 01:21:09 +00007684 int
7685 icheck;
7686
7687 /* 0: not a known sRGB profile
7688 * 1: HP-Microsoft sRGB v2
7689 * 2: ICC sRGB v4 perceptual
7690 * 3: ICC sRGB v2 perceptual no black-compensation
7691 */
7692 png_uint_32
7693 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7694 check_len[4] = {0, 3144, 60960, 3052};
7695
7696 png_uint_32
7697 length,
7698 profile_crc;
7699
cristy0d57eec2011-09-04 22:13:56 +00007700 unsigned char
7701 *data;
7702
glennrp29a106e2011-09-06 17:11:42 +00007703 length=(png_uint_32) GetStringInfoLength(profile);
7704
glennrpee7b4c02011-10-04 01:21:09 +00007705 for (icheck=3; icheck > 0; icheck--)
cristy0d57eec2011-09-04 22:13:56 +00007706 {
glennrpee7b4c02011-10-04 01:21:09 +00007707 if (length == check_len[icheck])
glennrp29a106e2011-09-06 17:11:42 +00007708 {
glennrpee7b4c02011-10-04 01:21:09 +00007709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7710 " Got a %lu-byte ICC profile (potentially sRGB)",
7711 (unsigned long) length);
glennrp29a106e2011-09-06 17:11:42 +00007712
glennrpee7b4c02011-10-04 01:21:09 +00007713 data=GetStringInfoDatum(profile);
7714 profile_crc=crc32(0,data,length);
glennrp29a106e2011-09-06 17:11:42 +00007715
glennrpee7b4c02011-10-04 01:21:09 +00007716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe54cd4b2011-10-04 01:31:35 +00007717 " with crc=%8x",(unsigned int) profile_crc);
glennrpee7b4c02011-10-04 01:21:09 +00007718
7719 if (profile_crc == check_crc[icheck])
7720 {
7721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7722 " It is sRGB.");
7723 if (image->rendering_intent==UndefinedIntent)
7724 image->rendering_intent=PerceptualIntent;
7725 break;
7726 }
glennrp29a106e2011-09-06 17:11:42 +00007727 }
glennrp29a106e2011-09-06 17:11:42 +00007728 }
glennrpee7b4c02011-10-04 01:21:09 +00007729 if (icheck == 0)
glennrp29a106e2011-09-06 17:11:42 +00007730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpee7b4c02011-10-04 01:21:09 +00007731 " Got a %lu-byte ICC profile",
glennrp29a106e2011-09-06 17:11:42 +00007732 (unsigned long) length);
7733 }
cristy0d57eec2011-09-04 22:13:56 +00007734 }
7735 name=GetNextImageProfile(image);
7736 }
7737 }
7738
glennrp8bb3a022010-12-13 20:40:04 +00007739 number_opaque = 0;
7740 number_semitransparent = 0;
7741 number_transparent = 0;
7742
glennrpfd05d622011-02-25 04:10:33 +00007743 if (logging != MagickFalse)
7744 {
7745 if (image->storage_class == UndefinedClass)
7746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7747 " storage_class=UndefinedClass");
7748 if (image->storage_class == DirectClass)
7749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7750 " storage_class=DirectClass");
7751 if (image->storage_class == PseudoClass)
7752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7753 " storage_class=PseudoClass");
7754 }
glennrp28af3712011-04-06 18:07:30 +00007755
glennrp7e65e932011-08-19 02:31:16 +00007756 if (image->storage_class == PseudoClass &&
7757 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7758 (mng_info->write_png_colortype != 0 &&
7759 mng_info->write_png_colortype != 4)))
7760 {
cristyea1a8aa2011-10-20 13:24:06 +00007761 (void) SyncImage(image,exception);
glennrp7e65e932011-08-19 02:31:16 +00007762 image->storage_class = DirectClass;
7763 }
7764
glennrpc6c391a2011-04-27 02:23:56 +00007765 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007766 {
glennrpc6c391a2011-04-27 02:23:56 +00007767 if (image->storage_class != PseudoClass && image->colormap != NULL)
7768 {
7769 /* Free the bogus colormap; it can cause trouble later */
7770 if (logging != MagickFalse)
7771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7772 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00007773 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00007774 image->colormap=NULL;
7775 }
glennrp28af3712011-04-06 18:07:30 +00007776 }
glennrpbb4f99d2011-05-22 11:13:17 +00007777
cristy510d06a2011-07-06 23:43:54 +00007778 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristye941a752011-10-15 01:52:48 +00007779 (void) TransformImageColorspace(image,RGBColorspace,exception);
glennrp0fe50b42010-11-16 03:52:51 +00007780
glennrp3241bd02010-12-12 04:36:28 +00007781 /*
7782 Sometimes we get PseudoClass images whose RGB values don't match
7783 the colors in the colormap. This code syncs the RGB values.
7784 */
7785 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
cristyea1a8aa2011-10-20 13:24:06 +00007786 (void) SyncImage(image,exception);
glennrp3241bd02010-12-12 04:36:28 +00007787
glennrpa6a06632011-01-19 15:15:34 +00007788#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7789 if (image->depth > 8)
7790 {
7791 if (logging != MagickFalse)
7792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7793 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7794
7795 image->depth=8;
7796 }
7797#endif
7798
glennrp8e58efd2011-05-20 12:16:29 +00007799 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007800 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7801 {
cristy4c08aed2011-07-01 19:47:50 +00007802 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007803 *r;
7804
glennrp8e58efd2011-05-20 12:16:29 +00007805 if (image->depth > 8)
7806 {
7807#if MAGICKCORE_QUANTUM_DEPTH > 16
7808 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007809 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007810
7811 for (y=0; y < (ssize_t) image->rows; y++)
7812 {
cristy8a20fa02011-12-27 15:54:31 +00007813 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007814
cristy4c08aed2011-07-01 19:47:50 +00007815 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007816 break;
7817
7818 for (x=0; x < (ssize_t) image->columns; x++)
7819 {
glennrp54cf7972011-08-06 14:28:09 +00007820 LBR16PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007821 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007822 }
glennrpbb4f99d2011-05-22 11:13:17 +00007823
glennrp8e58efd2011-05-20 12:16:29 +00007824 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7825 break;
7826 }
7827
7828 if (image->storage_class == PseudoClass && image->colormap != NULL)
7829 {
cristy3e08f112011-05-24 13:19:30 +00007830 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007831 {
glennrp91d99252011-06-25 14:30:13 +00007832 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007833 }
7834 }
7835#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7836 }
7837
7838 else if (image->depth > 4)
7839 {
7840#if MAGICKCORE_QUANTUM_DEPTH > 8
7841 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007842 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007843
7844 for (y=0; y < (ssize_t) image->rows; y++)
7845 {
cristyc82a27b2011-10-21 01:07:16 +00007846 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007847
cristy4c08aed2011-07-01 19:47:50 +00007848 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007849 break;
7850
7851 for (x=0; x < (ssize_t) image->columns; x++)
7852 {
glennrp54cf7972011-08-06 14:28:09 +00007853 LBR08PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007854 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007855 }
glennrpbb4f99d2011-05-22 11:13:17 +00007856
glennrp8e58efd2011-05-20 12:16:29 +00007857 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7858 break;
7859 }
7860
7861 if (image->storage_class == PseudoClass && image->colormap != NULL)
7862 {
cristy3e08f112011-05-24 13:19:30 +00007863 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007864 {
glennrp91d99252011-06-25 14:30:13 +00007865 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007866 }
7867 }
7868#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7869 }
7870 else
7871 if (image->depth > 2)
7872 {
7873 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007874 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007875
7876 for (y=0; y < (ssize_t) image->rows; y++)
7877 {
cristy8a20fa02011-12-27 15:54:31 +00007878 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007879
cristy4c08aed2011-07-01 19:47:50 +00007880 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007881 break;
7882
7883 for (x=0; x < (ssize_t) image->columns; x++)
7884 {
glennrp54cf7972011-08-06 14:28:09 +00007885 LBR04PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007886 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007887 }
glennrpbb4f99d2011-05-22 11:13:17 +00007888
glennrp8e58efd2011-05-20 12:16:29 +00007889 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7890 break;
7891 }
7892
7893 if (image->storage_class == PseudoClass && image->colormap != NULL)
7894 {
cristy3e08f112011-05-24 13:19:30 +00007895 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007896 {
glennrp91d99252011-06-25 14:30:13 +00007897 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007898 }
7899 }
7900 }
7901
7902 else if (image->depth > 1)
7903 {
7904 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007905 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007906
7907 for (y=0; y < (ssize_t) image->rows; y++)
7908 {
cristy8a20fa02011-12-27 15:54:31 +00007909 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007910
cristy4c08aed2011-07-01 19:47:50 +00007911 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007912 break;
7913
7914 for (x=0; x < (ssize_t) image->columns; x++)
7915 {
glennrp54cf7972011-08-06 14:28:09 +00007916 LBR02PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007917 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007918 }
glennrpbb4f99d2011-05-22 11:13:17 +00007919
glennrp8e58efd2011-05-20 12:16:29 +00007920 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7921 break;
7922 }
7923
7924 if (image->storage_class == PseudoClass && image->colormap != NULL)
7925 {
cristy3e08f112011-05-24 13:19:30 +00007926 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007927 {
glennrp91d99252011-06-25 14:30:13 +00007928 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007929 }
7930 }
7931 }
7932 else
7933 {
7934 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007935 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007936
7937 for (y=0; y < (ssize_t) image->rows; y++)
7938 {
cristy8a20fa02011-12-27 15:54:31 +00007939 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007940
cristy4c08aed2011-07-01 19:47:50 +00007941 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007942 break;
7943
7944 for (x=0; x < (ssize_t) image->columns; x++)
7945 {
glennrp54cf7972011-08-06 14:28:09 +00007946 LBR01PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007947 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007948 }
glennrpbb4f99d2011-05-22 11:13:17 +00007949
glennrp8e58efd2011-05-20 12:16:29 +00007950 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7951 break;
7952 }
7953
7954 if (image->storage_class == PseudoClass && image->colormap != NULL)
7955 {
cristy3e08f112011-05-24 13:19:30 +00007956 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007957 {
glennrp91d99252011-06-25 14:30:13 +00007958 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007959 }
7960 }
7961 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007962 }
7963
glennrp67b9c1a2011-04-22 18:47:36 +00007964 /* To do: set to next higher multiple of 8 */
7965 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007966 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007967
glennrp2b013e42010-11-24 16:55:50 +00007968#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7969 /* PNG does not handle depths greater than 16 so reduce it even
7970 * if lossy
7971 */
glennrp8e58efd2011-05-20 12:16:29 +00007972 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007973 image->depth=16;
7974#endif
7975
glennrp3faa9a32011-04-23 14:00:25 +00007976#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00007977 if (image->depth > 8)
7978 {
7979 /* To do: fill low byte properly */
7980 image->depth=16;
7981 }
7982
glennrpc722dd82011-02-24 05:13:21 +00007983 if (image->depth == 16 && mng_info->write_png_depth != 16)
cristyc82a27b2011-10-21 01:07:16 +00007984 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007985 image->depth = 8;
7986#endif
7987
glennrpc8c2f062011-02-25 19:00:33 +00007988 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007989 * we reduce the transparency to binary and run again, then if there
7990 * 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 +00007991 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7992 * palette. Then (To do) we take care of a final reduction that is only
7993 * needed if there are still 256 colors present and one of them has both
7994 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007995 */
glennrp82b3c532011-03-22 19:20:54 +00007996
glennrp8ca51ad2011-05-12 21:22:32 +00007997 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007998 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007999 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00008000
glennrp8ca51ad2011-05-12 21:22:32 +00008001 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00008002 {
8003 /* BUILD_PALETTE
8004 *
8005 * Sometimes we get DirectClass images that have 256 colors or fewer.
8006 * This code will build a colormap.
8007 *
8008 * Also, sometimes we get PseudoClass images with an out-of-date
8009 * colormap. This code will replace the colormap with a new one.
8010 * Sometimes we get PseudoClass images that have more than 256 colors.
8011 * This code will delete the colormap and change the image to
8012 * DirectClass.
8013 *
cristy4c08aed2011-07-01 19:47:50 +00008014 * If image->matte is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00008015 * even though it sometimes contains left-over non-opaque values.
8016 *
8017 * Also we gather some information (number of opaque, transparent,
8018 * and semitransparent pixels, and whether the image has any non-gray
8019 * pixels or only black-and-white pixels) that we might need later.
8020 *
8021 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8022 * we need to check for bogus non-opaque values, at least.
8023 */
glennrp3c218112010-11-27 15:31:26 +00008024
glennrpd71e86a2011-02-24 01:28:37 +00008025 int
8026 n;
glennrp3c218112010-11-27 15:31:26 +00008027
cristy101ab702011-10-13 13:06:32 +00008028 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008029 opaque[260],
8030 semitransparent[260],
8031 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00008032
cristy4c08aed2011-07-01 19:47:50 +00008033 register const Quantum
8034 *s;
glennrp8bb3a022010-12-13 20:40:04 +00008035
cristy4c08aed2011-07-01 19:47:50 +00008036 register Quantum
8037 *q,
glennrpfd05d622011-02-25 04:10:33 +00008038 *r;
8039
glennrpd71e86a2011-02-24 01:28:37 +00008040 if (logging != MagickFalse)
8041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8042 " Enter BUILD_PALETTE:");
8043
8044 if (logging != MagickFalse)
8045 {
glennrp03812ae2010-12-24 01:31:34 +00008046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008047 " image->columns=%.20g",(double) image->columns);
8048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8049 " image->rows=%.20g",(double) image->rows);
8050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8051 " image->matte=%.20g",(double) image->matte);
8052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8053 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008054
glennrpfd05d622011-02-25 04:10:33 +00008055 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008056 {
8057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008058 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008060 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008061
glennrpd71e86a2011-02-24 01:28:37 +00008062 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008063 {
glennrpd71e86a2011-02-24 01:28:37 +00008064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8065 " %d (%d,%d,%d,%d)",
8066 (int) i,
8067 (int) image->colormap[i].red,
8068 (int) image->colormap[i].green,
8069 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008070 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008071 }
glennrp2cc891a2010-12-24 13:44:32 +00008072
glennrpd71e86a2011-02-24 01:28:37 +00008073 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8074 {
8075 if (i > 255)
8076 {
8077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8078 " %d (%d,%d,%d,%d)",
8079 (int) i,
8080 (int) image->colormap[i].red,
8081 (int) image->colormap[i].green,
8082 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008083 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008084 }
8085 }
glennrp03812ae2010-12-24 01:31:34 +00008086 }
glennrp7ddcc222010-12-11 05:01:05 +00008087
glennrpd71e86a2011-02-24 01:28:37 +00008088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8089 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008090
glennrpd71e86a2011-02-24 01:28:37 +00008091 if (image->colors == 0)
cristyc458f912011-12-27 20:26:40 +00008092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8093 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008094
glennrp8d3d6e52011-04-19 04:39:51 +00008095 if (ping_preserve_colormap == MagickFalse)
8096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8097 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008098 }
8099
glennrpd71e86a2011-02-24 01:28:37 +00008100 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008101 number_opaque = 0;
8102 number_semitransparent = 0;
8103 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008104
8105 for (y=0; y < (ssize_t) image->rows; y++)
8106 {
8107 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8108
cristyacd2ed22011-08-30 01:44:23 +00008109 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008110 break;
8111
8112 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008113 {
glennrp4737d522011-04-29 03:33:42 +00008114 if (image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008115 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008116 {
8117 if (number_opaque < 259)
8118 {
8119 if (number_opaque == 0)
8120 {
cristy101ab702011-10-13 13:06:32 +00008121 GetPixelInfoPixel(image, q, opaque);
cristy4c08aed2011-07-01 19:47:50 +00008122 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008123 number_opaque=1;
8124 }
glennrp2cc891a2010-12-24 13:44:32 +00008125
glennrpd71e86a2011-02-24 01:28:37 +00008126 for (i=0; i< (ssize_t) number_opaque; i++)
8127 {
cristy4c08aed2011-07-01 19:47:50 +00008128 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008129 break;
8130 }
glennrp7ddcc222010-12-11 05:01:05 +00008131
cristyc458f912011-12-27 20:26:40 +00008132 if (i == (ssize_t) number_opaque && number_opaque < 259)
glennrpd71e86a2011-02-24 01:28:37 +00008133 {
8134 number_opaque++;
cristy101ab702011-10-13 13:06:32 +00008135 GetPixelInfoPixel(image, q, opaque+i);
cristy4c08aed2011-07-01 19:47:50 +00008136 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008137 }
8138 }
8139 }
cristy4c08aed2011-07-01 19:47:50 +00008140 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008141 {
8142 if (number_transparent < 259)
8143 {
8144 if (number_transparent == 0)
8145 {
cristy101ab702011-10-13 13:06:32 +00008146 GetPixelInfoPixel(image, q, transparent);
cristy4c08aed2011-07-01 19:47:50 +00008147 ping_trans_color.red=(unsigned short)
8148 GetPixelRed(image,q);
8149 ping_trans_color.green=(unsigned short)
8150 GetPixelGreen(image,q);
8151 ping_trans_color.blue=(unsigned short)
8152 GetPixelBlue(image,q);
8153 ping_trans_color.gray=(unsigned short)
8154 GetPixelRed(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008155 number_transparent = 1;
8156 }
8157
8158 for (i=0; i< (ssize_t) number_transparent; i++)
8159 {
cristy4c08aed2011-07-01 19:47:50 +00008160 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008161 break;
8162 }
8163
8164 if (i == (ssize_t) number_transparent &&
8165 number_transparent < 259)
8166 {
8167 number_transparent++;
cristy101ab702011-10-13 13:06:32 +00008168 GetPixelInfoPixel(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008169 }
8170 }
8171 }
8172 else
8173 {
8174 if (number_semitransparent < 259)
8175 {
8176 if (number_semitransparent == 0)
8177 {
cristy101ab702011-10-13 13:06:32 +00008178 GetPixelInfoPixel(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008179 number_semitransparent = 1;
8180 }
8181
8182 for (i=0; i< (ssize_t) number_semitransparent; i++)
8183 {
cristy4c08aed2011-07-01 19:47:50 +00008184 if (IsPixelEquivalent(image,q, semitransparent+i)
8185 && GetPixelAlpha(image,q) ==
8186 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008187 break;
8188 }
8189
8190 if (i == (ssize_t) number_semitransparent &&
8191 number_semitransparent < 259)
8192 {
8193 number_semitransparent++;
cristy101ab702011-10-13 13:06:32 +00008194 GetPixelInfoPixel(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008195 }
8196 }
8197 }
cristyed231572011-07-14 02:18:59 +00008198 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008199 }
8200 }
8201
cristy4054bfb2011-08-29 23:41:39 +00008202 if (mng_info->write_png8 == MagickFalse &&
8203 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008204 {
8205 /* Add the background color to the palette, if it
8206 * isn't already there.
8207 */
glennrpc6c391a2011-04-27 02:23:56 +00008208 if (logging != MagickFalse)
8209 {
8210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8211 " Check colormap for background (%d,%d,%d)",
8212 (int) image->background_color.red,
8213 (int) image->background_color.green,
8214 (int) image->background_color.blue);
8215 }
glennrpd71e86a2011-02-24 01:28:37 +00008216 for (i=0; i<number_opaque; i++)
8217 {
glennrpca7ad3a2011-04-26 04:44:54 +00008218 if (opaque[i].red == image->background_color.red &&
8219 opaque[i].green == image->background_color.green &&
8220 opaque[i].blue == image->background_color.blue)
8221 break;
glennrpd71e86a2011-02-24 01:28:37 +00008222 }
glennrpd71e86a2011-02-24 01:28:37 +00008223 if (number_opaque < 259 && i == number_opaque)
8224 {
glennrp8e045c82011-04-27 16:40:27 +00008225 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008226 ping_background.index = i;
8227 if (logging != MagickFalse)
8228 {
8229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8230 " background_color index is %d",(int) i);
8231 }
8232
glennrpd71e86a2011-02-24 01:28:37 +00008233 }
glennrpa080bc32011-03-11 18:03:44 +00008234 else if (logging != MagickFalse)
8235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8236 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008237 }
8238
8239 image_colors=number_opaque+number_transparent+number_semitransparent;
8240
glennrpa080bc32011-03-11 18:03:44 +00008241 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8242 {
8243 /* No room for the background color; remove it. */
8244 number_opaque--;
8245 image_colors--;
8246 }
8247
glennrpd71e86a2011-02-24 01:28:37 +00008248 if (logging != MagickFalse)
8249 {
8250 if (image_colors > 256)
8251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8252 " image has more than 256 colors");
8253
8254 else
8255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8256 " image has %d colors",image_colors);
8257 }
8258
glennrp8d3d6e52011-04-19 04:39:51 +00008259 if (ping_preserve_colormap != MagickFalse)
8260 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008261
glennrpfd05d622011-02-25 04:10:33 +00008262 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008263 {
8264 ping_have_color=MagickFalse;
8265 ping_have_non_bw=MagickFalse;
8266
8267 if(image_colors > 256)
8268 {
8269 for (y=0; y < (ssize_t) image->rows; y++)
8270 {
8271 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8272
cristyacd2ed22011-08-30 01:44:23 +00008273 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008274 break;
8275
glennrpe5e6b802011-07-20 14:44:40 +00008276 s=q;
8277 for (x=0; x < (ssize_t) image->columns; x++)
8278 {
8279 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8280 GetPixelRed(image,s) != GetPixelBlue(image,s))
8281 {
8282 ping_have_color=MagickTrue;
8283 ping_have_non_bw=MagickTrue;
8284 break;
8285 }
8286 s+=GetPixelChannels(image);
8287 }
8288
8289 if (ping_have_color != MagickFalse)
8290 break;
8291
glennrpd71e86a2011-02-24 01:28:37 +00008292 /* Worst case is black-and-white; we are looking at every
8293 * pixel twice.
8294 */
8295
glennrpd71e86a2011-02-24 01:28:37 +00008296 if (ping_have_non_bw == MagickFalse)
8297 {
8298 s=q;
8299 for (x=0; x < (ssize_t) image->columns; x++)
8300 {
cristy4c08aed2011-07-01 19:47:50 +00008301 if (GetPixelRed(image,s) != 0 &&
8302 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008303 {
8304 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008305 break;
glennrpd71e86a2011-02-24 01:28:37 +00008306 }
cristyed231572011-07-14 02:18:59 +00008307 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008308 }
glennrpe5e6b802011-07-20 14:44:40 +00008309 }
glennrpd71e86a2011-02-24 01:28:37 +00008310 }
glennrpbb4f99d2011-05-22 11:13:17 +00008311 }
8312 }
glennrpd71e86a2011-02-24 01:28:37 +00008313
8314 if (image_colors < 257)
8315 {
cristy101ab702011-10-13 13:06:32 +00008316 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008317 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008318
glennrpd71e86a2011-02-24 01:28:37 +00008319 /*
8320 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008321 */
8322
glennrpd71e86a2011-02-24 01:28:37 +00008323 if (logging != MagickFalse)
8324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8325 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008326
glennrpd71e86a2011-02-24 01:28:37 +00008327 /* Sort palette, transparent first */;
8328
8329 n = 0;
8330
8331 for (i=0; i<number_transparent; i++)
8332 colormap[n++] = transparent[i];
8333
8334 for (i=0; i<number_semitransparent; i++)
8335 colormap[n++] = semitransparent[i];
8336
8337 for (i=0; i<number_opaque; i++)
8338 colormap[n++] = opaque[i];
8339
glennrpc6c391a2011-04-27 02:23:56 +00008340 ping_background.index +=
8341 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008342
glennrpd71e86a2011-02-24 01:28:37 +00008343 /* image_colors < 257; search the colormap instead of the pixels
8344 * to get ping_have_color and ping_have_non_bw
8345 */
8346 for (i=0; i<n; i++)
8347 {
8348 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008349 {
glennrpd71e86a2011-02-24 01:28:37 +00008350 if (colormap[i].red != colormap[i].green ||
8351 colormap[i].red != colormap[i].blue)
8352 {
8353 ping_have_color=MagickTrue;
8354 ping_have_non_bw=MagickTrue;
8355 break;
8356 }
8357 }
8358
8359 if (ping_have_non_bw == MagickFalse)
8360 {
8361 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008362 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008363 }
glennrp8bb3a022010-12-13 20:40:04 +00008364 }
8365
glennrpd71e86a2011-02-24 01:28:37 +00008366 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8367 (number_transparent == 0 && number_semitransparent == 0)) &&
8368 (((mng_info->write_png_colortype-1) ==
8369 PNG_COLOR_TYPE_PALETTE) ||
8370 (mng_info->write_png_colortype == 0)))
8371 {
glennrp6185c532011-01-14 17:58:40 +00008372 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008373 {
glennrpd71e86a2011-02-24 01:28:37 +00008374 if (n != (ssize_t) image_colors)
8375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8376 " image_colors (%d) and n (%d) don't match",
8377 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008378
glennrpd71e86a2011-02-24 01:28:37 +00008379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8380 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008381 }
glennrp03812ae2010-12-24 01:31:34 +00008382
glennrpd71e86a2011-02-24 01:28:37 +00008383 image->colors = image_colors;
8384
cristy018f07f2011-09-04 21:15:19 +00008385 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008386 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008387 ThrowWriterException(ResourceLimitError,
8388 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008389
8390 for (i=0; i< (ssize_t) image_colors; i++)
8391 image->colormap[i] = colormap[i];
8392
8393 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008394 {
glennrpd71e86a2011-02-24 01:28:37 +00008395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8396 " image->colors=%d (%d)",
8397 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008398
glennrpd71e86a2011-02-24 01:28:37 +00008399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8400 " Update the pixel indexes");
8401 }
glennrp6185c532011-01-14 17:58:40 +00008402
glennrpfd05d622011-02-25 04:10:33 +00008403 /* Sync the pixel indices with the new colormap */
8404
glennrpd71e86a2011-02-24 01:28:37 +00008405 for (y=0; y < (ssize_t) image->rows; y++)
8406 {
cristy97707062011-12-27 18:25:00 +00008407 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp6185c532011-01-14 17:58:40 +00008408
cristyacd2ed22011-08-30 01:44:23 +00008409 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008410 break;
glennrp6185c532011-01-14 17:58:40 +00008411
glennrpd71e86a2011-02-24 01:28:37 +00008412 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008413 {
glennrpd71e86a2011-02-24 01:28:37 +00008414 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008415 {
glennrpd71e86a2011-02-24 01:28:37 +00008416 if ((image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008417 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8418 image->colormap[i].red == GetPixelRed(image,q) &&
8419 image->colormap[i].green == GetPixelGreen(image,q) &&
8420 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008421 {
cristy4c08aed2011-07-01 19:47:50 +00008422 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008423 break;
glennrp6185c532011-01-14 17:58:40 +00008424 }
glennrp6185c532011-01-14 17:58:40 +00008425 }
cristyed231572011-07-14 02:18:59 +00008426 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008427 }
glennrp6185c532011-01-14 17:58:40 +00008428
glennrpd71e86a2011-02-24 01:28:37 +00008429 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8430 break;
8431 }
8432 }
8433 }
8434
8435 if (logging != MagickFalse)
8436 {
8437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8438 " image->colors=%d", (int) image->colors);
8439
8440 if (image->colormap != NULL)
8441 {
8442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008443 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008444
8445 for (i=0; i < (ssize_t) image->colors; i++)
8446 {
cristy72988482011-03-29 16:34:38 +00008447 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008448 {
8449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8450 " %d (%d,%d,%d,%d)",
8451 (int) i,
8452 (int) image->colormap[i].red,
8453 (int) image->colormap[i].green,
8454 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008455 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008456 }
glennrp6185c532011-01-14 17:58:40 +00008457 }
8458 }
glennrp03812ae2010-12-24 01:31:34 +00008459
glennrpd71e86a2011-02-24 01:28:37 +00008460 if (number_transparent < 257)
8461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8462 " number_transparent = %d",
8463 number_transparent);
8464 else
glennrp03812ae2010-12-24 01:31:34 +00008465
glennrpd71e86a2011-02-24 01:28:37 +00008466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8467 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008468
glennrpd71e86a2011-02-24 01:28:37 +00008469 if (number_opaque < 257)
8470 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8471 " number_opaque = %d",
8472 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008473
glennrpd71e86a2011-02-24 01:28:37 +00008474 else
8475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8476 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008477
glennrpd71e86a2011-02-24 01:28:37 +00008478 if (number_semitransparent < 257)
8479 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8480 " number_semitransparent = %d",
8481 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008482
glennrpd71e86a2011-02-24 01:28:37 +00008483 else
8484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8485 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008486
glennrpd71e86a2011-02-24 01:28:37 +00008487 if (ping_have_non_bw == MagickFalse)
8488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8489 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008490
glennrpd71e86a2011-02-24 01:28:37 +00008491 else if (ping_have_color == MagickFalse)
8492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8493 " All pixels and the background are gray");
8494
8495 else
8496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8497 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008498
glennrp03812ae2010-12-24 01:31:34 +00008499 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8500 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008501 }
glennrpfd05d622011-02-25 04:10:33 +00008502
glennrpc8c2f062011-02-25 19:00:33 +00008503 if (mng_info->write_png8 == MagickFalse)
8504 break;
glennrpfd05d622011-02-25 04:10:33 +00008505
glennrpc8c2f062011-02-25 19:00:33 +00008506 /* Make any reductions necessary for the PNG8 format */
8507 if (image_colors <= 256 &&
8508 image_colors != 0 && image->colormap != NULL &&
8509 number_semitransparent == 0 &&
8510 number_transparent <= 1)
8511 break;
8512
8513 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008514 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8515 * transparent color so if more than one is transparent we merge
8516 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008517 */
glennrp130fc452011-08-20 03:43:18 +00008518 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008519 {
8520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8521 " Thresholding the alpha channel to binary");
8522
8523 for (y=0; y < (ssize_t) image->rows; y++)
8524 {
cristy8a20fa02011-12-27 15:54:31 +00008525 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008526
cristy4c08aed2011-07-01 19:47:50 +00008527 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008528 break;
8529
8530 for (x=0; x < (ssize_t) image->columns; x++)
8531 {
glennrpf73547f2011-08-20 04:40:26 +00008532 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008533 {
cristy803640d2011-11-17 02:11:32 +00008534 SetPixelInfoPixel(image,&image->background_color,r);
cristy4c08aed2011-07-01 19:47:50 +00008535 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008536 }
8537 else
cristy4c08aed2011-07-01 19:47:50 +00008538 SetPixelAlpha(image,OpaqueAlpha,r);
cristyed231572011-07-14 02:18:59 +00008539 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008540 }
glennrpbb4f99d2011-05-22 11:13:17 +00008541
glennrpc8c2f062011-02-25 19:00:33 +00008542 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8543 break;
8544
8545 if (image_colors != 0 && image_colors <= 256 &&
8546 image->colormap != NULL)
8547 for (i=0; i<image_colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00008548 image->colormap[i].alpha =
8549 (image->colormap[i].alpha > TransparentAlpha/2 ?
8550 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008551 }
8552 continue;
8553 }
8554
8555 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008556 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8557 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8558 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008559 */
glennrpd3371642011-03-22 19:42:23 +00008560 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8561 {
8562 if (logging != MagickFalse)
8563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8564 " Quantizing the background color to 4-4-4");
8565
8566 tried_444 = MagickTrue;
8567
glennrp91d99252011-06-25 14:30:13 +00008568 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008569
8570 if (logging != MagickFalse)
8571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8572 " Quantizing the pixel colors to 4-4-4");
8573
8574 if (image->colormap == NULL)
8575 {
8576 for (y=0; y < (ssize_t) image->rows; y++)
8577 {
cristy8a20fa02011-12-27 15:54:31 +00008578 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpd3371642011-03-22 19:42:23 +00008579
cristy4c08aed2011-07-01 19:47:50 +00008580 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008581 break;
8582
8583 for (x=0; x < (ssize_t) image->columns; x++)
8584 {
cristy4c08aed2011-07-01 19:47:50 +00008585 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008586 LBR04PixelRGB(r);
cristy8a20fa02011-12-27 15:54:31 +00008587 r+=GetPixelChannels(image);
glennrpd3371642011-03-22 19:42:23 +00008588 }
glennrpbb4f99d2011-05-22 11:13:17 +00008589
glennrpd3371642011-03-22 19:42:23 +00008590 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8591 break;
8592 }
8593 }
8594
8595 else /* Should not reach this; colormap already exists and
8596 must be <= 256 */
8597 {
8598 if (logging != MagickFalse)
8599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8600 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008601
glennrpd3371642011-03-22 19:42:23 +00008602 for (i=0; i<image_colors; i++)
8603 {
glennrp91d99252011-06-25 14:30:13 +00008604 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008605 }
8606 }
8607 continue;
8608 }
8609
glennrp82b3c532011-03-22 19:20:54 +00008610 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8611 {
8612 if (logging != MagickFalse)
8613 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8614 " Quantizing the background color to 3-3-3");
8615
8616 tried_333 = MagickTrue;
8617
glennrp91d99252011-06-25 14:30:13 +00008618 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008619
8620 if (logging != MagickFalse)
8621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008622 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008623
8624 if (image->colormap == NULL)
8625 {
8626 for (y=0; y < (ssize_t) image->rows; y++)
8627 {
cristy8a20fa02011-12-27 15:54:31 +00008628 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp82b3c532011-03-22 19:20:54 +00008629
cristy4c08aed2011-07-01 19:47:50 +00008630 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008631 break;
8632
8633 for (x=0; x < (ssize_t) image->columns; x++)
8634 {
cristy4c08aed2011-07-01 19:47:50 +00008635 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8636 LBR03RGB(r);
cristy8a20fa02011-12-27 15:54:31 +00008637 r+=GetPixelChannels(image);
glennrp82b3c532011-03-22 19:20:54 +00008638 }
glennrpbb4f99d2011-05-22 11:13:17 +00008639
glennrp82b3c532011-03-22 19:20:54 +00008640 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8641 break;
8642 }
8643 }
8644
8645 else /* Should not reach this; colormap already exists and
8646 must be <= 256 */
8647 {
8648 if (logging != MagickFalse)
8649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008650 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008651 for (i=0; i<image_colors; i++)
8652 {
glennrp91d99252011-06-25 14:30:13 +00008653 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008654 }
glennrpd3371642011-03-22 19:42:23 +00008655 }
8656 continue;
glennrp82b3c532011-03-22 19:20:54 +00008657 }
glennrpc8c2f062011-02-25 19:00:33 +00008658
glennrp8ca51ad2011-05-12 21:22:32 +00008659 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008660 {
8661 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008663 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008664
glennrp8ca51ad2011-05-12 21:22:32 +00008665 tried_332 = MagickTrue;
8666
glennrp3faa9a32011-04-23 14:00:25 +00008667 /* Red and green were already done so we only quantize the blue
8668 * channel
8669 */
8670
glennrp91d99252011-06-25 14:30:13 +00008671 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008672
glennrpc8c2f062011-02-25 19:00:33 +00008673 if (logging != MagickFalse)
8674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008675 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008676
glennrpc8c2f062011-02-25 19:00:33 +00008677 if (image->colormap == NULL)
8678 {
8679 for (y=0; y < (ssize_t) image->rows; y++)
8680 {
cristy8a20fa02011-12-27 15:54:31 +00008681 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008682
cristy4c08aed2011-07-01 19:47:50 +00008683 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008684 break;
8685
8686 for (x=0; x < (ssize_t) image->columns; x++)
8687 {
cristy4c08aed2011-07-01 19:47:50 +00008688 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008689 LBR02PixelBlue(r);
cristy8a20fa02011-12-27 15:54:31 +00008690 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008691 }
glennrpbb4f99d2011-05-22 11:13:17 +00008692
glennrpc8c2f062011-02-25 19:00:33 +00008693 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8694 break;
8695 }
8696 }
glennrpfd05d622011-02-25 04:10:33 +00008697
glennrpc8c2f062011-02-25 19:00:33 +00008698 else /* Should not reach this; colormap already exists and
8699 must be <= 256 */
8700 {
8701 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008703 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008704 for (i=0; i<image_colors; i++)
8705 {
glennrp91d99252011-06-25 14:30:13 +00008706 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008707 }
8708 }
8709 continue;
8710 }
8711 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008712
8713 if (image_colors == 0 || image_colors > 256)
8714 {
8715 /* Take care of special case with 256 colors + 1 transparent
8716 * color. We don't need to quantize to 2-3-2-1; we only need to
8717 * eliminate one color, so we'll merge the two darkest red
8718 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8719 */
8720 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8721 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8722 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8723 {
8724 image->background_color.red=ScaleCharToQuantum(0x24);
8725 }
glennrpbb4f99d2011-05-22 11:13:17 +00008726
glennrp8ca51ad2011-05-12 21:22:32 +00008727 if (image->colormap == NULL)
8728 {
8729 for (y=0; y < (ssize_t) image->rows; y++)
8730 {
cristy8a20fa02011-12-27 15:54:31 +00008731 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008732
cristy4c08aed2011-07-01 19:47:50 +00008733 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008734 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008735
glennrp8ca51ad2011-05-12 21:22:32 +00008736 for (x=0; x < (ssize_t) image->columns; x++)
8737 {
cristy4c08aed2011-07-01 19:47:50 +00008738 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8739 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8740 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8741 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008742 {
cristy4c08aed2011-07-01 19:47:50 +00008743 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008744 }
cristyed231572011-07-14 02:18:59 +00008745 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008746 }
glennrpbb4f99d2011-05-22 11:13:17 +00008747
glennrp8ca51ad2011-05-12 21:22:32 +00008748 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8749 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008750
glennrp8ca51ad2011-05-12 21:22:32 +00008751 }
8752 }
8753
8754 else
8755 {
8756 for (i=0; i<image_colors; i++)
8757 {
8758 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8759 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8760 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8761 {
8762 image->colormap[i].red=ScaleCharToQuantum(0x24);
8763 }
8764 }
8765 }
8766 }
glennrpd71e86a2011-02-24 01:28:37 +00008767 }
glennrpfd05d622011-02-25 04:10:33 +00008768 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008769
glennrpfd05d622011-02-25 04:10:33 +00008770 /* If we are excluding the tRNS chunk and there is transparency,
8771 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8772 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008773 */
glennrp0e8ea192010-12-24 18:00:33 +00008774 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8775 (number_transparent != 0 || number_semitransparent != 0))
8776 {
glennrpd17915c2011-04-29 14:24:22 +00008777 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008778
8779 if (ping_have_color == MagickFalse)
8780 mng_info->write_png_colortype = 5;
8781
8782 else
8783 mng_info->write_png_colortype = 7;
8784
glennrp8d579662011-02-23 02:05:02 +00008785 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008786 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008787 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008788
glennrp0e8ea192010-12-24 18:00:33 +00008789 }
8790
glennrpfd05d622011-02-25 04:10:33 +00008791 /* See if cheap transparency is possible. It is only possible
8792 * when there is a single transparent color, no semitransparent
8793 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008794 * as the transparent color. We only need this information if
8795 * we are writing a PNG with colortype 0 or 2, and we have not
8796 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008797 */
glennrp5a39f372011-02-25 04:52:16 +00008798 if (number_transparent == 1 &&
8799 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008800 {
8801 ping_have_cheap_transparency = MagickTrue;
8802
8803 if (number_semitransparent != 0)
8804 ping_have_cheap_transparency = MagickFalse;
8805
8806 else if (image_colors == 0 || image_colors > 256 ||
8807 image->colormap == NULL)
8808 {
cristy4c08aed2011-07-01 19:47:50 +00008809 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008810 *q;
8811
glennrpfd05d622011-02-25 04:10:33 +00008812 for (y=0; y < (ssize_t) image->rows; y++)
8813 {
8814 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8815
cristyacd2ed22011-08-30 01:44:23 +00008816 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008817 break;
8818
8819 for (x=0; x < (ssize_t) image->columns; x++)
8820 {
cristy4c08aed2011-07-01 19:47:50 +00008821 if (GetPixelAlpha(image,q) != TransparentAlpha &&
glennrp847370c2011-07-05 17:37:15 +00008822 (unsigned short) GetPixelRed(image,q) ==
8823 ping_trans_color.red &&
8824 (unsigned short) GetPixelGreen(image,q) ==
8825 ping_trans_color.green &&
8826 (unsigned short) GetPixelBlue(image,q) ==
8827 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008828 {
8829 ping_have_cheap_transparency = MagickFalse;
8830 break;
8831 }
8832
cristyed231572011-07-14 02:18:59 +00008833 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008834 }
glennrpbb4f99d2011-05-22 11:13:17 +00008835
glennrpfd05d622011-02-25 04:10:33 +00008836 if (ping_have_cheap_transparency == MagickFalse)
8837 break;
8838 }
8839 }
8840 else
8841 {
glennrp67b9c1a2011-04-22 18:47:36 +00008842 /* Assuming that image->colormap[0] is the one transparent color
8843 * and that all others are opaque.
8844 */
glennrpfd05d622011-02-25 04:10:33 +00008845 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008846 for (i=1; i<image_colors; i++)
8847 if (image->colormap[i].red == image->colormap[0].red &&
8848 image->colormap[i].green == image->colormap[0].green &&
8849 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008850 {
glennrp67b9c1a2011-04-22 18:47:36 +00008851 ping_have_cheap_transparency = MagickFalse;
8852 break;
glennrpfd05d622011-02-25 04:10:33 +00008853 }
8854 }
glennrpbb4f99d2011-05-22 11:13:17 +00008855
glennrpfd05d622011-02-25 04:10:33 +00008856 if (logging != MagickFalse)
8857 {
8858 if (ping_have_cheap_transparency == MagickFalse)
8859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8860 " Cheap transparency is not possible.");
8861
8862 else
8863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8864 " Cheap transparency is possible.");
8865 }
8866 }
8867 else
8868 ping_have_cheap_transparency = MagickFalse;
8869
glennrp8640fb52010-11-23 15:48:26 +00008870 image_depth=image->depth;
8871
glennrp26c990a2010-11-23 02:23:20 +00008872 quantum_info = (QuantumInfo *) NULL;
8873 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008874 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008875 image_matte=image->matte;
8876
glennrp0fe50b42010-11-16 03:52:51 +00008877 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008878 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008879
glennrp52a479c2011-02-26 21:14:38 +00008880 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8881 (image->colors == 0 || image->colormap == NULL))
8882 {
glennrp52a479c2011-02-26 21:14:38 +00008883 image_info=DestroyImageInfo(image_info);
8884 image=DestroyImage(image);
cristyc82a27b2011-10-21 01:07:16 +00008885 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
glennrp15e01552011-03-06 23:29:17 +00008886 "Cannot write PNG8 or color-type 3; colormap is NULL",
8887 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008888#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8889 UnlockSemaphoreInfo(ping_semaphore);
8890#endif
8891 return(MagickFalse);
8892 }
8893
cristy3ed852e2009-09-05 21:47:34 +00008894 /*
8895 Allocate the PNG structures
8896 */
8897#ifdef PNG_USER_MEM_SUPPORTED
cristyc82a27b2011-10-21 01:07:16 +00008898 error_info.image=image;
8899 error_info.exception=exception;
8900 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008901 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8902 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008903
cristy3ed852e2009-09-05 21:47:34 +00008904#else
cristyc82a27b2011-10-21 01:07:16 +00008905 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008906 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008907
cristy3ed852e2009-09-05 21:47:34 +00008908#endif
8909 if (ping == (png_struct *) NULL)
8910 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008911
cristy3ed852e2009-09-05 21:47:34 +00008912 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008913
cristy3ed852e2009-09-05 21:47:34 +00008914 if (ping_info == (png_info *) NULL)
8915 {
8916 png_destroy_write_struct(&ping,(png_info **) NULL);
8917 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8918 }
glennrp0fe50b42010-11-16 03:52:51 +00008919
cristy3ed852e2009-09-05 21:47:34 +00008920 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008921 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008922
glennrp5af765f2010-03-30 11:12:18 +00008923 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008924 {
8925 /*
8926 PNG write failed.
8927 */
8928#ifdef PNG_DEBUG
8929 if (image_info->verbose)
8930 (void) printf("PNG write has failed.\n");
8931#endif
8932 png_destroy_write_struct(&ping,&ping_info);
8933#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008934 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008935#endif
glennrpda8f3a72011-02-27 23:54:12 +00008936 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008937 (void) CloseBlob(image);
8938 image_info=DestroyImageInfo(image_info);
8939 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008940 return(MagickFalse);
8941 }
8942 /*
8943 Prepare PNG for writing.
8944 */
8945#if defined(PNG_MNG_FEATURES_SUPPORTED)
8946 if (mng_info->write_mng)
8947 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008948
cristy3ed852e2009-09-05 21:47:34 +00008949#else
8950# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8951 if (mng_info->write_mng)
8952 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008953
cristy3ed852e2009-09-05 21:47:34 +00008954# endif
8955#endif
glennrp2b013e42010-11-24 16:55:50 +00008956
cristy3ed852e2009-09-05 21:47:34 +00008957 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008958
cristy4e5bc842010-06-09 13:56:01 +00008959 ping_width=(png_uint_32) image->columns;
8960 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008961
cristy3ed852e2009-09-05 21:47:34 +00008962 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8963 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008964
cristy3ed852e2009-09-05 21:47:34 +00008965 if (mng_info->write_png_depth != 0)
8966 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008967
cristy3ed852e2009-09-05 21:47:34 +00008968 /* Adjust requested depth to next higher valid depth if necessary */
8969 if (image_depth > 8)
8970 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008971
cristy3ed852e2009-09-05 21:47:34 +00008972 if ((image_depth > 4) && (image_depth < 8))
8973 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008974
cristy3ed852e2009-09-05 21:47:34 +00008975 if (image_depth == 3)
8976 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008977
cristy3ed852e2009-09-05 21:47:34 +00008978 if (logging != MagickFalse)
8979 {
8980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008981 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008983 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008985 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008987 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008989 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008990 }
glennrp8640fb52010-11-23 15:48:26 +00008991
cristy3ed852e2009-09-05 21:47:34 +00008992 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008993 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008994
glennrp26f37912010-12-23 16:22:42 +00008995
cristy3ed852e2009-09-05 21:47:34 +00008996#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008997 if (ping_exclude_pHYs == MagickFalse)
8998 {
cristy2a11bef2011-10-28 18:33:11 +00008999 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00009000 (!mng_info->write_mng || !mng_info->equal_physs))
9001 {
glennrp0fe50b42010-11-16 03:52:51 +00009002 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9004 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00009005
9006 if (image->units == PixelsPerInchResolution)
9007 {
glennrpdfd70802010-11-14 01:23:35 +00009008 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00009009 ping_pHYs_x_resolution=
cristy2a11bef2011-10-28 18:33:11 +00009010 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
glennrp823b55c2011-03-14 18:46:46 +00009011 ping_pHYs_y_resolution=
cristy2a11bef2011-10-28 18:33:11 +00009012 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00009013 }
glennrpdfd70802010-11-14 01:23:35 +00009014
cristy3ed852e2009-09-05 21:47:34 +00009015 else if (image->units == PixelsPerCentimeterResolution)
9016 {
glennrpdfd70802010-11-14 01:23:35 +00009017 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
cristy2a11bef2011-10-28 18:33:11 +00009018 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9019 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00009020 }
glennrp991d11d2010-11-12 21:55:28 +00009021
cristy3ed852e2009-09-05 21:47:34 +00009022 else
9023 {
glennrpdfd70802010-11-14 01:23:35 +00009024 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
cristy2a11bef2011-10-28 18:33:11 +00009025 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9026 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00009027 }
glennrp991d11d2010-11-12 21:55:28 +00009028
glennrp823b55c2011-03-14 18:46:46 +00009029 if (logging != MagickFalse)
9030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9031 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9032 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9033 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009034 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009035 }
glennrp26f37912010-12-23 16:22:42 +00009036 }
cristy3ed852e2009-09-05 21:47:34 +00009037#endif
glennrpa521b2f2010-10-29 04:11:03 +00009038
glennrp26f37912010-12-23 16:22:42 +00009039 if (ping_exclude_bKGD == MagickFalse)
9040 {
glennrpa521b2f2010-10-29 04:11:03 +00009041 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009042 {
glennrpa521b2f2010-10-29 04:11:03 +00009043 unsigned int
9044 mask;
cristy3ed852e2009-09-05 21:47:34 +00009045
glennrpa521b2f2010-10-29 04:11:03 +00009046 mask=0xffff;
9047 if (ping_bit_depth == 8)
9048 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009049
glennrpa521b2f2010-10-29 04:11:03 +00009050 if (ping_bit_depth == 4)
9051 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009052
glennrpa521b2f2010-10-29 04:11:03 +00009053 if (ping_bit_depth == 2)
9054 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009055
glennrpa521b2f2010-10-29 04:11:03 +00009056 if (ping_bit_depth == 1)
9057 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009058
glennrpa521b2f2010-10-29 04:11:03 +00009059 ping_background.red=(png_uint_16)
9060 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009061
glennrpa521b2f2010-10-29 04:11:03 +00009062 ping_background.green=(png_uint_16)
9063 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009064
glennrpa521b2f2010-10-29 04:11:03 +00009065 ping_background.blue=(png_uint_16)
9066 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009067
9068 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009069 }
cristy3ed852e2009-09-05 21:47:34 +00009070
glennrp0fe50b42010-11-16 03:52:51 +00009071 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009072 {
9073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9074 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9076 " background_color index is %d",
9077 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009078
9079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9080 " ping_bit_depth=%d",ping_bit_depth);
9081 }
glennrp0fe50b42010-11-16 03:52:51 +00009082
9083 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009084 }
glennrp0fe50b42010-11-16 03:52:51 +00009085
cristy3ed852e2009-09-05 21:47:34 +00009086 /*
9087 Select the color type.
9088 */
9089 matte=image_matte;
9090 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009091
glennrp1273f7b2011-02-24 03:20:30 +00009092 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009093 {
glennrp0fe50b42010-11-16 03:52:51 +00009094
glennrpfd05d622011-02-25 04:10:33 +00009095 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009096 for reducing the sample depth from 8. */
9097
glennrp0fe50b42010-11-16 03:52:51 +00009098 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009099
glennrp8bb3a022010-12-13 20:40:04 +00009100 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009101
9102 /*
9103 Set image palette.
9104 */
9105 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9106
glennrp0fe50b42010-11-16 03:52:51 +00009107 if (logging != MagickFalse)
9108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9109 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009110 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009111
9112 for (i=0; i < (ssize_t) number_colors; i++)
9113 {
9114 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9115 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9116 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9117 if (logging != MagickFalse)
9118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009119#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009120 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009121#else
9122 " %5ld (%5d,%5d,%5d)",
9123#endif
glennrp0fe50b42010-11-16 03:52:51 +00009124 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9125
9126 }
glennrp2b013e42010-11-24 16:55:50 +00009127
glennrp8bb3a022010-12-13 20:40:04 +00009128 ping_have_PLTE=MagickTrue;
9129 image_depth=ping_bit_depth;
9130 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009131
glennrp58e01762011-01-07 15:28:54 +00009132 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009133 {
glennrp0fe50b42010-11-16 03:52:51 +00009134 /*
9135 Identify which colormap entry is transparent.
9136 */
9137 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009138 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009139
glennrp8bb3a022010-12-13 20:40:04 +00009140 for (i=0; i < (ssize_t) number_transparent; i++)
9141 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009142
glennrp0fe50b42010-11-16 03:52:51 +00009143
glennrp2cc891a2010-12-24 13:44:32 +00009144 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009145 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009146
9147 if (ping_num_trans == 0)
9148 ping_have_tRNS=MagickFalse;
9149
glennrp8bb3a022010-12-13 20:40:04 +00009150 else
9151 ping_have_tRNS=MagickTrue;
9152 }
glennrp0fe50b42010-11-16 03:52:51 +00009153
glennrp1273f7b2011-02-24 03:20:30 +00009154 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009155 {
glennrp1273f7b2011-02-24 03:20:30 +00009156 /*
9157 * Identify which colormap entry is the background color.
9158 */
9159
glennrp4f25bd02011-01-01 18:51:28 +00009160 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9161 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9162 break;
glennrp0fe50b42010-11-16 03:52:51 +00009163
glennrp4f25bd02011-01-01 18:51:28 +00009164 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009165
9166 if (logging != MagickFalse)
9167 {
9168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9169 " background_color index is %d",
9170 (int) ping_background.index);
9171 }
glennrp4f25bd02011-01-01 18:51:28 +00009172 }
cristy3ed852e2009-09-05 21:47:34 +00009173 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009174
glennrp7e65e932011-08-19 02:31:16 +00009175 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009176 {
9177 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009178 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009179 }
glennrp0fe50b42010-11-16 03:52:51 +00009180
glennrp7e65e932011-08-19 02:31:16 +00009181 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009182 {
9183 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009184 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009185 }
glennrp0fe50b42010-11-16 03:52:51 +00009186
glennrp8bb3a022010-12-13 20:40:04 +00009187 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009188 {
glennrp5af765f2010-03-30 11:12:18 +00009189 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009190
glennrp8bb3a022010-12-13 20:40:04 +00009191 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009192 {
glennrp5af765f2010-03-30 11:12:18 +00009193 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009194
glennrp5af765f2010-03-30 11:12:18 +00009195 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9196 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009197 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009198
glennrp8bb3a022010-12-13 20:40:04 +00009199 else
9200 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009201
9202 if (logging != MagickFalse)
9203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9204 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009205 }
glennrp0fe50b42010-11-16 03:52:51 +00009206
glennrp7c4c9e62011-03-21 20:23:32 +00009207 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009208 {
9209 if (logging != MagickFalse)
9210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009211 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009212
glennrpd6bf1612010-12-17 17:28:54 +00009213 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009214 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009215
glennrpd6bf1612010-12-17 17:28:54 +00009216 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009217 {
glennrp5af765f2010-03-30 11:12:18 +00009218 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009219 image_matte=MagickFalse;
9220 }
glennrp0fe50b42010-11-16 03:52:51 +00009221
glennrpd6bf1612010-12-17 17:28:54 +00009222 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009223 {
glennrp5af765f2010-03-30 11:12:18 +00009224 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009225 image_matte=MagickTrue;
9226 }
glennrp0fe50b42010-11-16 03:52:51 +00009227
glennrp5aa37f62011-01-02 03:07:57 +00009228 if (image_info->type == PaletteType ||
9229 image_info->type == PaletteMatteType)
9230 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9231
glennrp7c4c9e62011-03-21 20:23:32 +00009232 if (mng_info->write_png_colortype == 0 &&
9233 (image_info->type == UndefinedType ||
9234 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009235 {
glennrp5aa37f62011-01-02 03:07:57 +00009236 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009237 {
glennrp5aa37f62011-01-02 03:07:57 +00009238 if (image_matte == MagickFalse)
9239 {
9240 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9241 image_matte=MagickFalse;
9242 }
glennrp0fe50b42010-11-16 03:52:51 +00009243
glennrp0b206f52011-01-07 04:55:32 +00009244 else
glennrp5aa37f62011-01-02 03:07:57 +00009245 {
9246 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9247 image_matte=MagickTrue;
9248 }
9249 }
9250 else
glennrp8bb3a022010-12-13 20:40:04 +00009251 {
glennrp5aa37f62011-01-02 03:07:57 +00009252 if (image_matte == MagickFalse)
9253 {
9254 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9255 image_matte=MagickFalse;
9256 }
glennrp8bb3a022010-12-13 20:40:04 +00009257
glennrp0b206f52011-01-07 04:55:32 +00009258 else
glennrp5aa37f62011-01-02 03:07:57 +00009259 {
9260 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9261 image_matte=MagickTrue;
9262 }
9263 }
glennrp0fe50b42010-11-16 03:52:51 +00009264 }
glennrp5aa37f62011-01-02 03:07:57 +00009265
cristy3ed852e2009-09-05 21:47:34 +00009266 }
glennrp0fe50b42010-11-16 03:52:51 +00009267
cristy3ed852e2009-09-05 21:47:34 +00009268 if (logging != MagickFalse)
9269 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009270 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009271
glennrp5af765f2010-03-30 11:12:18 +00009272 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009273 {
9274 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9275 ping_color_type == PNG_COLOR_TYPE_RGB ||
9276 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9277 ping_bit_depth=8;
9278 }
cristy3ed852e2009-09-05 21:47:34 +00009279
glennrpd6bf1612010-12-17 17:28:54 +00009280 old_bit_depth=ping_bit_depth;
9281
glennrp5af765f2010-03-30 11:12:18 +00009282 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009283 {
glennrp8d579662011-02-23 02:05:02 +00009284 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9285 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009286 }
glennrp8640fb52010-11-23 15:48:26 +00009287
glennrp5af765f2010-03-30 11:12:18 +00009288 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009289 {
cristy35ef8242010-06-03 16:24:13 +00009290 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009291 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009292
9293 if (image->colors == 0)
9294 {
glennrp0fe50b42010-11-16 03:52:51 +00009295 /* DO SOMETHING */
cristyc82a27b2011-10-21 01:07:16 +00009296 (void) ThrowMagickException(exception,
glennrp0f111982010-07-07 20:18:33 +00009297 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009298 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009299 }
9300
cristy35ef8242010-06-03 16:24:13 +00009301 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009302 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009303 }
glennrp2b013e42010-11-24 16:55:50 +00009304
glennrpd6bf1612010-12-17 17:28:54 +00009305 if (logging != MagickFalse)
9306 {
9307 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9308 " Number of colors: %.20g",(double) image_colors);
9309
9310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9311 " Tentative PNG bit depth: %d",ping_bit_depth);
9312 }
9313
9314 if (ping_bit_depth < (int) mng_info->write_png_depth)
9315 ping_bit_depth = mng_info->write_png_depth;
9316 }
glennrp2cc891a2010-12-24 13:44:32 +00009317
glennrp5af765f2010-03-30 11:12:18 +00009318 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009319
cristy3ed852e2009-09-05 21:47:34 +00009320 if (logging != MagickFalse)
9321 {
9322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009323 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009324
cristy3ed852e2009-09-05 21:47:34 +00009325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009326 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009327
cristy3ed852e2009-09-05 21:47:34 +00009328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009329 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009330
cristy3ed852e2009-09-05 21:47:34 +00009331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009332
glennrp8640fb52010-11-23 15:48:26 +00009333 " image->depth: %.20g",(double) image->depth);
9334
9335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009336 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009337 }
9338
glennrp58e01762011-01-07 15:28:54 +00009339 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009340 {
glennrp4f25bd02011-01-01 18:51:28 +00009341 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009342 {
glennrp7c4c9e62011-03-21 20:23:32 +00009343 if (mng_info->write_png_colortype == 0)
9344 {
9345 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009346
glennrp7c4c9e62011-03-21 20:23:32 +00009347 if (ping_have_color != MagickFalse)
9348 ping_color_type=PNG_COLOR_TYPE_RGBA;
9349 }
glennrp4f25bd02011-01-01 18:51:28 +00009350
9351 /*
9352 * Determine if there is any transparent color.
9353 */
9354 if (number_transparent + number_semitransparent == 0)
9355 {
9356 /*
9357 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9358 */
glennrpa6a06632011-01-19 15:15:34 +00009359
glennrp4f25bd02011-01-01 18:51:28 +00009360 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009361
9362 if (mng_info->write_png_colortype == 0)
9363 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009364 }
9365
9366 else
9367 {
9368 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009369 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009370
9371 mask=0xffff;
9372
9373 if (ping_bit_depth == 8)
9374 mask=0x00ff;
9375
9376 if (ping_bit_depth == 4)
9377 mask=0x000f;
9378
9379 if (ping_bit_depth == 2)
9380 mask=0x0003;
9381
9382 if (ping_bit_depth == 1)
9383 mask=0x0001;
9384
9385 ping_trans_color.red=(png_uint_16)
9386 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9387
9388 ping_trans_color.green=(png_uint_16)
9389 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9390
9391 ping_trans_color.blue=(png_uint_16)
9392 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9393
9394 ping_trans_color.gray=(png_uint_16)
cristy101ab702011-10-13 13:06:32 +00009395 (ScaleQuantumToShort(GetPixelInfoIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009396 image->colormap)) & mask);
9397
9398 ping_trans_color.index=(png_byte) 0;
9399
9400 ping_have_tRNS=MagickTrue;
9401 }
9402
9403 if (ping_have_tRNS != MagickFalse)
9404 {
9405 /*
glennrpfd05d622011-02-25 04:10:33 +00009406 * Determine if there is one and only one transparent color
9407 * and if so if it is fully transparent.
9408 */
9409 if (ping_have_cheap_transparency == MagickFalse)
9410 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009411 }
9412
9413 if (ping_have_tRNS != MagickFalse)
9414 {
glennrp7c4c9e62011-03-21 20:23:32 +00009415 if (mng_info->write_png_colortype == 0)
9416 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009417
9418 if (image_depth == 8)
9419 {
9420 ping_trans_color.red&=0xff;
9421 ping_trans_color.green&=0xff;
9422 ping_trans_color.blue&=0xff;
9423 ping_trans_color.gray&=0xff;
9424 }
9425 }
9426 }
cristy3ed852e2009-09-05 21:47:34 +00009427 else
9428 {
cristy3ed852e2009-09-05 21:47:34 +00009429 if (image_depth == 8)
9430 {
glennrp5af765f2010-03-30 11:12:18 +00009431 ping_trans_color.red&=0xff;
9432 ping_trans_color.green&=0xff;
9433 ping_trans_color.blue&=0xff;
9434 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009435 }
9436 }
9437 }
glennrp8640fb52010-11-23 15:48:26 +00009438
cristy3ed852e2009-09-05 21:47:34 +00009439 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009440
glennrp2e09f552010-11-14 00:38:48 +00009441 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009442 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009443
glennrp39992b42010-11-14 00:03:43 +00009444 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009445 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009446 ping_have_color == MagickFalse &&
9447 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009448 {
cristy35ef8242010-06-03 16:24:13 +00009449 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009450
cristy3ed852e2009-09-05 21:47:34 +00009451 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009452 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009453
glennrp7c4c9e62011-03-21 20:23:32 +00009454 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009455 {
glennrp5af765f2010-03-30 11:12:18 +00009456 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009457
cristy3ed852e2009-09-05 21:47:34 +00009458 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009459 {
9460 if (logging != MagickFalse)
9461 {
9462 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9463 " Scaling ping_trans_color (0)");
9464 }
9465 ping_trans_color.gray*=0x0101;
9466 }
cristy3ed852e2009-09-05 21:47:34 +00009467 }
glennrp0fe50b42010-11-16 03:52:51 +00009468
cristy3ed852e2009-09-05 21:47:34 +00009469 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9470 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009471
glennrp136ee3a2011-04-27 15:47:45 +00009472 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009473 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009474 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009475
cristy3ed852e2009-09-05 21:47:34 +00009476 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009477 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009478
cristy3ed852e2009-09-05 21:47:34 +00009479 else
9480 {
glennrp5af765f2010-03-30 11:12:18 +00009481 ping_bit_depth=8;
9482 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009483 {
9484 if(!mng_info->write_png_depth)
9485 {
glennrp5af765f2010-03-30 11:12:18 +00009486 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009487
cristy35ef8242010-06-03 16:24:13 +00009488 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009489 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009490 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009491 }
9492 }
glennrp2b013e42010-11-24 16:55:50 +00009493
glennrp0fe50b42010-11-16 03:52:51 +00009494 else if (ping_color_type ==
9495 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009496 mng_info->IsPalette)
9497 {
cristy3ed852e2009-09-05 21:47:34 +00009498 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009499
cristy3ed852e2009-09-05 21:47:34 +00009500 int
9501 depth_4_ok=MagickTrue,
9502 depth_2_ok=MagickTrue,
9503 depth_1_ok=MagickTrue;
9504
cristybb503372010-05-27 20:51:26 +00009505 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009506 {
9507 unsigned char
9508 intensity;
9509
9510 intensity=ScaleQuantumToChar(image->colormap[i].red);
9511
9512 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9513 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9514 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9515 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009516 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009517 depth_1_ok=MagickFalse;
9518 }
glennrp2b013e42010-11-24 16:55:50 +00009519
cristy3ed852e2009-09-05 21:47:34 +00009520 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009521 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009522
cristy3ed852e2009-09-05 21:47:34 +00009523 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009524 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009525
cristy3ed852e2009-09-05 21:47:34 +00009526 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009527 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009528 }
9529 }
glennrp2b013e42010-11-24 16:55:50 +00009530
glennrp5af765f2010-03-30 11:12:18 +00009531 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009532 }
glennrp0fe50b42010-11-16 03:52:51 +00009533
cristy3ed852e2009-09-05 21:47:34 +00009534 else
glennrp0fe50b42010-11-16 03:52:51 +00009535
cristy3ed852e2009-09-05 21:47:34 +00009536 if (mng_info->IsPalette)
9537 {
glennrp17a14852010-05-10 03:01:59 +00009538 number_colors=image_colors;
9539
cristy3ed852e2009-09-05 21:47:34 +00009540 if (image_depth <= 8)
9541 {
cristy3ed852e2009-09-05 21:47:34 +00009542 /*
9543 Set image palette.
9544 */
glennrp5af765f2010-03-30 11:12:18 +00009545 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009546
glennrp58e01762011-01-07 15:28:54 +00009547 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009548 {
glennrp9c1eb072010-06-06 22:19:15 +00009549 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009550
glennrp3b51f0e2010-11-27 18:14:08 +00009551 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9553 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009554 }
glennrp0fe50b42010-11-16 03:52:51 +00009555
cristy3ed852e2009-09-05 21:47:34 +00009556 else
9557 {
cristybb503372010-05-27 20:51:26 +00009558 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009559 {
9560 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9561 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9562 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9563 }
glennrp0fe50b42010-11-16 03:52:51 +00009564
glennrp3b51f0e2010-11-27 18:14:08 +00009565 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009567 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009568 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009569
glennrp39992b42010-11-14 00:03:43 +00009570 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009571 }
glennrp0fe50b42010-11-16 03:52:51 +00009572
cristy3ed852e2009-09-05 21:47:34 +00009573 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009574 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009575 {
cristybefe4d22010-06-07 01:18:58 +00009576 size_t
9577 one;
9578
glennrp5af765f2010-03-30 11:12:18 +00009579 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009580 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009581
cristy94b11832011-09-08 19:46:03 +00009582 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009583 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009584 }
glennrp0fe50b42010-11-16 03:52:51 +00009585
glennrp5af765f2010-03-30 11:12:18 +00009586 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009587
glennrp58e01762011-01-07 15:28:54 +00009588 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009589 {
glennrp0fe50b42010-11-16 03:52:51 +00009590 /*
glennrpd6bf1612010-12-17 17:28:54 +00009591 * Set up trans_colors array.
9592 */
glennrp0fe50b42010-11-16 03:52:51 +00009593 assert(number_colors <= 256);
9594
glennrpd6bf1612010-12-17 17:28:54 +00009595 ping_num_trans=(unsigned short) (number_transparent +
9596 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009597
9598 if (ping_num_trans == 0)
9599 ping_have_tRNS=MagickFalse;
9600
glennrpd6bf1612010-12-17 17:28:54 +00009601 else
glennrp0fe50b42010-11-16 03:52:51 +00009602 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009603 if (logging != MagickFalse)
9604 {
9605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9606 " Scaling ping_trans_color (1)");
9607 }
glennrpd6bf1612010-12-17 17:28:54 +00009608 ping_have_tRNS=MagickTrue;
9609
9610 for (i=0; i < ping_num_trans; i++)
9611 {
cristy4c08aed2011-07-01 19:47:50 +00009612 ping_trans_alpha[i]= (png_byte)
9613 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009614 }
glennrp0fe50b42010-11-16 03:52:51 +00009615 }
9616 }
cristy3ed852e2009-09-05 21:47:34 +00009617 }
9618 }
glennrp0fe50b42010-11-16 03:52:51 +00009619
cristy3ed852e2009-09-05 21:47:34 +00009620 else
9621 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009622
cristy3ed852e2009-09-05 21:47:34 +00009623 if (image_depth < 8)
9624 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009625
cristy3ed852e2009-09-05 21:47:34 +00009626 if ((save_image_depth == 16) && (image_depth == 8))
9627 {
glennrp4f25bd02011-01-01 18:51:28 +00009628 if (logging != MagickFalse)
9629 {
9630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9631 " Scaling ping_trans_color from (%d,%d,%d)",
9632 (int) ping_trans_color.red,
9633 (int) ping_trans_color.green,
9634 (int) ping_trans_color.blue);
9635 }
9636
glennrp5af765f2010-03-30 11:12:18 +00009637 ping_trans_color.red*=0x0101;
9638 ping_trans_color.green*=0x0101;
9639 ping_trans_color.blue*=0x0101;
9640 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009641
9642 if (logging != MagickFalse)
9643 {
9644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9645 " to (%d,%d,%d)",
9646 (int) ping_trans_color.red,
9647 (int) ping_trans_color.green,
9648 (int) ping_trans_color.blue);
9649 }
cristy3ed852e2009-09-05 21:47:34 +00009650 }
9651 }
9652
cristy4383ec82011-01-05 15:42:32 +00009653 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9654 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009655
cristy3ed852e2009-09-05 21:47:34 +00009656 /*
9657 Adjust background and transparency samples in sub-8-bit grayscale files.
9658 */
glennrp5af765f2010-03-30 11:12:18 +00009659 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009660 PNG_COLOR_TYPE_GRAY)
9661 {
9662 png_uint_16
9663 maxval;
9664
cristy35ef8242010-06-03 16:24:13 +00009665 size_t
9666 one=1;
9667
cristy22ffd972010-06-03 16:51:47 +00009668 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009669
glennrp4f25bd02011-01-01 18:51:28 +00009670 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009671 {
cristy3ed852e2009-09-05 21:47:34 +00009672
glennrp9f0fa852011-12-15 12:20:50 +00009673 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9674 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9675 &image->background_color))) +.5)));
cristy3ed852e2009-09-05 21:47:34 +00009676
9677 if (logging != MagickFalse)
9678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009679 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9681 " background_color index is %d",
9682 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009683
glennrp991d11d2010-11-12 21:55:28 +00009684 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009685 }
cristy3ed852e2009-09-05 21:47:34 +00009686
glennrp3e3e20f2011-06-09 04:21:43 +00009687 if (logging != MagickFalse)
9688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9689 " Scaling ping_trans_color.gray from %d",
9690 (int)ping_trans_color.gray);
9691
glennrp9be9b1c2011-06-09 12:21:45 +00009692 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009693 ping_trans_color.gray)+.5);
9694
9695 if (logging != MagickFalse)
9696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9697 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009698 }
glennrp17a14852010-05-10 03:01:59 +00009699
glennrp26f37912010-12-23 16:22:42 +00009700 if (ping_exclude_bKGD == MagickFalse)
9701 {
glennrp1273f7b2011-02-24 03:20:30 +00009702 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009703 {
9704 /*
9705 Identify which colormap entry is the background color.
9706 */
9707
glennrp17a14852010-05-10 03:01:59 +00009708 number_colors=image_colors;
9709
glennrpa521b2f2010-10-29 04:11:03 +00009710 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9711 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009712 break;
9713
9714 ping_background.index=(png_byte) i;
9715
glennrp3b51f0e2010-11-27 18:14:08 +00009716 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009717 {
9718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009719 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009720 }
glennrp0fe50b42010-11-16 03:52:51 +00009721
cristy13d07042010-11-21 20:56:18 +00009722 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009723 {
9724 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009725
9726 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009727 {
9728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9729 " background =(%d,%d,%d)",
9730 (int) ping_background.red,
9731 (int) ping_background.green,
9732 (int) ping_background.blue);
9733 }
9734 }
glennrpa521b2f2010-10-29 04:11:03 +00009735
glennrpd6bf1612010-12-17 17:28:54 +00009736 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009737 {
glennrp3b51f0e2010-11-27 18:14:08 +00009738 if (logging != MagickFalse)
9739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9740 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009741 ping_have_bKGD = MagickFalse;
9742 }
glennrp17a14852010-05-10 03:01:59 +00009743 }
glennrp26f37912010-12-23 16:22:42 +00009744 }
glennrp17a14852010-05-10 03:01:59 +00009745
cristy3ed852e2009-09-05 21:47:34 +00009746 if (logging != MagickFalse)
9747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009748 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009749 /*
9750 Initialize compression level and filtering.
9751 */
9752 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009753 {
9754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9755 " Setting up deflate compression");
9756
9757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9758 " Compression buffer size: 32768");
9759 }
9760
cristy3ed852e2009-09-05 21:47:34 +00009761 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009762
cristy3ed852e2009-09-05 21:47:34 +00009763 if (logging != MagickFalse)
9764 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9765 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009766
cristy4054bfb2011-08-29 23:41:39 +00009767 png_set_compression_mem_level(ping, 9);
9768
glennrp10d739e2011-06-29 18:00:52 +00009769 /* Untangle the "-quality" setting:
9770
9771 Undefined is 0; the default is used.
9772 Default is 75
9773
9774 10's digit:
9775
9776 0: Use Z_HUFFMAN_ONLY strategy with the
9777 zlib default compression level
9778
9779 1-9: the zlib compression level
9780
9781 1's digit:
9782
9783 0-4: the PNG filter method
9784
9785 5: libpng adaptive filtering if compression level > 5
9786 libpng filter type "none" if compression level <= 5
9787 or if image is grayscale or palette
9788
9789 6: libpng adaptive filtering
9790
9791 7: "LOCO" filtering (intrapixel differing) if writing
9792 a MNG, othewise "none". Did not work in IM-6.7.0-9
9793 and earlier because of a missing "else".
9794
9795 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009796 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009797
9798 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009799 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009800
9801 Note that using the -quality option, not all combinations of
9802 PNG filter type, zlib compression level, and zlib compression
cristy5e6be1e2011-07-16 01:23:39 +00009803 strategy are possible. This will be addressed soon in a
cristy5d6fc9c2011-12-27 03:10:42 +00009804 release that accomodates "-define png:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +00009805
9806 */
9807
cristy3ed852e2009-09-05 21:47:34 +00009808 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9809 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009810
glennrp18682582011-06-30 18:11:47 +00009811 if (quality <= 9)
9812 {
9813 if (mng_info->write_png_compression_strategy == 0)
9814 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9815 }
9816
9817 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009818 {
9819 int
9820 level;
9821
cristybb503372010-05-27 20:51:26 +00009822 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009823
glennrp18682582011-06-30 18:11:47 +00009824 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009825 }
glennrp0fe50b42010-11-16 03:52:51 +00009826
glennrp18682582011-06-30 18:11:47 +00009827 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009828 {
glennrp18682582011-06-30 18:11:47 +00009829 if ((quality %10) == 8 || (quality %10) == 9)
9830 mng_info->write_png_compression_strategy=Z_RLE;
cristy3ed852e2009-09-05 21:47:34 +00009831 }
glennrp0fe50b42010-11-16 03:52:51 +00009832
glennrp18682582011-06-30 18:11:47 +00009833 if (mng_info->write_png_compression_filter == 0)
9834 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9835
cristy3ed852e2009-09-05 21:47:34 +00009836 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009837 {
glennrp18682582011-06-30 18:11:47 +00009838 if (mng_info->write_png_compression_level)
9839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9840 " Compression level: %d",
9841 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009842
glennrp18682582011-06-30 18:11:47 +00009843 if (mng_info->write_png_compression_strategy)
9844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9845 " Compression strategy: %d",
9846 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009847
glennrp18682582011-06-30 18:11:47 +00009848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9849 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009850
cristy4054bfb2011-08-29 23:41:39 +00009851 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +00009852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9853 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +00009854 else if (mng_info->write_png_compression_filter == 0 ||
9855 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +00009856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9857 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009858 else
9859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9860 " Base filter method: %d",
9861 (int) mng_info->write_png_compression_filter-1);
9862 }
glennrp2b013e42010-11-24 16:55:50 +00009863
glennrp18682582011-06-30 18:11:47 +00009864 if (mng_info->write_png_compression_level != 0)
9865 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9866
9867 if (mng_info->write_png_compression_filter == 6)
9868 {
9869 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9870 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9871 (quality < 50))
9872 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9873 else
9874 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9875 }
cristy4054bfb2011-08-29 23:41:39 +00009876 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +00009877 mng_info->write_png_compression_filter == 10)
9878 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9879
9880 else if (mng_info->write_png_compression_filter == 8)
9881 {
9882#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9883 if (mng_info->write_mng)
9884 {
9885 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9886 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9887 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9888 }
9889#endif
cristy4054bfb2011-08-29 23:41:39 +00009890 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +00009891 }
9892
9893 else if (mng_info->write_png_compression_filter == 9)
9894 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9895
9896 else if (mng_info->write_png_compression_filter != 0)
9897 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9898 mng_info->write_png_compression_filter-1);
9899
9900 if (mng_info->write_png_compression_strategy != 0)
9901 png_set_compression_strategy(ping,
9902 mng_info->write_png_compression_strategy-1);
9903
cristy0d57eec2011-09-04 22:13:56 +00009904 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9905 if (ping_exclude_sRGB != MagickFalse ||
9906 (image->rendering_intent == UndefinedIntent))
9907 {
9908 if ((ping_exclude_tEXt == MagickFalse ||
9909 ping_exclude_zTXt == MagickFalse) &&
9910 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009911 {
9912 ResetImageProfileIterator(image);
9913 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009914 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009915 profile=GetImageProfile(image,name);
9916
9917 if (profile != (StringInfo *) NULL)
9918 {
glennrp5af765f2010-03-30 11:12:18 +00009919#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009920 if ((LocaleCompare(name,"ICC") == 0) ||
9921 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009922 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009923
9924 if (ping_exclude_iCCP == MagickFalse)
9925 {
cristy9f027d12011-09-21 01:17:17 +00009926 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009927#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009928 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009929#else
9930 (png_const_bytep) GetStringInfoDatum(profile),
9931#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009932 (png_uint_32) GetStringInfoLength(profile));
9933 }
glennrp26f37912010-12-23 16:22:42 +00009934 }
glennrp0fe50b42010-11-16 03:52:51 +00009935
glennrpc8cbc5d2011-01-01 00:12:34 +00009936 else
cristy3ed852e2009-09-05 21:47:34 +00009937#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009938 if (ping_exclude_zCCP == MagickFalse)
9939 {
glennrpcf002022011-01-30 02:38:15 +00009940 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009941 (unsigned char *) name,(unsigned char *) name,
9942 GetStringInfoDatum(profile),
9943 (png_uint_32) GetStringInfoLength(profile));
9944 }
9945 }
glennrp0b206f52011-01-07 04:55:32 +00009946
glennrpc8cbc5d2011-01-01 00:12:34 +00009947 if (logging != MagickFalse)
9948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9949 " Setting up text chunk with %s profile",name);
9950
9951 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009952 }
cristy0d57eec2011-09-04 22:13:56 +00009953 }
cristy3ed852e2009-09-05 21:47:34 +00009954 }
9955
9956#if defined(PNG_WRITE_sRGB_SUPPORTED)
9957 if ((mng_info->have_write_global_srgb == 0) &&
9958 ((image->rendering_intent != UndefinedIntent) ||
9959 (image->colorspace == sRGBColorspace)))
9960 {
glennrp26f37912010-12-23 16:22:42 +00009961 if (ping_exclude_sRGB == MagickFalse)
9962 {
9963 /*
9964 Note image rendering intent.
9965 */
9966 if (logging != MagickFalse)
9967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9968 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009969
glennrp26f37912010-12-23 16:22:42 +00009970 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009971 Magick_RenderingIntent_to_PNG_RenderingIntent(
9972 image->rendering_intent)));
glennrp26f37912010-12-23 16:22:42 +00009973 }
cristy3ed852e2009-09-05 21:47:34 +00009974 }
glennrp26f37912010-12-23 16:22:42 +00009975
glennrp5af765f2010-03-30 11:12:18 +00009976 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009977#endif
9978 {
glennrp2cc891a2010-12-24 13:44:32 +00009979 if (ping_exclude_gAMA == MagickFalse &&
9980 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009981 (image->gamma < .45 || image->gamma > .46)))
9982 {
cristy3ed852e2009-09-05 21:47:34 +00009983 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9984 {
9985 /*
9986 Note image gamma.
9987 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9988 */
9989 if (logging != MagickFalse)
9990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9991 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009992
cristy3ed852e2009-09-05 21:47:34 +00009993 png_set_gAMA(ping,ping_info,image->gamma);
9994 }
glennrp26f37912010-12-23 16:22:42 +00009995 }
glennrp2b013e42010-11-24 16:55:50 +00009996
glennrp26f37912010-12-23 16:22:42 +00009997 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009998 {
glennrp26f37912010-12-23 16:22:42 +00009999 if ((mng_info->have_write_global_chrm == 0) &&
10000 (image->chromaticity.red_primary.x != 0.0))
10001 {
10002 /*
10003 Note image chromaticity.
10004 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10005 */
10006 PrimaryInfo
10007 bp,
10008 gp,
10009 rp,
10010 wp;
cristy3ed852e2009-09-05 21:47:34 +000010011
glennrp26f37912010-12-23 16:22:42 +000010012 wp=image->chromaticity.white_point;
10013 rp=image->chromaticity.red_primary;
10014 gp=image->chromaticity.green_primary;
10015 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +000010016
glennrp26f37912010-12-23 16:22:42 +000010017 if (logging != MagickFalse)
10018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10019 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010020
glennrp26f37912010-12-23 16:22:42 +000010021 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10022 bp.x,bp.y);
10023 }
10024 }
cristy3ed852e2009-09-05 21:47:34 +000010025 }
glennrpdfd70802010-11-14 01:23:35 +000010026
glennrp5af765f2010-03-30 11:12:18 +000010027 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +000010028
10029 if (mng_info->write_mng)
10030 png_set_sig_bytes(ping,8);
10031
cristy5d6fc9c2011-12-27 03:10:42 +000010032 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
cristy3ed852e2009-09-05 21:47:34 +000010033
glennrpd6bf1612010-12-17 17:28:54 +000010034 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +000010035 {
10036 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +000010037 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010038 {
glennrp5af765f2010-03-30 11:12:18 +000010039 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +000010040
glennrp5af765f2010-03-30 11:12:18 +000010041 if (ping_bit_depth < 8)
10042 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +000010043 }
glennrp0fe50b42010-11-16 03:52:51 +000010044
cristy3ed852e2009-09-05 21:47:34 +000010045 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +000010046 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +000010047 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +000010048 }
10049
glennrp0e8ea192010-12-24 18:00:33 +000010050 if (ping_need_colortype_warning != MagickFalse ||
10051 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +000010052 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +000010053 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +000010054 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +000010055 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +000010056 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +000010057 {
10058 if (logging != MagickFalse)
10059 {
glennrp0e8ea192010-12-24 18:00:33 +000010060 if (ping_need_colortype_warning != MagickFalse)
10061 {
10062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10063 " Image has transparency but tRNS chunk was excluded");
10064 }
10065
cristy3ed852e2009-09-05 21:47:34 +000010066 if (mng_info->write_png_depth)
10067 {
10068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010069 " Defined png:bit-depth=%u, Computed depth=%u",
cristy3ed852e2009-09-05 21:47:34 +000010070 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +000010071 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +000010072 }
glennrp0e8ea192010-12-24 18:00:33 +000010073
cristy3ed852e2009-09-05 21:47:34 +000010074 if (mng_info->write_png_colortype)
10075 {
10076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010077 " Defined png:color-type=%u, Computed color type=%u",
cristy3ed852e2009-09-05 21:47:34 +000010078 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +000010079 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010080 }
10081 }
glennrp0e8ea192010-12-24 18:00:33 +000010082
glennrp3bd2e412010-08-10 13:34:52 +000010083 png_warning(ping,
cristy5d6fc9c2011-12-27 03:10:42 +000010084 "Cannot write image with defined png:bit-depth or png:color-type.");
cristy3ed852e2009-09-05 21:47:34 +000010085 }
10086
glennrp58e01762011-01-07 15:28:54 +000010087 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010088 {
10089 /* Add an opaque matte channel */
10090 image->matte = MagickTrue;
cristye941a752011-10-15 01:52:48 +000010091 (void) SetImageAlpha(image,OpaqueAlpha,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010092
glennrpb4a13412010-05-05 12:47:19 +000010093 if (logging != MagickFalse)
10094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10095 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +000010096 }
10097
glennrp0e319732011-01-25 21:53:13 +000010098 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +000010099 {
glennrp991d11d2010-11-12 21:55:28 +000010100 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +000010101 {
glennrp991d11d2010-11-12 21:55:28 +000010102 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010103 if (logging != MagickFalse)
10104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10105 " Setting ping_have_tRNS=MagickTrue.");
10106 }
glennrpe9c26dc2010-05-30 01:56:35 +000010107 }
10108
cristy3ed852e2009-09-05 21:47:34 +000010109 if (logging != MagickFalse)
10110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10111 " Writing PNG header chunks");
10112
glennrp5af765f2010-03-30 11:12:18 +000010113 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10114 ping_bit_depth,ping_color_type,
10115 ping_interlace_method,ping_compression_method,
10116 ping_filter_method);
10117
glennrp39992b42010-11-14 00:03:43 +000010118 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10119 {
glennrpf09bded2011-01-08 01:15:59 +000010120 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010121
glennrp3b51f0e2010-11-27 18:14:08 +000010122 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +000010123 {
glennrp8640fb52010-11-23 15:48:26 +000010124 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +000010125 {
glennrpd6bf1612010-12-17 17:28:54 +000010126 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +000010127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010128 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10129 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010130 (int) palette[i].red,
10131 (int) palette[i].green,
10132 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +000010133 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010134 (int) ping_trans_alpha[i]);
10135 else
10136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010137 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010138 (int) i,
10139 (int) palette[i].red,
10140 (int) palette[i].green,
10141 (int) palette[i].blue);
10142 }
glennrp39992b42010-11-14 00:03:43 +000010143 }
glennrp39992b42010-11-14 00:03:43 +000010144 }
10145
glennrp26f37912010-12-23 16:22:42 +000010146 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010147 {
glennrp26f37912010-12-23 16:22:42 +000010148 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010149 {
glennrp26f37912010-12-23 16:22:42 +000010150 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010151 if (logging)
10152 {
10153 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10154 " Setting up bKGD chunk");
10155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10156 " background color = (%d,%d,%d)",
10157 (int) ping_background.red,
10158 (int) ping_background.green,
10159 (int) ping_background.blue);
10160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10161 " index = %d, gray=%d",
10162 (int) ping_background.index,
10163 (int) ping_background.gray);
10164 }
10165 }
glennrp26f37912010-12-23 16:22:42 +000010166 }
10167
10168 if (ping_exclude_pHYs == MagickFalse)
10169 {
10170 if (ping_have_pHYs != MagickFalse)
10171 {
10172 png_set_pHYs(ping,ping_info,
10173 ping_pHYs_x_resolution,
10174 ping_pHYs_y_resolution,
10175 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010176
10177 if (logging)
10178 {
10179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10180 " Setting up pHYs chunk");
10181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10182 " x_resolution=%lu",
10183 (unsigned long) ping_pHYs_x_resolution);
10184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10185 " y_resolution=%lu",
10186 (unsigned long) ping_pHYs_y_resolution);
10187 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10188 " unit_type=%lu",
10189 (unsigned long) ping_pHYs_unit_type);
10190 }
glennrp26f37912010-12-23 16:22:42 +000010191 }
glennrpdfd70802010-11-14 01:23:35 +000010192 }
10193
10194#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010195 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010196 {
glennrp26f37912010-12-23 16:22:42 +000010197 if (image->page.x || image->page.y)
10198 {
10199 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10200 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010201
glennrp26f37912010-12-23 16:22:42 +000010202 if (logging != MagickFalse)
10203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10204 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10205 (int) image->page.x, (int) image->page.y);
10206 }
glennrpdfd70802010-11-14 01:23:35 +000010207 }
10208#endif
10209
glennrpda8f3a72011-02-27 23:54:12 +000010210 if (mng_info->need_blob != MagickFalse)
10211 {
cristyc82a27b2011-10-21 01:07:16 +000010212 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
glennrpda8f3a72011-02-27 23:54:12 +000010213 MagickFalse)
10214 png_error(ping,"WriteBlob Failed");
10215
10216 ping_have_blob=MagickTrue;
10217 }
10218
cristy3ed852e2009-09-05 21:47:34 +000010219 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010220
glennrp39992b42010-11-14 00:03:43 +000010221 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010222 {
glennrp3b51f0e2010-11-27 18:14:08 +000010223 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010224 {
10225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10226 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10227 }
10228
10229 if (ping_color_type == 3)
10230 (void) png_set_tRNS(ping, ping_info,
10231 ping_trans_alpha,
10232 ping_num_trans,
10233 NULL);
10234
10235 else
10236 {
10237 (void) png_set_tRNS(ping, ping_info,
10238 NULL,
10239 0,
10240 &ping_trans_color);
10241
glennrp3b51f0e2010-11-27 18:14:08 +000010242 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010243 {
10244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010245 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010246 (int) ping_trans_color.red,
10247 (int) ping_trans_color.green,
10248 (int) ping_trans_color.blue);
10249 }
10250 }
glennrp991d11d2010-11-12 21:55:28 +000010251 }
10252
cristy3ed852e2009-09-05 21:47:34 +000010253 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010254 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010255
cristy3ed852e2009-09-05 21:47:34 +000010256 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010257
cristy3ed852e2009-09-05 21:47:34 +000010258 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010259 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010260
glennrp26f37912010-12-23 16:22:42 +000010261 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010262 {
glennrp4f25bd02011-01-01 18:51:28 +000010263 if ((image->page.width != 0 && image->page.width != image->columns) ||
10264 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010265 {
10266 unsigned char
10267 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010268
glennrp26f37912010-12-23 16:22:42 +000010269 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10270 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010271 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010272 PNGLong(chunk+4,(png_uint_32) image->page.width);
10273 PNGLong(chunk+8,(png_uint_32) image->page.height);
10274 chunk[12]=0; /* unit = pixels */
10275 (void) WriteBlob(image,13,chunk);
10276 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10277 }
cristy3ed852e2009-09-05 21:47:34 +000010278 }
10279
10280#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010281 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010282#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010283 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010284#undef PNG_HAVE_IDAT
10285#endif
10286
10287 png_set_packing(ping);
10288 /*
10289 Allocate memory.
10290 */
10291 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010292 if (image_depth > 8)
10293 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010294 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010295 {
glennrpb4a13412010-05-05 12:47:19 +000010296 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010297 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010298 break;
glennrp0fe50b42010-11-16 03:52:51 +000010299
glennrpb4a13412010-05-05 12:47:19 +000010300 case PNG_COLOR_TYPE_GRAY_ALPHA:
10301 rowbytes*=2;
10302 break;
glennrp0fe50b42010-11-16 03:52:51 +000010303
glennrpb4a13412010-05-05 12:47:19 +000010304 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010305 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010306 break;
glennrp0fe50b42010-11-16 03:52:51 +000010307
glennrpb4a13412010-05-05 12:47:19 +000010308 default:
10309 break;
cristy3ed852e2009-09-05 21:47:34 +000010310 }
glennrp3b51f0e2010-11-27 18:14:08 +000010311
10312 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010313 {
10314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10315 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010316
glennrpb4a13412010-05-05 12:47:19 +000010317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010318 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010319 }
glennrpcf002022011-01-30 02:38:15 +000010320 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10321 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010322
glennrpcf002022011-01-30 02:38:15 +000010323 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010324 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010325
cristy3ed852e2009-09-05 21:47:34 +000010326 /*
10327 Initialize image scanlines.
10328 */
glennrp5af765f2010-03-30 11:12:18 +000010329 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010330 {
10331 /*
10332 PNG write failed.
10333 */
10334#ifdef PNG_DEBUG
10335 if (image_info->verbose)
10336 (void) printf("PNG write has failed.\n");
10337#endif
10338 png_destroy_write_struct(&ping,&ping_info);
10339 if (quantum_info != (QuantumInfo *) NULL)
10340 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010341 if (ping_pixels != (unsigned char *) NULL)
10342 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010343#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010344 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010345#endif
glennrpda8f3a72011-02-27 23:54:12 +000010346 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010347 (void) CloseBlob(image);
10348 image_info=DestroyImageInfo(image_info);
10349 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010350 return(MagickFalse);
10351 }
cristyed552522009-10-16 14:04:35 +000010352 quantum_info=AcquireQuantumInfo(image_info,image);
10353 if (quantum_info == (QuantumInfo *) NULL)
10354 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010355 quantum_info->format=UndefinedQuantumFormat;
10356 quantum_info->depth=image_depth;
10357 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010358
cristy3ed852e2009-09-05 21:47:34 +000010359 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010360 !mng_info->write_png32) &&
10361 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010362 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010363 image_matte == MagickFalse &&
10364 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010365 {
glennrp8bb3a022010-12-13 20:40:04 +000010366 /* Palette, Bilevel, or Opaque Monochrome */
cristy4c08aed2011-07-01 19:47:50 +000010367 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010368 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010369
cristy3ed852e2009-09-05 21:47:34 +000010370 quantum_info->depth=8;
10371 for (pass=0; pass < num_passes; pass++)
10372 {
10373 /*
10374 Convert PseudoClass image to a PNG monochrome image.
10375 */
cristybb503372010-05-27 20:51:26 +000010376 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010377 {
glennrpd71e86a2011-02-24 01:28:37 +000010378 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10380 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010381
cristyc82a27b2011-10-21 01:07:16 +000010382 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010383
cristy4c08aed2011-07-01 19:47:50 +000010384 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010385 break;
glennrp0fe50b42010-11-16 03:52:51 +000010386
cristy3ed852e2009-09-05 21:47:34 +000010387 if (mng_info->IsPalette)
10388 {
cristy4c08aed2011-07-01 19:47:50 +000010389 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010390 quantum_info,GrayQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010391 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10392 mng_info->write_png_depth &&
10393 mng_info->write_png_depth != old_bit_depth)
10394 {
10395 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010396 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010397 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010398 >> (8-old_bit_depth));
10399 }
10400 }
glennrp0fe50b42010-11-16 03:52:51 +000010401
cristy3ed852e2009-09-05 21:47:34 +000010402 else
10403 {
cristy4c08aed2011-07-01 19:47:50 +000010404 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010405 quantum_info,RedQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010406 }
glennrp0fe50b42010-11-16 03:52:51 +000010407
cristy3ed852e2009-09-05 21:47:34 +000010408 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010409 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010410 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010411 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010412
glennrp3b51f0e2010-11-27 18:14:08 +000010413 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10415 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010416
glennrpcf002022011-01-30 02:38:15 +000010417 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010418 }
10419 if (image->previous == (Image *) NULL)
10420 {
10421 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10422 if (status == MagickFalse)
10423 break;
10424 }
10425 }
10426 }
glennrp0fe50b42010-11-16 03:52:51 +000010427
glennrp8bb3a022010-12-13 20:40:04 +000010428 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010429 {
glennrp0fe50b42010-11-16 03:52:51 +000010430 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010431 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010432 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010433 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010434 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010435 {
cristy4c08aed2011-07-01 19:47:50 +000010436 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010437 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010438
glennrp8bb3a022010-12-13 20:40:04 +000010439 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010440 {
glennrp8bb3a022010-12-13 20:40:04 +000010441
cristybb503372010-05-27 20:51:26 +000010442 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010443 {
cristyc82a27b2011-10-21 01:07:16 +000010444 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010445
cristy4c08aed2011-07-01 19:47:50 +000010446 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010447 break;
glennrp2cc891a2010-12-24 13:44:32 +000010448
glennrp5af765f2010-03-30 11:12:18 +000010449 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010450 {
glennrp8bb3a022010-12-13 20:40:04 +000010451 if (mng_info->IsPalette)
cristy4c08aed2011-07-01 19:47:50 +000010452 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010453 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010454
glennrp8bb3a022010-12-13 20:40:04 +000010455 else
cristy4c08aed2011-07-01 19:47:50 +000010456 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010457 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010458
glennrp3b51f0e2010-11-27 18:14:08 +000010459 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010461 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010462 }
glennrp2cc891a2010-12-24 13:44:32 +000010463
glennrp8bb3a022010-12-13 20:40:04 +000010464 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10465 {
10466 if (logging != MagickFalse && y == 0)
10467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10468 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010469
cristy4c08aed2011-07-01 19:47:50 +000010470 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010471 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010472 }
glennrp2cc891a2010-12-24 13:44:32 +000010473
glennrp3b51f0e2010-11-27 18:14:08 +000010474 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010476 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010477
glennrpcf002022011-01-30 02:38:15 +000010478 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010479 }
glennrp2cc891a2010-12-24 13:44:32 +000010480
glennrp8bb3a022010-12-13 20:40:04 +000010481 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010482 {
glennrp8bb3a022010-12-13 20:40:04 +000010483 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10484 if (status == MagickFalse)
10485 break;
cristy3ed852e2009-09-05 21:47:34 +000010486 }
cristy3ed852e2009-09-05 21:47:34 +000010487 }
10488 }
glennrp8bb3a022010-12-13 20:40:04 +000010489
10490 else
10491 {
cristy4c08aed2011-07-01 19:47:50 +000010492 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010493 *p;
10494
10495 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010496 {
glennrp8bb3a022010-12-13 20:40:04 +000010497 if ((image_depth > 8) || (mng_info->write_png24 ||
10498 mng_info->write_png32 ||
10499 (!mng_info->write_png8 && !mng_info->IsPalette)))
10500 {
10501 for (y=0; y < (ssize_t) image->rows; y++)
10502 {
10503 p=GetVirtualPixels(image,0,y,image->columns,1,
cristyc82a27b2011-10-21 01:07:16 +000010504 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010505
cristy4c08aed2011-07-01 19:47:50 +000010506 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010507 break;
glennrp2cc891a2010-12-24 13:44:32 +000010508
glennrp8bb3a022010-12-13 20:40:04 +000010509 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10510 {
10511 if (image->storage_class == DirectClass)
cristy4c08aed2011-07-01 19:47:50 +000010512 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010513 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010514
glennrp8bb3a022010-12-13 20:40:04 +000010515 else
cristy4c08aed2011-07-01 19:47:50 +000010516 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010517 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010518 }
glennrp2cc891a2010-12-24 13:44:32 +000010519
glennrp8bb3a022010-12-13 20:40:04 +000010520 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10521 {
cristy4c08aed2011-07-01 19:47:50 +000010522 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010523 quantum_info,GrayAlphaQuantum,ping_pixels,
cristyc82a27b2011-10-21 01:07:16 +000010524 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010525
glennrp8bb3a022010-12-13 20:40:04 +000010526 if (logging != MagickFalse && y == 0)
10527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10528 " Writing GRAY_ALPHA PNG pixels (3)");
10529 }
glennrp2cc891a2010-12-24 13:44:32 +000010530
glennrp8bb3a022010-12-13 20:40:04 +000010531 else if (image_matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +000010532 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010533 quantum_info,RGBAQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010534
glennrp8bb3a022010-12-13 20:40:04 +000010535 else
cristy4c08aed2011-07-01 19:47:50 +000010536 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010537 quantum_info,RGBQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010538
glennrp8bb3a022010-12-13 20:40:04 +000010539 if (logging != MagickFalse && y == 0)
10540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10541 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010542
glennrpcf002022011-01-30 02:38:15 +000010543 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010544 }
10545 }
glennrp2cc891a2010-12-24 13:44:32 +000010546
glennrp8bb3a022010-12-13 20:40:04 +000010547 else
10548 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10549 mng_info->write_png32 ||
10550 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10551 {
10552 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10553 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10554 {
10555 if (logging != MagickFalse)
10556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10557 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010558
glennrp8bb3a022010-12-13 20:40:04 +000010559 quantum_info->depth=8;
10560 image_depth=8;
10561 }
glennrp2cc891a2010-12-24 13:44:32 +000010562
glennrp8bb3a022010-12-13 20:40:04 +000010563 for (y=0; y < (ssize_t) image->rows; y++)
10564 {
10565 if (logging != MagickFalse && y == 0)
10566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10567 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010568
cristy97707062011-12-27 18:25:00 +000010569 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000010570
cristy4c08aed2011-07-01 19:47:50 +000010571 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010572 break;
glennrp2cc891a2010-12-24 13:44:32 +000010573
glennrp8bb3a022010-12-13 20:40:04 +000010574 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010575 {
glennrp4bf89732011-03-21 13:48:28 +000010576 quantum_info->depth=image->depth;
10577
cristy4c08aed2011-07-01 19:47:50 +000010578 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010579 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp44757ab2011-03-17 12:57:03 +000010580 }
glennrp2cc891a2010-12-24 13:44:32 +000010581
glennrp8bb3a022010-12-13 20:40:04 +000010582 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10583 {
10584 if (logging != MagickFalse && y == 0)
10585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10586 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010587
cristy4c08aed2011-07-01 19:47:50 +000010588 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010589 quantum_info,GrayAlphaQuantum,ping_pixels,
cristyc82a27b2011-10-21 01:07:16 +000010590 exception);
glennrp8bb3a022010-12-13 20:40:04 +000010591 }
glennrp2cc891a2010-12-24 13:44:32 +000010592
glennrp8bb3a022010-12-13 20:40:04 +000010593 else
glennrp8bb3a022010-12-13 20:40:04 +000010594 {
cristy4c08aed2011-07-01 19:47:50 +000010595 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010596 quantum_info,IndexQuantum,ping_pixels,exception);
glennrp5eae7602011-02-22 15:21:32 +000010597
10598 if (logging != MagickFalse && y <= 2)
10599 {
10600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010601 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010602
10603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10604 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10605 (int)ping_pixels[0],(int)ping_pixels[1]);
10606 }
glennrp8bb3a022010-12-13 20:40:04 +000010607 }
glennrpcf002022011-01-30 02:38:15 +000010608 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010609 }
10610 }
glennrp2cc891a2010-12-24 13:44:32 +000010611
glennrp8bb3a022010-12-13 20:40:04 +000010612 if (image->previous == (Image *) NULL)
10613 {
10614 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10615 if (status == MagickFalse)
10616 break;
10617 }
cristy3ed852e2009-09-05 21:47:34 +000010618 }
glennrp8bb3a022010-12-13 20:40:04 +000010619 }
10620 }
10621
cristyb32b90a2009-09-07 21:45:48 +000010622 if (quantum_info != (QuantumInfo *) NULL)
10623 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010624
10625 if (logging != MagickFalse)
10626 {
10627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010628 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010629
cristy3ed852e2009-09-05 21:47:34 +000010630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010631 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010632
cristy3ed852e2009-09-05 21:47:34 +000010633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010634 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010635
cristy3ed852e2009-09-05 21:47:34 +000010636 if (mng_info->write_png_depth)
10637 {
10638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010639 " Defined png:bit-depth: %d",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010640 }
glennrp0fe50b42010-11-16 03:52:51 +000010641
cristy3ed852e2009-09-05 21:47:34 +000010642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010643 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010644
cristy3ed852e2009-09-05 21:47:34 +000010645 if (mng_info->write_png_colortype)
10646 {
10647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010648 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010649 }
glennrp0fe50b42010-11-16 03:52:51 +000010650
cristy3ed852e2009-09-05 21:47:34 +000010651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010652 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010653
cristy3ed852e2009-09-05 21:47:34 +000010654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010655 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010656 }
10657 /*
glennrpa0ed0092011-04-18 16:36:29 +000010658 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010659 */
glennrp823b55c2011-03-14 18:46:46 +000010660 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010661 {
glennrp26f37912010-12-23 16:22:42 +000010662 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010663 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010664 while (property != (const char *) NULL)
10665 {
10666 png_textp
10667 text;
glennrp2cc891a2010-12-24 13:44:32 +000010668
cristyd15e6592011-10-15 00:13:06 +000010669 value=GetImageProperty(image,property,exception);
glennrpa0ed0092011-04-18 16:36:29 +000010670
10671 /* Don't write any "png:" properties; those are just for "identify" */
10672 if (LocaleNCompare(property,"png:",4) != 0 &&
10673
10674 /* Suppress density and units if we wrote a pHYs chunk */
10675 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010676 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010677 LocaleCompare(property,"units") != 0) &&
10678
10679 /* Suppress the IM-generated Date:create and Date:modify */
10680 (ping_exclude_date == MagickFalse ||
10681 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010682 {
glennrpc70af4a2011-03-07 00:08:23 +000010683 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010684 {
glennrpc70af4a2011-03-07 00:08:23 +000010685 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10686 text[0].key=(char *) property;
10687 text[0].text=(char *) value;
10688 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010689
glennrpc70af4a2011-03-07 00:08:23 +000010690 if (ping_exclude_tEXt != MagickFalse)
10691 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10692
10693 else if (ping_exclude_zTXt != MagickFalse)
10694 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10695
10696 else
glennrp26f37912010-12-23 16:22:42 +000010697 {
glennrpc70af4a2011-03-07 00:08:23 +000010698 text[0].compression=image_info->compression == NoCompression ||
10699 (image_info->compression == UndefinedCompression &&
10700 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10701 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010702 }
glennrp2cc891a2010-12-24 13:44:32 +000010703
glennrpc70af4a2011-03-07 00:08:23 +000010704 if (logging != MagickFalse)
10705 {
10706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10707 " Setting up text chunk");
10708
10709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10710 " keyword: %s",text[0].key);
10711 }
10712
10713 png_set_text(ping,ping_info,text,1);
10714 png_free(ping,text);
10715 }
glennrp26f37912010-12-23 16:22:42 +000010716 }
10717 property=GetNextImageProperty(image);
10718 }
cristy3ed852e2009-09-05 21:47:34 +000010719 }
10720
10721 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010722 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010723
10724 if (logging != MagickFalse)
10725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10726 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010727
cristy3ed852e2009-09-05 21:47:34 +000010728 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010729
cristy3ed852e2009-09-05 21:47:34 +000010730 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10731 {
10732 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010733 (ping_width != mng_info->page.width) ||
10734 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010735 {
10736 unsigned char
10737 chunk[32];
10738
10739 /*
10740 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10741 */
10742 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10743 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010744 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010745 chunk[4]=4;
10746 chunk[5]=0; /* frame name separator (no name) */
10747 chunk[6]=1; /* flag for changing delay, for next frame only */
10748 chunk[7]=0; /* flag for changing frame timeout */
10749 chunk[8]=1; /* flag for changing frame clipping for next frame */
10750 chunk[9]=0; /* flag for changing frame sync_id */
10751 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10752 chunk[14]=0; /* clipping boundaries delta type */
10753 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10754 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010755 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010756 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10757 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010758 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010759 (void) WriteBlob(image,31,chunk);
10760 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10761 mng_info->old_framing_mode=4;
10762 mng_info->framing_mode=1;
10763 }
glennrp0fe50b42010-11-16 03:52:51 +000010764
cristy3ed852e2009-09-05 21:47:34 +000010765 else
10766 mng_info->framing_mode=3;
10767 }
10768 if (mng_info->write_mng && !mng_info->need_fram &&
10769 ((int) image->dispose == 3))
cristyc82a27b2011-10-21 01:07:16 +000010770 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010771 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010772 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010773
cristy3ed852e2009-09-05 21:47:34 +000010774 /*
10775 Free PNG resources.
10776 */
glennrp5af765f2010-03-30 11:12:18 +000010777
cristy3ed852e2009-09-05 21:47:34 +000010778 png_destroy_write_struct(&ping,&ping_info);
10779
glennrpcf002022011-01-30 02:38:15 +000010780 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010781
10782#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010783 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010784#endif
10785
glennrpda8f3a72011-02-27 23:54:12 +000010786 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010787 (void) CloseBlob(image);
10788
10789 image_info=DestroyImageInfo(image_info);
10790 image=DestroyImage(image);
10791
10792 /* Store bit depth actually written */
10793 s[0]=(char) ping_bit_depth;
10794 s[1]='\0';
10795
cristyd15e6592011-10-15 00:13:06 +000010796 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
glennrpb9cfe272010-12-21 15:08:06 +000010797
cristy3ed852e2009-09-05 21:47:34 +000010798 if (logging != MagickFalse)
10799 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10800 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010801
cristy3ed852e2009-09-05 21:47:34 +000010802 return(MagickTrue);
10803/* End write one PNG image */
10804}
10805
10806/*
10807%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10808% %
10809% %
10810% %
10811% W r i t e P N G I m a g e %
10812% %
10813% %
10814% %
10815%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10816%
10817% WritePNGImage() writes a Portable Network Graphics (PNG) or
10818% Multiple-image Network Graphics (MNG) image file.
10819%
10820% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10821%
10822% The format of the WritePNGImage method is:
10823%
cristy1e178e72011-08-28 19:44:34 +000010824% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10825% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010826%
10827% A description of each parameter follows:
10828%
10829% o image_info: the image info.
10830%
10831% o image: The image.
10832%
cristy1e178e72011-08-28 19:44:34 +000010833% o exception: return any errors or warnings in this structure.
10834%
cristy3ed852e2009-09-05 21:47:34 +000010835% Returns MagickTrue on success, MagickFalse on failure.
10836%
10837% Communicating with the PNG encoder:
10838%
10839% While the datastream written is always in PNG format and normally would
10840% be given the "png" file extension, this method also writes the following
cristy5d6fc9c2011-12-27 03:10:42 +000010841% pseudo-formats which are subsets of png:
cristy3ed852e2009-09-05 21:47:34 +000010842%
glennrp5a39f372011-02-25 04:52:16 +000010843% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10844% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010845% is present, the tRNS chunk must only have values 0 and 255
10846% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010847% transparent). If other values are present they will be
10848% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010849% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000010850% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10851% of any resulting fully-transparent pixels is changed to
10852% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000010853%
10854% If you want better quantization or dithering of the colors
10855% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010856% PNG encoder. The pixels contain 8-bit indices even if
10857% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010858% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010859% PNG grayscale type might be slightly more efficient. Please
10860% note that writing to the PNG8 format may result in loss
10861% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010862%
10863% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10864% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010865% one of the colors as transparent. The only loss incurred
10866% is reduction of sample depth to 8. If the image has more
10867% than one transparent color, has semitransparent pixels, or
10868% has an opaque pixel with the same RGB components as the
10869% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010870%
10871% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10872% transparency is permitted, i.e., the alpha sample for
10873% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010874% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010875% The only loss in data is the reduction of the sample depth
10876% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010877%
10878% o -define: For more precise control of the PNG output, you can use the
10879% Image options "png:bit-depth" and "png:color-type". These
10880% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010881% from the application programming interfaces. The options
10882% are case-independent and are converted to lowercase before
10883% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010884%
10885% png:color-type can be 0, 2, 3, 4, or 6.
10886%
10887% When png:color-type is 0 (Grayscale), png:bit-depth can
10888% be 1, 2, 4, 8, or 16.
10889%
10890% When png:color-type is 2 (RGB), png:bit-depth can
10891% be 8 or 16.
10892%
10893% When png:color-type is 3 (Indexed), png:bit-depth can
10894% be 1, 2, 4, or 8. This refers to the number of bits
10895% used to store the index. The color samples always have
10896% bit-depth 8 in indexed PNG files.
10897%
10898% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10899% png:bit-depth can be 8 or 16.
10900%
glennrp5a39f372011-02-25 04:52:16 +000010901% If the image cannot be written without loss with the requested bit-depth
10902% and color-type, a PNG file will not be written, and the encoder will
10903% return MagickFalse.
10904%
cristy3ed852e2009-09-05 21:47:34 +000010905% Since image encoders should not be responsible for the "heavy lifting",
10906% the user should make sure that ImageMagick has already reduced the
10907% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010908% transparency prior to attempting to write the image with depth, color,
glennrp54dc0692011-07-01 19:02:13 +000010909% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010910%
cristy3ed852e2009-09-05 21:47:34 +000010911% Note that another definition, "png:bit-depth-written" exists, but it
10912% is not intended for external use. It is only used internally by the
10913% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10914%
10915% It is possible to request that the PNG encoder write previously-formatted
10916% ancillary chunks in the output PNG file, using the "-profile" commandline
10917% option as shown below or by setting the profile via a programming
10918% interface:
10919%
10920% -profile PNG-chunk-x:<file>
10921%
10922% where x is a location flag and <file> is a file containing the chunk
10923% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010924% This encoder will compute the chunk length and CRC, so those must not
10925% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010926%
10927% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10928% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10929% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010930% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010931%
glennrpbb8a7332010-11-13 15:17:35 +000010932% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010933%
glennrp3241bd02010-12-12 04:36:28 +000010934% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010935%
glennrpd6afd542010-11-19 01:53:05 +000010936% o 32-bit depth is reduced to 16.
10937% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10938% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010939% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010940% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010941% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010942% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10943% this can be done without loss and a larger bit depth N was not
cristy5d6fc9c2011-12-27 03:10:42 +000010944% requested via the "-define png:bit-depth=N" option.
glennrpd6afd542010-11-19 01:53:05 +000010945% o If matte channel is present but only one transparent color is
10946% present, RGB+tRNS is written instead of RGBA
10947% o Opaque matte channel is removed (or added, if color-type 4 or 6
10948% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010949%
cristy3ed852e2009-09-05 21:47:34 +000010950%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10951*/
10952static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +000010953 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010954{
10955 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010956 excluding,
10957 logging,
10958 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010959 status;
10960
10961 MngInfo
10962 *mng_info;
10963
10964 const char
10965 *value;
10966
10967 int
glennrp21f0e622011-01-07 16:20:57 +000010968 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010969 source;
10970
cristy3ed852e2009-09-05 21:47:34 +000010971 /*
10972 Open image file.
10973 */
10974 assert(image_info != (const ImageInfo *) NULL);
10975 assert(image_info->signature == MagickSignature);
10976 assert(image != (Image *) NULL);
10977 assert(image->signature == MagickSignature);
10978 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010979 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010980 /*
10981 Allocate a MngInfo structure.
10982 */
10983 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010984 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010985
cristy3ed852e2009-09-05 21:47:34 +000010986 if (mng_info == (MngInfo *) NULL)
10987 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010988
cristy3ed852e2009-09-05 21:47:34 +000010989 /*
10990 Initialize members of the MngInfo structure.
10991 */
10992 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10993 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010994 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010995 have_mng_structure=MagickTrue;
10996
10997 /* See if user has requested a specific PNG subformat */
10998
10999 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11000 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11001 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11002
11003 if (mng_info->write_png8)
11004 {
glennrp9c1eb072010-06-06 22:19:15 +000011005 mng_info->write_png_colortype = /* 3 */ 4;
11006 mng_info->write_png_depth = 8;
11007 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000011008 }
11009
11010 if (mng_info->write_png24)
11011 {
glennrp9c1eb072010-06-06 22:19:15 +000011012 mng_info->write_png_colortype = /* 2 */ 3;
11013 mng_info->write_png_depth = 8;
11014 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011015
glennrp9c1eb072010-06-06 22:19:15 +000011016 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011017 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011018
glennrp9c1eb072010-06-06 22:19:15 +000011019 else
cristy018f07f2011-09-04 21:15:19 +000011020 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011021
cristyea1a8aa2011-10-20 13:24:06 +000011022 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011023 }
11024
11025 if (mng_info->write_png32)
11026 {
glennrp9c1eb072010-06-06 22:19:15 +000011027 mng_info->write_png_colortype = /* 6 */ 7;
11028 mng_info->write_png_depth = 8;
11029 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011030
glennrp9c1eb072010-06-06 22:19:15 +000011031 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011032 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011033
glennrp9c1eb072010-06-06 22:19:15 +000011034 else
cristy018f07f2011-09-04 21:15:19 +000011035 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011036
cristyea1a8aa2011-10-20 13:24:06 +000011037 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011038 }
11039
11040 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011041
cristy3ed852e2009-09-05 21:47:34 +000011042 if (value != (char *) NULL)
11043 {
11044 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011045 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011046
cristy3ed852e2009-09-05 21:47:34 +000011047 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011048 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011049
cristy3ed852e2009-09-05 21:47:34 +000011050 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011051 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011052
cristy3ed852e2009-09-05 21:47:34 +000011053 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011054 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011055
cristy3ed852e2009-09-05 21:47:34 +000011056 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011057 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011058
glennrpbb8a7332010-11-13 15:17:35 +000011059 else
cristyc82a27b2011-10-21 01:07:16 +000011060 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011061 GetMagickModule(),CoderWarning,
11062 "ignoring invalid defined png:bit-depth",
11063 "=%s",value);
11064
cristy3ed852e2009-09-05 21:47:34 +000011065 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011067 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011068 }
glennrp0fe50b42010-11-16 03:52:51 +000011069
cristy3ed852e2009-09-05 21:47:34 +000011070 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011071
cristy3ed852e2009-09-05 21:47:34 +000011072 if (value != (char *) NULL)
11073 {
11074 /* We must store colortype+1 because 0 is a valid colortype */
11075 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011076 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011077
cristy3ed852e2009-09-05 21:47:34 +000011078 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011079 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011080
cristy3ed852e2009-09-05 21:47:34 +000011081 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011082 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011083
cristy3ed852e2009-09-05 21:47:34 +000011084 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011085 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011086
cristy3ed852e2009-09-05 21:47:34 +000011087 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011088 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011089
glennrpbb8a7332010-11-13 15:17:35 +000011090 else
cristyc82a27b2011-10-21 01:07:16 +000011091 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011092 GetMagickModule(),CoderWarning,
11093 "ignoring invalid defined png:color-type",
11094 "=%s",value);
11095
cristy3ed852e2009-09-05 21:47:34 +000011096 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011098 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011099 }
11100
glennrp0e8ea192010-12-24 18:00:33 +000011101 /* Check for chunks to be excluded:
11102 *
glennrp0dff56c2011-01-29 19:10:02 +000011103 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011104 * listed in the "unused_chunks" array, above.
11105 *
cristy5d6fc9c2011-12-27 03:10:42 +000011106 * Chunks can be listed for exclusion via a "png:exclude-chunk"
glennrp0e8ea192010-12-24 18:00:33 +000011107 * define (in the image properties or in the image artifacts)
11108 * or via a mng_info member. For convenience, in addition
11109 * to or instead of a comma-separated list of chunks, the
11110 * "exclude-chunk" string can be simply "all" or "none".
11111 *
11112 * The exclude-chunk define takes priority over the mng_info.
11113 *
cristy5d6fc9c2011-12-27 03:10:42 +000011114 * A "png:include-chunk" define takes priority over both the
11115 * mng_info and the "png:exclude-chunk" define. Like the
glennrp0e8ea192010-12-24 18:00:33 +000011116 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011117 * well as a comma-separated list. Chunks that are unknown to
11118 * ImageMagick are always excluded, regardless of their "copy-safe"
11119 * status according to the PNG specification, and even if they
11120 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000011121 *
11122 * Finally, all chunks listed in the "unused_chunks" array are
11123 * automatically excluded, regardless of the other instructions
11124 * or lack thereof.
11125 *
11126 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11127 * will not be written and the gAMA chunk will only be written if it
11128 * is not between .45 and .46, or approximately (1.0/2.2).
11129 *
11130 * If you exclude tRNS and the image has transparency, the colortype
11131 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11132 *
11133 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011134 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011135 */
11136
glennrp26f37912010-12-23 16:22:42 +000011137 mng_info->ping_exclude_bKGD=MagickFalse;
11138 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011139 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011140 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11141 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011142 mng_info->ping_exclude_iCCP=MagickFalse;
11143 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11144 mng_info->ping_exclude_oFFs=MagickFalse;
11145 mng_info->ping_exclude_pHYs=MagickFalse;
11146 mng_info->ping_exclude_sRGB=MagickFalse;
11147 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011148 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011149 mng_info->ping_exclude_vpAg=MagickFalse;
11150 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11151 mng_info->ping_exclude_zTXt=MagickFalse;
11152
glennrp8d3d6e52011-04-19 04:39:51 +000011153 mng_info->ping_preserve_colormap=MagickFalse;
11154
11155 value=GetImageArtifact(image,"png:preserve-colormap");
11156 if (value == NULL)
11157 value=GetImageOption(image_info,"png:preserve-colormap");
11158 if (value != NULL)
11159 mng_info->ping_preserve_colormap=MagickTrue;
11160
glennrp18682582011-06-30 18:11:47 +000011161 /* Thes compression-level, compression-strategy, and compression-filter
11162 * defines take precedence over values from the -quality option.
11163 */
11164 value=GetImageArtifact(image,"png:compression-level");
11165 if (value == NULL)
11166 value=GetImageOption(image_info,"png:compression-level");
11167 if (value != NULL)
11168 {
glennrp18682582011-06-30 18:11:47 +000011169 /* We have to add 1 to everything because 0 is a valid input,
11170 * and we want to use 0 (the default) to mean undefined.
11171 */
11172 if (LocaleCompare(value,"0") == 0)
11173 mng_info->write_png_compression_level = 1;
11174
11175 if (LocaleCompare(value,"1") == 0)
11176 mng_info->write_png_compression_level = 2;
11177
11178 else if (LocaleCompare(value,"2") == 0)
11179 mng_info->write_png_compression_level = 3;
11180
11181 else if (LocaleCompare(value,"3") == 0)
11182 mng_info->write_png_compression_level = 4;
11183
11184 else if (LocaleCompare(value,"4") == 0)
11185 mng_info->write_png_compression_level = 5;
11186
11187 else if (LocaleCompare(value,"5") == 0)
11188 mng_info->write_png_compression_level = 6;
11189
11190 else if (LocaleCompare(value,"6") == 0)
11191 mng_info->write_png_compression_level = 7;
11192
11193 else if (LocaleCompare(value,"7") == 0)
11194 mng_info->write_png_compression_level = 8;
11195
11196 else if (LocaleCompare(value,"8") == 0)
11197 mng_info->write_png_compression_level = 9;
11198
11199 else if (LocaleCompare(value,"9") == 0)
11200 mng_info->write_png_compression_level = 10;
11201
11202 else
cristyc82a27b2011-10-21 01:07:16 +000011203 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011204 GetMagickModule(),CoderWarning,
11205 "ignoring invalid defined png:compression-level",
11206 "=%s",value);
11207 }
11208
11209 value=GetImageArtifact(image,"png:compression-strategy");
11210 if (value == NULL)
11211 value=GetImageOption(image_info,"png:compression-strategy");
11212 if (value != NULL)
11213 {
11214
11215 if (LocaleCompare(value,"0") == 0)
11216 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11217
11218 else if (LocaleCompare(value,"1") == 0)
11219 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11220
11221 else if (LocaleCompare(value,"2") == 0)
11222 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11223
11224 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011225#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011226 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011227#else
11228 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11229#endif
glennrp18682582011-06-30 18:11:47 +000011230
11231 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011232#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011233 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011234#else
11235 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11236#endif
glennrp18682582011-06-30 18:11:47 +000011237
11238 else
cristyc82a27b2011-10-21 01:07:16 +000011239 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011240 GetMagickModule(),CoderWarning,
11241 "ignoring invalid defined png:compression-strategy",
11242 "=%s",value);
11243 }
11244
11245 value=GetImageArtifact(image,"png:compression-filter");
11246 if (value == NULL)
11247 value=GetImageOption(image_info,"png:compression-filter");
11248 if (value != NULL)
11249 {
11250
11251 /* To do: combinations of filters allowed by libpng
11252 * masks 0x08 through 0xf8
11253 *
11254 * Implement this as a comma-separated list of 0,1,2,3,4,5
11255 * where 5 is a special case meaning PNG_ALL_FILTERS.
11256 */
11257
11258 if (LocaleCompare(value,"0") == 0)
11259 mng_info->write_png_compression_filter = 1;
11260
11261 if (LocaleCompare(value,"1") == 0)
11262 mng_info->write_png_compression_filter = 2;
11263
11264 else if (LocaleCompare(value,"2") == 0)
11265 mng_info->write_png_compression_filter = 3;
11266
11267 else if (LocaleCompare(value,"3") == 0)
11268 mng_info->write_png_compression_filter = 4;
11269
11270 else if (LocaleCompare(value,"4") == 0)
11271 mng_info->write_png_compression_filter = 5;
11272
11273 else if (LocaleCompare(value,"5") == 0)
11274 mng_info->write_png_compression_filter = 6;
11275
glennrp18682582011-06-30 18:11:47 +000011276 else
cristyc82a27b2011-10-21 01:07:16 +000011277 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011278 GetMagickModule(),CoderWarning,
11279 "ignoring invalid defined png:compression-filter",
11280 "=%s",value);
11281 }
11282
glennrp03812ae2010-12-24 01:31:34 +000011283 excluding=MagickFalse;
11284
glennrp5c7cf4e2010-12-24 00:30:00 +000011285 for (source=0; source<1; source++)
11286 {
11287 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011288 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011289 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011290
11291 if (value == NULL)
11292 value=GetImageArtifact(image,"png:exclude-chunks");
11293 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011294 else
glennrpacba0042010-12-24 14:27:26 +000011295 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011296 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011297
glennrpacba0042010-12-24 14:27:26 +000011298 if (value == NULL)
11299 value=GetImageOption(image_info,"png:exclude-chunks");
11300 }
11301
glennrp03812ae2010-12-24 01:31:34 +000011302 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011303 {
glennrp03812ae2010-12-24 01:31:34 +000011304
11305 size_t
11306 last;
11307
11308 excluding=MagickTrue;
11309
11310 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011311 {
11312 if (source == 0)
11313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11314 " png:exclude-chunk=%s found in image artifacts.\n", value);
11315 else
11316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11317 " png:exclude-chunk=%s found in image properties.\n", value);
11318 }
glennrp03812ae2010-12-24 01:31:34 +000011319
11320 last=strlen(value);
11321
11322 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011323 {
glennrp03812ae2010-12-24 01:31:34 +000011324
11325 if (LocaleNCompare(value+i,"all",3) == 0)
11326 {
11327 mng_info->ping_exclude_bKGD=MagickTrue;
11328 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011329 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011330 mng_info->ping_exclude_EXIF=MagickTrue;
11331 mng_info->ping_exclude_gAMA=MagickTrue;
11332 mng_info->ping_exclude_iCCP=MagickTrue;
11333 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11334 mng_info->ping_exclude_oFFs=MagickTrue;
11335 mng_info->ping_exclude_pHYs=MagickTrue;
11336 mng_info->ping_exclude_sRGB=MagickTrue;
11337 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011338 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011339 mng_info->ping_exclude_vpAg=MagickTrue;
11340 mng_info->ping_exclude_zCCP=MagickTrue;
11341 mng_info->ping_exclude_zTXt=MagickTrue;
11342 i--;
11343 }
glennrp2cc891a2010-12-24 13:44:32 +000011344
glennrp03812ae2010-12-24 01:31:34 +000011345 if (LocaleNCompare(value+i,"none",4) == 0)
11346 {
11347 mng_info->ping_exclude_bKGD=MagickFalse;
11348 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011349 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011350 mng_info->ping_exclude_EXIF=MagickFalse;
11351 mng_info->ping_exclude_gAMA=MagickFalse;
11352 mng_info->ping_exclude_iCCP=MagickFalse;
11353 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11354 mng_info->ping_exclude_oFFs=MagickFalse;
11355 mng_info->ping_exclude_pHYs=MagickFalse;
11356 mng_info->ping_exclude_sRGB=MagickFalse;
11357 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011358 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011359 mng_info->ping_exclude_vpAg=MagickFalse;
11360 mng_info->ping_exclude_zCCP=MagickFalse;
11361 mng_info->ping_exclude_zTXt=MagickFalse;
11362 }
glennrp2cc891a2010-12-24 13:44:32 +000011363
glennrp03812ae2010-12-24 01:31:34 +000011364 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11365 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011366
glennrp03812ae2010-12-24 01:31:34 +000011367 if (LocaleNCompare(value+i,"chrm",4) == 0)
11368 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011369
glennrpa0ed0092011-04-18 16:36:29 +000011370 if (LocaleNCompare(value+i,"date",4) == 0)
11371 mng_info->ping_exclude_date=MagickTrue;
11372
glennrp03812ae2010-12-24 01:31:34 +000011373 if (LocaleNCompare(value+i,"exif",4) == 0)
11374 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011375
glennrp03812ae2010-12-24 01:31:34 +000011376 if (LocaleNCompare(value+i,"gama",4) == 0)
11377 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011378
glennrp03812ae2010-12-24 01:31:34 +000011379 if (LocaleNCompare(value+i,"iccp",4) == 0)
11380 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011381
glennrp03812ae2010-12-24 01:31:34 +000011382 /*
11383 if (LocaleNCompare(value+i,"itxt",4) == 0)
11384 mng_info->ping_exclude_iTXt=MagickTrue;
11385 */
glennrp2cc891a2010-12-24 13:44:32 +000011386
glennrp03812ae2010-12-24 01:31:34 +000011387 if (LocaleNCompare(value+i,"gama",4) == 0)
11388 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011389
glennrp03812ae2010-12-24 01:31:34 +000011390 if (LocaleNCompare(value+i,"offs",4) == 0)
11391 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011392
glennrp03812ae2010-12-24 01:31:34 +000011393 if (LocaleNCompare(value+i,"phys",4) == 0)
11394 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011395
glennrpa1e3b7b2010-12-24 16:37:33 +000011396 if (LocaleNCompare(value+i,"srgb",4) == 0)
11397 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011398
glennrp03812ae2010-12-24 01:31:34 +000011399 if (LocaleNCompare(value+i,"text",4) == 0)
11400 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011401
glennrpa1e3b7b2010-12-24 16:37:33 +000011402 if (LocaleNCompare(value+i,"trns",4) == 0)
11403 mng_info->ping_exclude_tRNS=MagickTrue;
11404
glennrp03812ae2010-12-24 01:31:34 +000011405 if (LocaleNCompare(value+i,"vpag",4) == 0)
11406 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011407
glennrp03812ae2010-12-24 01:31:34 +000011408 if (LocaleNCompare(value+i,"zccp",4) == 0)
11409 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011410
glennrp03812ae2010-12-24 01:31:34 +000011411 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11412 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011413
glennrp03812ae2010-12-24 01:31:34 +000011414 }
glennrpce91ed52010-12-23 22:37:49 +000011415 }
glennrp26f37912010-12-23 16:22:42 +000011416 }
11417
glennrp5c7cf4e2010-12-24 00:30:00 +000011418 for (source=0; source<1; source++)
11419 {
11420 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011421 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011422 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011423
11424 if (value == NULL)
11425 value=GetImageArtifact(image,"png:include-chunks");
11426 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011427 else
glennrpacba0042010-12-24 14:27:26 +000011428 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011429 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011430
glennrpacba0042010-12-24 14:27:26 +000011431 if (value == NULL)
11432 value=GetImageOption(image_info,"png:include-chunks");
11433 }
11434
glennrp03812ae2010-12-24 01:31:34 +000011435 if (value != NULL)
11436 {
11437 size_t
11438 last;
glennrp26f37912010-12-23 16:22:42 +000011439
glennrp03812ae2010-12-24 01:31:34 +000011440 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011441
glennrp03812ae2010-12-24 01:31:34 +000011442 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011443 {
11444 if (source == 0)
11445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11446 " png:include-chunk=%s found in image artifacts.\n", value);
11447 else
11448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11449 " png:include-chunk=%s found in image properties.\n", value);
11450 }
glennrp03812ae2010-12-24 01:31:34 +000011451
11452 last=strlen(value);
11453
11454 for (i=0; i<(int) last; i+=5)
11455 {
11456 if (LocaleNCompare(value+i,"all",3) == 0)
11457 {
11458 mng_info->ping_exclude_bKGD=MagickFalse;
11459 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011460 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011461 mng_info->ping_exclude_EXIF=MagickFalse;
11462 mng_info->ping_exclude_gAMA=MagickFalse;
11463 mng_info->ping_exclude_iCCP=MagickFalse;
11464 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11465 mng_info->ping_exclude_oFFs=MagickFalse;
11466 mng_info->ping_exclude_pHYs=MagickFalse;
11467 mng_info->ping_exclude_sRGB=MagickFalse;
11468 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011469 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011470 mng_info->ping_exclude_vpAg=MagickFalse;
11471 mng_info->ping_exclude_zCCP=MagickFalse;
11472 mng_info->ping_exclude_zTXt=MagickFalse;
11473 i--;
11474 }
glennrp2cc891a2010-12-24 13:44:32 +000011475
glennrp03812ae2010-12-24 01:31:34 +000011476 if (LocaleNCompare(value+i,"none",4) == 0)
11477 {
11478 mng_info->ping_exclude_bKGD=MagickTrue;
11479 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011480 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011481 mng_info->ping_exclude_EXIF=MagickTrue;
11482 mng_info->ping_exclude_gAMA=MagickTrue;
11483 mng_info->ping_exclude_iCCP=MagickTrue;
11484 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11485 mng_info->ping_exclude_oFFs=MagickTrue;
11486 mng_info->ping_exclude_pHYs=MagickTrue;
11487 mng_info->ping_exclude_sRGB=MagickTrue;
11488 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011489 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011490 mng_info->ping_exclude_vpAg=MagickTrue;
11491 mng_info->ping_exclude_zCCP=MagickTrue;
11492 mng_info->ping_exclude_zTXt=MagickTrue;
11493 }
glennrp2cc891a2010-12-24 13:44:32 +000011494
glennrp03812ae2010-12-24 01:31:34 +000011495 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11496 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011497
glennrp03812ae2010-12-24 01:31:34 +000011498 if (LocaleNCompare(value+i,"chrm",4) == 0)
11499 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011500
glennrpa0ed0092011-04-18 16:36:29 +000011501 if (LocaleNCompare(value+i,"date",4) == 0)
11502 mng_info->ping_exclude_date=MagickFalse;
11503
glennrp03812ae2010-12-24 01:31:34 +000011504 if (LocaleNCompare(value+i,"exif",4) == 0)
11505 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011506
glennrp03812ae2010-12-24 01:31:34 +000011507 if (LocaleNCompare(value+i,"gama",4) == 0)
11508 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011509
glennrp03812ae2010-12-24 01:31:34 +000011510 if (LocaleNCompare(value+i,"iccp",4) == 0)
11511 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011512
glennrp03812ae2010-12-24 01:31:34 +000011513 /*
11514 if (LocaleNCompare(value+i,"itxt",4) == 0)
11515 mng_info->ping_exclude_iTXt=MagickFalse;
11516 */
glennrp2cc891a2010-12-24 13:44:32 +000011517
glennrp03812ae2010-12-24 01:31:34 +000011518 if (LocaleNCompare(value+i,"gama",4) == 0)
11519 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011520
glennrp03812ae2010-12-24 01:31:34 +000011521 if (LocaleNCompare(value+i,"offs",4) == 0)
11522 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011523
glennrp03812ae2010-12-24 01:31:34 +000011524 if (LocaleNCompare(value+i,"phys",4) == 0)
11525 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011526
glennrpa1e3b7b2010-12-24 16:37:33 +000011527 if (LocaleNCompare(value+i,"srgb",4) == 0)
11528 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011529
glennrp03812ae2010-12-24 01:31:34 +000011530 if (LocaleNCompare(value+i,"text",4) == 0)
11531 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011532
glennrpa1e3b7b2010-12-24 16:37:33 +000011533 if (LocaleNCompare(value+i,"trns",4) == 0)
11534 mng_info->ping_exclude_tRNS=MagickFalse;
11535
glennrp03812ae2010-12-24 01:31:34 +000011536 if (LocaleNCompare(value+i,"vpag",4) == 0)
11537 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011538
glennrp03812ae2010-12-24 01:31:34 +000011539 if (LocaleNCompare(value+i,"zccp",4) == 0)
11540 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011541
glennrp03812ae2010-12-24 01:31:34 +000011542 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11543 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011544
glennrp03812ae2010-12-24 01:31:34 +000011545 }
glennrpce91ed52010-12-23 22:37:49 +000011546 }
glennrp26f37912010-12-23 16:22:42 +000011547 }
11548
glennrp03812ae2010-12-24 01:31:34 +000011549 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011550 {
11551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011552 " Chunks to be excluded from the output png:");
glennrp26f37912010-12-23 16:22:42 +000011553 if (mng_info->ping_exclude_bKGD != MagickFalse)
11554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11555 " bKGD");
11556 if (mng_info->ping_exclude_cHRM != MagickFalse)
11557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11558 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011559 if (mng_info->ping_exclude_date != MagickFalse)
11560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11561 " date");
glennrp26f37912010-12-23 16:22:42 +000011562 if (mng_info->ping_exclude_EXIF != MagickFalse)
11563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11564 " EXIF");
11565 if (mng_info->ping_exclude_gAMA != MagickFalse)
11566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11567 " gAMA");
11568 if (mng_info->ping_exclude_iCCP != MagickFalse)
11569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11570 " iCCP");
11571/*
11572 if (mng_info->ping_exclude_iTXt != MagickFalse)
11573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11574 " iTXt");
11575*/
11576 if (mng_info->ping_exclude_oFFs != MagickFalse)
11577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11578 " oFFs");
11579 if (mng_info->ping_exclude_pHYs != MagickFalse)
11580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11581 " pHYs");
11582 if (mng_info->ping_exclude_sRGB != MagickFalse)
11583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11584 " sRGB");
11585 if (mng_info->ping_exclude_tEXt != MagickFalse)
11586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11587 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011588 if (mng_info->ping_exclude_tRNS != MagickFalse)
11589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11590 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011591 if (mng_info->ping_exclude_vpAg != MagickFalse)
11592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11593 " vpAg");
11594 if (mng_info->ping_exclude_zCCP != MagickFalse)
11595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11596 " zCCP");
11597 if (mng_info->ping_exclude_zTXt != MagickFalse)
11598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11599 " zTXt");
11600 }
11601
glennrpb9cfe272010-12-21 15:08:06 +000011602 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011603
cristy018f07f2011-09-04 21:15:19 +000011604 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011605
11606 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011607
cristy3ed852e2009-09-05 21:47:34 +000011608 if (logging != MagickFalse)
11609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011610
cristy3ed852e2009-09-05 21:47:34 +000011611 return(status);
11612}
11613
11614#if defined(JNG_SUPPORTED)
11615
11616/* Write one JNG image */
11617static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +000011618 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011619{
11620 Image
11621 *jpeg_image;
11622
11623 ImageInfo
11624 *jpeg_image_info;
11625
11626 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011627 logging,
cristy3ed852e2009-09-05 21:47:34 +000011628 status;
11629
11630 size_t
11631 length;
11632
11633 unsigned char
11634 *blob,
11635 chunk[80],
11636 *p;
11637
11638 unsigned int
11639 jng_alpha_compression_method,
11640 jng_alpha_sample_depth,
11641 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011642 transparent;
11643
cristybb503372010-05-27 20:51:26 +000011644 size_t
glennrp59575fa2011-12-31 21:31:39 +000011645 jng_alpha_quality,
cristy3ed852e2009-09-05 21:47:34 +000011646 jng_quality;
11647
11648 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011649 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011650
11651 blob=(unsigned char *) NULL;
11652 jpeg_image=(Image *) NULL;
11653 jpeg_image_info=(ImageInfo *) NULL;
11654
11655 status=MagickTrue;
11656 transparent=image_info->type==GrayscaleMatteType ||
glennrp59575fa2011-12-31 21:31:39 +000011657 image_info->type==TrueColorMatteType || image->matte != MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +000011658
glennrp59575fa2011-12-31 21:31:39 +000011659 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
11660
11661 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
11662
11663 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
11664 image_info->quality;
11665
11666 if (jng_alpha_quality >= 1000)
11667 jng_alpha_quality /= 1000;
cristy3ed852e2009-09-05 21:47:34 +000011668
11669 if (transparent)
11670 {
11671 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011672
cristy3ed852e2009-09-05 21:47:34 +000011673 /* Create JPEG blob, image, and image_info */
11674 if (logging != MagickFalse)
11675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +000011676 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011677
cristy3ed852e2009-09-05 21:47:34 +000011678 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011679
cristy3ed852e2009-09-05 21:47:34 +000011680 if (jpeg_image_info == (ImageInfo *) NULL)
11681 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011682
cristy3ed852e2009-09-05 21:47:34 +000011683 if (logging != MagickFalse)
11684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11685 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011686
cristy262346f2012-01-11 19:34:10 +000011687 jpeg_image=SeparateImage(image,AlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +000011688 if (jpeg_image == (Image *) NULL)
11689 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11690 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +000011691 jpeg_image->matte=MagickFalse;
glennrp59575fa2011-12-31 21:31:39 +000011692 jpeg_image->quality=jng_alpha_quality;
cristy3ed852e2009-09-05 21:47:34 +000011693 jpeg_image_info->type=GrayscaleType;
cristy018f07f2011-09-04 21:15:19 +000011694 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000011695 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011696 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011697 "%s",jpeg_image->filename);
11698 }
glennrp59575fa2011-12-31 21:31:39 +000011699 else
11700 {
11701 jng_alpha_compression_method=0;
11702 jng_color_type=10;
11703 jng_alpha_sample_depth=0;
11704 }
cristy3ed852e2009-09-05 21:47:34 +000011705
11706 /* To do: check bit depth of PNG alpha channel */
11707
11708 /* Check if image is grayscale. */
11709 if (image_info->type != TrueColorMatteType && image_info->type !=
cristyc82a27b2011-10-21 01:07:16 +000011710 TrueColorType && ImageIsGray(image,exception))
cristy3ed852e2009-09-05 21:47:34 +000011711 jng_color_type-=2;
11712
glennrp59575fa2011-12-31 21:31:39 +000011713 if (logging != MagickFalse)
11714 {
11715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11716 " JNG Quality = %d",(int) jng_quality);
11717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11718 " JNG Color Type = %d",jng_color_type);
11719 if (transparent)
11720 {
11721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11722 " JNG Alpha Compression = %d",jng_alpha_compression_method);
11723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11724 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
11725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11726 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
11727 }
11728 }
11729
cristy3ed852e2009-09-05 21:47:34 +000011730 if (transparent)
11731 {
11732 if (jng_alpha_compression_method==0)
11733 {
11734 const char
11735 *value;
11736
cristy4c08aed2011-07-01 19:47:50 +000011737 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011738 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000011739 exception);
cristy3ed852e2009-09-05 21:47:34 +000011740 if (logging != MagickFalse)
11741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11742 " Creating PNG blob.");
11743 length=0;
11744
11745 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11746 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11747 jpeg_image_info->interlace=NoInterlace;
11748
glennrpcc5d45b2012-01-06 04:06:10 +000011749 /* Exclude all ancillary chunks */
11750 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
11751
cristy3ed852e2009-09-05 21:47:34 +000011752 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyc82a27b2011-10-21 01:07:16 +000011753 exception);
cristy3ed852e2009-09-05 21:47:34 +000011754
11755 /* Retrieve sample depth used */
cristyd15e6592011-10-15 00:13:06 +000011756 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
cristy3ed852e2009-09-05 21:47:34 +000011757 if (value != (char *) NULL)
11758 jng_alpha_sample_depth= (unsigned int) value[0];
11759 }
11760 else
11761 {
cristy4c08aed2011-07-01 19:47:50 +000011762 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011763
11764 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000011765 exception);
cristy3ed852e2009-09-05 21:47:34 +000011766
11767 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11768 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11769 jpeg_image_info->interlace=NoInterlace;
11770 if (logging != MagickFalse)
11771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11772 " Creating blob.");
11773 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyc82a27b2011-10-21 01:07:16 +000011774 exception);
cristy3ed852e2009-09-05 21:47:34 +000011775 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011776
cristy3ed852e2009-09-05 21:47:34 +000011777 if (logging != MagickFalse)
11778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011779 " Successfully read jpeg_image into a blob, length=%.20g.",
11780 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011781
11782 }
11783 /* Destroy JPEG image and image_info */
11784 jpeg_image=DestroyImage(jpeg_image);
11785 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11786 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11787 }
11788
11789 /* Write JHDR chunk */
11790 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11791 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011792 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011793 PNGLong(chunk+4,(png_uint_32) image->columns);
11794 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011795 chunk[12]=jng_color_type;
11796 chunk[13]=8; /* sample depth */
11797 chunk[14]=8; /*jng_image_compression_method */
11798 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11799 chunk[16]=jng_alpha_sample_depth;
11800 chunk[17]=jng_alpha_compression_method;
11801 chunk[18]=0; /*jng_alpha_filter_method */
11802 chunk[19]=0; /*jng_alpha_interlace_method */
11803 (void) WriteBlob(image,20,chunk);
11804 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11805 if (logging != MagickFalse)
11806 {
11807 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011808 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011809
cristy3ed852e2009-09-05 21:47:34 +000011810 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011811 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011812
cristy3ed852e2009-09-05 21:47:34 +000011813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11814 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011815
cristy3ed852e2009-09-05 21:47:34 +000011816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11817 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011818
cristy3ed852e2009-09-05 21:47:34 +000011819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11820 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011821
cristy3ed852e2009-09-05 21:47:34 +000011822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11823 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011824
cristy3ed852e2009-09-05 21:47:34 +000011825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11826 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011827
cristy3ed852e2009-09-05 21:47:34 +000011828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11829 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011830
cristy3ed852e2009-09-05 21:47:34 +000011831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11832 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011833
cristy3ed852e2009-09-05 21:47:34 +000011834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11835 " JNG alpha interlace:%5d",0);
11836 }
11837
glennrp0fe50b42010-11-16 03:52:51 +000011838 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011839 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011840
11841 /*
11842 Write leading ancillary chunks
11843 */
11844
11845 if (transparent)
11846 {
11847 /*
11848 Write JNG bKGD chunk
11849 */
11850
11851 unsigned char
11852 blue,
11853 green,
11854 red;
11855
cristybb503372010-05-27 20:51:26 +000011856 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011857 num_bytes;
11858
11859 if (jng_color_type == 8 || jng_color_type == 12)
11860 num_bytes=6L;
11861 else
11862 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011863 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011864 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011865 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011866 red=ScaleQuantumToChar(image->background_color.red);
11867 green=ScaleQuantumToChar(image->background_color.green);
11868 blue=ScaleQuantumToChar(image->background_color.blue);
11869 *(chunk+4)=0;
11870 *(chunk+5)=red;
11871 *(chunk+6)=0;
11872 *(chunk+7)=green;
11873 *(chunk+8)=0;
11874 *(chunk+9)=blue;
11875 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11876 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11877 }
11878
11879 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11880 {
11881 /*
11882 Write JNG sRGB chunk
11883 */
11884 (void) WriteBlobMSBULong(image,1L);
11885 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011886 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011887
cristy3ed852e2009-09-05 21:47:34 +000011888 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011889 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011890 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011891 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011892
cristy3ed852e2009-09-05 21:47:34 +000011893 else
glennrpe610a072010-08-05 17:08:46 +000011894 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011895 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011896 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011897
cristy3ed852e2009-09-05 21:47:34 +000011898 (void) WriteBlob(image,5,chunk);
11899 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11900 }
11901 else
11902 {
11903 if (image->gamma != 0.0)
11904 {
11905 /*
11906 Write JNG gAMA chunk
11907 */
11908 (void) WriteBlobMSBULong(image,4L);
11909 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011910 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011911 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011912 (void) WriteBlob(image,8,chunk);
11913 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11914 }
glennrp0fe50b42010-11-16 03:52:51 +000011915
cristy3ed852e2009-09-05 21:47:34 +000011916 if ((mng_info->equal_chrms == MagickFalse) &&
11917 (image->chromaticity.red_primary.x != 0.0))
11918 {
11919 PrimaryInfo
11920 primary;
11921
11922 /*
11923 Write JNG cHRM chunk
11924 */
11925 (void) WriteBlobMSBULong(image,32L);
11926 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011927 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011928 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011929 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11930 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011931 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011932 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11933 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011934 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011935 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11936 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011937 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011938 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11939 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011940 (void) WriteBlob(image,36,chunk);
11941 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11942 }
11943 }
glennrp0fe50b42010-11-16 03:52:51 +000011944
cristy2a11bef2011-10-28 18:33:11 +000011945 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000011946 {
11947 /*
11948 Write JNG pHYs chunk
11949 */
11950 (void) WriteBlobMSBULong(image,9L);
11951 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011952 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011953 if (image->units == PixelsPerInchResolution)
11954 {
cristy35ef8242010-06-03 16:24:13 +000011955 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011956 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011957
cristy35ef8242010-06-03 16:24:13 +000011958 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011959 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011960
cristy3ed852e2009-09-05 21:47:34 +000011961 chunk[12]=1;
11962 }
glennrp0fe50b42010-11-16 03:52:51 +000011963
cristy3ed852e2009-09-05 21:47:34 +000011964 else
11965 {
11966 if (image->units == PixelsPerCentimeterResolution)
11967 {
cristy35ef8242010-06-03 16:24:13 +000011968 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011969 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011970
cristy35ef8242010-06-03 16:24:13 +000011971 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011972 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011973
cristy3ed852e2009-09-05 21:47:34 +000011974 chunk[12]=1;
11975 }
glennrp0fe50b42010-11-16 03:52:51 +000011976
cristy3ed852e2009-09-05 21:47:34 +000011977 else
11978 {
cristy2a11bef2011-10-28 18:33:11 +000011979 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
11980 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011981 chunk[12]=0;
11982 }
11983 }
11984 (void) WriteBlob(image,13,chunk);
11985 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11986 }
11987
11988 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11989 {
11990 /*
11991 Write JNG oFFs chunk
11992 */
11993 (void) WriteBlobMSBULong(image,9L);
11994 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011995 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011996 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11997 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011998 chunk[12]=0;
11999 (void) WriteBlob(image,13,chunk);
12000 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12001 }
12002 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12003 {
12004 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12005 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000012006 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000012007 PNGLong(chunk+4,(png_uint_32) image->page.width);
12008 PNGLong(chunk+8,(png_uint_32) image->page.height);
12009 chunk[12]=0; /* unit = pixels */
12010 (void) WriteBlob(image,13,chunk);
12011 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12012 }
12013
12014
12015 if (transparent)
12016 {
12017 if (jng_alpha_compression_method==0)
12018 {
cristybb503372010-05-27 20:51:26 +000012019 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012020 i;
12021
cristybb503372010-05-27 20:51:26 +000012022 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012023 len;
12024
12025 /* Write IDAT chunk header */
12026 if (logging != MagickFalse)
12027 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012028 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000012029 length);
cristy3ed852e2009-09-05 21:47:34 +000012030
12031 /* Copy IDAT chunks */
12032 len=0;
12033 p=blob+8;
cristybb503372010-05-27 20:51:26 +000012034 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000012035 {
12036 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12037 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012038
cristy3ed852e2009-09-05 21:47:34 +000012039 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12040 {
12041 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000012042 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000012043 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000012044 (void) WriteBlob(image,(size_t) len+4,p);
12045 (void) WriteBlobMSBULong(image,
12046 crc32(0,p,(uInt) len+4));
12047 }
glennrp0fe50b42010-11-16 03:52:51 +000012048
cristy3ed852e2009-09-05 21:47:34 +000012049 else
12050 {
12051 if (logging != MagickFalse)
12052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012053 " Skipping %c%c%c%c chunk, length=%.20g.",
12054 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012055 }
12056 p+=(8+len);
12057 }
12058 }
12059 else
12060 {
12061 /* Write JDAA chunk header */
12062 if (logging != MagickFalse)
12063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012064 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012065 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012066 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012067 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012068 /* Write JDAT chunk(s) data */
12069 (void) WriteBlob(image,4,chunk);
12070 (void) WriteBlob(image,length,blob);
12071 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12072 (uInt) length));
12073 }
12074 blob=(unsigned char *) RelinquishMagickMemory(blob);
12075 }
12076
12077 /* Encode image as a JPEG blob */
12078 if (logging != MagickFalse)
12079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12080 " Creating jpeg_image_info.");
12081 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12082 if (jpeg_image_info == (ImageInfo *) NULL)
12083 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12084
12085 if (logging != MagickFalse)
12086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12087 " Creating jpeg_image.");
12088
cristyc82a27b2011-10-21 01:07:16 +000012089 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012090 if (jpeg_image == (Image *) NULL)
12091 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12092 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12093
12094 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012095 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012096 jpeg_image->filename);
12097
12098 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000012099 exception);
cristy3ed852e2009-09-05 21:47:34 +000012100
12101 if (logging != MagickFalse)
12102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012103 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12104 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012105
12106 if (jng_color_type == 8 || jng_color_type == 12)
12107 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012108
glennrp59575fa2011-12-31 21:31:39 +000012109 jpeg_image_info->quality=jng_quality;
12110 jpeg_image->quality=jng_quality;
cristy3ed852e2009-09-05 21:47:34 +000012111 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12112 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012113
cristy3ed852e2009-09-05 21:47:34 +000012114 if (logging != MagickFalse)
12115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12116 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012117
cristyc82a27b2011-10-21 01:07:16 +000012118 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
glennrp0fe50b42010-11-16 03:52:51 +000012119
cristy3ed852e2009-09-05 21:47:34 +000012120 if (logging != MagickFalse)
12121 {
12122 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012123 " Successfully read jpeg_image into a blob, length=%.20g.",
12124 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012125
12126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012127 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012128 }
glennrp0fe50b42010-11-16 03:52:51 +000012129
cristy3ed852e2009-09-05 21:47:34 +000012130 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012131 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012132 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012133 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012134 (void) WriteBlob(image,4,chunk);
12135 (void) WriteBlob(image,length,blob);
12136 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12137
12138 jpeg_image=DestroyImage(jpeg_image);
12139 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12140 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12141 blob=(unsigned char *) RelinquishMagickMemory(blob);
12142
12143 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012144 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012145
12146 /* Write IEND chunk */
12147 (void) WriteBlobMSBULong(image,0L);
12148 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012149 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012150 (void) WriteBlob(image,4,chunk);
12151 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12152
12153 if (logging != MagickFalse)
12154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12155 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012156
cristy3ed852e2009-09-05 21:47:34 +000012157 return(status);
12158}
12159
12160
12161/*
12162%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12163% %
12164% %
12165% %
12166% W r i t e J N G I m a g e %
12167% %
12168% %
12169% %
12170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12171%
12172% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12173%
12174% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12175%
12176% The format of the WriteJNGImage method is:
12177%
cristy1e178e72011-08-28 19:44:34 +000012178% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12179% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012180%
12181% A description of each parameter follows:
12182%
12183% o image_info: the image info.
12184%
12185% o image: The image.
12186%
cristy1e178e72011-08-28 19:44:34 +000012187% o exception: return any errors or warnings in this structure.
12188%
cristy3ed852e2009-09-05 21:47:34 +000012189%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12190*/
cristy1e178e72011-08-28 19:44:34 +000012191static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12192 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012193{
12194 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012195 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012196 logging,
cristy3ed852e2009-09-05 21:47:34 +000012197 status;
12198
12199 MngInfo
12200 *mng_info;
12201
cristy3ed852e2009-09-05 21:47:34 +000012202 /*
12203 Open image file.
12204 */
12205 assert(image_info != (const ImageInfo *) NULL);
12206 assert(image_info->signature == MagickSignature);
12207 assert(image != (Image *) NULL);
12208 assert(image->signature == MagickSignature);
12209 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012210 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristyc82a27b2011-10-21 01:07:16 +000012211 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012212 if (status == MagickFalse)
12213 return(status);
12214
12215 /*
12216 Allocate a MngInfo structure.
12217 */
12218 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012219 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012220 if (mng_info == (MngInfo *) NULL)
12221 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12222 /*
12223 Initialize members of the MngInfo structure.
12224 */
12225 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12226 mng_info->image=image;
12227 have_mng_structure=MagickTrue;
12228
12229 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12230
cristy018f07f2011-09-04 21:15:19 +000012231 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012232 (void) CloseBlob(image);
12233
12234 (void) CatchImageException(image);
12235 MngInfoFreeStruct(mng_info,&have_mng_structure);
12236 if (logging != MagickFalse)
12237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12238 return(status);
12239}
12240#endif
12241
cristy1e178e72011-08-28 19:44:34 +000012242static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12243 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012244{
12245 const char
12246 *option;
12247
12248 Image
12249 *next_image;
12250
12251 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012252 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012253 status;
12254
glennrp03812ae2010-12-24 01:31:34 +000012255 volatile MagickBooleanType
12256 logging;
12257
cristy3ed852e2009-09-05 21:47:34 +000012258 MngInfo
12259 *mng_info;
12260
12261 int
cristy3ed852e2009-09-05 21:47:34 +000012262 image_count,
12263 need_iterations,
12264 need_matte;
12265
12266 volatile int
12267#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12268 defined(PNG_MNG_FEATURES_SUPPORTED)
12269 need_local_plte,
12270#endif
12271 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012272 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012273 use_global_plte;
12274
cristybb503372010-05-27 20:51:26 +000012275 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012276 i;
12277
12278 unsigned char
12279 chunk[800];
12280
12281 volatile unsigned int
12282 write_jng,
12283 write_mng;
12284
cristybb503372010-05-27 20:51:26 +000012285 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012286 scene;
12287
cristybb503372010-05-27 20:51:26 +000012288 size_t
cristy3ed852e2009-09-05 21:47:34 +000012289 final_delay=0,
12290 initial_delay;
12291
glennrpd5045b42010-03-24 12:40:35 +000012292#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012293 if (image_info->verbose)
12294 printf("Your PNG library (libpng-%s) is rather old.\n",
12295 PNG_LIBPNG_VER_STRING);
12296#endif
12297
12298 /*
12299 Open image file.
12300 */
12301 assert(image_info != (const ImageInfo *) NULL);
12302 assert(image_info->signature == MagickSignature);
12303 assert(image != (Image *) NULL);
12304 assert(image->signature == MagickSignature);
12305 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012306 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristyc82a27b2011-10-21 01:07:16 +000012307 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012308 if (status == MagickFalse)
12309 return(status);
12310
12311 /*
12312 Allocate a MngInfo structure.
12313 */
12314 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012315 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012316 if (mng_info == (MngInfo *) NULL)
12317 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12318 /*
12319 Initialize members of the MngInfo structure.
12320 */
12321 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12322 mng_info->image=image;
12323 have_mng_structure=MagickTrue;
12324 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12325
12326 /*
12327 * See if user has requested a specific PNG subformat to be used
12328 * for all of the PNGs in the MNG being written, e.g.,
12329 *
12330 * convert *.png png8:animation.mng
12331 *
12332 * To do: check -define png:bit_depth and png:color_type as well,
12333 * or perhaps use mng:bit_depth and mng:color_type instead for
12334 * global settings.
12335 */
12336
12337 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12338 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12339 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12340
12341 write_jng=MagickFalse;
12342 if (image_info->compression == JPEGCompression)
12343 write_jng=MagickTrue;
12344
12345 mng_info->adjoin=image_info->adjoin &&
12346 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12347
cristy3ed852e2009-09-05 21:47:34 +000012348 if (logging != MagickFalse)
12349 {
12350 /* Log some info about the input */
12351 Image
12352 *p;
12353
12354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12355 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012356
cristy3ed852e2009-09-05 21:47:34 +000012357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012358 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012359
cristy3ed852e2009-09-05 21:47:34 +000012360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12361 " Type: %d",image_info->type);
12362
12363 scene=0;
12364 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12365 {
12366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012367 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012368
cristy3ed852e2009-09-05 21:47:34 +000012369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012370 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012371
cristy3ed852e2009-09-05 21:47:34 +000012372 if (p->matte)
12373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12374 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012375
cristy3ed852e2009-09-05 21:47:34 +000012376 else
12377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12378 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012379
cristy3ed852e2009-09-05 21:47:34 +000012380 if (p->storage_class == PseudoClass)
12381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12382 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012383
cristy3ed852e2009-09-05 21:47:34 +000012384 else
12385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12386 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012387
cristy3ed852e2009-09-05 21:47:34 +000012388 if (p->colors)
12389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012390 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012391
cristy3ed852e2009-09-05 21:47:34 +000012392 else
12393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12394 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012395
cristy3ed852e2009-09-05 21:47:34 +000012396 if (mng_info->adjoin == MagickFalse)
12397 break;
12398 }
12399 }
12400
cristy3ed852e2009-09-05 21:47:34 +000012401 use_global_plte=MagickFalse;
12402 all_images_are_gray=MagickFalse;
12403#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12404 need_local_plte=MagickTrue;
12405#endif
12406 need_defi=MagickFalse;
12407 need_matte=MagickFalse;
12408 mng_info->framing_mode=1;
12409 mng_info->old_framing_mode=1;
12410
12411 if (write_mng)
12412 if (image_info->page != (char *) NULL)
12413 {
12414 /*
12415 Determine image bounding box.
12416 */
12417 SetGeometry(image,&mng_info->page);
12418 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12419 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12420 }
12421 if (write_mng)
12422 {
12423 unsigned int
12424 need_geom;
12425
12426 unsigned short
12427 red,
12428 green,
12429 blue;
12430
12431 mng_info->page=image->page;
12432 need_geom=MagickTrue;
12433 if (mng_info->page.width || mng_info->page.height)
12434 need_geom=MagickFalse;
12435 /*
12436 Check all the scenes.
12437 */
12438 initial_delay=image->delay;
12439 need_iterations=MagickFalse;
12440 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12441 mng_info->equal_physs=MagickTrue,
12442 mng_info->equal_gammas=MagickTrue;
12443 mng_info->equal_srgbs=MagickTrue;
12444 mng_info->equal_backgrounds=MagickTrue;
12445 image_count=0;
12446#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12447 defined(PNG_MNG_FEATURES_SUPPORTED)
12448 all_images_are_gray=MagickTrue;
12449 mng_info->equal_palettes=MagickFalse;
12450 need_local_plte=MagickFalse;
12451#endif
12452 for (next_image=image; next_image != (Image *) NULL; )
12453 {
12454 if (need_geom)
12455 {
12456 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12457 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012458
cristy3ed852e2009-09-05 21:47:34 +000012459 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12460 mng_info->page.height=next_image->rows+next_image->page.y;
12461 }
glennrp0fe50b42010-11-16 03:52:51 +000012462
cristy3ed852e2009-09-05 21:47:34 +000012463 if (next_image->page.x || next_image->page.y)
12464 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012465
cristy3ed852e2009-09-05 21:47:34 +000012466 if (next_image->matte)
12467 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012468
cristy3ed852e2009-09-05 21:47:34 +000012469 if ((int) next_image->dispose >= BackgroundDispose)
12470 if (next_image->matte || next_image->page.x || next_image->page.y ||
12471 ((next_image->columns < mng_info->page.width) &&
12472 (next_image->rows < mng_info->page.height)))
12473 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012474
cristy3ed852e2009-09-05 21:47:34 +000012475 if (next_image->iterations)
12476 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012477
cristy3ed852e2009-09-05 21:47:34 +000012478 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012479
cristy3ed852e2009-09-05 21:47:34 +000012480 if (final_delay != initial_delay || final_delay > 1UL*
12481 next_image->ticks_per_second)
12482 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012483
cristy3ed852e2009-09-05 21:47:34 +000012484#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12485 defined(PNG_MNG_FEATURES_SUPPORTED)
12486 /*
12487 check for global palette possibility.
12488 */
12489 if (image->matte != MagickFalse)
12490 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012491
cristy3ed852e2009-09-05 21:47:34 +000012492 if (need_local_plte == 0)
12493 {
cristyc82a27b2011-10-21 01:07:16 +000012494 if (ImageIsGray(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012495 all_images_are_gray=MagickFalse;
12496 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12497 if (use_global_plte == 0)
12498 use_global_plte=mng_info->equal_palettes;
12499 need_local_plte=!mng_info->equal_palettes;
12500 }
12501#endif
12502 if (GetNextImageInList(next_image) != (Image *) NULL)
12503 {
12504 if (next_image->background_color.red !=
12505 next_image->next->background_color.red ||
12506 next_image->background_color.green !=
12507 next_image->next->background_color.green ||
12508 next_image->background_color.blue !=
12509 next_image->next->background_color.blue)
12510 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012511
cristy3ed852e2009-09-05 21:47:34 +000012512 if (next_image->gamma != next_image->next->gamma)
12513 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012514
cristy3ed852e2009-09-05 21:47:34 +000012515 if (next_image->rendering_intent !=
12516 next_image->next->rendering_intent)
12517 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012518
cristy3ed852e2009-09-05 21:47:34 +000012519 if ((next_image->units != next_image->next->units) ||
cristy2a11bef2011-10-28 18:33:11 +000012520 (next_image->resolution.x != next_image->next->resolution.x) ||
12521 (next_image->resolution.y != next_image->next->resolution.y))
cristy3ed852e2009-09-05 21:47:34 +000012522 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012523
cristy3ed852e2009-09-05 21:47:34 +000012524 if (mng_info->equal_chrms)
12525 {
12526 if (next_image->chromaticity.red_primary.x !=
12527 next_image->next->chromaticity.red_primary.x ||
12528 next_image->chromaticity.red_primary.y !=
12529 next_image->next->chromaticity.red_primary.y ||
12530 next_image->chromaticity.green_primary.x !=
12531 next_image->next->chromaticity.green_primary.x ||
12532 next_image->chromaticity.green_primary.y !=
12533 next_image->next->chromaticity.green_primary.y ||
12534 next_image->chromaticity.blue_primary.x !=
12535 next_image->next->chromaticity.blue_primary.x ||
12536 next_image->chromaticity.blue_primary.y !=
12537 next_image->next->chromaticity.blue_primary.y ||
12538 next_image->chromaticity.white_point.x !=
12539 next_image->next->chromaticity.white_point.x ||
12540 next_image->chromaticity.white_point.y !=
12541 next_image->next->chromaticity.white_point.y)
12542 mng_info->equal_chrms=MagickFalse;
12543 }
12544 }
12545 image_count++;
12546 next_image=GetNextImageInList(next_image);
12547 }
12548 if (image_count < 2)
12549 {
12550 mng_info->equal_backgrounds=MagickFalse;
12551 mng_info->equal_chrms=MagickFalse;
12552 mng_info->equal_gammas=MagickFalse;
12553 mng_info->equal_srgbs=MagickFalse;
12554 mng_info->equal_physs=MagickFalse;
12555 use_global_plte=MagickFalse;
12556#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12557 need_local_plte=MagickTrue;
12558#endif
12559 need_iterations=MagickFalse;
12560 }
glennrp0fe50b42010-11-16 03:52:51 +000012561
cristy3ed852e2009-09-05 21:47:34 +000012562 if (mng_info->need_fram == MagickFalse)
12563 {
12564 /*
12565 Only certain framing rates 100/n are exactly representable without
12566 the FRAM chunk but we'll allow some slop in VLC files
12567 */
12568 if (final_delay == 0)
12569 {
12570 if (need_iterations != MagickFalse)
12571 {
12572 /*
12573 It's probably a GIF with loop; don't run it *too* fast.
12574 */
glennrp02617122010-07-28 13:07:35 +000012575 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012576 {
12577 final_delay=10;
cristyc82a27b2011-10-21 01:07:16 +000012578 (void) ThrowMagickException(exception,GetMagickModule(),
12579 CoderWarning,
glennrpd908de42010-07-28 13:28:27 +000012580 "input has zero delay between all frames; assuming",
12581 " 10 cs `%s'","");
12582 }
cristy3ed852e2009-09-05 21:47:34 +000012583 }
12584 else
12585 mng_info->ticks_per_second=0;
12586 }
12587 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012588 mng_info->ticks_per_second=(png_uint_32)
12589 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012590 if (final_delay > 50)
12591 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012592
cristy3ed852e2009-09-05 21:47:34 +000012593 if (final_delay > 75)
12594 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012595
cristy3ed852e2009-09-05 21:47:34 +000012596 if (final_delay > 125)
12597 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012598
cristy3ed852e2009-09-05 21:47:34 +000012599 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12600 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12601 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12602 1UL*image->ticks_per_second))
12603 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12604 }
glennrp0fe50b42010-11-16 03:52:51 +000012605
cristy3ed852e2009-09-05 21:47:34 +000012606 if (mng_info->need_fram != MagickFalse)
12607 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12608 /*
12609 If pseudocolor, we should also check to see if all the
12610 palettes are identical and write a global PLTE if they are.
12611 ../glennrp Feb 99.
12612 */
12613 /*
12614 Write the MNG version 1.0 signature and MHDR chunk.
12615 */
12616 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12617 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12618 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012619 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012620 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12621 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012622 PNGLong(chunk+12,mng_info->ticks_per_second);
12623 PNGLong(chunk+16,0L); /* layer count=unknown */
12624 PNGLong(chunk+20,0L); /* frame count=unknown */
12625 PNGLong(chunk+24,0L); /* play time=unknown */
12626 if (write_jng)
12627 {
12628 if (need_matte)
12629 {
12630 if (need_defi || mng_info->need_fram || use_global_plte)
12631 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012632
cristy3ed852e2009-09-05 21:47:34 +000012633 else
12634 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12635 }
glennrp0fe50b42010-11-16 03:52:51 +000012636
cristy3ed852e2009-09-05 21:47:34 +000012637 else
12638 {
12639 if (need_defi || mng_info->need_fram || use_global_plte)
12640 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012641
cristy3ed852e2009-09-05 21:47:34 +000012642 else
12643 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12644 }
12645 }
glennrp0fe50b42010-11-16 03:52:51 +000012646
cristy3ed852e2009-09-05 21:47:34 +000012647 else
12648 {
12649 if (need_matte)
12650 {
12651 if (need_defi || mng_info->need_fram || use_global_plte)
12652 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012653
cristy3ed852e2009-09-05 21:47:34 +000012654 else
12655 PNGLong(chunk+28,9L); /* simplicity=VLC */
12656 }
glennrp0fe50b42010-11-16 03:52:51 +000012657
cristy3ed852e2009-09-05 21:47:34 +000012658 else
12659 {
12660 if (need_defi || mng_info->need_fram || use_global_plte)
12661 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012662
cristy3ed852e2009-09-05 21:47:34 +000012663 else
12664 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12665 }
12666 }
12667 (void) WriteBlob(image,32,chunk);
12668 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12669 option=GetImageOption(image_info,"mng:need-cacheoff");
12670 if (option != (const char *) NULL)
12671 {
12672 size_t
12673 length;
12674
12675 /*
12676 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12677 */
12678 PNGType(chunk,mng_nEED);
12679 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012680 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012681 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012682 length+=4;
12683 (void) WriteBlob(image,length,chunk);
12684 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12685 }
12686 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12687 (GetNextImageInList(image) != (Image *) NULL) &&
12688 (image->iterations != 1))
12689 {
12690 /*
12691 Write MNG TERM chunk
12692 */
12693 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12694 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012695 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012696 chunk[4]=3; /* repeat animation */
12697 chunk[5]=0; /* show last frame when done */
12698 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12699 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012700
cristy3ed852e2009-09-05 21:47:34 +000012701 if (image->iterations == 0)
12702 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012703
cristy3ed852e2009-09-05 21:47:34 +000012704 else
12705 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012706
cristy3ed852e2009-09-05 21:47:34 +000012707 if (logging != MagickFalse)
12708 {
12709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012710 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12711 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012712
cristy3ed852e2009-09-05 21:47:34 +000012713 if (image->iterations == 0)
12714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012715 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012716
cristy3ed852e2009-09-05 21:47:34 +000012717 else
12718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012719 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012720 }
12721 (void) WriteBlob(image,14,chunk);
12722 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12723 }
12724 /*
12725 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12726 */
12727 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12728 mng_info->equal_srgbs)
12729 {
12730 /*
12731 Write MNG sRGB chunk
12732 */
12733 (void) WriteBlobMSBULong(image,1L);
12734 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012735 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012736
cristy3ed852e2009-09-05 21:47:34 +000012737 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012738 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012739 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012740 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012741
cristy3ed852e2009-09-05 21:47:34 +000012742 else
glennrpe610a072010-08-05 17:08:46 +000012743 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012744 Magick_RenderingIntent_to_PNG_RenderingIntent(
12745 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012746
cristy3ed852e2009-09-05 21:47:34 +000012747 (void) WriteBlob(image,5,chunk);
12748 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12749 mng_info->have_write_global_srgb=MagickTrue;
12750 }
glennrp0fe50b42010-11-16 03:52:51 +000012751
cristy3ed852e2009-09-05 21:47:34 +000012752 else
12753 {
12754 if (image->gamma && mng_info->equal_gammas)
12755 {
12756 /*
12757 Write MNG gAMA chunk
12758 */
12759 (void) WriteBlobMSBULong(image,4L);
12760 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012761 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012762 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012763 (void) WriteBlob(image,8,chunk);
12764 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12765 mng_info->have_write_global_gama=MagickTrue;
12766 }
12767 if (mng_info->equal_chrms)
12768 {
12769 PrimaryInfo
12770 primary;
12771
12772 /*
12773 Write MNG cHRM chunk
12774 */
12775 (void) WriteBlobMSBULong(image,32L);
12776 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012777 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012778 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012779 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12780 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012781 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012782 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12783 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012784 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012785 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12786 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012787 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012788 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12789 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012790 (void) WriteBlob(image,36,chunk);
12791 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12792 mng_info->have_write_global_chrm=MagickTrue;
12793 }
12794 }
cristy2a11bef2011-10-28 18:33:11 +000012795 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012796 {
12797 /*
12798 Write MNG pHYs chunk
12799 */
12800 (void) WriteBlobMSBULong(image,9L);
12801 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012802 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012803
cristy3ed852e2009-09-05 21:47:34 +000012804 if (image->units == PixelsPerInchResolution)
12805 {
cristy35ef8242010-06-03 16:24:13 +000012806 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012807 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012808
cristy35ef8242010-06-03 16:24:13 +000012809 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012810 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012811
cristy3ed852e2009-09-05 21:47:34 +000012812 chunk[12]=1;
12813 }
glennrp0fe50b42010-11-16 03:52:51 +000012814
cristy3ed852e2009-09-05 21:47:34 +000012815 else
12816 {
12817 if (image->units == PixelsPerCentimeterResolution)
12818 {
cristy35ef8242010-06-03 16:24:13 +000012819 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012820 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012821
cristy35ef8242010-06-03 16:24:13 +000012822 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012823 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012824
cristy3ed852e2009-09-05 21:47:34 +000012825 chunk[12]=1;
12826 }
glennrp0fe50b42010-11-16 03:52:51 +000012827
cristy3ed852e2009-09-05 21:47:34 +000012828 else
12829 {
cristy2a11bef2011-10-28 18:33:11 +000012830 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12831 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012832 chunk[12]=0;
12833 }
12834 }
12835 (void) WriteBlob(image,13,chunk);
12836 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12837 }
12838 /*
12839 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12840 or does not cover the entire frame.
12841 */
12842 if (write_mng && (image->matte || image->page.x > 0 ||
12843 image->page.y > 0 || (image->page.width &&
12844 (image->page.width+image->page.x < mng_info->page.width))
12845 || (image->page.height && (image->page.height+image->page.y
12846 < mng_info->page.height))))
12847 {
12848 (void) WriteBlobMSBULong(image,6L);
12849 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012850 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012851 red=ScaleQuantumToShort(image->background_color.red);
12852 green=ScaleQuantumToShort(image->background_color.green);
12853 blue=ScaleQuantumToShort(image->background_color.blue);
12854 PNGShort(chunk+4,red);
12855 PNGShort(chunk+6,green);
12856 PNGShort(chunk+8,blue);
12857 (void) WriteBlob(image,10,chunk);
12858 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12859 if (mng_info->equal_backgrounds)
12860 {
12861 (void) WriteBlobMSBULong(image,6L);
12862 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012863 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012864 (void) WriteBlob(image,10,chunk);
12865 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12866 }
12867 }
12868
12869#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12870 if ((need_local_plte == MagickFalse) &&
12871 (image->storage_class == PseudoClass) &&
12872 (all_images_are_gray == MagickFalse))
12873 {
cristybb503372010-05-27 20:51:26 +000012874 size_t
cristy3ed852e2009-09-05 21:47:34 +000012875 data_length;
12876
12877 /*
12878 Write MNG PLTE chunk
12879 */
12880 data_length=3*image->colors;
12881 (void) WriteBlobMSBULong(image,data_length);
12882 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012883 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012884
cristybb503372010-05-27 20:51:26 +000012885 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012886 {
cristy5f07f702011-09-26 17:29:10 +000012887 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12888 image->colormap[i].red) & 0xff);
12889 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12890 image->colormap[i].green) & 0xff);
12891 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12892 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000012893 }
glennrp0fe50b42010-11-16 03:52:51 +000012894
cristy3ed852e2009-09-05 21:47:34 +000012895 (void) WriteBlob(image,data_length+4,chunk);
12896 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12897 mng_info->have_write_global_plte=MagickTrue;
12898 }
12899#endif
12900 }
12901 scene=0;
12902 mng_info->delay=0;
12903#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12904 defined(PNG_MNG_FEATURES_SUPPORTED)
12905 mng_info->equal_palettes=MagickFalse;
12906#endif
12907 do
12908 {
12909 if (mng_info->adjoin)
12910 {
12911#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12912 defined(PNG_MNG_FEATURES_SUPPORTED)
12913 /*
12914 If we aren't using a global palette for the entire MNG, check to
12915 see if we can use one for two or more consecutive images.
12916 */
12917 if (need_local_plte && use_global_plte && !all_images_are_gray)
12918 {
12919 if (mng_info->IsPalette)
12920 {
12921 /*
12922 When equal_palettes is true, this image has the same palette
12923 as the previous PseudoClass image
12924 */
12925 mng_info->have_write_global_plte=mng_info->equal_palettes;
12926 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12927 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12928 {
12929 /*
12930 Write MNG PLTE chunk
12931 */
cristybb503372010-05-27 20:51:26 +000012932 size_t
cristy3ed852e2009-09-05 21:47:34 +000012933 data_length;
12934
12935 data_length=3*image->colors;
12936 (void) WriteBlobMSBULong(image,data_length);
12937 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012938 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012939
cristybb503372010-05-27 20:51:26 +000012940 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012941 {
12942 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12943 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12944 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12945 }
glennrp0fe50b42010-11-16 03:52:51 +000012946
cristy3ed852e2009-09-05 21:47:34 +000012947 (void) WriteBlob(image,data_length+4,chunk);
12948 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12949 (uInt) (data_length+4)));
12950 mng_info->have_write_global_plte=MagickTrue;
12951 }
12952 }
12953 else
12954 mng_info->have_write_global_plte=MagickFalse;
12955 }
12956#endif
12957 if (need_defi)
12958 {
cristybb503372010-05-27 20:51:26 +000012959 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012960 previous_x,
12961 previous_y;
12962
12963 if (scene)
12964 {
12965 previous_x=mng_info->page.x;
12966 previous_y=mng_info->page.y;
12967 }
12968 else
12969 {
12970 previous_x=0;
12971 previous_y=0;
12972 }
12973 mng_info->page=image->page;
12974 if ((mng_info->page.x != previous_x) ||
12975 (mng_info->page.y != previous_y))
12976 {
12977 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12978 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012979 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012980 chunk[4]=0; /* object 0 MSB */
12981 chunk[5]=0; /* object 0 LSB */
12982 chunk[6]=0; /* visible */
12983 chunk[7]=0; /* abstract */
12984 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12985 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12986 (void) WriteBlob(image,16,chunk);
12987 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12988 }
12989 }
12990 }
12991
12992 mng_info->write_mng=write_mng;
12993
12994 if ((int) image->dispose >= 3)
12995 mng_info->framing_mode=3;
12996
12997 if (mng_info->need_fram && mng_info->adjoin &&
12998 ((image->delay != mng_info->delay) ||
12999 (mng_info->framing_mode != mng_info->old_framing_mode)))
13000 {
13001 if (image->delay == mng_info->delay)
13002 {
13003 /*
13004 Write a MNG FRAM chunk with the new framing mode.
13005 */
13006 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13007 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013008 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000013009 chunk[4]=(unsigned char) mng_info->framing_mode;
13010 (void) WriteBlob(image,5,chunk);
13011 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13012 }
13013 else
13014 {
13015 /*
13016 Write a MNG FRAM chunk with the delay.
13017 */
13018 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13019 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013020 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013021 chunk[4]=(unsigned char) mng_info->framing_mode;
13022 chunk[5]=0; /* frame name separator (no name) */
13023 chunk[6]=2; /* flag for changing default delay */
13024 chunk[7]=0; /* flag for changing frame timeout */
13025 chunk[8]=0; /* flag for changing frame clipping */
13026 chunk[9]=0; /* flag for changing frame sync_id */
13027 PNGLong(chunk+10,(png_uint_32)
13028 ((mng_info->ticks_per_second*
13029 image->delay)/MagickMax(image->ticks_per_second,1)));
13030 (void) WriteBlob(image,14,chunk);
13031 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000013032 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000013033 }
13034 mng_info->old_framing_mode=mng_info->framing_mode;
13035 }
13036
13037#if defined(JNG_SUPPORTED)
13038 if (image_info->compression == JPEGCompression)
13039 {
13040 ImageInfo
13041 *write_info;
13042
13043 if (logging != MagickFalse)
13044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13045 " Writing JNG object.");
13046 /* To do: specify the desired alpha compression method. */
13047 write_info=CloneImageInfo(image_info);
13048 write_info->compression=UndefinedCompression;
cristy018f07f2011-09-04 21:15:19 +000013049 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013050 write_info=DestroyImageInfo(write_info);
13051 }
13052 else
13053#endif
13054 {
13055 if (logging != MagickFalse)
13056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13057 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013058
glennrpb9cfe272010-12-21 15:08:06 +000013059 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013060 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013061
13062 /* We don't want any ancillary chunks written */
13063 mng_info->ping_exclude_bKGD=MagickTrue;
13064 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013065 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013066 mng_info->ping_exclude_EXIF=MagickTrue;
13067 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013068 mng_info->ping_exclude_iCCP=MagickTrue;
13069 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13070 mng_info->ping_exclude_oFFs=MagickTrue;
13071 mng_info->ping_exclude_pHYs=MagickTrue;
13072 mng_info->ping_exclude_sRGB=MagickTrue;
13073 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013074 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013075 mng_info->ping_exclude_vpAg=MagickTrue;
13076 mng_info->ping_exclude_zCCP=MagickTrue;
13077 mng_info->ping_exclude_zTXt=MagickTrue;
13078
cristy018f07f2011-09-04 21:15:19 +000013079 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013080 }
13081
13082 if (status == MagickFalse)
13083 {
13084 MngInfoFreeStruct(mng_info,&have_mng_structure);
13085 (void) CloseBlob(image);
13086 return(MagickFalse);
13087 }
13088 (void) CatchImageException(image);
13089 if (GetNextImageInList(image) == (Image *) NULL)
13090 break;
13091 image=SyncNextImageInList(image);
13092 status=SetImageProgress(image,SaveImagesTag,scene++,
13093 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013094
cristy3ed852e2009-09-05 21:47:34 +000013095 if (status == MagickFalse)
13096 break;
glennrp0fe50b42010-11-16 03:52:51 +000013097
cristy3ed852e2009-09-05 21:47:34 +000013098 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013099
cristy3ed852e2009-09-05 21:47:34 +000013100 if (write_mng)
13101 {
13102 while (GetPreviousImageInList(image) != (Image *) NULL)
13103 image=GetPreviousImageInList(image);
13104 /*
13105 Write the MEND chunk.
13106 */
13107 (void) WriteBlobMSBULong(image,0x00000000L);
13108 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013109 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013110 (void) WriteBlob(image,4,chunk);
13111 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13112 }
13113 /*
13114 Relinquish resources.
13115 */
13116 (void) CloseBlob(image);
13117 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013118
cristy3ed852e2009-09-05 21:47:34 +000013119 if (logging != MagickFalse)
13120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013121
cristy3ed852e2009-09-05 21:47:34 +000013122 return(MagickTrue);
13123}
glennrpd5045b42010-03-24 12:40:35 +000013124#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013125
cristy3ed852e2009-09-05 21:47:34 +000013126static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13127{
glennrp3bd393f2011-12-21 18:54:53 +000013128 (void) image;
cristy3ed852e2009-09-05 21:47:34 +000013129 printf("Your PNG library is too old: You have libpng-%s\n",
13130 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013131
cristy3ed852e2009-09-05 21:47:34 +000013132 ThrowBinaryException(CoderError,"PNG library is too old",
13133 image_info->filename);
13134}
glennrp39992b42010-11-14 00:03:43 +000013135
cristy3ed852e2009-09-05 21:47:34 +000013136static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13137{
13138 return(WritePNGImage(image_info,image));
13139}
glennrpd5045b42010-03-24 12:40:35 +000013140#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013141#endif