blob: f83869211677a80dd2dc7f5804a49e5da96258b9 [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;
2702 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002703 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002704 {
cristybefe4d22010-06-07 01:18:58 +00002705 size_t
2706 one;
2707
cristy3ed852e2009-09-05 21:47:34 +00002708 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002709 one=1;
2710 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002711#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2712 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002713 image->colors=256;
2714#else
2715 if (image->colors > 65536L)
2716 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002717#endif
glennrpfaa852b2010-03-30 12:17:00 +00002718 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002719 {
2720 int
2721 number_colors;
2722
2723 png_colorp
2724 palette;
2725
2726 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002727 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002728
cristy3ed852e2009-09-05 21:47:34 +00002729 if (logging != MagickFalse)
2730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2731 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2732 }
2733 }
2734
2735 if (image->storage_class == PseudoClass)
2736 {
2737 /*
2738 Initialize image colormap.
2739 */
cristy018f07f2011-09-04 21:15:19 +00002740 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002741 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002742
glennrpfaa852b2010-03-30 12:17:00 +00002743 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002744 {
2745 int
2746 number_colors;
2747
2748 png_colorp
2749 palette;
2750
2751 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002752
glennrp6af6cf12011-04-22 13:05:16 +00002753 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002754 {
2755 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2756 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2757 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2758 }
glennrp6af6cf12011-04-22 13:05:16 +00002759
glennrp67b9c1a2011-04-22 18:47:36 +00002760 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002761 {
2762 image->colormap[i].red=0;
2763 image->colormap[i].green=0;
2764 image->colormap[i].blue=0;
2765 }
cristy3ed852e2009-09-05 21:47:34 +00002766 }
glennrp0fe50b42010-11-16 03:52:51 +00002767
cristy3ed852e2009-09-05 21:47:34 +00002768 else
2769 {
cristybb503372010-05-27 20:51:26 +00002770 size_t
cristy3ed852e2009-09-05 21:47:34 +00002771 scale;
2772
glennrpfaa852b2010-03-30 12:17:00 +00002773 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002774
cristy3ed852e2009-09-05 21:47:34 +00002775 if (scale < 1)
2776 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002777
cristybb503372010-05-27 20:51:26 +00002778 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002779 {
2780 image->colormap[i].red=(Quantum) (i*scale);
2781 image->colormap[i].green=(Quantum) (i*scale);
2782 image->colormap[i].blue=(Quantum) (i*scale);
2783 }
2784 }
2785 }
glennrp147bc912011-03-30 18:47:21 +00002786
glennrpcb395ac2011-03-30 19:50:23 +00002787 /* Set some properties for reporting by "identify" */
2788 {
glennrp147bc912011-03-30 18:47:21 +00002789 char
2790 msg[MaxTextExtent];
2791
2792 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2793 ping_interlace_method in value */
2794
cristy3b6fd2e2011-05-20 12:53:50 +00002795 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002796 "%d, %d",(int) ping_width, (int) ping_height);
cristy5d6fc9c2011-12-27 03:10:42 +00002797 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002798
cristy3b6fd2e2011-05-20 12:53:50 +00002799 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
cristy5d6fc9c2011-12-27 03:10:42 +00002800 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002801
cristy3b6fd2e2011-05-20 12:53:50 +00002802 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
cristy5d6fc9c2011-12-27 03:10:42 +00002803 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002804
cristy3b6fd2e2011-05-20 12:53:50 +00002805 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002806 (int) ping_interlace_method);
cristy5d6fc9c2011-12-27 03:10:42 +00002807 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
glennrpcb395ac2011-03-30 19:50:23 +00002808 }
glennrp147bc912011-03-30 18:47:21 +00002809
cristy3ed852e2009-09-05 21:47:34 +00002810 /*
2811 Read image scanlines.
2812 */
2813 if (image->delay != 0)
2814 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002815
glennrp0ca69b12010-07-26 01:57:52 +00002816 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002817 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2818 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002819 {
2820 if (logging != MagickFalse)
2821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002822 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002823 mng_info->scenes_found-1);
2824 png_destroy_read_struct(&ping,&ping_info,&end_info);
2825#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002826 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002827#endif
2828 if (logging != MagickFalse)
2829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2830 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002831
cristy3ed852e2009-09-05 21:47:34 +00002832 return(image);
2833 }
glennrp0fe50b42010-11-16 03:52:51 +00002834
cristy3ed852e2009-09-05 21:47:34 +00002835 if (logging != MagickFalse)
2836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2837 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002838
cristy3ed852e2009-09-05 21:47:34 +00002839 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002840 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2841 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002842
cristy3ed852e2009-09-05 21:47:34 +00002843 else
glennrpcf002022011-01-30 02:38:15 +00002844 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2845 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002846
glennrpcf002022011-01-30 02:38:15 +00002847 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002848 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002849
cristy3ed852e2009-09-05 21:47:34 +00002850 if (logging != MagickFalse)
2851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2852 " Converting PNG pixels to pixel packets");
2853 /*
2854 Convert PNG pixels to pixel packets.
2855 */
glennrpfaa852b2010-03-30 12:17:00 +00002856 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002857 {
2858 /*
2859 PNG image is corrupt.
2860 */
2861 png_destroy_read_struct(&ping,&ping_info,&end_info);
2862#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002863 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002864#endif
2865 if (quantum_info != (QuantumInfo *) NULL)
2866 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002867
glennrpcf002022011-01-30 02:38:15 +00002868 if (ping_pixels != (unsigned char *) NULL)
2869 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002870
cristy3ed852e2009-09-05 21:47:34 +00002871 if (logging != MagickFalse)
2872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2873 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002874
cristy3ed852e2009-09-05 21:47:34 +00002875 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002876 {
cristyc82a27b2011-10-21 01:07:16 +00002877 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002878 image->columns=0;
2879 }
glennrp0fe50b42010-11-16 03:52:51 +00002880
cristy3ed852e2009-09-05 21:47:34 +00002881 return(GetFirstImageInList(image));
2882 }
glennrp0fe50b42010-11-16 03:52:51 +00002883
cristyed552522009-10-16 14:04:35 +00002884 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002885
cristyed552522009-10-16 14:04:35 +00002886 if (quantum_info == (QuantumInfo *) NULL)
2887 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002888
glennrpc8cbc5d2011-01-01 00:12:34 +00002889 {
2890
2891 MagickBooleanType
2892 found_transparent_pixel;
2893
2894 found_transparent_pixel=MagickFalse;
2895
cristy3ed852e2009-09-05 21:47:34 +00002896 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002897 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002898 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002899 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002900 /*
2901 Convert image to DirectClass pixel packets.
2902 */
glennrp67b9c1a2011-04-22 18:47:36 +00002903#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2904 int
2905 depth;
2906
2907 depth=(ssize_t) ping_bit_depth;
2908#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002909 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2910 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2911 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2912 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002913
glennrpc8cbc5d2011-01-01 00:12:34 +00002914 for (y=0; y < (ssize_t) image->rows; y++)
2915 {
2916 if (num_passes > 1)
2917 row_offset=ping_rowbytes*y;
2918
2919 else
2920 row_offset=0;
2921
glennrpcf002022011-01-30 02:38:15 +00002922 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002923 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2924
cristyacd2ed22011-08-30 01:44:23 +00002925 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00002926 break;
2927
glennrpc8cbc5d2011-01-01 00:12:34 +00002928 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2929 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002930 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002931
2932 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2933 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002934 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002935
2936 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2937 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002938 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002939
2940 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2941 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002942 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002943
2944 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2945 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002946 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002947
glennrpc8cbc5d2011-01-01 00:12:34 +00002948 if (found_transparent_pixel == MagickFalse)
2949 {
2950 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002951 if (y== 0 && logging != MagickFalse)
2952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2953 " Looking for cheap transparent pixel");
2954
glennrpc8cbc5d2011-01-01 00:12:34 +00002955 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2956 {
glennrp5aa37f62011-01-02 03:07:57 +00002957 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2958 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy4c08aed2011-07-01 19:47:50 +00002959 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00002960 {
glennrpa6a06632011-01-19 15:15:34 +00002961 if (logging != MagickFalse)
2962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2963 " ...got one.");
2964
glennrpc8cbc5d2011-01-01 00:12:34 +00002965 found_transparent_pixel = MagickTrue;
2966 break;
2967 }
glennrp4f25bd02011-01-01 18:51:28 +00002968 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2969 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp847370c2011-07-05 17:37:15 +00002970 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2971 transparent_color.red &&
2972 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2973 transparent_color.green &&
2974 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2975 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002976 {
glennrpa6a06632011-01-19 15:15:34 +00002977 if (logging != MagickFalse)
2978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2979 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002980 found_transparent_pixel = MagickTrue;
2981 break;
2982 }
cristyed231572011-07-14 02:18:59 +00002983 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00002984 }
2985 }
2986
2987 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2988 {
2989 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2990 image->rows);
2991
2992 if (status == MagickFalse)
2993 break;
2994 }
2995 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2996 break;
2997 }
2998
2999 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3000 {
3001 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00003002 if (status == MagickFalse)
3003 break;
3004 }
cristy3ed852e2009-09-05 21:47:34 +00003005 }
cristy3ed852e2009-09-05 21:47:34 +00003006 }
glennrp0fe50b42010-11-16 03:52:51 +00003007
cristy3ed852e2009-09-05 21:47:34 +00003008 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00003009
cristy3ed852e2009-09-05 21:47:34 +00003010 for (pass=0; pass < num_passes; pass++)
3011 {
3012 Quantum
3013 *quantum_scanline;
3014
3015 register Quantum
3016 *r;
3017
3018 /*
3019 Convert grayscale image to PseudoClass pixel packets.
3020 */
glennrpc17d96f2011-06-27 01:20:11 +00003021 if (logging != MagickFalse)
3022 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3023 " Converting grayscale pixels to pixel packets");
cristy4c08aed2011-07-01 19:47:50 +00003024
glennrpfaa852b2010-03-30 12:17:00 +00003025 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00003026 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00003027
cristy3ed852e2009-09-05 21:47:34 +00003028 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3029 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003030
cristy3ed852e2009-09-05 21:47:34 +00003031 if (quantum_scanline == (Quantum *) NULL)
3032 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003033
cristybb503372010-05-27 20:51:26 +00003034 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003035 {
3036 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003037 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003038
cristy3ed852e2009-09-05 21:47:34 +00003039 else
3040 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003041
glennrpcf002022011-01-30 02:38:15 +00003042 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00003043 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003044
cristyacd2ed22011-08-30 01:44:23 +00003045 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003046 break;
glennrp0fe50b42010-11-16 03:52:51 +00003047
glennrpcf002022011-01-30 02:38:15 +00003048 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003049 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003050
glennrpfaa852b2010-03-30 12:17:00 +00003051 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003052 {
3053 case 1:
3054 {
cristybb503372010-05-27 20:51:26 +00003055 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003056 bit;
3057
cristybb503372010-05-27 20:51:26 +00003058 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003059 {
3060 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003061 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003062 p++;
3063 }
glennrp0fe50b42010-11-16 03:52:51 +00003064
cristy3ed852e2009-09-05 21:47:34 +00003065 if ((image->columns % 8) != 0)
3066 {
cristybb503372010-05-27 20:51:26 +00003067 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003068 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003069 }
glennrp0fe50b42010-11-16 03:52:51 +00003070
cristy3ed852e2009-09-05 21:47:34 +00003071 break;
3072 }
glennrp47b9dd52010-11-24 18:12:06 +00003073
cristy3ed852e2009-09-05 21:47:34 +00003074 case 2:
3075 {
cristybb503372010-05-27 20:51:26 +00003076 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003077 {
glennrpa18d5bc2011-04-23 14:51:34 +00003078 *r++=(*p >> 6) & 0x03;
3079 *r++=(*p >> 4) & 0x03;
3080 *r++=(*p >> 2) & 0x03;
3081 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003082 }
glennrp0fe50b42010-11-16 03:52:51 +00003083
cristy3ed852e2009-09-05 21:47:34 +00003084 if ((image->columns % 4) != 0)
3085 {
cristybb503372010-05-27 20:51:26 +00003086 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003087 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003088 }
glennrp0fe50b42010-11-16 03:52:51 +00003089
cristy3ed852e2009-09-05 21:47:34 +00003090 break;
3091 }
glennrp47b9dd52010-11-24 18:12:06 +00003092
cristy3ed852e2009-09-05 21:47:34 +00003093 case 4:
3094 {
cristybb503372010-05-27 20:51:26 +00003095 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003096 {
glennrpa18d5bc2011-04-23 14:51:34 +00003097 *r++=(*p >> 4) & 0x0f;
3098 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003099 }
glennrp0fe50b42010-11-16 03:52:51 +00003100
cristy3ed852e2009-09-05 21:47:34 +00003101 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003102 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003103
cristy3ed852e2009-09-05 21:47:34 +00003104 break;
3105 }
glennrp47b9dd52010-11-24 18:12:06 +00003106
cristy3ed852e2009-09-05 21:47:34 +00003107 case 8:
3108 {
glennrpfaa852b2010-03-30 12:17:00 +00003109 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003110 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003111 {
glennrpa18d5bc2011-04-23 14:51:34 +00003112 *r++=*p++;
cristy4c08aed2011-07-01 19:47:50 +00003113 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3114 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003115 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003116 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003117 }
glennrp0fe50b42010-11-16 03:52:51 +00003118
cristy3ed852e2009-09-05 21:47:34 +00003119 else
cristybb503372010-05-27 20:51:26 +00003120 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003121 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003122
cristy3ed852e2009-09-05 21:47:34 +00003123 break;
3124 }
glennrp47b9dd52010-11-24 18:12:06 +00003125
cristy3ed852e2009-09-05 21:47:34 +00003126 case 16:
3127 {
cristybb503372010-05-27 20:51:26 +00003128 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003129 {
glennrpc17d96f2011-06-27 01:20:11 +00003130#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003131 size_t
3132 quantum;
3133
3134 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003135 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003136
3137 else
glennrpc17d96f2011-06-27 01:20:11 +00003138 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003139
glennrp58f77c72011-04-23 14:09:09 +00003140 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003141 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003142 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003143
3144 if (ping_color_type == 4)
3145 {
glennrpc17d96f2011-06-27 01:20:11 +00003146 if (image->colors > 256)
3147 quantum=((*p++) << 8);
3148 else
3149 quantum=0;
3150
glennrp58f77c72011-04-23 14:09:09 +00003151 quantum|=(*p++);
cristy4c08aed2011-07-01 19:47:50 +00003152 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3153 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003154 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003155 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003156 }
glennrp58f77c72011-04-23 14:09:09 +00003157
3158#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3159 *r++=(*p++);
3160 p++; /* strip low byte */
3161
3162 if (ping_color_type == 4)
3163 {
cristy4c08aed2011-07-01 19:47:50 +00003164 SetPixelAlpha(image,*p++,q);
3165 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003166 found_transparent_pixel = MagickTrue;
3167 p++;
cristyed231572011-07-14 02:18:59 +00003168 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003169 }
cristy3ed852e2009-09-05 21:47:34 +00003170#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003171 }
glennrp47b9dd52010-11-24 18:12:06 +00003172
cristy3ed852e2009-09-05 21:47:34 +00003173 break;
3174 }
glennrp47b9dd52010-11-24 18:12:06 +00003175
cristy3ed852e2009-09-05 21:47:34 +00003176 default:
3177 break;
3178 }
glennrp3faa9a32011-04-23 14:00:25 +00003179
cristy3ed852e2009-09-05 21:47:34 +00003180 /*
3181 Transfer image scanline.
3182 */
3183 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003184
cristy4c08aed2011-07-01 19:47:50 +00003185 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3186
cristyacd2ed22011-08-30 01:44:23 +00003187 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00003188 break;
cristybb503372010-05-27 20:51:26 +00003189 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00003190 {
3191 SetPixelIndex(image,*r++,q);
cristyed231572011-07-14 02:18:59 +00003192 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00003193 }
glennrp0fe50b42010-11-16 03:52:51 +00003194
cristy3ed852e2009-09-05 21:47:34 +00003195 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3196 break;
glennrp0fe50b42010-11-16 03:52:51 +00003197
cristy7a287bf2010-02-14 02:18:09 +00003198 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3199 {
cristycee97112010-05-28 00:44:52 +00003200 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003201 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003202
cristy7a287bf2010-02-14 02:18:09 +00003203 if (status == MagickFalse)
3204 break;
3205 }
cristy3ed852e2009-09-05 21:47:34 +00003206 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003207
cristy7a287bf2010-02-14 02:18:09 +00003208 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003209 {
3210 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003211
cristy3ed852e2009-09-05 21:47:34 +00003212 if (status == MagickFalse)
3213 break;
3214 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003215
cristy3ed852e2009-09-05 21:47:34 +00003216 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3217 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003218
3219 image->matte=found_transparent_pixel;
3220
3221 if (logging != MagickFalse)
3222 {
3223 if (found_transparent_pixel != MagickFalse)
3224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3225 " Found transparent pixel");
3226 else
glennrp5aa37f62011-01-02 03:07:57 +00003227 {
3228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3229 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003230
glennrp5aa37f62011-01-02 03:07:57 +00003231 ping_color_type&=0x03;
3232 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003233 }
3234 }
3235
cristyb32b90a2009-09-07 21:45:48 +00003236 if (quantum_info != (QuantumInfo *) NULL)
3237 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00003238
cristy5c6f7892010-05-05 22:53:29 +00003239 if (image->storage_class == PseudoClass)
3240 {
cristyaeb2cbc2010-05-07 13:28:58 +00003241 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003242 matte;
3243
3244 matte=image->matte;
3245 image->matte=MagickFalse;
cristyea1a8aa2011-10-20 13:24:06 +00003246 (void) SyncImage(image,exception);
cristyaeb2cbc2010-05-07 13:28:58 +00003247 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003248 }
glennrp47b9dd52010-11-24 18:12:06 +00003249
glennrp4eb39312011-03-30 21:34:55 +00003250 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003251
3252 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003253 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003254 {
3255 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003256 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003257 image->colors=2;
cristyea1a8aa2011-10-20 13:24:06 +00003258 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003259#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003260 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003261#endif
3262 if (logging != MagickFalse)
3263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3264 " exit ReadOnePNGImage() early.");
3265 return(image);
3266 }
glennrp47b9dd52010-11-24 18:12:06 +00003267
glennrpfaa852b2010-03-30 12:17:00 +00003268 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003269 {
3270 ClassType
3271 storage_class;
3272
3273 /*
3274 Image has a transparent background.
3275 */
3276 storage_class=image->storage_class;
3277 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003278
glennrp3c218112010-11-27 15:31:26 +00003279/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003280
glennrp0fe50b42010-11-16 03:52:51 +00003281 if (storage_class == PseudoClass)
3282 {
3283 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003284 {
glennrp0fe50b42010-11-16 03:52:51 +00003285 for (x=0; x < ping_num_trans; x++)
3286 {
cristy4c08aed2011-07-01 19:47:50 +00003287 image->colormap[x].alpha =
3288 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003289 }
glennrpc11cf6a2010-03-20 16:46:19 +00003290 }
glennrp47b9dd52010-11-24 18:12:06 +00003291
glennrp0fe50b42010-11-16 03:52:51 +00003292 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3293 {
3294 for (x=0; x < (int) image->colors; x++)
3295 {
3296 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy4c08aed2011-07-01 19:47:50 +00003297 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003298 {
cristy4c08aed2011-07-01 19:47:50 +00003299 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003300 }
3301 }
3302 }
cristyea1a8aa2011-10-20 13:24:06 +00003303 (void) SyncImage(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003304 }
glennrp47b9dd52010-11-24 18:12:06 +00003305
glennrpa6a06632011-01-19 15:15:34 +00003306#if 1 /* Should have already been done above, but glennrp problem P10
3307 * needs this.
3308 */
glennrp0fe50b42010-11-16 03:52:51 +00003309 else
3310 {
3311 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003312 {
glennrp0fe50b42010-11-16 03:52:51 +00003313 image->storage_class=storage_class;
3314 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3315
cristyacd2ed22011-08-30 01:44:23 +00003316 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003317 break;
3318
glennrp0fe50b42010-11-16 03:52:51 +00003319
glennrpa6a06632011-01-19 15:15:34 +00003320 /* Caution: on a Q8 build, this does not distinguish between
3321 * 16-bit colors that differ only in the low byte
3322 */
glennrp0fe50b42010-11-16 03:52:51 +00003323 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3324 {
glennrp847370c2011-07-05 17:37:15 +00003325 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3326 transparent_color.red &&
3327 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3328 transparent_color.green &&
3329 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3330 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003331 {
cristy4c08aed2011-07-01 19:47:50 +00003332 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003333 }
glennrp0fe50b42010-11-16 03:52:51 +00003334
glennrp67b9c1a2011-04-22 18:47:36 +00003335#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003336 else
glennrp4f25bd02011-01-01 18:51:28 +00003337 {
cristy4c08aed2011-07-01 19:47:50 +00003338 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003339 }
glennrpa6a06632011-01-19 15:15:34 +00003340#endif
glennrp0fe50b42010-11-16 03:52:51 +00003341
cristyed231572011-07-14 02:18:59 +00003342 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003343 }
3344
3345 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3346 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003347 }
glennrp0fe50b42010-11-16 03:52:51 +00003348 }
glennrpa6a06632011-01-19 15:15:34 +00003349#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003350
cristy3ed852e2009-09-05 21:47:34 +00003351 image->storage_class=DirectClass;
3352 }
glennrp3c218112010-11-27 15:31:26 +00003353
cristyb40fc462010-08-08 00:49:49 +00003354 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3355 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3356 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00003357
cristyeb3b22a2011-03-31 20:16:11 +00003358 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003359 {
3360 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003361 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3362 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003363 else
glennrpa0ed0092011-04-18 16:36:29 +00003364 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3365 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003366
glennrp4eb39312011-03-30 21:34:55 +00003367 if (status != MagickFalse)
3368 for (i=0; i < (ssize_t) num_text; i++)
3369 {
3370 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003371
glennrp4eb39312011-03-30 21:34:55 +00003372 if (logging != MagickFalse)
3373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3374 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003375
glennrp4eb39312011-03-30 21:34:55 +00003376 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003377 {
cristyd15e6592011-10-15 00:13:06 +00003378 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i,
3379 exception);
glennrp4eb39312011-03-30 21:34:55 +00003380 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003381 }
glennrp0fe50b42010-11-16 03:52:51 +00003382
glennrp4eb39312011-03-30 21:34:55 +00003383 else
3384 {
3385 char
3386 *value;
3387
3388 length=text[i].text_length;
3389 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3390 sizeof(*value));
3391 if (value == (char *) NULL)
3392 {
cristyc82a27b2011-10-21 01:07:16 +00003393 (void) ThrowMagickException(exception,GetMagickModule(),
glennrp4eb39312011-03-30 21:34:55 +00003394 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3395 image->filename);
3396 break;
3397 }
3398 *value='\0';
3399 (void) ConcatenateMagickString(value,text[i].text,length+2);
3400
3401 /* Don't save "density" or "units" property if we have a pHYs
3402 * chunk
3403 */
3404 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3405 (LocaleCompare(text[i].key,"density") != 0 &&
3406 LocaleCompare(text[i].key,"units") != 0))
cristyd15e6592011-10-15 00:13:06 +00003407 (void) SetImageProperty(image,text[i].key,value,exception);
glennrp4eb39312011-03-30 21:34:55 +00003408
3409 if (logging != MagickFalse)
3410 {
3411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3412 " length: %lu",(unsigned long) length);
3413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3414 " Keyword: %s",text[i].key);
3415 }
3416
3417 value=DestroyString(value);
3418 }
3419 }
3420 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003421 }
glennrp3c218112010-11-27 15:31:26 +00003422
cristy3ed852e2009-09-05 21:47:34 +00003423#ifdef MNG_OBJECT_BUFFERS
3424 /*
3425 Store the object if necessary.
3426 */
3427 if (object_id && !mng_info->frozen[object_id])
3428 {
3429 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3430 {
3431 /*
3432 create a new object buffer.
3433 */
3434 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003435 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003436
cristy3ed852e2009-09-05 21:47:34 +00003437 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3438 {
3439 mng_info->ob[object_id]->image=(Image *) NULL;
3440 mng_info->ob[object_id]->reference_count=1;
3441 }
3442 }
glennrp47b9dd52010-11-24 18:12:06 +00003443
cristy3ed852e2009-09-05 21:47:34 +00003444 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3445 mng_info->ob[object_id]->frozen)
3446 {
3447 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
cristyc82a27b2011-10-21 01:07:16 +00003448 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003449 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3450 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003451
cristy3ed852e2009-09-05 21:47:34 +00003452 if (mng_info->ob[object_id]->frozen)
cristyc82a27b2011-10-21 01:07:16 +00003453 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003454 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3455 "`%s'",image->filename);
3456 }
glennrp0fe50b42010-11-16 03:52:51 +00003457
cristy3ed852e2009-09-05 21:47:34 +00003458 else
3459 {
cristy3ed852e2009-09-05 21:47:34 +00003460
3461 if (mng_info->ob[object_id]->image != (Image *) NULL)
3462 mng_info->ob[object_id]->image=DestroyImage
3463 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003464
cristy3ed852e2009-09-05 21:47:34 +00003465 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
cristyc82a27b2011-10-21 01:07:16 +00003466 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003467
cristy3ed852e2009-09-05 21:47:34 +00003468 if (mng_info->ob[object_id]->image != (Image *) NULL)
3469 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003470
cristy3ed852e2009-09-05 21:47:34 +00003471 else
cristyc82a27b2011-10-21 01:07:16 +00003472 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003473 ResourceLimitError,"Cloning image for object buffer failed",
3474 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003475
glennrpfaa852b2010-03-30 12:17:00 +00003476 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003477 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003478
glennrpfaa852b2010-03-30 12:17:00 +00003479 mng_info->ob[object_id]->width=ping_width;
3480 mng_info->ob[object_id]->height=ping_height;
3481 mng_info->ob[object_id]->color_type=ping_color_type;
3482 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3483 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3484 mng_info->ob[object_id]->compression_method=
3485 ping_compression_method;
3486 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003487
glennrpfaa852b2010-03-30 12:17:00 +00003488 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003489 {
3490 int
3491 number_colors;
3492
3493 png_colorp
3494 plte;
3495
3496 /*
3497 Copy the PLTE to the object buffer.
3498 */
3499 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3500 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003501
cristy3ed852e2009-09-05 21:47:34 +00003502 for (i=0; i < number_colors; i++)
3503 {
3504 mng_info->ob[object_id]->plte[i]=plte[i];
3505 }
3506 }
glennrp47b9dd52010-11-24 18:12:06 +00003507
cristy3ed852e2009-09-05 21:47:34 +00003508 else
3509 mng_info->ob[object_id]->plte_length=0;
3510 }
3511 }
3512#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003513
3514 /* Set image->matte to MagickTrue if the input colortype supports
3515 * alpha or if a valid tRNS chunk is present, no matter whether there
3516 * is actual transparency present.
3517 */
3518 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3519 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3520 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3521 MagickTrue : MagickFalse;
3522
glennrpcb395ac2011-03-30 19:50:23 +00003523 /* Set more properties for identify to retrieve */
3524 {
3525 char
3526 msg[MaxTextExtent];
3527
glennrp4eb39312011-03-30 21:34:55 +00003528 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003529 {
3530 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003531 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003532 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
cristy5d6fc9c2011-12-27 03:10:42 +00003533 (void) SetImageProperty(image,"png:text ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003534 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003535 }
3536
3537 if (num_raw_profiles != 0)
3538 {
cristy3b6fd2e2011-05-20 12:53:50 +00003539 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003540 "%d were found", num_raw_profiles);
cristy5d6fc9c2011-12-27 03:10:42 +00003541 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003542 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003543 }
3544
glennrpcb395ac2011-03-30 19:50:23 +00003545 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003546 {
cristy3b6fd2e2011-05-20 12:53:50 +00003547 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003548 "chunk was found (see Chromaticity, above)");
cristy5d6fc9c2011-12-27 03:10:42 +00003549 (void) SetImageProperty(image,"png:cHRM ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003550 exception);
glennrp59612252011-03-30 21:45:21 +00003551 }
glennrpcb395ac2011-03-30 19:50:23 +00003552
3553 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003554 {
cristy3b6fd2e2011-05-20 12:53:50 +00003555 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003556 "chunk was found (see Background color, above)");
cristy5d6fc9c2011-12-27 03:10:42 +00003557 (void) SetImageProperty(image,"png:bKGD ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003558 exception);
glennrp59612252011-03-30 21:45:21 +00003559 }
3560
cristy3b6fd2e2011-05-20 12:53:50 +00003561 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003562 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003563
3564 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy5d6fc9c2011-12-27 03:10:42 +00003565 (void) SetImageProperty(image,"png:iCCP ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003566 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003567
glennrpcb395ac2011-03-30 19:50:23 +00003568 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy5d6fc9c2011-12-27 03:10:42 +00003569 (void) SetImageProperty(image,"png:tRNS ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003570 exception);
glennrp4eb39312011-03-30 21:34:55 +00003571
3572#if defined(PNG_sRGB_SUPPORTED)
3573 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3574 {
cristy3b6fd2e2011-05-20 12:53:50 +00003575 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003576 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003577 (int) intent);
cristy5d6fc9c2011-12-27 03:10:42 +00003578 (void) SetImageProperty(image,"png:sRGB ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003579 exception);
glennrp4eb39312011-03-30 21:34:55 +00003580 }
3581#endif
3582
3583 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3584 {
cristy3b6fd2e2011-05-20 12:53:50 +00003585 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003586 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003587 file_gamma);
cristy5d6fc9c2011-12-27 03:10:42 +00003588 (void) SetImageProperty(image,"png:gAMA ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003589 exception);
glennrp4eb39312011-03-30 21:34:55 +00003590 }
3591
3592#if defined(PNG_pHYs_SUPPORTED)
3593 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3594 {
cristy3b6fd2e2011-05-20 12:53:50 +00003595 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003596 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003597 (double) x_resolution,(double) y_resolution, unit_type);
cristy5d6fc9c2011-12-27 03:10:42 +00003598 (void) SetImageProperty(image,"png:pHYs ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003599 exception);
glennrp4eb39312011-03-30 21:34:55 +00003600 }
3601#endif
3602
3603#if defined(PNG_oFFs_SUPPORTED)
3604 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3605 {
cristy3b6fd2e2011-05-20 12:53:50 +00003606 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003607 (double) image->page.x,(double) image->page.y);
cristy5d6fc9c2011-12-27 03:10:42 +00003608 (void) SetImageProperty(image,"png:oFFs ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003609 exception);
glennrp4eb39312011-03-30 21:34:55 +00003610 }
3611#endif
3612
glennrp07523c72011-03-31 18:12:10 +00003613 if ((image->page.width != 0 && image->page.width != image->columns) ||
3614 (image->page.height != 0 && image->page.height != image->rows))
3615 {
cristy3b6fd2e2011-05-20 12:53:50 +00003616 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003617 "width=%.20g, height=%.20g",
3618 (double) image->page.width,(double) image->page.height);
cristy5d6fc9c2011-12-27 03:10:42 +00003619 (void) SetImageProperty(image,"png:vpAg ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003620 exception);
glennrp07523c72011-03-31 18:12:10 +00003621 }
glennrpcb395ac2011-03-30 19:50:23 +00003622 }
3623
cristy3ed852e2009-09-05 21:47:34 +00003624 /*
3625 Relinquish resources.
3626 */
3627 png_destroy_read_struct(&ping,&ping_info,&end_info);
3628
glennrpcf002022011-01-30 02:38:15 +00003629 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003630#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003631 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003632#endif
3633
3634 if (logging != MagickFalse)
3635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3636 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003637
cristy3ed852e2009-09-05 21:47:34 +00003638 return(image);
3639
3640/* end of reading one PNG image */
3641}
3642
3643static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3644{
3645 Image
3646 *image,
3647 *previous;
3648
3649 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003650 have_mng_structure,
3651 logging,
cristy3ed852e2009-09-05 21:47:34 +00003652 status;
3653
3654 MngInfo
3655 *mng_info;
3656
3657 char
3658 magic_number[MaxTextExtent];
3659
cristy3ed852e2009-09-05 21:47:34 +00003660 ssize_t
3661 count;
3662
3663 /*
3664 Open image file.
3665 */
3666 assert(image_info != (const ImageInfo *) NULL);
3667 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003668
cristy3ed852e2009-09-05 21:47:34 +00003669 if (image_info->debug != MagickFalse)
3670 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3671 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003672
cristy3ed852e2009-09-05 21:47:34 +00003673 assert(exception != (ExceptionInfo *) NULL);
3674 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003675 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy9950d572011-10-01 18:22:35 +00003676 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003677 mng_info=(MngInfo *) NULL;
3678 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003679
cristy3ed852e2009-09-05 21:47:34 +00003680 if (status == MagickFalse)
3681 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003682
cristy3ed852e2009-09-05 21:47:34 +00003683 /*
3684 Verify PNG signature.
3685 */
3686 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003687
glennrpdde35db2011-02-21 12:06:32 +00003688 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003689 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003690
cristy3ed852e2009-09-05 21:47:34 +00003691 /*
3692 Allocate a MngInfo structure.
3693 */
3694 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003695 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003696
cristy3ed852e2009-09-05 21:47:34 +00003697 if (mng_info == (MngInfo *) NULL)
3698 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003699
cristy3ed852e2009-09-05 21:47:34 +00003700 /*
3701 Initialize members of the MngInfo structure.
3702 */
3703 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3704 mng_info->image=image;
3705 have_mng_structure=MagickTrue;
3706
3707 previous=image;
3708 image=ReadOnePNGImage(mng_info,image_info,exception);
3709 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003710
cristy3ed852e2009-09-05 21:47:34 +00003711 if (image == (Image *) NULL)
3712 {
3713 if (previous != (Image *) NULL)
3714 {
3715 if (previous->signature != MagickSignature)
3716 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003717
cristy3ed852e2009-09-05 21:47:34 +00003718 (void) CloseBlob(previous);
3719 (void) DestroyImageList(previous);
3720 }
glennrp0fe50b42010-11-16 03:52:51 +00003721
cristy3ed852e2009-09-05 21:47:34 +00003722 if (logging != MagickFalse)
3723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3724 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003725
cristy3ed852e2009-09-05 21:47:34 +00003726 return((Image *) NULL);
3727 }
glennrp47b9dd52010-11-24 18:12:06 +00003728
cristy3ed852e2009-09-05 21:47:34 +00003729 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003730
cristy3ed852e2009-09-05 21:47:34 +00003731 if ((image->columns == 0) || (image->rows == 0))
3732 {
3733 if (logging != MagickFalse)
3734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3735 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003736
cristy3ed852e2009-09-05 21:47:34 +00003737 ThrowReaderException(CorruptImageError,"CorruptImage");
3738 }
glennrp47b9dd52010-11-24 18:12:06 +00003739
cristy3ed852e2009-09-05 21:47:34 +00003740 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3741 {
cristy018f07f2011-09-04 21:15:19 +00003742 (void) SetImageType(image,TrueColorType,exception);
cristy3ed852e2009-09-05 21:47:34 +00003743 image->matte=MagickFalse;
3744 }
glennrp0fe50b42010-11-16 03:52:51 +00003745
cristy3ed852e2009-09-05 21:47:34 +00003746 if (LocaleCompare(image_info->magick,"PNG32") == 0)
cristy018f07f2011-09-04 21:15:19 +00003747 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003748
cristy3ed852e2009-09-05 21:47:34 +00003749 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3751 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3752 (double) image->page.width,(double) image->page.height,
3753 (double) image->page.x,(double) image->page.y);
3754
3755 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003757
cristy3ed852e2009-09-05 21:47:34 +00003758 return(image);
3759}
3760
3761
3762
3763#if defined(JNG_SUPPORTED)
3764/*
3765%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3766% %
3767% %
3768% %
3769% R e a d O n e J N G I m a g e %
3770% %
3771% %
3772% %
3773%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3774%
3775% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3776% (minus the 8-byte signature) and returns it. It allocates the memory
3777% necessary for the new Image structure and returns a pointer to the new
3778% image.
3779%
3780% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3781%
3782% The format of the ReadOneJNGImage method is:
3783%
3784% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3785% ExceptionInfo *exception)
3786%
3787% A description of each parameter follows:
3788%
3789% o mng_info: Specifies a pointer to a MngInfo structure.
3790%
3791% o image_info: the image info.
3792%
3793% o exception: return any errors or warnings in this structure.
3794%
3795*/
3796static Image *ReadOneJNGImage(MngInfo *mng_info,
3797 const ImageInfo *image_info, ExceptionInfo *exception)
3798{
3799 Image
3800 *alpha_image,
3801 *color_image,
3802 *image,
3803 *jng_image;
3804
3805 ImageInfo
3806 *alpha_image_info,
3807 *color_image_info;
3808
cristy4383ec82011-01-05 15:42:32 +00003809 MagickBooleanType
3810 logging;
3811
cristybb503372010-05-27 20:51:26 +00003812 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003813 y;
3814
3815 MagickBooleanType
3816 status;
3817
3818 png_uint_32
3819 jng_height,
3820 jng_width;
3821
3822 png_byte
3823 jng_color_type,
3824 jng_image_sample_depth,
3825 jng_image_compression_method,
3826 jng_image_interlace_method,
3827 jng_alpha_sample_depth,
3828 jng_alpha_compression_method,
3829 jng_alpha_filter_method,
3830 jng_alpha_interlace_method;
3831
cristy4c08aed2011-07-01 19:47:50 +00003832 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003833 *s;
3834
cristybb503372010-05-27 20:51:26 +00003835 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003836 i,
3837 x;
3838
cristy4c08aed2011-07-01 19:47:50 +00003839 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003840 *q;
3841
3842 register unsigned char
3843 *p;
3844
3845 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003846 read_JSEP,
3847 reading_idat,
3848 skip_to_iend;
3849
cristybb503372010-05-27 20:51:26 +00003850 size_t
cristy3ed852e2009-09-05 21:47:34 +00003851 length;
3852
3853 jng_alpha_compression_method=0;
3854 jng_alpha_sample_depth=8;
3855 jng_color_type=0;
3856 jng_height=0;
3857 jng_width=0;
3858 alpha_image=(Image *) NULL;
3859 color_image=(Image *) NULL;
3860 alpha_image_info=(ImageInfo *) NULL;
3861 color_image_info=(ImageInfo *) NULL;
3862
3863 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003864 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003865
3866 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003867
cristy4c08aed2011-07-01 19:47:50 +00003868 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003869 {
3870 /*
3871 Allocate next image structure.
3872 */
3873 if (logging != MagickFalse)
3874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3875 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003876
cristy9950d572011-10-01 18:22:35 +00003877 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003878
cristy3ed852e2009-09-05 21:47:34 +00003879 if (GetNextImageInList(image) == (Image *) NULL)
3880 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003881
cristy3ed852e2009-09-05 21:47:34 +00003882 image=SyncNextImageInList(image);
3883 }
3884 mng_info->image=image;
3885
3886 /*
3887 Signature bytes have already been read.
3888 */
3889
3890 read_JSEP=MagickFalse;
3891 reading_idat=MagickFalse;
3892 skip_to_iend=MagickFalse;
3893 for (;;)
3894 {
3895 char
3896 type[MaxTextExtent];
3897
3898 unsigned char
3899 *chunk;
3900
3901 unsigned int
3902 count;
3903
3904 /*
3905 Read a new JNG chunk.
3906 */
3907 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3908 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003909
cristy3ed852e2009-09-05 21:47:34 +00003910 if (status == MagickFalse)
3911 break;
glennrp0fe50b42010-11-16 03:52:51 +00003912
cristy3ed852e2009-09-05 21:47:34 +00003913 type[0]='\0';
3914 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3915 length=ReadBlobMSBLong(image);
3916 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3917
3918 if (logging != MagickFalse)
3919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003920 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3921 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003922
3923 if (length > PNG_UINT_31_MAX || count == 0)
3924 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003925
cristy3ed852e2009-09-05 21:47:34 +00003926 p=NULL;
3927 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003928
cristy3ed852e2009-09-05 21:47:34 +00003929 if (length)
3930 {
3931 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003932
cristy3ed852e2009-09-05 21:47:34 +00003933 if (chunk == (unsigned char *) NULL)
3934 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003935
cristybb503372010-05-27 20:51:26 +00003936 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003937 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003938
cristy3ed852e2009-09-05 21:47:34 +00003939 p=chunk;
3940 }
glennrp47b9dd52010-11-24 18:12:06 +00003941
cristy3ed852e2009-09-05 21:47:34 +00003942 (void) ReadBlobMSBLong(image); /* read crc word */
3943
3944 if (skip_to_iend)
3945 {
3946 if (length)
3947 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003948
cristy3ed852e2009-09-05 21:47:34 +00003949 continue;
3950 }
3951
3952 if (memcmp(type,mng_JHDR,4) == 0)
3953 {
3954 if (length == 16)
3955 {
cristybb503372010-05-27 20:51:26 +00003956 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003957 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003958 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003959 (p[6] << 8) | p[7]);
3960 jng_color_type=p[8];
3961 jng_image_sample_depth=p[9];
3962 jng_image_compression_method=p[10];
3963 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003964
cristy3ed852e2009-09-05 21:47:34 +00003965 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3966 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003967
cristy3ed852e2009-09-05 21:47:34 +00003968 jng_alpha_sample_depth=p[12];
3969 jng_alpha_compression_method=p[13];
3970 jng_alpha_filter_method=p[14];
3971 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003972
cristy3ed852e2009-09-05 21:47:34 +00003973 if (logging != MagickFalse)
3974 {
3975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003976 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003977
cristy3ed852e2009-09-05 21:47:34 +00003978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003979 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003980
cristy3ed852e2009-09-05 21:47:34 +00003981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3982 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003983
cristy3ed852e2009-09-05 21:47:34 +00003984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3985 " jng_image_sample_depth: %3d",
3986 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003987
cristy3ed852e2009-09-05 21:47:34 +00003988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3989 " jng_image_compression_method:%3d",
3990 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003991
cristy3ed852e2009-09-05 21:47:34 +00003992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3993 " jng_image_interlace_method: %3d",
3994 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003995
cristy3ed852e2009-09-05 21:47:34 +00003996 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3997 " jng_alpha_sample_depth: %3d",
3998 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003999
cristy3ed852e2009-09-05 21:47:34 +00004000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4001 " jng_alpha_compression_method:%3d",
4002 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004003
cristy3ed852e2009-09-05 21:47:34 +00004004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4005 " jng_alpha_filter_method: %3d",
4006 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00004007
cristy3ed852e2009-09-05 21:47:34 +00004008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4009 " jng_alpha_interlace_method: %3d",
4010 jng_alpha_interlace_method);
4011 }
4012 }
glennrp47b9dd52010-11-24 18:12:06 +00004013
cristy3ed852e2009-09-05 21:47:34 +00004014 if (length)
4015 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004016
cristy3ed852e2009-09-05 21:47:34 +00004017 continue;
4018 }
4019
4020
4021 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4022 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4023 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4024 {
4025 /*
4026 o create color_image
4027 o open color_blob, attached to color_image
4028 o if (color type has alpha)
4029 open alpha_blob, attached to alpha_image
4030 */
4031
cristy73bd4a52010-10-05 11:24:23 +00004032 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004033
cristy3ed852e2009-09-05 21:47:34 +00004034 if (color_image_info == (ImageInfo *) NULL)
4035 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004036
cristy3ed852e2009-09-05 21:47:34 +00004037 GetImageInfo(color_image_info);
cristy9950d572011-10-01 18:22:35 +00004038 color_image=AcquireImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004039
cristy3ed852e2009-09-05 21:47:34 +00004040 if (color_image == (Image *) NULL)
4041 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4042
4043 if (logging != MagickFalse)
4044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4045 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004046
cristy3ed852e2009-09-05 21:47:34 +00004047 (void) AcquireUniqueFilename(color_image->filename);
4048 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4049 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004050
cristy3ed852e2009-09-05 21:47:34 +00004051 if (status == MagickFalse)
4052 return((Image *) NULL);
4053
4054 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4055 {
4056 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004057 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004058
cristy3ed852e2009-09-05 21:47:34 +00004059 if (alpha_image_info == (ImageInfo *) NULL)
4060 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004061
cristy3ed852e2009-09-05 21:47:34 +00004062 GetImageInfo(alpha_image_info);
cristy9950d572011-10-01 18:22:35 +00004063 alpha_image=AcquireImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004064
cristy3ed852e2009-09-05 21:47:34 +00004065 if (alpha_image == (Image *) NULL)
4066 {
4067 alpha_image=DestroyImage(alpha_image);
4068 ThrowReaderException(ResourceLimitError,
4069 "MemoryAllocationFailed");
4070 }
glennrp0fe50b42010-11-16 03:52:51 +00004071
cristy3ed852e2009-09-05 21:47:34 +00004072 if (logging != MagickFalse)
4073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4074 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004075
cristy3ed852e2009-09-05 21:47:34 +00004076 (void) AcquireUniqueFilename(alpha_image->filename);
4077 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4078 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004079
cristy3ed852e2009-09-05 21:47:34 +00004080 if (status == MagickFalse)
4081 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004082
cristy3ed852e2009-09-05 21:47:34 +00004083 if (jng_alpha_compression_method == 0)
4084 {
4085 unsigned char
4086 data[18];
4087
4088 if (logging != MagickFalse)
4089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4090 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004091
cristy3ed852e2009-09-05 21:47:34 +00004092 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4093 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004094
cristy3ed852e2009-09-05 21:47:34 +00004095 (void) WriteBlobMSBULong(alpha_image,13L);
4096 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004097 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004098 PNGLong(data+4,jng_width);
4099 PNGLong(data+8,jng_height);
4100 data[12]=jng_alpha_sample_depth;
4101 data[13]=0; /* color_type gray */
4102 data[14]=0; /* compression method 0 */
4103 data[15]=0; /* filter_method 0 */
4104 data[16]=0; /* interlace_method 0 */
4105 (void) WriteBlob(alpha_image,17,data);
4106 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4107 }
4108 }
4109 reading_idat=MagickTrue;
4110 }
4111
4112 if (memcmp(type,mng_JDAT,4) == 0)
4113 {
glennrp47b9dd52010-11-24 18:12:06 +00004114 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004115
4116 if (logging != MagickFalse)
4117 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4118 " Copying JDAT chunk data to color_blob.");
4119
4120 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004121
cristy3ed852e2009-09-05 21:47:34 +00004122 if (length)
4123 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004124
cristy3ed852e2009-09-05 21:47:34 +00004125 continue;
4126 }
4127
4128 if (memcmp(type,mng_IDAT,4) == 0)
4129 {
4130 png_byte
4131 data[5];
4132
glennrp47b9dd52010-11-24 18:12:06 +00004133 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004134
4135 if (image_info->ping == MagickFalse)
4136 {
4137 if (logging != MagickFalse)
4138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4139 " Copying IDAT chunk data to alpha_blob.");
4140
cristybb503372010-05-27 20:51:26 +00004141 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004142 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004143 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004144 (void) WriteBlob(alpha_image,4,data);
4145 (void) WriteBlob(alpha_image,length,chunk);
4146 (void) WriteBlobMSBULong(alpha_image,
4147 crc32(crc32(0,data,4),chunk,(uInt) length));
4148 }
glennrp0fe50b42010-11-16 03:52:51 +00004149
cristy3ed852e2009-09-05 21:47:34 +00004150 if (length)
4151 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004152
cristy3ed852e2009-09-05 21:47:34 +00004153 continue;
4154 }
4155
4156 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4157 {
glennrp47b9dd52010-11-24 18:12:06 +00004158 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004159
4160 if (image_info->ping == MagickFalse)
4161 {
4162 if (logging != MagickFalse)
4163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4164 " Copying JDAA chunk data to alpha_blob.");
4165
4166 (void) WriteBlob(alpha_image,length,chunk);
4167 }
glennrp0fe50b42010-11-16 03:52:51 +00004168
cristy3ed852e2009-09-05 21:47:34 +00004169 if (length)
4170 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004171
cristy3ed852e2009-09-05 21:47:34 +00004172 continue;
4173 }
4174
4175 if (memcmp(type,mng_JSEP,4) == 0)
4176 {
4177 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004178
cristy3ed852e2009-09-05 21:47:34 +00004179 if (length)
4180 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004181
cristy3ed852e2009-09-05 21:47:34 +00004182 continue;
4183 }
4184
4185 if (memcmp(type,mng_bKGD,4) == 0)
4186 {
4187 if (length == 2)
4188 {
4189 image->background_color.red=ScaleCharToQuantum(p[1]);
4190 image->background_color.green=image->background_color.red;
4191 image->background_color.blue=image->background_color.red;
4192 }
glennrp0fe50b42010-11-16 03:52:51 +00004193
cristy3ed852e2009-09-05 21:47:34 +00004194 if (length == 6)
4195 {
4196 image->background_color.red=ScaleCharToQuantum(p[1]);
4197 image->background_color.green=ScaleCharToQuantum(p[3]);
4198 image->background_color.blue=ScaleCharToQuantum(p[5]);
4199 }
glennrp0fe50b42010-11-16 03:52:51 +00004200
cristy3ed852e2009-09-05 21:47:34 +00004201 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4202 continue;
4203 }
4204
4205 if (memcmp(type,mng_gAMA,4) == 0)
4206 {
4207 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004208 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004209
cristy3ed852e2009-09-05 21:47:34 +00004210 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4211 continue;
4212 }
4213
4214 if (memcmp(type,mng_cHRM,4) == 0)
4215 {
4216 if (length == 32)
4217 {
cristy8182b072010-05-30 20:10:53 +00004218 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4219 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4220 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4221 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4222 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4223 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4224 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4225 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004226 }
glennrp47b9dd52010-11-24 18:12:06 +00004227
cristy3ed852e2009-09-05 21:47:34 +00004228 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4229 continue;
4230 }
4231
4232 if (memcmp(type,mng_sRGB,4) == 0)
4233 {
4234 if (length == 1)
4235 {
glennrpe610a072010-08-05 17:08:46 +00004236 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004237 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004238 image->gamma=0.45455f;
4239 image->chromaticity.red_primary.x=0.6400f;
4240 image->chromaticity.red_primary.y=0.3300f;
4241 image->chromaticity.green_primary.x=0.3000f;
4242 image->chromaticity.green_primary.y=0.6000f;
4243 image->chromaticity.blue_primary.x=0.1500f;
4244 image->chromaticity.blue_primary.y=0.0600f;
4245 image->chromaticity.white_point.x=0.3127f;
4246 image->chromaticity.white_point.y=0.3290f;
4247 }
glennrp47b9dd52010-11-24 18:12:06 +00004248
cristy3ed852e2009-09-05 21:47:34 +00004249 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4250 continue;
4251 }
4252
4253 if (memcmp(type,mng_oFFs,4) == 0)
4254 {
4255 if (length > 8)
4256 {
glennrp5eae7602011-02-22 15:21:32 +00004257 image->page.x=(ssize_t) mng_get_long(p);
4258 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004259
cristy3ed852e2009-09-05 21:47:34 +00004260 if ((int) p[8] != 0)
4261 {
4262 image->page.x/=10000;
4263 image->page.y/=10000;
4264 }
4265 }
glennrp47b9dd52010-11-24 18:12:06 +00004266
cristy3ed852e2009-09-05 21:47:34 +00004267 if (length)
4268 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004269
cristy3ed852e2009-09-05 21:47:34 +00004270 continue;
4271 }
4272
4273 if (memcmp(type,mng_pHYs,4) == 0)
4274 {
4275 if (length > 8)
4276 {
cristy2a11bef2011-10-28 18:33:11 +00004277 image->resolution.x=(double) mng_get_long(p);
4278 image->resolution.y=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004279 if ((int) p[8] == PNG_RESOLUTION_METER)
4280 {
4281 image->units=PixelsPerCentimeterResolution;
cristy2a11bef2011-10-28 18:33:11 +00004282 image->resolution.x=image->resolution.x/100.0f;
4283 image->resolution.y=image->resolution.y/100.0f;
cristy3ed852e2009-09-05 21:47:34 +00004284 }
4285 }
glennrp0fe50b42010-11-16 03:52:51 +00004286
cristy3ed852e2009-09-05 21:47:34 +00004287 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4288 continue;
4289 }
4290
4291#if 0
4292 if (memcmp(type,mng_iCCP,4) == 0)
4293 {
glennrpfd05d622011-02-25 04:10:33 +00004294 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004295 if (length)
4296 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004297
cristy3ed852e2009-09-05 21:47:34 +00004298 continue;
4299 }
4300#endif
4301
4302 if (length)
4303 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4304
4305 if (memcmp(type,mng_IEND,4))
4306 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004307
cristy3ed852e2009-09-05 21:47:34 +00004308 break;
4309 }
4310
4311
4312 /* IEND found */
4313
4314 /*
4315 Finish up reading image data:
4316
4317 o read main image from color_blob.
4318
4319 o close color_blob.
4320
4321 o if (color_type has alpha)
4322 if alpha_encoding is PNG
4323 read secondary image from alpha_blob via ReadPNG
4324 if alpha_encoding is JPEG
4325 read secondary image from alpha_blob via ReadJPEG
4326
4327 o close alpha_blob.
4328
4329 o copy intensity of secondary image into
cristy4c08aed2011-07-01 19:47:50 +00004330 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004331
4332 o destroy the secondary image.
4333 */
4334
4335 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004336
cristy3ed852e2009-09-05 21:47:34 +00004337 if (logging != MagickFalse)
4338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4339 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004340
cristy3b6fd2e2011-05-20 12:53:50 +00004341 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004342 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004343
cristy3ed852e2009-09-05 21:47:34 +00004344 color_image_info->ping=MagickFalse; /* To do: avoid this */
4345 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004346
cristy3ed852e2009-09-05 21:47:34 +00004347 if (jng_image == (Image *) NULL)
4348 return((Image *) NULL);
4349
4350 (void) RelinquishUniqueFileResource(color_image->filename);
4351 color_image=DestroyImage(color_image);
4352 color_image_info=DestroyImageInfo(color_image_info);
4353
4354 if (jng_image == (Image *) NULL)
4355 return((Image *) NULL);
4356
4357 if (logging != MagickFalse)
4358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4359 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004360
cristy3ed852e2009-09-05 21:47:34 +00004361 image->rows=jng_height;
4362 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004363
cristybb503372010-05-27 20:51:26 +00004364 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004365 {
cristyc82a27b2011-10-21 01:07:16 +00004366 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00004367 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004368 for (x=(ssize_t) image->columns; x != 0; x--)
4369 {
4370 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4371 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4372 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004373 q+=GetPixelChannels(image);
4374 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004375 }
glennrp47b9dd52010-11-24 18:12:06 +00004376
cristy3ed852e2009-09-05 21:47:34 +00004377 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4378 break;
4379 }
glennrp0fe50b42010-11-16 03:52:51 +00004380
cristy3ed852e2009-09-05 21:47:34 +00004381 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004382
cristy3ed852e2009-09-05 21:47:34 +00004383 if (image_info->ping == MagickFalse)
4384 {
4385 if (jng_color_type >= 12)
4386 {
4387 if (jng_alpha_compression_method == 0)
4388 {
4389 png_byte
4390 data[5];
4391 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4392 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004393 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004394 (void) WriteBlob(alpha_image,4,data);
4395 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4396 }
glennrp0fe50b42010-11-16 03:52:51 +00004397
cristy3ed852e2009-09-05 21:47:34 +00004398 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004399
cristy3ed852e2009-09-05 21:47:34 +00004400 if (logging != MagickFalse)
4401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00004402 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004403
cristy3b6fd2e2011-05-20 12:53:50 +00004404 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004405 "%s",alpha_image->filename);
4406
4407 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004408
cristy3ed852e2009-09-05 21:47:34 +00004409 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004410 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004411 {
4412 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristyc82a27b2011-10-21 01:07:16 +00004413 exception);
cristy3ed852e2009-09-05 21:47:34 +00004414 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004415
cristy3ed852e2009-09-05 21:47:34 +00004416 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00004417 for (x=(ssize_t) image->columns; x != 0; x--)
4418 {
4419 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004420 q+=GetPixelChannels(image);
4421 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004422 }
glennrp0fe50b42010-11-16 03:52:51 +00004423
cristy3ed852e2009-09-05 21:47:34 +00004424 else
cristy4c08aed2011-07-01 19:47:50 +00004425 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004426 {
cristy4c08aed2011-07-01 19:47:50 +00004427 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4428 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004429 image->matte=MagickTrue;
cristyed231572011-07-14 02:18:59 +00004430 q+=GetPixelChannels(image);
4431 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004432 }
glennrp0fe50b42010-11-16 03:52:51 +00004433
cristy3ed852e2009-09-05 21:47:34 +00004434 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4435 break;
4436 }
4437 (void) RelinquishUniqueFileResource(alpha_image->filename);
4438 alpha_image=DestroyImage(alpha_image);
4439 alpha_image_info=DestroyImageInfo(alpha_image_info);
4440 if (jng_image != (Image *) NULL)
4441 jng_image=DestroyImage(jng_image);
4442 }
4443 }
4444
glennrp47b9dd52010-11-24 18:12:06 +00004445 /* Read the JNG image. */
4446
cristy3ed852e2009-09-05 21:47:34 +00004447 if (mng_info->mng_type == 0)
4448 {
4449 mng_info->mng_width=jng_width;
4450 mng_info->mng_height=jng_height;
4451 }
glennrp0fe50b42010-11-16 03:52:51 +00004452
cristy3ed852e2009-09-05 21:47:34 +00004453 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004454 {
4455 image->page.width=jng_width;
4456 image->page.height=jng_height;
4457 }
4458
cristy3ed852e2009-09-05 21:47:34 +00004459 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004460 {
4461 image->page.x=mng_info->x_off[mng_info->object_id];
4462 image->page.y=mng_info->y_off[mng_info->object_id];
4463 }
4464
cristy3ed852e2009-09-05 21:47:34 +00004465 else
glennrp0fe50b42010-11-16 03:52:51 +00004466 {
4467 image->page.y=mng_info->y_off[mng_info->object_id];
4468 }
4469
cristy3ed852e2009-09-05 21:47:34 +00004470 mng_info->image_found++;
4471 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4472 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004473
cristy3ed852e2009-09-05 21:47:34 +00004474 if (logging != MagickFalse)
4475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4476 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004477
cristy3ed852e2009-09-05 21:47:34 +00004478 return(image);
4479}
4480
4481/*
4482%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4483% %
4484% %
4485% %
4486% R e a d J N G I m a g e %
4487% %
4488% %
4489% %
4490%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4491%
4492% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4493% (including the 8-byte signature) and returns it. It allocates the memory
4494% necessary for the new Image structure and returns a pointer to the new
4495% image.
4496%
4497% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4498%
4499% The format of the ReadJNGImage method is:
4500%
4501% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4502% *exception)
4503%
4504% A description of each parameter follows:
4505%
4506% o image_info: the image info.
4507%
4508% o exception: return any errors or warnings in this structure.
4509%
4510*/
4511
4512static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4513{
4514 Image
4515 *image,
4516 *previous;
4517
4518 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004519 have_mng_structure,
4520 logging,
cristy3ed852e2009-09-05 21:47:34 +00004521 status;
4522
4523 MngInfo
4524 *mng_info;
4525
4526 char
4527 magic_number[MaxTextExtent];
4528
cristy3ed852e2009-09-05 21:47:34 +00004529 size_t
4530 count;
4531
4532 /*
4533 Open image file.
4534 */
4535 assert(image_info != (const ImageInfo *) NULL);
4536 assert(image_info->signature == MagickSignature);
4537 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4538 assert(exception != (ExceptionInfo *) NULL);
4539 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004540 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy9950d572011-10-01 18:22:35 +00004541 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004542 mng_info=(MngInfo *) NULL;
4543 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004544
cristy3ed852e2009-09-05 21:47:34 +00004545 if (status == MagickFalse)
4546 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004547
cristy3ed852e2009-09-05 21:47:34 +00004548 if (LocaleCompare(image_info->magick,"JNG") != 0)
4549 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004550
glennrp47b9dd52010-11-24 18:12:06 +00004551 /* Verify JNG signature. */
4552
cristy3ed852e2009-09-05 21:47:34 +00004553 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004554
glennrp3b8763e2011-02-21 12:08:18 +00004555 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004556 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004557
glennrp47b9dd52010-11-24 18:12:06 +00004558 /* Allocate a MngInfo structure. */
4559
cristy3ed852e2009-09-05 21:47:34 +00004560 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004561 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004562
cristy3ed852e2009-09-05 21:47:34 +00004563 if (mng_info == (MngInfo *) NULL)
4564 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004565
glennrp47b9dd52010-11-24 18:12:06 +00004566 /* Initialize members of the MngInfo structure. */
4567
cristy3ed852e2009-09-05 21:47:34 +00004568 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4569 have_mng_structure=MagickTrue;
4570
4571 mng_info->image=image;
4572 previous=image;
4573 image=ReadOneJNGImage(mng_info,image_info,exception);
4574 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004575
cristy3ed852e2009-09-05 21:47:34 +00004576 if (image == (Image *) NULL)
4577 {
4578 if (IsImageObject(previous) != MagickFalse)
4579 {
4580 (void) CloseBlob(previous);
4581 (void) DestroyImageList(previous);
4582 }
glennrp0fe50b42010-11-16 03:52:51 +00004583
cristy3ed852e2009-09-05 21:47:34 +00004584 if (logging != MagickFalse)
4585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4586 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004587
cristy3ed852e2009-09-05 21:47:34 +00004588 return((Image *) NULL);
4589 }
4590 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004591
cristy3ed852e2009-09-05 21:47:34 +00004592 if (image->columns == 0 || image->rows == 0)
4593 {
4594 if (logging != MagickFalse)
4595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4596 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004597
cristy3ed852e2009-09-05 21:47:34 +00004598 ThrowReaderException(CorruptImageError,"CorruptImage");
4599 }
glennrp0fe50b42010-11-16 03:52:51 +00004600
cristy3ed852e2009-09-05 21:47:34 +00004601 if (logging != MagickFalse)
4602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004603
cristy3ed852e2009-09-05 21:47:34 +00004604 return(image);
4605}
4606#endif
4607
4608static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4609{
4610 char
4611 page_geometry[MaxTextExtent];
4612
4613 Image
4614 *image,
4615 *previous;
4616
cristy4383ec82011-01-05 15:42:32 +00004617 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004618 logging,
4619 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004620
cristy3ed852e2009-09-05 21:47:34 +00004621 volatile int
4622 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004623 object_id,
4624 term_chunk_found,
4625 skip_to_iend;
4626
cristybb503372010-05-27 20:51:26 +00004627 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004628 image_count=0;
4629
4630 MagickBooleanType
4631 status;
4632
4633 MagickOffsetType
4634 offset;
4635
4636 MngInfo
4637 *mng_info;
4638
4639 MngBox
4640 default_fb,
4641 fb,
4642 previous_fb;
4643
4644#if defined(MNG_INSERT_LAYERS)
cristy101ab702011-10-13 13:06:32 +00004645 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004646 mng_background_color;
4647#endif
4648
4649 register unsigned char
4650 *p;
4651
cristybb503372010-05-27 20:51:26 +00004652 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004653 i;
4654
4655 size_t
4656 count;
4657
cristybb503372010-05-27 20:51:26 +00004658 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004659 loop_level;
4660
4661 volatile short
4662 skipping_loop;
4663
4664#if defined(MNG_INSERT_LAYERS)
4665 unsigned int
4666 mandatory_back=0;
4667#endif
4668
4669 volatile unsigned int
4670#ifdef MNG_OBJECT_BUFFERS
4671 mng_background_object=0,
4672#endif
4673 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4674
cristybb503372010-05-27 20:51:26 +00004675 size_t
cristy3ed852e2009-09-05 21:47:34 +00004676 default_frame_timeout,
4677 frame_timeout,
4678#if defined(MNG_INSERT_LAYERS)
4679 image_height,
4680 image_width,
4681#endif
4682 length;
4683
glennrp38ea0832010-06-02 18:50:28 +00004684 /* These delays are all measured in image ticks_per_second,
4685 * not in MNG ticks_per_second
4686 */
cristybb503372010-05-27 20:51:26 +00004687 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004688 default_frame_delay,
4689 final_delay,
4690 final_image_delay,
4691 frame_delay,
4692#if defined(MNG_INSERT_LAYERS)
4693 insert_layers,
4694#endif
4695 mng_iterations=1,
4696 simplicity=0,
4697 subframe_height=0,
4698 subframe_width=0;
4699
4700 previous_fb.top=0;
4701 previous_fb.bottom=0;
4702 previous_fb.left=0;
4703 previous_fb.right=0;
4704 default_fb.top=0;
4705 default_fb.bottom=0;
4706 default_fb.left=0;
4707 default_fb.right=0;
4708
glennrp47b9dd52010-11-24 18:12:06 +00004709 /* Open image file. */
4710
cristy3ed852e2009-09-05 21:47:34 +00004711 assert(image_info != (const ImageInfo *) NULL);
4712 assert(image_info->signature == MagickSignature);
4713 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4714 assert(exception != (ExceptionInfo *) NULL);
4715 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004716 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy9950d572011-10-01 18:22:35 +00004717 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004718 mng_info=(MngInfo *) NULL;
4719 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004720
cristy3ed852e2009-09-05 21:47:34 +00004721 if (status == MagickFalse)
4722 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004723
cristy3ed852e2009-09-05 21:47:34 +00004724 first_mng_object=MagickFalse;
4725 skipping_loop=(-1);
4726 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004727
4728 /* Allocate a MngInfo structure. */
4729
cristy73bd4a52010-10-05 11:24:23 +00004730 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004731
cristy3ed852e2009-09-05 21:47:34 +00004732 if (mng_info == (MngInfo *) NULL)
4733 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004734
glennrp47b9dd52010-11-24 18:12:06 +00004735 /* Initialize members of the MngInfo structure. */
4736
cristy3ed852e2009-09-05 21:47:34 +00004737 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4738 mng_info->image=image;
4739 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004740
4741 if (LocaleCompare(image_info->magick,"MNG") == 0)
4742 {
4743 char
4744 magic_number[MaxTextExtent];
4745
glennrp47b9dd52010-11-24 18:12:06 +00004746 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004747 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4748 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4749 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004750
4751 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004752 for (i=0; i < MNG_MAX_OBJECTS; i++)
4753 {
cristybb503372010-05-27 20:51:26 +00004754 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4755 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004756 }
4757 mng_info->exists[0]=MagickTrue;
4758 }
glennrp47b9dd52010-11-24 18:12:06 +00004759
cristy3ed852e2009-09-05 21:47:34 +00004760 first_mng_object=MagickTrue;
4761 mng_type=0;
4762#if defined(MNG_INSERT_LAYERS)
4763 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4764#endif
4765 default_frame_delay=0;
4766 default_frame_timeout=0;
4767 frame_delay=0;
4768 final_delay=1;
4769 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4770 object_id=0;
4771 skip_to_iend=MagickFalse;
4772 term_chunk_found=MagickFalse;
4773 mng_info->framing_mode=1;
4774#if defined(MNG_INSERT_LAYERS)
4775 mandatory_back=MagickFalse;
4776#endif
4777#if defined(MNG_INSERT_LAYERS)
4778 mng_background_color=image->background_color;
4779#endif
4780 default_fb=mng_info->frame;
4781 previous_fb=mng_info->frame;
4782 do
4783 {
4784 char
4785 type[MaxTextExtent];
4786
4787 if (LocaleCompare(image_info->magick,"MNG") == 0)
4788 {
4789 unsigned char
4790 *chunk;
4791
4792 /*
4793 Read a new chunk.
4794 */
4795 type[0]='\0';
4796 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4797 length=ReadBlobMSBLong(image);
4798 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4799
4800 if (logging != MagickFalse)
4801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004802 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4803 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004804
4805 if (length > PNG_UINT_31_MAX)
4806 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004807
cristy3ed852e2009-09-05 21:47:34 +00004808 if (count == 0)
4809 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004810
cristy3ed852e2009-09-05 21:47:34 +00004811 p=NULL;
4812 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004813
cristy3ed852e2009-09-05 21:47:34 +00004814 if (length)
4815 {
4816 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004817
cristy3ed852e2009-09-05 21:47:34 +00004818 if (chunk == (unsigned char *) NULL)
4819 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004820
cristybb503372010-05-27 20:51:26 +00004821 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004822 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004823
cristy3ed852e2009-09-05 21:47:34 +00004824 p=chunk;
4825 }
glennrp0fe50b42010-11-16 03:52:51 +00004826
cristy3ed852e2009-09-05 21:47:34 +00004827 (void) ReadBlobMSBLong(image); /* read crc word */
4828
4829#if !defined(JNG_SUPPORTED)
4830 if (memcmp(type,mng_JHDR,4) == 0)
4831 {
4832 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004833
cristy3ed852e2009-09-05 21:47:34 +00004834 if (mng_info->jhdr_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00004835 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004836 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004837
cristy3ed852e2009-09-05 21:47:34 +00004838 mng_info->jhdr_warning++;
4839 }
4840#endif
4841 if (memcmp(type,mng_DHDR,4) == 0)
4842 {
4843 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004844
cristy3ed852e2009-09-05 21:47:34 +00004845 if (mng_info->dhdr_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00004846 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004847 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004848
cristy3ed852e2009-09-05 21:47:34 +00004849 mng_info->dhdr_warning++;
4850 }
4851 if (memcmp(type,mng_MEND,4) == 0)
4852 break;
glennrp47b9dd52010-11-24 18:12:06 +00004853
cristy3ed852e2009-09-05 21:47:34 +00004854 if (skip_to_iend)
4855 {
4856 if (memcmp(type,mng_IEND,4) == 0)
4857 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004858
cristy3ed852e2009-09-05 21:47:34 +00004859 if (length)
4860 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004861
cristy3ed852e2009-09-05 21:47:34 +00004862 if (logging != MagickFalse)
4863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4864 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004865
cristy3ed852e2009-09-05 21:47:34 +00004866 continue;
4867 }
glennrp0fe50b42010-11-16 03:52:51 +00004868
cristy3ed852e2009-09-05 21:47:34 +00004869 if (memcmp(type,mng_MHDR,4) == 0)
4870 {
cristybb503372010-05-27 20:51:26 +00004871 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004872 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004873
cristybb503372010-05-27 20:51:26 +00004874 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004875 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004876
cristy3ed852e2009-09-05 21:47:34 +00004877 if (logging != MagickFalse)
4878 {
4879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004880 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004882 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004883 }
glennrp0fe50b42010-11-16 03:52:51 +00004884
cristy3ed852e2009-09-05 21:47:34 +00004885 p+=8;
cristy8182b072010-05-30 20:10:53 +00004886 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004887
cristy3ed852e2009-09-05 21:47:34 +00004888 if (mng_info->ticks_per_second == 0)
4889 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004890
cristy3ed852e2009-09-05 21:47:34 +00004891 else
4892 default_frame_delay=1UL*image->ticks_per_second/
4893 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004894
cristy3ed852e2009-09-05 21:47:34 +00004895 frame_delay=default_frame_delay;
4896 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004897
cristy3ed852e2009-09-05 21:47:34 +00004898 if (length > 16)
4899 {
4900 p+=16;
cristy8182b072010-05-30 20:10:53 +00004901 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004902 }
glennrp0fe50b42010-11-16 03:52:51 +00004903
cristy3ed852e2009-09-05 21:47:34 +00004904 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004905
cristy3ed852e2009-09-05 21:47:34 +00004906 if ((simplicity != 0) && ((simplicity | 11) == 11))
4907 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004908
cristy3ed852e2009-09-05 21:47:34 +00004909 if ((simplicity != 0) && ((simplicity | 9) == 9))
4910 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004911
cristy3ed852e2009-09-05 21:47:34 +00004912#if defined(MNG_INSERT_LAYERS)
4913 if (mng_type != 3)
4914 insert_layers=MagickTrue;
4915#endif
cristy4c08aed2011-07-01 19:47:50 +00004916 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004917 {
glennrp47b9dd52010-11-24 18:12:06 +00004918 /* Allocate next image structure. */
cristy9950d572011-10-01 18:22:35 +00004919 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004920
cristy3ed852e2009-09-05 21:47:34 +00004921 if (GetNextImageInList(image) == (Image *) NULL)
4922 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004923
cristy3ed852e2009-09-05 21:47:34 +00004924 image=SyncNextImageInList(image);
4925 mng_info->image=image;
4926 }
4927
4928 if ((mng_info->mng_width > 65535L) ||
4929 (mng_info->mng_height > 65535L))
4930 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004931
cristy3b6fd2e2011-05-20 12:53:50 +00004932 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004933 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004934 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004935
cristy3ed852e2009-09-05 21:47:34 +00004936 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004937 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004938 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004939 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004940 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004941
cristy3ed852e2009-09-05 21:47:34 +00004942 for (i=0; i < MNG_MAX_OBJECTS; i++)
4943 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004944
cristy3ed852e2009-09-05 21:47:34 +00004945 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4946 continue;
4947 }
4948
4949 if (memcmp(type,mng_TERM,4) == 0)
4950 {
4951 int
4952 repeat=0;
4953
4954
4955 if (length)
4956 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004957
cristy3ed852e2009-09-05 21:47:34 +00004958 if (repeat == 3)
4959 {
cristy8182b072010-05-30 20:10:53 +00004960 final_delay=(png_uint_32) mng_get_long(&p[2]);
4961 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004962
cristy3ed852e2009-09-05 21:47:34 +00004963 if (mng_iterations == PNG_UINT_31_MAX)
4964 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004965
cristy3ed852e2009-09-05 21:47:34 +00004966 image->iterations=mng_iterations;
4967 term_chunk_found=MagickTrue;
4968 }
glennrp0fe50b42010-11-16 03:52:51 +00004969
cristy3ed852e2009-09-05 21:47:34 +00004970 if (logging != MagickFalse)
4971 {
4972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4973 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004974
cristy3ed852e2009-09-05 21:47:34 +00004975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004976 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004977
cristy3ed852e2009-09-05 21:47:34 +00004978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004979 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004980 }
glennrp0fe50b42010-11-16 03:52:51 +00004981
cristy3ed852e2009-09-05 21:47:34 +00004982 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4983 continue;
4984 }
4985 if (memcmp(type,mng_DEFI,4) == 0)
4986 {
4987 if (mng_type == 3)
cristyc82a27b2011-10-21 01:07:16 +00004988 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004989 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4990 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004991
cristy3ed852e2009-09-05 21:47:34 +00004992 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004993
cristy3ed852e2009-09-05 21:47:34 +00004994 if (mng_type == 2 && object_id != 0)
cristyc82a27b2011-10-21 01:07:16 +00004995 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004996 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4997 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004998
cristy3ed852e2009-09-05 21:47:34 +00004999 if (object_id > MNG_MAX_OBJECTS)
5000 {
5001 /*
5002 Instead ofsuing a warning we should allocate a larger
5003 MngInfo structure and continue.
5004 */
cristyc82a27b2011-10-21 01:07:16 +00005005 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005006 CoderError,"object id too large","`%s'",image->filename);
5007 object_id=MNG_MAX_OBJECTS;
5008 }
glennrp0fe50b42010-11-16 03:52:51 +00005009
cristy3ed852e2009-09-05 21:47:34 +00005010 if (mng_info->exists[object_id])
5011 if (mng_info->frozen[object_id])
5012 {
5013 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
cristyc82a27b2011-10-21 01:07:16 +00005014 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005015 GetMagickModule(),CoderError,
5016 "DEFI cannot redefine a frozen MNG object","`%s'",
5017 image->filename);
5018 continue;
5019 }
glennrp0fe50b42010-11-16 03:52:51 +00005020
cristy3ed852e2009-09-05 21:47:34 +00005021 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005022
cristy3ed852e2009-09-05 21:47:34 +00005023 if (length > 2)
5024 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00005025
cristy3ed852e2009-09-05 21:47:34 +00005026 /*
5027 Extract object offset info.
5028 */
5029 if (length > 11)
5030 {
glennrp0fe50b42010-11-16 03:52:51 +00005031 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5032 (p[5] << 16) | (p[6] << 8) | p[7]);
5033
5034 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5035 (p[9] << 16) | (p[10] << 8) | p[11]);
5036
cristy3ed852e2009-09-05 21:47:34 +00005037 if (logging != MagickFalse)
5038 {
5039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005040 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005041 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005042
cristy3ed852e2009-09-05 21:47:34 +00005043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005044 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005045 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005046 }
5047 }
glennrp0fe50b42010-11-16 03:52:51 +00005048
cristy3ed852e2009-09-05 21:47:34 +00005049 /*
5050 Extract object clipping info.
5051 */
5052 if (length > 27)
5053 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5054 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005055
cristy3ed852e2009-09-05 21:47:34 +00005056 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5057 continue;
5058 }
5059 if (memcmp(type,mng_bKGD,4) == 0)
5060 {
5061 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005062
cristy3ed852e2009-09-05 21:47:34 +00005063 if (length > 5)
5064 {
5065 mng_info->mng_global_bkgd.red=
5066 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005067
cristy3ed852e2009-09-05 21:47:34 +00005068 mng_info->mng_global_bkgd.green=
5069 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005070
cristy3ed852e2009-09-05 21:47:34 +00005071 mng_info->mng_global_bkgd.blue=
5072 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005073
cristy3ed852e2009-09-05 21:47:34 +00005074 mng_info->have_global_bkgd=MagickTrue;
5075 }
glennrp0fe50b42010-11-16 03:52:51 +00005076
cristy3ed852e2009-09-05 21:47:34 +00005077 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5078 continue;
5079 }
5080 if (memcmp(type,mng_BACK,4) == 0)
5081 {
5082#if defined(MNG_INSERT_LAYERS)
5083 if (length > 6)
5084 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005085
cristy3ed852e2009-09-05 21:47:34 +00005086 else
5087 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005088
cristy3ed852e2009-09-05 21:47:34 +00005089 if (mandatory_back && length > 5)
5090 {
5091 mng_background_color.red=
5092 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005093
cristy3ed852e2009-09-05 21:47:34 +00005094 mng_background_color.green=
5095 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005096
cristy3ed852e2009-09-05 21:47:34 +00005097 mng_background_color.blue=
5098 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005099
cristy4c08aed2011-07-01 19:47:50 +00005100 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005101 }
glennrp0fe50b42010-11-16 03:52:51 +00005102
cristy3ed852e2009-09-05 21:47:34 +00005103#ifdef MNG_OBJECT_BUFFERS
5104 if (length > 8)
5105 mng_background_object=(p[7] << 8) | p[8];
5106#endif
5107#endif
5108 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5109 continue;
5110 }
glennrp47b9dd52010-11-24 18:12:06 +00005111
cristy3ed852e2009-09-05 21:47:34 +00005112 if (memcmp(type,mng_PLTE,4) == 0)
5113 {
glennrp47b9dd52010-11-24 18:12:06 +00005114 /* Read global PLTE. */
5115
cristy3ed852e2009-09-05 21:47:34 +00005116 if (length && (length < 769))
5117 {
5118 if (mng_info->global_plte == (png_colorp) NULL)
5119 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5120 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005121
cristybb503372010-05-27 20:51:26 +00005122 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005123 {
5124 mng_info->global_plte[i].red=p[3*i];
5125 mng_info->global_plte[i].green=p[3*i+1];
5126 mng_info->global_plte[i].blue=p[3*i+2];
5127 }
glennrp0fe50b42010-11-16 03:52:51 +00005128
cristy35ef8242010-06-03 16:24:13 +00005129 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005130 }
5131#ifdef MNG_LOOSE
5132 for ( ; i < 256; i++)
5133 {
5134 mng_info->global_plte[i].red=i;
5135 mng_info->global_plte[i].green=i;
5136 mng_info->global_plte[i].blue=i;
5137 }
glennrp0fe50b42010-11-16 03:52:51 +00005138
cristy3ed852e2009-09-05 21:47:34 +00005139 if (length)
5140 mng_info->global_plte_length=256;
5141#endif
5142 else
5143 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005144
cristy3ed852e2009-09-05 21:47:34 +00005145 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5146 continue;
5147 }
glennrp47b9dd52010-11-24 18:12:06 +00005148
cristy3ed852e2009-09-05 21:47:34 +00005149 if (memcmp(type,mng_tRNS,4) == 0)
5150 {
5151 /* read global tRNS */
5152
5153 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005154 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005155 mng_info->global_trns[i]=p[i];
5156
5157#ifdef MNG_LOOSE
5158 for ( ; i < 256; i++)
5159 mng_info->global_trns[i]=255;
5160#endif
cristy12560f32010-06-03 16:51:08 +00005161 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005162 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5163 continue;
5164 }
5165 if (memcmp(type,mng_gAMA,4) == 0)
5166 {
5167 if (length == 4)
5168 {
cristybb503372010-05-27 20:51:26 +00005169 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005170 igamma;
5171
cristy8182b072010-05-30 20:10:53 +00005172 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005173 mng_info->global_gamma=((float) igamma)*0.00001;
5174 mng_info->have_global_gama=MagickTrue;
5175 }
glennrp0fe50b42010-11-16 03:52:51 +00005176
cristy3ed852e2009-09-05 21:47:34 +00005177 else
5178 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005179
cristy3ed852e2009-09-05 21:47:34 +00005180 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5181 continue;
5182 }
5183
5184 if (memcmp(type,mng_cHRM,4) == 0)
5185 {
glennrp47b9dd52010-11-24 18:12:06 +00005186 /* Read global cHRM */
5187
cristy3ed852e2009-09-05 21:47:34 +00005188 if (length == 32)
5189 {
cristy8182b072010-05-30 20:10:53 +00005190 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5191 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5192 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005193 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005194 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005195 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005196 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005197 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005198 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005199 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005200 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005201 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005202 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005203 mng_info->have_global_chrm=MagickTrue;
5204 }
5205 else
5206 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005207
cristy3ed852e2009-09-05 21:47:34 +00005208 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5209 continue;
5210 }
glennrp47b9dd52010-11-24 18:12:06 +00005211
cristy3ed852e2009-09-05 21:47:34 +00005212 if (memcmp(type,mng_sRGB,4) == 0)
5213 {
5214 /*
5215 Read global sRGB.
5216 */
5217 if (length)
5218 {
glennrpe610a072010-08-05 17:08:46 +00005219 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005220 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005221 mng_info->have_global_srgb=MagickTrue;
5222 }
5223 else
5224 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005225
cristy3ed852e2009-09-05 21:47:34 +00005226 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5227 continue;
5228 }
glennrp47b9dd52010-11-24 18:12:06 +00005229
cristy3ed852e2009-09-05 21:47:34 +00005230 if (memcmp(type,mng_iCCP,4) == 0)
5231 {
glennrpfd05d622011-02-25 04:10:33 +00005232 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005233
5234 /*
5235 Read global iCCP.
5236 */
5237 if (length)
5238 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005239
cristy3ed852e2009-09-05 21:47:34 +00005240 continue;
5241 }
glennrp47b9dd52010-11-24 18:12:06 +00005242
cristy3ed852e2009-09-05 21:47:34 +00005243 if (memcmp(type,mng_FRAM,4) == 0)
5244 {
5245 if (mng_type == 3)
cristyc82a27b2011-10-21 01:07:16 +00005246 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005247 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5248 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005249
cristy3ed852e2009-09-05 21:47:34 +00005250 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5251 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005252
cristy3ed852e2009-09-05 21:47:34 +00005253 frame_delay=default_frame_delay;
5254 frame_timeout=default_frame_timeout;
5255 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005256
cristy3ed852e2009-09-05 21:47:34 +00005257 if (length)
5258 if (p[0])
5259 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005260
cristy3ed852e2009-09-05 21:47:34 +00005261 if (logging != MagickFalse)
5262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5263 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005264
cristy3ed852e2009-09-05 21:47:34 +00005265 if (length > 6)
5266 {
glennrp47b9dd52010-11-24 18:12:06 +00005267 /* Note the delay and frame clipping boundaries. */
5268
cristy3ed852e2009-09-05 21:47:34 +00005269 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005270
cristybb503372010-05-27 20:51:26 +00005271 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005272 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005273
cristy3ed852e2009-09-05 21:47:34 +00005274 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005275
cristybb503372010-05-27 20:51:26 +00005276 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005277 {
5278 int
5279 change_delay,
5280 change_timeout,
5281 change_clipping;
5282
5283 change_delay=(*p++);
5284 change_timeout=(*p++);
5285 change_clipping=(*p++);
5286 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005287
cristy3ed852e2009-09-05 21:47:34 +00005288 if (change_delay)
5289 {
cristy8182b072010-05-30 20:10:53 +00005290 frame_delay=1UL*image->ticks_per_second*
5291 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005292
cristy8182b072010-05-30 20:10:53 +00005293 if (mng_info->ticks_per_second != 0)
5294 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005295
glennrpbb010dd2010-06-01 13:07:15 +00005296 else
5297 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005298
cristy3ed852e2009-09-05 21:47:34 +00005299 if (change_delay == 2)
5300 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005301
cristy3ed852e2009-09-05 21:47:34 +00005302 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005303
cristy3ed852e2009-09-05 21:47:34 +00005304 if (logging != MagickFalse)
5305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005306 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005307 }
glennrp47b9dd52010-11-24 18:12:06 +00005308
cristy3ed852e2009-09-05 21:47:34 +00005309 if (change_timeout)
5310 {
glennrpbb010dd2010-06-01 13:07:15 +00005311 frame_timeout=1UL*image->ticks_per_second*
5312 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005313
glennrpbb010dd2010-06-01 13:07:15 +00005314 if (mng_info->ticks_per_second != 0)
5315 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005316
glennrpbb010dd2010-06-01 13:07:15 +00005317 else
5318 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005319
cristy3ed852e2009-09-05 21:47:34 +00005320 if (change_delay == 2)
5321 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005322
cristy3ed852e2009-09-05 21:47:34 +00005323 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005324
cristy3ed852e2009-09-05 21:47:34 +00005325 if (logging != MagickFalse)
5326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005327 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005328 }
glennrp47b9dd52010-11-24 18:12:06 +00005329
cristy3ed852e2009-09-05 21:47:34 +00005330 if (change_clipping)
5331 {
5332 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5333 p+=17;
5334 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005335
cristy3ed852e2009-09-05 21:47:34 +00005336 if (logging != MagickFalse)
5337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005338 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005339 (double) fb.left,(double) fb.right,(double) fb.top,
5340 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005341
cristy3ed852e2009-09-05 21:47:34 +00005342 if (change_clipping == 2)
5343 default_fb=fb;
5344 }
5345 }
5346 }
5347 mng_info->clip=fb;
5348 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005349
cristybb503372010-05-27 20:51:26 +00005350 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005351 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005352
cristybb503372010-05-27 20:51:26 +00005353 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005354 -mng_info->clip.top);
5355 /*
5356 Insert a background layer behind the frame if framing_mode is 4.
5357 */
5358#if defined(MNG_INSERT_LAYERS)
5359 if (logging != MagickFalse)
5360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005361 " subframe_width=%.20g, subframe_height=%.20g",(double)
5362 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005363
cristy3ed852e2009-09-05 21:47:34 +00005364 if (insert_layers && (mng_info->framing_mode == 4) &&
5365 (subframe_width) && (subframe_height))
5366 {
glennrp47b9dd52010-11-24 18:12:06 +00005367 /* Allocate next image structure. */
cristy4c08aed2011-07-01 19:47:50 +00005368 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005369 {
cristy9950d572011-10-01 18:22:35 +00005370 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005371
cristy3ed852e2009-09-05 21:47:34 +00005372 if (GetNextImageInList(image) == (Image *) NULL)
5373 {
5374 image=DestroyImageList(image);
5375 MngInfoFreeStruct(mng_info,&have_mng_structure);
5376 return((Image *) NULL);
5377 }
glennrp47b9dd52010-11-24 18:12:06 +00005378
cristy3ed852e2009-09-05 21:47:34 +00005379 image=SyncNextImageInList(image);
5380 }
glennrp0fe50b42010-11-16 03:52:51 +00005381
cristy3ed852e2009-09-05 21:47:34 +00005382 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005383
cristy3ed852e2009-09-05 21:47:34 +00005384 if (term_chunk_found)
5385 {
5386 image->start_loop=MagickTrue;
5387 image->iterations=mng_iterations;
5388 term_chunk_found=MagickFalse;
5389 }
glennrp0fe50b42010-11-16 03:52:51 +00005390
cristy3ed852e2009-09-05 21:47:34 +00005391 else
5392 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005393
cristy3ed852e2009-09-05 21:47:34 +00005394 image->columns=subframe_width;
5395 image->rows=subframe_height;
5396 image->page.width=subframe_width;
5397 image->page.height=subframe_height;
5398 image->page.x=mng_info->clip.left;
5399 image->page.y=mng_info->clip.top;
5400 image->background_color=mng_background_color;
5401 image->matte=MagickFalse;
5402 image->delay=0;
cristyea1a8aa2011-10-20 13:24:06 +00005403 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005404
cristy3ed852e2009-09-05 21:47:34 +00005405 if (logging != MagickFalse)
5406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005407 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005408 (double) mng_info->clip.left,(double) mng_info->clip.right,
5409 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005410 }
5411#endif
5412 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5413 continue;
5414 }
5415 if (memcmp(type,mng_CLIP,4) == 0)
5416 {
5417 unsigned int
5418 first_object,
5419 last_object;
5420
5421 /*
5422 Read CLIP.
5423 */
5424 first_object=(p[0] << 8) | p[1];
5425 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005426
cristy3ed852e2009-09-05 21:47:34 +00005427 for (i=(int) first_object; i <= (int) last_object; i++)
5428 {
5429 if (mng_info->exists[i] && !mng_info->frozen[i])
5430 {
5431 MngBox
5432 box;
5433
5434 box=mng_info->object_clip[i];
5435 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5436 }
5437 }
glennrp47b9dd52010-11-24 18:12:06 +00005438
cristy3ed852e2009-09-05 21:47:34 +00005439 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5440 continue;
5441 }
5442 if (memcmp(type,mng_SAVE,4) == 0)
5443 {
5444 for (i=1; i < MNG_MAX_OBJECTS; i++)
5445 if (mng_info->exists[i])
5446 {
5447 mng_info->frozen[i]=MagickTrue;
5448#ifdef MNG_OBJECT_BUFFERS
5449 if (mng_info->ob[i] != (MngBuffer *) NULL)
5450 mng_info->ob[i]->frozen=MagickTrue;
5451#endif
5452 }
glennrp0fe50b42010-11-16 03:52:51 +00005453
cristy3ed852e2009-09-05 21:47:34 +00005454 if (length)
5455 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005456
cristy3ed852e2009-09-05 21:47:34 +00005457 continue;
5458 }
5459
5460 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5461 {
glennrp47b9dd52010-11-24 18:12:06 +00005462 /* Read DISC or SEEK. */
5463
cristy3ed852e2009-09-05 21:47:34 +00005464 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5465 {
5466 for (i=1; i < MNG_MAX_OBJECTS; i++)
5467 MngInfoDiscardObject(mng_info,i);
5468 }
glennrp0fe50b42010-11-16 03:52:51 +00005469
cristy3ed852e2009-09-05 21:47:34 +00005470 else
5471 {
cristybb503372010-05-27 20:51:26 +00005472 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005473 j;
5474
cristybb503372010-05-27 20:51:26 +00005475 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005476 {
5477 i=p[j] << 8 | p[j+1];
5478 MngInfoDiscardObject(mng_info,i);
5479 }
5480 }
glennrp0fe50b42010-11-16 03:52:51 +00005481
cristy3ed852e2009-09-05 21:47:34 +00005482 if (length)
5483 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005484
cristy3ed852e2009-09-05 21:47:34 +00005485 continue;
5486 }
glennrp47b9dd52010-11-24 18:12:06 +00005487
cristy3ed852e2009-09-05 21:47:34 +00005488 if (memcmp(type,mng_MOVE,4) == 0)
5489 {
cristybb503372010-05-27 20:51:26 +00005490 size_t
cristy3ed852e2009-09-05 21:47:34 +00005491 first_object,
5492 last_object;
5493
glennrp47b9dd52010-11-24 18:12:06 +00005494 /* read MOVE */
5495
cristy3ed852e2009-09-05 21:47:34 +00005496 first_object=(p[0] << 8) | p[1];
5497 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005498 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005499 {
5500 if (mng_info->exists[i] && !mng_info->frozen[i])
5501 {
5502 MngPair
5503 new_pair;
5504
5505 MngPair
5506 old_pair;
5507
5508 old_pair.a=mng_info->x_off[i];
5509 old_pair.b=mng_info->y_off[i];
5510 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5511 mng_info->x_off[i]=new_pair.a;
5512 mng_info->y_off[i]=new_pair.b;
5513 }
5514 }
glennrp47b9dd52010-11-24 18:12:06 +00005515
cristy3ed852e2009-09-05 21:47:34 +00005516 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5517 continue;
5518 }
5519
5520 if (memcmp(type,mng_LOOP,4) == 0)
5521 {
cristybb503372010-05-27 20:51:26 +00005522 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005523 loop_level=chunk[0];
5524 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005525
5526 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005527 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005528
cristy3ed852e2009-09-05 21:47:34 +00005529 if (logging != MagickFalse)
5530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005531 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5532 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005533
cristy3ed852e2009-09-05 21:47:34 +00005534 if (loop_iters == 0)
5535 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005536
cristy3ed852e2009-09-05 21:47:34 +00005537 else
5538 {
5539 mng_info->loop_jump[loop_level]=TellBlob(image);
5540 mng_info->loop_count[loop_level]=loop_iters;
5541 }
glennrp0fe50b42010-11-16 03:52:51 +00005542
cristy3ed852e2009-09-05 21:47:34 +00005543 mng_info->loop_iteration[loop_level]=0;
5544 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5545 continue;
5546 }
glennrp47b9dd52010-11-24 18:12:06 +00005547
cristy3ed852e2009-09-05 21:47:34 +00005548 if (memcmp(type,mng_ENDL,4) == 0)
5549 {
5550 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005551
cristy3ed852e2009-09-05 21:47:34 +00005552 if (skipping_loop > 0)
5553 {
5554 if (skipping_loop == loop_level)
5555 {
5556 /*
5557 Found end of zero-iteration loop.
5558 */
5559 skipping_loop=(-1);
5560 mng_info->loop_active[loop_level]=0;
5561 }
5562 }
glennrp47b9dd52010-11-24 18:12:06 +00005563
cristy3ed852e2009-09-05 21:47:34 +00005564 else
5565 {
5566 if (mng_info->loop_active[loop_level] == 1)
5567 {
5568 mng_info->loop_count[loop_level]--;
5569 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005570
cristy3ed852e2009-09-05 21:47:34 +00005571 if (logging != MagickFalse)
5572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005573 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005574 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005575 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005576
cristy3ed852e2009-09-05 21:47:34 +00005577 if (mng_info->loop_count[loop_level] != 0)
5578 {
5579 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5580 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005581
cristy3ed852e2009-09-05 21:47:34 +00005582 if (offset < 0)
5583 ThrowReaderException(CorruptImageError,
5584 "ImproperImageHeader");
5585 }
glennrp47b9dd52010-11-24 18:12:06 +00005586
cristy3ed852e2009-09-05 21:47:34 +00005587 else
5588 {
5589 short
5590 last_level;
5591
5592 /*
5593 Finished loop.
5594 */
5595 mng_info->loop_active[loop_level]=0;
5596 last_level=(-1);
5597 for (i=0; i < loop_level; i++)
5598 if (mng_info->loop_active[i] == 1)
5599 last_level=(short) i;
5600 loop_level=last_level;
5601 }
5602 }
5603 }
glennrp47b9dd52010-11-24 18:12:06 +00005604
cristy3ed852e2009-09-05 21:47:34 +00005605 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5606 continue;
5607 }
glennrp47b9dd52010-11-24 18:12:06 +00005608
cristy3ed852e2009-09-05 21:47:34 +00005609 if (memcmp(type,mng_CLON,4) == 0)
5610 {
5611 if (mng_info->clon_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005612 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005613 CoderError,"CLON is not implemented yet","`%s'",
5614 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005615
cristy3ed852e2009-09-05 21:47:34 +00005616 mng_info->clon_warning++;
5617 }
glennrp47b9dd52010-11-24 18:12:06 +00005618
cristy3ed852e2009-09-05 21:47:34 +00005619 if (memcmp(type,mng_MAGN,4) == 0)
5620 {
5621 png_uint_16
5622 magn_first,
5623 magn_last,
5624 magn_mb,
5625 magn_ml,
5626 magn_mr,
5627 magn_mt,
5628 magn_mx,
5629 magn_my,
5630 magn_methx,
5631 magn_methy;
5632
5633 if (length > 1)
5634 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005635
cristy3ed852e2009-09-05 21:47:34 +00005636 else
5637 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005638
cristy3ed852e2009-09-05 21:47:34 +00005639 if (length > 3)
5640 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005641
cristy3ed852e2009-09-05 21:47:34 +00005642 else
5643 magn_last=magn_first;
5644#ifndef MNG_OBJECT_BUFFERS
5645 if (magn_first || magn_last)
5646 if (mng_info->magn_warning == 0)
5647 {
cristyc82a27b2011-10-21 01:07:16 +00005648 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005649 GetMagickModule(),CoderError,
5650 "MAGN is not implemented yet for nonzero objects",
5651 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005652
cristy3ed852e2009-09-05 21:47:34 +00005653 mng_info->magn_warning++;
5654 }
5655#endif
5656 if (length > 4)
5657 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005658
cristy3ed852e2009-09-05 21:47:34 +00005659 else
5660 magn_methx=0;
5661
5662 if (length > 6)
5663 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005664
cristy3ed852e2009-09-05 21:47:34 +00005665 else
5666 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005667
cristy3ed852e2009-09-05 21:47:34 +00005668 if (magn_mx == 0)
5669 magn_mx=1;
5670
5671 if (length > 8)
5672 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005673
cristy3ed852e2009-09-05 21:47:34 +00005674 else
5675 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005676
cristy3ed852e2009-09-05 21:47:34 +00005677 if (magn_my == 0)
5678 magn_my=1;
5679
5680 if (length > 10)
5681 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005682
cristy3ed852e2009-09-05 21:47:34 +00005683 else
5684 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005685
cristy3ed852e2009-09-05 21:47:34 +00005686 if (magn_ml == 0)
5687 magn_ml=1;
5688
5689 if (length > 12)
5690 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005691
cristy3ed852e2009-09-05 21:47:34 +00005692 else
5693 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005694
cristy3ed852e2009-09-05 21:47:34 +00005695 if (magn_mr == 0)
5696 magn_mr=1;
5697
5698 if (length > 14)
5699 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005700
cristy3ed852e2009-09-05 21:47:34 +00005701 else
5702 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005703
cristy3ed852e2009-09-05 21:47:34 +00005704 if (magn_mt == 0)
5705 magn_mt=1;
5706
5707 if (length > 16)
5708 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005709
cristy3ed852e2009-09-05 21:47:34 +00005710 else
5711 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005712
cristy3ed852e2009-09-05 21:47:34 +00005713 if (magn_mb == 0)
5714 magn_mb=1;
5715
5716 if (length > 17)
5717 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005718
cristy3ed852e2009-09-05 21:47:34 +00005719 else
5720 magn_methy=magn_methx;
5721
glennrp47b9dd52010-11-24 18:12:06 +00005722
cristy3ed852e2009-09-05 21:47:34 +00005723 if (magn_methx > 5 || magn_methy > 5)
5724 if (mng_info->magn_warning == 0)
5725 {
cristyc82a27b2011-10-21 01:07:16 +00005726 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005727 GetMagickModule(),CoderError,
5728 "Unknown MAGN method in MNG datastream","`%s'",
5729 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005730
cristy3ed852e2009-09-05 21:47:34 +00005731 mng_info->magn_warning++;
5732 }
5733#ifdef MNG_OBJECT_BUFFERS
5734 /* Magnify existing objects in the range magn_first to magn_last */
5735#endif
5736 if (magn_first == 0 || magn_last == 0)
5737 {
5738 /* Save the magnification factors for object 0 */
5739 mng_info->magn_mb=magn_mb;
5740 mng_info->magn_ml=magn_ml;
5741 mng_info->magn_mr=magn_mr;
5742 mng_info->magn_mt=magn_mt;
5743 mng_info->magn_mx=magn_mx;
5744 mng_info->magn_my=magn_my;
5745 mng_info->magn_methx=magn_methx;
5746 mng_info->magn_methy=magn_methy;
5747 }
5748 }
glennrp47b9dd52010-11-24 18:12:06 +00005749
cristy3ed852e2009-09-05 21:47:34 +00005750 if (memcmp(type,mng_PAST,4) == 0)
5751 {
5752 if (mng_info->past_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005753 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005754 CoderError,"PAST is not implemented yet","`%s'",
5755 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005756
cristy3ed852e2009-09-05 21:47:34 +00005757 mng_info->past_warning++;
5758 }
glennrp47b9dd52010-11-24 18:12:06 +00005759
cristy3ed852e2009-09-05 21:47:34 +00005760 if (memcmp(type,mng_SHOW,4) == 0)
5761 {
5762 if (mng_info->show_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005763 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005764 CoderError,"SHOW is not implemented yet","`%s'",
5765 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005766
cristy3ed852e2009-09-05 21:47:34 +00005767 mng_info->show_warning++;
5768 }
glennrp47b9dd52010-11-24 18:12:06 +00005769
cristy3ed852e2009-09-05 21:47:34 +00005770 if (memcmp(type,mng_sBIT,4) == 0)
5771 {
5772 if (length < 4)
5773 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005774
cristy3ed852e2009-09-05 21:47:34 +00005775 else
5776 {
5777 mng_info->global_sbit.gray=p[0];
5778 mng_info->global_sbit.red=p[0];
5779 mng_info->global_sbit.green=p[1];
5780 mng_info->global_sbit.blue=p[2];
5781 mng_info->global_sbit.alpha=p[3];
5782 mng_info->have_global_sbit=MagickTrue;
5783 }
5784 }
5785 if (memcmp(type,mng_pHYs,4) == 0)
5786 {
5787 if (length > 8)
5788 {
5789 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005790 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005791 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005792 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005793 mng_info->global_phys_unit_type=p[8];
5794 mng_info->have_global_phys=MagickTrue;
5795 }
glennrp47b9dd52010-11-24 18:12:06 +00005796
cristy3ed852e2009-09-05 21:47:34 +00005797 else
5798 mng_info->have_global_phys=MagickFalse;
5799 }
5800 if (memcmp(type,mng_pHYg,4) == 0)
5801 {
5802 if (mng_info->phyg_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005803 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005804 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005805
cristy3ed852e2009-09-05 21:47:34 +00005806 mng_info->phyg_warning++;
5807 }
5808 if (memcmp(type,mng_BASI,4) == 0)
5809 {
5810 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005811
cristy3ed852e2009-09-05 21:47:34 +00005812 if (mng_info->basi_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005813 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005814 CoderError,"BASI is not implemented yet","`%s'",
5815 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005816
cristy3ed852e2009-09-05 21:47:34 +00005817 mng_info->basi_warning++;
5818#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005819 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005820 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005821 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005822 (p[6] << 8) | p[7]);
5823 basi_color_type=p[8];
5824 basi_compression_method=p[9];
5825 basi_filter_type=p[10];
5826 basi_interlace_method=p[11];
5827 if (length > 11)
5828 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005829
cristy3ed852e2009-09-05 21:47:34 +00005830 else
5831 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005832
cristy3ed852e2009-09-05 21:47:34 +00005833 if (length > 13)
5834 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005835
cristy3ed852e2009-09-05 21:47:34 +00005836 else
5837 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005838
cristy3ed852e2009-09-05 21:47:34 +00005839 if (length > 15)
5840 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005841
cristy3ed852e2009-09-05 21:47:34 +00005842 else
5843 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005844
cristy3ed852e2009-09-05 21:47:34 +00005845 if (length > 17)
5846 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005847
cristy3ed852e2009-09-05 21:47:34 +00005848 else
5849 {
5850 if (basi_sample_depth == 16)
5851 basi_alpha=65535L;
5852 else
5853 basi_alpha=255;
5854 }
glennrp47b9dd52010-11-24 18:12:06 +00005855
cristy3ed852e2009-09-05 21:47:34 +00005856 if (length > 19)
5857 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005858
cristy3ed852e2009-09-05 21:47:34 +00005859 else
5860 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005861
cristy3ed852e2009-09-05 21:47:34 +00005862#endif
5863 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5864 continue;
5865 }
glennrp47b9dd52010-11-24 18:12:06 +00005866
cristy3ed852e2009-09-05 21:47:34 +00005867 if (memcmp(type,mng_IHDR,4)
5868#if defined(JNG_SUPPORTED)
5869 && memcmp(type,mng_JHDR,4)
5870#endif
5871 )
5872 {
5873 /* Not an IHDR or JHDR chunk */
5874 if (length)
5875 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005876
cristy3ed852e2009-09-05 21:47:34 +00005877 continue;
5878 }
5879/* Process IHDR */
5880 if (logging != MagickFalse)
5881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5882 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005883
cristy3ed852e2009-09-05 21:47:34 +00005884 mng_info->exists[object_id]=MagickTrue;
5885 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005886
cristy3ed852e2009-09-05 21:47:34 +00005887 if (mng_info->invisible[object_id])
5888 {
5889 if (logging != MagickFalse)
5890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5891 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005892
cristy3ed852e2009-09-05 21:47:34 +00005893 skip_to_iend=MagickTrue;
5894 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5895 continue;
5896 }
5897#if defined(MNG_INSERT_LAYERS)
5898 if (length < 8)
5899 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005900
cristy8182b072010-05-30 20:10:53 +00005901 image_width=(size_t) mng_get_long(p);
5902 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005903#endif
5904 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5905
5906 /*
5907 Insert a transparent background layer behind the entire animation
5908 if it is not full screen.
5909 */
5910#if defined(MNG_INSERT_LAYERS)
5911 if (insert_layers && mng_type && first_mng_object)
5912 {
5913 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5914 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005915 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005916 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005917 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005918 {
cristy4c08aed2011-07-01 19:47:50 +00005919 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005920 {
5921 /*
5922 Allocate next image structure.
5923 */
cristy9950d572011-10-01 18:22:35 +00005924 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005925
cristy3ed852e2009-09-05 21:47:34 +00005926 if (GetNextImageInList(image) == (Image *) NULL)
5927 {
5928 image=DestroyImageList(image);
5929 MngInfoFreeStruct(mng_info,&have_mng_structure);
5930 return((Image *) NULL);
5931 }
glennrp47b9dd52010-11-24 18:12:06 +00005932
cristy3ed852e2009-09-05 21:47:34 +00005933 image=SyncNextImageInList(image);
5934 }
5935 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005936
cristy3ed852e2009-09-05 21:47:34 +00005937 if (term_chunk_found)
5938 {
5939 image->start_loop=MagickTrue;
5940 image->iterations=mng_iterations;
5941 term_chunk_found=MagickFalse;
5942 }
glennrp47b9dd52010-11-24 18:12:06 +00005943
cristy3ed852e2009-09-05 21:47:34 +00005944 else
5945 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005946
5947 /* Make a background rectangle. */
5948
cristy3ed852e2009-09-05 21:47:34 +00005949 image->delay=0;
5950 image->columns=mng_info->mng_width;
5951 image->rows=mng_info->mng_height;
5952 image->page.width=mng_info->mng_width;
5953 image->page.height=mng_info->mng_height;
5954 image->page.x=0;
5955 image->page.y=0;
5956 image->background_color=mng_background_color;
cristyea1a8aa2011-10-20 13:24:06 +00005957 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005958 if (logging != MagickFalse)
5959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005960 " Inserted transparent background layer, W=%.20g, H=%.20g",
5961 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005962 }
5963 }
5964 /*
5965 Insert a background layer behind the upcoming image if
5966 framing_mode is 3, and we haven't already inserted one.
5967 */
5968 if (insert_layers && (mng_info->framing_mode == 3) &&
5969 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5970 (simplicity & 0x08)))
5971 {
cristy4c08aed2011-07-01 19:47:50 +00005972 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005973 {
5974 /*
5975 Allocate next image structure.
5976 */
cristy9950d572011-10-01 18:22:35 +00005977 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005978
cristy3ed852e2009-09-05 21:47:34 +00005979 if (GetNextImageInList(image) == (Image *) NULL)
5980 {
5981 image=DestroyImageList(image);
5982 MngInfoFreeStruct(mng_info,&have_mng_structure);
5983 return((Image *) NULL);
5984 }
glennrp47b9dd52010-11-24 18:12:06 +00005985
cristy3ed852e2009-09-05 21:47:34 +00005986 image=SyncNextImageInList(image);
5987 }
glennrp0fe50b42010-11-16 03:52:51 +00005988
cristy3ed852e2009-09-05 21:47:34 +00005989 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005990
cristy3ed852e2009-09-05 21:47:34 +00005991 if (term_chunk_found)
5992 {
5993 image->start_loop=MagickTrue;
5994 image->iterations=mng_iterations;
5995 term_chunk_found=MagickFalse;
5996 }
glennrp0fe50b42010-11-16 03:52:51 +00005997
cristy3ed852e2009-09-05 21:47:34 +00005998 else
5999 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006000
cristy3ed852e2009-09-05 21:47:34 +00006001 image->delay=0;
6002 image->columns=subframe_width;
6003 image->rows=subframe_height;
6004 image->page.width=subframe_width;
6005 image->page.height=subframe_height;
6006 image->page.x=mng_info->clip.left;
6007 image->page.y=mng_info->clip.top;
6008 image->background_color=mng_background_color;
6009 image->matte=MagickFalse;
cristyea1a8aa2011-10-20 13:24:06 +00006010 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006011
cristy3ed852e2009-09-05 21:47:34 +00006012 if (logging != MagickFalse)
6013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00006014 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00006015 (double) mng_info->clip.left,(double) mng_info->clip.right,
6016 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00006017 }
6018#endif /* MNG_INSERT_LAYERS */
6019 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006020
cristy4c08aed2011-07-01 19:47:50 +00006021 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006022 {
6023 /*
6024 Allocate next image structure.
6025 */
cristy9950d572011-10-01 18:22:35 +00006026 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006027
cristy3ed852e2009-09-05 21:47:34 +00006028 if (GetNextImageInList(image) == (Image *) NULL)
6029 {
6030 image=DestroyImageList(image);
6031 MngInfoFreeStruct(mng_info,&have_mng_structure);
6032 return((Image *) NULL);
6033 }
glennrp47b9dd52010-11-24 18:12:06 +00006034
cristy3ed852e2009-09-05 21:47:34 +00006035 image=SyncNextImageInList(image);
6036 }
6037 mng_info->image=image;
6038 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6039 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006040
cristy3ed852e2009-09-05 21:47:34 +00006041 if (status == MagickFalse)
6042 break;
glennrp0fe50b42010-11-16 03:52:51 +00006043
cristy3ed852e2009-09-05 21:47:34 +00006044 if (term_chunk_found)
6045 {
6046 image->start_loop=MagickTrue;
6047 term_chunk_found=MagickFalse;
6048 }
glennrp0fe50b42010-11-16 03:52:51 +00006049
cristy3ed852e2009-09-05 21:47:34 +00006050 else
6051 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006052
cristy3ed852e2009-09-05 21:47:34 +00006053 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6054 {
6055 image->delay=frame_delay;
6056 frame_delay=default_frame_delay;
6057 }
glennrp0fe50b42010-11-16 03:52:51 +00006058
cristy3ed852e2009-09-05 21:47:34 +00006059 else
6060 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006061
cristy3ed852e2009-09-05 21:47:34 +00006062 image->page.width=mng_info->mng_width;
6063 image->page.height=mng_info->mng_height;
6064 image->page.x=mng_info->x_off[object_id];
6065 image->page.y=mng_info->y_off[object_id];
6066 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006067
cristy3ed852e2009-09-05 21:47:34 +00006068 /*
6069 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6070 */
glennrp47b9dd52010-11-24 18:12:06 +00006071
cristy3ed852e2009-09-05 21:47:34 +00006072 if (logging != MagickFalse)
6073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6074 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6075 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006076
cristybb503372010-05-27 20:51:26 +00006077 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006078
cristy3ed852e2009-09-05 21:47:34 +00006079 if (offset < 0)
6080 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6081 }
6082
6083 previous=image;
6084 mng_info->image=image;
6085 mng_info->mng_type=mng_type;
6086 mng_info->object_id=object_id;
6087
6088 if (memcmp(type,mng_IHDR,4) == 0)
6089 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006090
cristy3ed852e2009-09-05 21:47:34 +00006091#if defined(JNG_SUPPORTED)
6092 else
6093 image=ReadOneJNGImage(mng_info,image_info,exception);
6094#endif
6095
6096 if (image == (Image *) NULL)
6097 {
6098 if (IsImageObject(previous) != MagickFalse)
6099 {
6100 (void) DestroyImageList(previous);
6101 (void) CloseBlob(previous);
6102 }
glennrp47b9dd52010-11-24 18:12:06 +00006103
cristy3ed852e2009-09-05 21:47:34 +00006104 MngInfoFreeStruct(mng_info,&have_mng_structure);
6105 return((Image *) NULL);
6106 }
glennrp0fe50b42010-11-16 03:52:51 +00006107
cristy3ed852e2009-09-05 21:47:34 +00006108 if (image->columns == 0 || image->rows == 0)
6109 {
6110 (void) CloseBlob(image);
6111 image=DestroyImageList(image);
6112 MngInfoFreeStruct(mng_info,&have_mng_structure);
6113 return((Image *) NULL);
6114 }
glennrp0fe50b42010-11-16 03:52:51 +00006115
cristy3ed852e2009-09-05 21:47:34 +00006116 mng_info->image=image;
6117
6118 if (mng_type)
6119 {
6120 MngBox
6121 crop_box;
6122
6123 if (mng_info->magn_methx || mng_info->magn_methy)
6124 {
6125 png_uint_32
6126 magnified_height,
6127 magnified_width;
6128
6129 if (logging != MagickFalse)
6130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6131 " Processing MNG MAGN chunk");
6132
6133 if (mng_info->magn_methx == 1)
6134 {
6135 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006136
cristy3ed852e2009-09-05 21:47:34 +00006137 if (image->columns > 1)
6138 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006139
cristy3ed852e2009-09-05 21:47:34 +00006140 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006141 magnified_width += (png_uint_32)
6142 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006143 }
glennrp47b9dd52010-11-24 18:12:06 +00006144
cristy3ed852e2009-09-05 21:47:34 +00006145 else
6146 {
cristy4e5bc842010-06-09 13:56:01 +00006147 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006148
cristy3ed852e2009-09-05 21:47:34 +00006149 if (image->columns > 1)
6150 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006151
cristy3ed852e2009-09-05 21:47:34 +00006152 if (image->columns > 2)
6153 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006154
cristy3ed852e2009-09-05 21:47:34 +00006155 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006156 magnified_width += (png_uint_32)
6157 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006158 }
glennrp47b9dd52010-11-24 18:12:06 +00006159
cristy3ed852e2009-09-05 21:47:34 +00006160 if (mng_info->magn_methy == 1)
6161 {
6162 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006163
cristy3ed852e2009-09-05 21:47:34 +00006164 if (image->rows > 1)
6165 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006166
cristy3ed852e2009-09-05 21:47:34 +00006167 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006168 magnified_height += (png_uint_32)
6169 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006170 }
glennrp47b9dd52010-11-24 18:12:06 +00006171
cristy3ed852e2009-09-05 21:47:34 +00006172 else
6173 {
cristy4e5bc842010-06-09 13:56:01 +00006174 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006175
cristy3ed852e2009-09-05 21:47:34 +00006176 if (image->rows > 1)
6177 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006178
cristy3ed852e2009-09-05 21:47:34 +00006179 if (image->rows > 2)
6180 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006181
cristy3ed852e2009-09-05 21:47:34 +00006182 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006183 magnified_height += (png_uint_32)
6184 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006185 }
glennrp47b9dd52010-11-24 18:12:06 +00006186
cristy3ed852e2009-09-05 21:47:34 +00006187 if (magnified_height > image->rows ||
6188 magnified_width > image->columns)
6189 {
6190 Image
6191 *large_image;
6192
6193 int
6194 yy;
6195
cristy4c08aed2011-07-01 19:47:50 +00006196 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006197 *next,
6198 *prev;
6199
6200 png_uint_16
6201 magn_methx,
6202 magn_methy;
6203
cristy4c08aed2011-07-01 19:47:50 +00006204 ssize_t
6205 m,
6206 y;
6207
6208 register Quantum
6209 *n,
6210 *q;
6211
6212 register ssize_t
6213 x;
6214
glennrp47b9dd52010-11-24 18:12:06 +00006215 /* Allocate next image structure. */
6216
cristy3ed852e2009-09-05 21:47:34 +00006217 if (logging != MagickFalse)
6218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6219 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006220
cristy9950d572011-10-01 18:22:35 +00006221 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006222
cristy3ed852e2009-09-05 21:47:34 +00006223 if (GetNextImageInList(image) == (Image *) NULL)
6224 {
6225 image=DestroyImageList(image);
6226 MngInfoFreeStruct(mng_info,&have_mng_structure);
6227 return((Image *) NULL);
6228 }
6229
6230 large_image=SyncNextImageInList(image);
6231
6232 large_image->columns=magnified_width;
6233 large_image->rows=magnified_height;
6234
6235 magn_methx=mng_info->magn_methx;
6236 magn_methy=mng_info->magn_methy;
6237
glennrp3faa9a32011-04-23 14:00:25 +00006238#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006239#define QM unsigned short
6240 if (magn_methx != 1 || magn_methy != 1)
6241 {
6242 /*
6243 Scale pixels to unsigned shorts to prevent
6244 overflow of intermediate values of interpolations
6245 */
cristybb503372010-05-27 20:51:26 +00006246 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006247 {
6248 q=GetAuthenticPixels(image,0,y,image->columns,1,
6249 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006250
cristybb503372010-05-27 20:51:26 +00006251 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006252 {
cristy4c08aed2011-07-01 19:47:50 +00006253 SetPixelRed(image,ScaleQuantumToShort(
6254 GetPixelRed(image,q)),q);
6255 SetPixelGreen(image,ScaleQuantumToShort(
6256 GetPixelGreen(image,q)),q);
6257 SetPixelBlue(image,ScaleQuantumToShort(
6258 GetPixelBlue(image,q)),q);
6259 SetPixelAlpha(image,ScaleQuantumToShort(
6260 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006261 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006262 }
glennrp47b9dd52010-11-24 18:12:06 +00006263
cristy3ed852e2009-09-05 21:47:34 +00006264 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6265 break;
6266 }
6267 }
6268#else
6269#define QM Quantum
6270#endif
6271
6272 if (image->matte != MagickFalse)
cristyea1a8aa2011-10-20 13:24:06 +00006273 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006274
cristy3ed852e2009-09-05 21:47:34 +00006275 else
6276 {
cristy4c08aed2011-07-01 19:47:50 +00006277 large_image->background_color.alpha=OpaqueAlpha;
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 if (magn_methx == 4)
6281 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006282
cristy3ed852e2009-09-05 21:47:34 +00006283 if (magn_methx == 5)
6284 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006285
cristy3ed852e2009-09-05 21:47:34 +00006286 if (magn_methy == 4)
6287 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006288
cristy3ed852e2009-09-05 21:47:34 +00006289 if (magn_methy == 5)
6290 magn_methy=3;
6291 }
6292
6293 /* magnify the rows into the right side of the large image */
6294
6295 if (logging != MagickFalse)
6296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006297 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006298 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006299 yy=0;
cristy8a20fa02011-12-27 15:54:31 +00006300 length=(size_t) image->columns*GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00006301 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6302 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006303
cristy4c08aed2011-07-01 19:47:50 +00006304 if ((prev == (Quantum *) NULL) ||
6305 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006306 {
6307 image=DestroyImageList(image);
6308 MngInfoFreeStruct(mng_info,&have_mng_structure);
6309 ThrowReaderException(ResourceLimitError,
6310 "MemoryAllocationFailed");
6311 }
glennrp47b9dd52010-11-24 18:12:06 +00006312
cristy3ed852e2009-09-05 21:47:34 +00006313 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6314 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006315
cristybb503372010-05-27 20:51:26 +00006316 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006317 {
6318 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006319 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006320
cristybb503372010-05-27 20:51:26 +00006321 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6322 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006323
cristybb503372010-05-27 20:51:26 +00006324 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6325 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006326
cristybb503372010-05-27 20:51:26 +00006327 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006328 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006329
cristy3ed852e2009-09-05 21:47:34 +00006330 else
cristybb503372010-05-27 20:51:26 +00006331 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006332
cristy3ed852e2009-09-05 21:47:34 +00006333 n=prev;
6334 prev=next;
6335 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006336
cristybb503372010-05-27 20:51:26 +00006337 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006338 {
6339 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6340 exception);
6341 (void) CopyMagickMemory(next,n,length);
6342 }
glennrp47b9dd52010-11-24 18:12:06 +00006343
cristy3ed852e2009-09-05 21:47:34 +00006344 for (i=0; i < m; i++, yy++)
6345 {
cristy4c08aed2011-07-01 19:47:50 +00006346 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006347 *pixels;
6348
cristybb503372010-05-27 20:51:26 +00006349 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006350 pixels=prev;
6351 n=next;
6352 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006353 1,exception);
cristy3ed852e2009-09-05 21:47:34 +00006354 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00006355
cristybb503372010-05-27 20:51:26 +00006356 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006357 {
glennrpfd05d622011-02-25 04:10:33 +00006358 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006359 /*
6360 if (image->storage_class == PseudoClass)
6361 {
6362 }
6363 */
6364
6365 if (magn_methy <= 1)
6366 {
glennrpbb4f99d2011-05-22 11:13:17 +00006367 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006368 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
glennrp847370c2011-07-05 17:37:15 +00006369 SetPixelGreen(large_image,GetPixelGreen(image,
6370 pixels),q);
6371 SetPixelBlue(large_image,GetPixelBlue(image,
6372 pixels),q);
6373 SetPixelAlpha(large_image,GetPixelAlpha(image,
6374 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006375 }
glennrp47b9dd52010-11-24 18:12:06 +00006376
cristy3ed852e2009-09-05 21:47:34 +00006377 else if (magn_methy == 2 || magn_methy == 4)
6378 {
6379 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006380 {
glennrp847370c2011-07-05 17:37:15 +00006381 SetPixelRed(large_image,GetPixelRed(image,
6382 pixels),q);
6383 SetPixelGreen(large_image,GetPixelGreen(image,
6384 pixels),q);
6385 SetPixelBlue(large_image,GetPixelBlue(image,
6386 pixels),q);
6387 SetPixelAlpha(large_image,GetPixelAlpha(image,
6388 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006389 }
glennrp47b9dd52010-11-24 18:12:06 +00006390
cristy3ed852e2009-09-05 21:47:34 +00006391 else
6392 {
6393 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006394 SetPixelRed(large_image,((QM) (((ssize_t)
6395 (2*i*(GetPixelRed(image,n)
6396 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006397 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006398 +GetPixelRed(image,pixels)))),q);
6399 SetPixelGreen(large_image,((QM) (((ssize_t)
6400 (2*i*(GetPixelGreen(image,n)
6401 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006402 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006403 +GetPixelGreen(image,pixels)))),q);
6404 SetPixelBlue(large_image,((QM) (((ssize_t)
6405 (2*i*(GetPixelBlue(image,n)
6406 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006407 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006408 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006409
cristy3ed852e2009-09-05 21:47:34 +00006410 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006411 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6412 (2*i*(GetPixelAlpha(image,n)
6413 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006414 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006415 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006416 }
glennrp47b9dd52010-11-24 18:12:06 +00006417
cristy3ed852e2009-09-05 21:47:34 +00006418 if (magn_methy == 4)
6419 {
6420 /* Replicate nearest */
6421 if (i <= ((m+1) << 1))
glennrp847370c2011-07-05 17:37:15 +00006422 SetPixelAlpha(large_image,GetPixelAlpha(image,
6423 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006424 else
glennrp847370c2011-07-05 17:37:15 +00006425 SetPixelAlpha(large_image,GetPixelAlpha(image,
6426 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006427 }
6428 }
glennrp47b9dd52010-11-24 18:12:06 +00006429
cristy3ed852e2009-09-05 21:47:34 +00006430 else /* if (magn_methy == 3 || magn_methy == 5) */
6431 {
6432 /* Replicate nearest */
6433 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006434 {
glennrp847370c2011-07-05 17:37:15 +00006435 SetPixelRed(large_image,GetPixelRed(image,
6436 pixels),q);
6437 SetPixelGreen(large_image,GetPixelGreen(image,
6438 pixels),q);
6439 SetPixelBlue(large_image,GetPixelBlue(image,
6440 pixels),q);
6441 SetPixelAlpha(large_image,GetPixelAlpha(image,
6442 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006443 }
glennrp47b9dd52010-11-24 18:12:06 +00006444
cristy3ed852e2009-09-05 21:47:34 +00006445 else
glennrpbb4f99d2011-05-22 11:13:17 +00006446 {
cristy4c08aed2011-07-01 19:47:50 +00006447 SetPixelRed(large_image,GetPixelRed(image,n),q);
glennrp847370c2011-07-05 17:37:15 +00006448 SetPixelGreen(large_image,GetPixelGreen(image,n),
6449 q);
6450 SetPixelBlue(large_image,GetPixelBlue(image,n),
6451 q);
6452 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6453 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006454 }
glennrp47b9dd52010-11-24 18:12:06 +00006455
cristy3ed852e2009-09-05 21:47:34 +00006456 if (magn_methy == 5)
6457 {
cristy4c08aed2011-07-01 19:47:50 +00006458 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6459 (GetPixelAlpha(image,n)
6460 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006461 +m))/((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006462 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006463 }
6464 }
cristyed231572011-07-14 02:18:59 +00006465 n+=GetPixelChannels(image);
6466 q+=GetPixelChannels(large_image);
6467 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006468 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006469
cristy3ed852e2009-09-05 21:47:34 +00006470 if (SyncAuthenticPixels(large_image,exception) == 0)
6471 break;
glennrp47b9dd52010-11-24 18:12:06 +00006472
cristy3ed852e2009-09-05 21:47:34 +00006473 } /* i */
6474 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006475
cristy4c08aed2011-07-01 19:47:50 +00006476 prev=(Quantum *) RelinquishMagickMemory(prev);
6477 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006478
6479 length=image->columns;
6480
6481 if (logging != MagickFalse)
6482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6483 " Delete original image");
6484
6485 DeleteImageFromList(&image);
6486
6487 image=large_image;
6488
6489 mng_info->image=image;
6490
6491 /* magnify the columns */
6492 if (logging != MagickFalse)
6493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006494 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006495
cristybb503372010-05-27 20:51:26 +00006496 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006497 {
cristy4c08aed2011-07-01 19:47:50 +00006498 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006499 *pixels;
6500
6501 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristyed231572011-07-14 02:18:59 +00006502 pixels=q+(image->columns-length)*GetPixelChannels(image);
6503 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006504
cristybb503372010-05-27 20:51:26 +00006505 for (x=(ssize_t) (image->columns-length);
6506 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006507 {
cristyed231572011-07-14 02:18:59 +00006508 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006509
cristybb503372010-05-27 20:51:26 +00006510 if (x == (ssize_t) (image->columns-length))
6511 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006512
cristybb503372010-05-27 20:51:26 +00006513 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6514 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006515
cristybb503372010-05-27 20:51:26 +00006516 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6517 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006518
cristybb503372010-05-27 20:51:26 +00006519 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006520 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006521
cristy3ed852e2009-09-05 21:47:34 +00006522 else
cristybb503372010-05-27 20:51:26 +00006523 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006524
cristy3ed852e2009-09-05 21:47:34 +00006525 for (i=0; i < m; i++)
6526 {
6527 if (magn_methx <= 1)
6528 {
6529 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006530 SetPixelRed(image,GetPixelRed(image,pixels),q);
6531 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6532 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6533 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006534 }
glennrp47b9dd52010-11-24 18:12:06 +00006535
cristy3ed852e2009-09-05 21:47:34 +00006536 else if (magn_methx == 2 || magn_methx == 4)
6537 {
6538 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006539 {
cristy4c08aed2011-07-01 19:47:50 +00006540 SetPixelRed(image,GetPixelRed(image,pixels),q);
6541 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6542 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6543 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006544 }
glennrp47b9dd52010-11-24 18:12:06 +00006545
cristyed231572011-07-14 02:18:59 +00006546 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006547 else
6548 {
6549 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006550 SetPixelRed(image,(QM) ((2*i*(
6551 GetPixelRed(image,n)
6552 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006553 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006554 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006555
cristy4c08aed2011-07-01 19:47:50 +00006556 SetPixelGreen(image,(QM) ((2*i*(
6557 GetPixelGreen(image,n)
6558 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006559 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006560 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006561
cristy4c08aed2011-07-01 19:47:50 +00006562 SetPixelBlue(image,(QM) ((2*i*(
6563 GetPixelBlue(image,n)
6564 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006565 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006566 GetPixelBlue(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006567 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006568 SetPixelAlpha(image,(QM) ((2*i*(
6569 GetPixelAlpha(image,n)
6570 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006571 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006572 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006573 }
glennrp47b9dd52010-11-24 18:12:06 +00006574
cristy3ed852e2009-09-05 21:47:34 +00006575 if (magn_methx == 4)
6576 {
6577 /* Replicate nearest */
6578 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006579 {
cristy4c08aed2011-07-01 19:47:50 +00006580 SetPixelAlpha(image,
6581 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006582 }
cristy3ed852e2009-09-05 21:47:34 +00006583 else
glennrpbb4f99d2011-05-22 11:13:17 +00006584 {
cristy4c08aed2011-07-01 19:47:50 +00006585 SetPixelAlpha(image,
6586 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006587 }
cristy3ed852e2009-09-05 21:47:34 +00006588 }
6589 }
glennrp47b9dd52010-11-24 18:12:06 +00006590
cristy3ed852e2009-09-05 21:47:34 +00006591 else /* if (magn_methx == 3 || magn_methx == 5) */
6592 {
6593 /* Replicate nearest */
6594 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006595 {
cristy4c08aed2011-07-01 19:47:50 +00006596 SetPixelRed(image,GetPixelRed(image,pixels),q);
6597 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6598 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6599 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006600 }
glennrp47b9dd52010-11-24 18:12:06 +00006601
cristy3ed852e2009-09-05 21:47:34 +00006602 else
glennrpbb4f99d2011-05-22 11:13:17 +00006603 {
cristy4c08aed2011-07-01 19:47:50 +00006604 SetPixelRed(image,GetPixelRed(image,n),q);
6605 SetPixelGreen(image,GetPixelGreen(image,n),q);
6606 SetPixelBlue(image,GetPixelBlue(image,n),q);
6607 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006608 }
glennrp47b9dd52010-11-24 18:12:06 +00006609
cristy3ed852e2009-09-05 21:47:34 +00006610 if (magn_methx == 5)
6611 {
6612 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006613 SetPixelAlpha(image,
6614 (QM) ((2*i*( GetPixelAlpha(image,n)
6615 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006616 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006617 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006618 }
6619 }
cristyed231572011-07-14 02:18:59 +00006620 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006621 }
cristyed231572011-07-14 02:18:59 +00006622 n+=GetPixelChannels(image);
6623 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006624 }
glennrp47b9dd52010-11-24 18:12:06 +00006625
cristy3ed852e2009-09-05 21:47:34 +00006626 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6627 break;
6628 }
glennrp3faa9a32011-04-23 14:00:25 +00006629#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006630 if (magn_methx != 1 || magn_methy != 1)
6631 {
6632 /*
6633 Rescale pixels to Quantum
6634 */
cristybb503372010-05-27 20:51:26 +00006635 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006636 {
6637 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006638
cristybb503372010-05-27 20:51:26 +00006639 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006640 {
cristy4c08aed2011-07-01 19:47:50 +00006641 SetPixelRed(image,ScaleShortToQuantum(
6642 GetPixelRed(image,q)),q);
6643 SetPixelGreen(image,ScaleShortToQuantum(
6644 GetPixelGreen(image,q)),q);
6645 SetPixelBlue(image,ScaleShortToQuantum(
6646 GetPixelBlue(image,q)),q);
6647 SetPixelAlpha(image,ScaleShortToQuantum(
6648 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006649 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006650 }
glennrp47b9dd52010-11-24 18:12:06 +00006651
cristy3ed852e2009-09-05 21:47:34 +00006652 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6653 break;
6654 }
6655 }
6656#endif
6657 if (logging != MagickFalse)
6658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6659 " Finished MAGN processing");
6660 }
6661 }
6662
6663 /*
6664 Crop_box is with respect to the upper left corner of the MNG.
6665 */
6666 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6667 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6668 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6669 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6670 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6671 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6672 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6673 if ((crop_box.left != (mng_info->image_box.left
6674 +mng_info->x_off[object_id])) ||
6675 (crop_box.right != (mng_info->image_box.right
6676 +mng_info->x_off[object_id])) ||
6677 (crop_box.top != (mng_info->image_box.top
6678 +mng_info->y_off[object_id])) ||
6679 (crop_box.bottom != (mng_info->image_box.bottom
6680 +mng_info->y_off[object_id])))
6681 {
6682 if (logging != MagickFalse)
6683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6684 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006685
cristy3ed852e2009-09-05 21:47:34 +00006686 if ((crop_box.left < crop_box.right) &&
6687 (crop_box.top < crop_box.bottom))
6688 {
6689 Image
6690 *im;
6691
6692 RectangleInfo
6693 crop_info;
6694
6695 /*
6696 Crop_info is with respect to the upper left corner of
6697 the image.
6698 */
6699 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6700 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006701 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6702 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006703 image->page.width=image->columns;
6704 image->page.height=image->rows;
6705 image->page.x=0;
6706 image->page.y=0;
6707 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006708
cristy3ed852e2009-09-05 21:47:34 +00006709 if (im != (Image *) NULL)
6710 {
6711 image->columns=im->columns;
6712 image->rows=im->rows;
6713 im=DestroyImage(im);
6714 image->page.width=image->columns;
6715 image->page.height=image->rows;
6716 image->page.x=crop_box.left;
6717 image->page.y=crop_box.top;
6718 }
6719 }
glennrp47b9dd52010-11-24 18:12:06 +00006720
cristy3ed852e2009-09-05 21:47:34 +00006721 else
6722 {
6723 /*
6724 No pixels in crop area. The MNG spec still requires
6725 a layer, though, so make a single transparent pixel in
6726 the top left corner.
6727 */
6728 image->columns=1;
6729 image->rows=1;
6730 image->colors=2;
cristyea1a8aa2011-10-20 13:24:06 +00006731 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006732 image->page.width=1;
6733 image->page.height=1;
6734 image->page.x=0;
6735 image->page.y=0;
6736 }
6737 }
6738#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6739 image=mng_info->image;
6740#endif
6741 }
6742
glennrp2b013e42010-11-24 16:55:50 +00006743#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6744 /* PNG does not handle depths greater than 16 so reduce it even
6745 * if lossy
6746 */
6747 if (image->depth > 16)
6748 image->depth=16;
6749#endif
6750
glennrp3faa9a32011-04-23 14:00:25 +00006751#if (MAGICKCORE_QUANTUM_DEPTH > 8)
cristyc82a27b2011-10-21 01:07:16 +00006752 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00006753 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006754#endif
glennrpd6afd542010-11-19 01:53:05 +00006755
cristy3ed852e2009-09-05 21:47:34 +00006756 if (image_info->number_scenes != 0)
6757 {
6758 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006759 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006760 break;
6761 }
glennrpd6afd542010-11-19 01:53:05 +00006762
cristy3ed852e2009-09-05 21:47:34 +00006763 if (logging != MagickFalse)
6764 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6765 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006766
cristy3ed852e2009-09-05 21:47:34 +00006767 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006768
cristy3ed852e2009-09-05 21:47:34 +00006769 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006770
cristy3ed852e2009-09-05 21:47:34 +00006771 if (logging != MagickFalse)
6772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6773 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006774
cristy3ed852e2009-09-05 21:47:34 +00006775#if defined(MNG_INSERT_LAYERS)
6776 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6777 (mng_info->mng_height))
6778 {
6779 /*
6780 Insert a background layer if nothing else was found.
6781 */
6782 if (logging != MagickFalse)
6783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6784 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006785
cristy4c08aed2011-07-01 19:47:50 +00006786 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006787 {
6788 /*
6789 Allocate next image structure.
6790 */
cristy9950d572011-10-01 18:22:35 +00006791 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006792 if (GetNextImageInList(image) == (Image *) NULL)
6793 {
6794 image=DestroyImageList(image);
6795 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006796
cristy3ed852e2009-09-05 21:47:34 +00006797 if (logging != MagickFalse)
6798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6799 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006800
cristy3ed852e2009-09-05 21:47:34 +00006801 return((Image *) NULL);
6802 }
6803 image=SyncNextImageInList(image);
6804 }
6805 image->columns=mng_info->mng_width;
6806 image->rows=mng_info->mng_height;
6807 image->page.width=mng_info->mng_width;
6808 image->page.height=mng_info->mng_height;
6809 image->page.x=0;
6810 image->page.y=0;
6811 image->background_color=mng_background_color;
6812 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006813
cristy3ed852e2009-09-05 21:47:34 +00006814 if (image_info->ping == MagickFalse)
cristyea1a8aa2011-10-20 13:24:06 +00006815 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006816
cristy3ed852e2009-09-05 21:47:34 +00006817 mng_info->image_found++;
6818 }
6819#endif
6820 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006821
cristy3ed852e2009-09-05 21:47:34 +00006822 if (mng_iterations == 1)
6823 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006824
cristy3ed852e2009-09-05 21:47:34 +00006825 while (GetPreviousImageInList(image) != (Image *) NULL)
6826 {
6827 image_count++;
6828 if (image_count > 10*mng_info->image_found)
6829 {
6830 if (logging != MagickFalse)
6831 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006832
cristyc82a27b2011-10-21 01:07:16 +00006833 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006834 CoderError,"Linked list is corrupted, beginning of list not found",
6835 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006836
cristy3ed852e2009-09-05 21:47:34 +00006837 return((Image *) NULL);
6838 }
glennrp0fe50b42010-11-16 03:52:51 +00006839
cristy3ed852e2009-09-05 21:47:34 +00006840 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006841
cristy3ed852e2009-09-05 21:47:34 +00006842 if (GetNextImageInList(image) == (Image *) NULL)
6843 {
6844 if (logging != MagickFalse)
6845 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006846
cristyc82a27b2011-10-21 01:07:16 +00006847 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006848 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6849 image_info->filename);
6850 }
6851 }
glennrp47b9dd52010-11-24 18:12:06 +00006852
cristy3ed852e2009-09-05 21:47:34 +00006853 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6854 GetNextImageInList(image) ==
6855 (Image *) NULL)
6856 {
6857 if (logging != MagickFalse)
6858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6859 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006860
cristyc82a27b2011-10-21 01:07:16 +00006861 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006862 CoderError,"image->next for first image is NULL but shouldn't be.",
6863 "`%s'",image_info->filename);
6864 }
glennrp47b9dd52010-11-24 18:12:06 +00006865
cristy3ed852e2009-09-05 21:47:34 +00006866 if (mng_info->image_found == 0)
6867 {
6868 if (logging != MagickFalse)
6869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6870 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006871
cristyc82a27b2011-10-21 01:07:16 +00006872 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006873 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006874
cristy3ed852e2009-09-05 21:47:34 +00006875 if (image != (Image *) NULL)
6876 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006877
cristy3ed852e2009-09-05 21:47:34 +00006878 MngInfoFreeStruct(mng_info,&have_mng_structure);
6879 return((Image *) NULL);
6880 }
6881
6882 if (mng_info->ticks_per_second)
6883 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6884 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006885
cristy3ed852e2009-09-05 21:47:34 +00006886 else
6887 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006888
cristy3ed852e2009-09-05 21:47:34 +00006889 /* Find final nonzero image delay */
6890 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006891
cristy3ed852e2009-09-05 21:47:34 +00006892 while (GetNextImageInList(image) != (Image *) NULL)
6893 {
6894 if (image->delay)
6895 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006896
cristy3ed852e2009-09-05 21:47:34 +00006897 image=GetNextImageInList(image);
6898 }
glennrp0fe50b42010-11-16 03:52:51 +00006899
cristy3ed852e2009-09-05 21:47:34 +00006900 if (final_delay < final_image_delay)
6901 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006902
cristy3ed852e2009-09-05 21:47:34 +00006903 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006904
cristy3ed852e2009-09-05 21:47:34 +00006905 if (logging != MagickFalse)
6906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006907 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6908 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006909
cristy3ed852e2009-09-05 21:47:34 +00006910 if (logging != MagickFalse)
6911 {
6912 int
6913 scene;
6914
6915 scene=0;
6916 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006917
cristy3ed852e2009-09-05 21:47:34 +00006918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6919 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006920
cristy3ed852e2009-09-05 21:47:34 +00006921 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006922 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006923
cristy3ed852e2009-09-05 21:47:34 +00006924 while (GetNextImageInList(image) != (Image *) NULL)
6925 {
6926 image=GetNextImageInList(image);
6927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006928 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006929 }
6930 }
6931
6932 image=GetFirstImageInList(image);
6933#ifdef MNG_COALESCE_LAYERS
6934 if (insert_layers)
6935 {
6936 Image
6937 *next_image,
6938 *next;
6939
cristybb503372010-05-27 20:51:26 +00006940 size_t
cristy3ed852e2009-09-05 21:47:34 +00006941 scene;
6942
6943 if (logging != MagickFalse)
6944 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006945
cristy3ed852e2009-09-05 21:47:34 +00006946 scene=image->scene;
cristyc82a27b2011-10-21 01:07:16 +00006947 next_image=CoalesceImages(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006948
cristy3ed852e2009-09-05 21:47:34 +00006949 if (next_image == (Image *) NULL)
6950 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006951
cristy3ed852e2009-09-05 21:47:34 +00006952 image=DestroyImageList(image);
6953 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006954
cristy3ed852e2009-09-05 21:47:34 +00006955 for (next=image; next != (Image *) NULL; next=next_image)
6956 {
6957 next->page.width=mng_info->mng_width;
6958 next->page.height=mng_info->mng_height;
6959 next->page.x=0;
6960 next->page.y=0;
6961 next->scene=scene++;
6962 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006963
cristy3ed852e2009-09-05 21:47:34 +00006964 if (next_image == (Image *) NULL)
6965 break;
glennrp47b9dd52010-11-24 18:12:06 +00006966
cristy3ed852e2009-09-05 21:47:34 +00006967 if (next->delay == 0)
6968 {
6969 scene--;
6970 next_image->previous=GetPreviousImageInList(next);
6971 if (GetPreviousImageInList(next) == (Image *) NULL)
6972 image=next_image;
6973 else
6974 next->previous->next=next_image;
6975 next=DestroyImage(next);
6976 }
6977 }
6978 }
6979#endif
6980
6981 while (GetNextImageInList(image) != (Image *) NULL)
6982 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006983
cristy3ed852e2009-09-05 21:47:34 +00006984 image->dispose=BackgroundDispose;
6985
6986 if (logging != MagickFalse)
6987 {
6988 int
6989 scene;
6990
6991 scene=0;
6992 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006993
cristy3ed852e2009-09-05 21:47:34 +00006994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6995 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006996
cristy3ed852e2009-09-05 21:47:34 +00006997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006998 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6999 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00007000
cristy3ed852e2009-09-05 21:47:34 +00007001 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00007002 {
7003 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007004
cristyf2faecf2010-05-28 19:19:36 +00007005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007006 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7007 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00007008 }
7009 }
glennrp47b9dd52010-11-24 18:12:06 +00007010
cristy3ed852e2009-09-05 21:47:34 +00007011 image=GetFirstImageInList(image);
7012 MngInfoFreeStruct(mng_info,&have_mng_structure);
7013 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00007014
cristy3ed852e2009-09-05 21:47:34 +00007015 if (logging != MagickFalse)
7016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00007017
cristy3ed852e2009-09-05 21:47:34 +00007018 return(GetFirstImageInList(image));
7019}
glennrp25c1e2b2010-03-25 01:39:56 +00007020#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007021static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7022{
7023 printf("Your PNG library is too old: You have libpng-%s\n",
7024 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00007025
cristy3ed852e2009-09-05 21:47:34 +00007026 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7027 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007028
cristy3ed852e2009-09-05 21:47:34 +00007029 return(Image *) NULL;
7030}
glennrp47b9dd52010-11-24 18:12:06 +00007031
cristy3ed852e2009-09-05 21:47:34 +00007032static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7033{
7034 return(ReadPNGImage(image_info,exception));
7035}
glennrp25c1e2b2010-03-25 01:39:56 +00007036#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007037#endif
7038
7039/*
7040%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7041% %
7042% %
7043% %
7044% R e g i s t e r P N G I m a g e %
7045% %
7046% %
7047% %
7048%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7049%
7050% RegisterPNGImage() adds properties for the PNG image format to
7051% the list of supported formats. The properties include the image format
7052% tag, a method to read and/or write the format, whether the format
7053% supports the saving of more than one frame to the same file or blob,
7054% whether the format supports native in-memory I/O, and a brief
7055% description of the format.
7056%
7057% The format of the RegisterPNGImage method is:
7058%
cristybb503372010-05-27 20:51:26 +00007059% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007060%
7061*/
cristybb503372010-05-27 20:51:26 +00007062ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007063{
7064 char
7065 version[MaxTextExtent];
7066
7067 MagickInfo
7068 *entry;
7069
7070 static const char
7071 *PNGNote=
7072 {
7073 "See http://www.libpng.org/ for details about the PNG format."
7074 },
glennrp47b9dd52010-11-24 18:12:06 +00007075
cristy3ed852e2009-09-05 21:47:34 +00007076 *JNGNote=
7077 {
7078 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7079 "format."
7080 },
glennrp47b9dd52010-11-24 18:12:06 +00007081
cristy3ed852e2009-09-05 21:47:34 +00007082 *MNGNote=
7083 {
7084 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7085 "format."
7086 };
7087
7088 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007089
cristy3ed852e2009-09-05 21:47:34 +00007090#if defined(PNG_LIBPNG_VER_STRING)
7091 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7092 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007093
cristy3ed852e2009-09-05 21:47:34 +00007094 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7095 {
7096 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7097 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7098 MaxTextExtent);
7099 }
7100#endif
glennrp47b9dd52010-11-24 18:12:06 +00007101
cristy3ed852e2009-09-05 21:47:34 +00007102 entry=SetMagickInfo("MNG");
7103 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007104
cristy3ed852e2009-09-05 21:47:34 +00007105#if defined(MAGICKCORE_PNG_DELEGATE)
7106 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7107 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7108#endif
glennrp47b9dd52010-11-24 18:12:06 +00007109
cristy3ed852e2009-09-05 21:47:34 +00007110 entry->magick=(IsImageFormatHandler *) IsMNG;
7111 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007112
cristy3ed852e2009-09-05 21:47:34 +00007113 if (*version != '\0')
7114 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007115
cristy3ed852e2009-09-05 21:47:34 +00007116 entry->module=ConstantString("PNG");
7117 entry->note=ConstantString(MNGNote);
7118 (void) RegisterMagickInfo(entry);
7119
7120 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007121
cristy3ed852e2009-09-05 21:47:34 +00007122#if defined(MAGICKCORE_PNG_DELEGATE)
7123 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7124 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7125#endif
glennrp47b9dd52010-11-24 18:12:06 +00007126
cristy3ed852e2009-09-05 21:47:34 +00007127 entry->magick=(IsImageFormatHandler *) IsPNG;
7128 entry->adjoin=MagickFalse;
7129 entry->description=ConstantString("Portable Network Graphics");
7130 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007131
cristy3ed852e2009-09-05 21:47:34 +00007132 if (*version != '\0')
7133 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007134
cristy3ed852e2009-09-05 21:47:34 +00007135 entry->note=ConstantString(PNGNote);
7136 (void) RegisterMagickInfo(entry);
7137
7138 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007139
cristy3ed852e2009-09-05 21:47:34 +00007140#if defined(MAGICKCORE_PNG_DELEGATE)
7141 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7142 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7143#endif
glennrp47b9dd52010-11-24 18:12:06 +00007144
cristy3ed852e2009-09-05 21:47:34 +00007145 entry->magick=(IsImageFormatHandler *) IsPNG;
7146 entry->adjoin=MagickFalse;
7147 entry->description=ConstantString(
7148 "8-bit indexed with optional binary transparency");
7149 entry->module=ConstantString("PNG");
7150 (void) RegisterMagickInfo(entry);
7151
7152 entry=SetMagickInfo("PNG24");
7153 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007154
cristy3ed852e2009-09-05 21:47:34 +00007155#if defined(ZLIB_VERSION)
7156 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7157 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007158
cristy3ed852e2009-09-05 21:47:34 +00007159 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7160 {
7161 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7162 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7163 }
7164#endif
glennrp47b9dd52010-11-24 18:12:06 +00007165
cristy3ed852e2009-09-05 21:47:34 +00007166 if (*version != '\0')
7167 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007168
cristy3ed852e2009-09-05 21:47:34 +00007169#if defined(MAGICKCORE_PNG_DELEGATE)
7170 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7171 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7172#endif
glennrp47b9dd52010-11-24 18:12:06 +00007173
cristy3ed852e2009-09-05 21:47:34 +00007174 entry->magick=(IsImageFormatHandler *) IsPNG;
7175 entry->adjoin=MagickFalse;
7176 entry->description=ConstantString("opaque 24-bit RGB");
7177 entry->module=ConstantString("PNG");
7178 (void) RegisterMagickInfo(entry);
7179
7180 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007181
cristy3ed852e2009-09-05 21:47:34 +00007182#if defined(MAGICKCORE_PNG_DELEGATE)
7183 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7184 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7185#endif
glennrp47b9dd52010-11-24 18:12:06 +00007186
cristy3ed852e2009-09-05 21:47:34 +00007187 entry->magick=(IsImageFormatHandler *) IsPNG;
7188 entry->adjoin=MagickFalse;
7189 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7190 entry->module=ConstantString("PNG");
7191 (void) RegisterMagickInfo(entry);
7192
7193 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007194
cristy3ed852e2009-09-05 21:47:34 +00007195#if defined(JNG_SUPPORTED)
7196#if defined(MAGICKCORE_PNG_DELEGATE)
7197 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7198 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7199#endif
7200#endif
glennrp47b9dd52010-11-24 18:12:06 +00007201
cristy3ed852e2009-09-05 21:47:34 +00007202 entry->magick=(IsImageFormatHandler *) IsJNG;
7203 entry->adjoin=MagickFalse;
7204 entry->description=ConstantString("JPEG Network Graphics");
7205 entry->module=ConstantString("PNG");
7206 entry->note=ConstantString(JNGNote);
7207 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007208
cristy18b17442009-10-25 18:36:48 +00007209#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007210 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007211#endif
glennrp47b9dd52010-11-24 18:12:06 +00007212
cristy3ed852e2009-09-05 21:47:34 +00007213 return(MagickImageCoderSignature);
7214}
7215
7216/*
7217%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7218% %
7219% %
7220% %
7221% U n r e g i s t e r P N G I m a g e %
7222% %
7223% %
7224% %
7225%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7226%
7227% UnregisterPNGImage() removes format registrations made by the
7228% PNG module from the list of supported formats.
7229%
7230% The format of the UnregisterPNGImage method is:
7231%
7232% UnregisterPNGImage(void)
7233%
7234*/
7235ModuleExport void UnregisterPNGImage(void)
7236{
7237 (void) UnregisterMagickInfo("MNG");
7238 (void) UnregisterMagickInfo("PNG");
7239 (void) UnregisterMagickInfo("PNG8");
7240 (void) UnregisterMagickInfo("PNG24");
7241 (void) UnregisterMagickInfo("PNG32");
7242 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007243
cristy3ed852e2009-09-05 21:47:34 +00007244#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007245 if (ping_semaphore != (SemaphoreInfo *) NULL)
7246 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007247#endif
7248}
7249
7250#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007251#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007252/*
7253%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7254% %
7255% %
7256% %
7257% W r i t e M N G I m a g e %
7258% %
7259% %
7260% %
7261%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7262%
7263% WriteMNGImage() writes an image in the Portable Network Graphics
7264% Group's "Multiple-image Network Graphics" encoded image format.
7265%
7266% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7267%
7268% The format of the WriteMNGImage method is:
7269%
cristy1e178e72011-08-28 19:44:34 +00007270% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7271% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007272%
7273% A description of each parameter follows.
7274%
7275% o image_info: the image info.
7276%
7277% o image: The image.
7278%
cristy1e178e72011-08-28 19:44:34 +00007279% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007280%
7281% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7282% "To do" under ReadPNGImage):
7283%
cristy3ed852e2009-09-05 21:47:34 +00007284% Preserve all unknown and not-yet-handled known chunks found in input
7285% PNG file and copy them into output PNG files according to the PNG
7286% copying rules.
7287%
7288% Write the iCCP chunk at MNG level when (icc profile length > 0)
7289%
7290% Improve selection of color type (use indexed-colour or indexed-colour
7291% with tRNS when 256 or fewer unique RGBA values are present).
7292%
7293% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7294% This will be complicated if we limit ourselves to generating MNG-LC
7295% files. For now we ignore disposal method 3 and simply overlay the next
7296% image on it.
7297%
7298% Check for identical PLTE's or PLTE/tRNS combinations and use a
7299% global MNG PLTE or PLTE/tRNS combination when appropriate.
7300% [mostly done 15 June 1999 but still need to take care of tRNS]
7301%
7302% Check for identical sRGB and replace with a global sRGB (and remove
7303% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7304% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7305% local gAMA/cHRM with local sRGB if appropriate).
7306%
7307% Check for identical sBIT chunks and write global ones.
7308%
7309% Provide option to skip writing the signature tEXt chunks.
7310%
7311% Use signatures to detect identical objects and reuse the first
7312% instance of such objects instead of writing duplicate objects.
7313%
7314% Use a smaller-than-32k value of compression window size when
7315% appropriate.
7316%
7317% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7318% ancillary text chunks and save profiles.
7319%
7320% Provide an option to force LC files (to ensure exact framing rate)
7321% instead of VLC.
7322%
7323% Provide an option to force VLC files instead of LC, even when offsets
7324% are present. This will involve expanding the embedded images with a
7325% transparent region at the top and/or left.
7326*/
7327
cristy3ed852e2009-09-05 21:47:34 +00007328static void
glennrpcf002022011-01-30 02:38:15 +00007329Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007330 png_info *ping_info, unsigned char *profile_type, unsigned char
7331 *profile_description, unsigned char *profile_data, png_uint_32 length)
7332{
cristy3ed852e2009-09-05 21:47:34 +00007333 png_textp
7334 text;
7335
cristybb503372010-05-27 20:51:26 +00007336 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007337 i;
7338
7339 unsigned char
7340 *sp;
7341
7342 png_charp
7343 dp;
7344
7345 png_uint_32
7346 allocated_length,
7347 description_length;
7348
7349 unsigned char
7350 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007351
cristy3ed852e2009-09-05 21:47:34 +00007352 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7353 return;
7354
7355 if (image_info->verbose)
7356 {
glennrp0fe50b42010-11-16 03:52:51 +00007357 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7358 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007359 }
glennrp0fe50b42010-11-16 03:52:51 +00007360
cristy3ed852e2009-09-05 21:47:34 +00007361 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7362 description_length=(png_uint_32) strlen((const char *) profile_description);
7363 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7364 + description_length);
7365 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7366 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7367 text[0].key[0]='\0';
7368 (void) ConcatenateMagickString(text[0].key,
7369 "Raw profile type ",MaxTextExtent);
7370 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7371 sp=profile_data;
7372 dp=text[0].text;
7373 *dp++='\n';
7374 (void) CopyMagickString(dp,(const char *) profile_description,
7375 allocated_length);
7376 dp+=description_length;
7377 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007378 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007379 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007380 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007381
cristybb503372010-05-27 20:51:26 +00007382 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007383 {
7384 if (i%36 == 0)
7385 *dp++='\n';
7386 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7387 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7388 }
glennrp47b9dd52010-11-24 18:12:06 +00007389
cristy3ed852e2009-09-05 21:47:34 +00007390 *dp++='\n';
7391 *dp='\0';
7392 text[0].text_length=(png_size_t) (dp-text[0].text);
7393 text[0].compression=image_info->compression == NoCompression ||
7394 (image_info->compression == UndefinedCompression &&
7395 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007396
cristy3ed852e2009-09-05 21:47:34 +00007397 if (text[0].text_length <= allocated_length)
7398 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007399
cristy3ed852e2009-09-05 21:47:34 +00007400 png_free(ping,text[0].text);
7401 png_free(ping,text[0].key);
7402 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007403}
7404
glennrpcf002022011-01-30 02:38:15 +00007405static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007406 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007407{
7408 char
7409 *name;
7410
7411 const StringInfo
7412 *profile;
7413
7414 unsigned char
7415 *data;
7416
7417 png_uint_32 length;
7418
7419 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007420
7421 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7422 {
cristy3ed852e2009-09-05 21:47:34 +00007423 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007424
cristy3ed852e2009-09-05 21:47:34 +00007425 if (profile != (const StringInfo *) NULL)
7426 {
7427 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007428 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007429
glennrp47b9dd52010-11-24 18:12:06 +00007430 if (LocaleNCompare(name,string,11) == 0)
7431 {
7432 if (logging != MagickFalse)
7433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7434 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007435
glennrpcf002022011-01-30 02:38:15 +00007436 ping_profile=CloneStringInfo(profile);
7437 data=GetStringInfoDatum(ping_profile),
7438 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007439 data[4]=data[3];
7440 data[3]=data[2];
7441 data[2]=data[1];
7442 data[1]=data[0];
7443 (void) WriteBlobMSBULong(image,length-5); /* data length */
7444 (void) WriteBlob(image,length-1,data+1);
7445 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007446 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007447 }
cristy3ed852e2009-09-05 21:47:34 +00007448 }
glennrp47b9dd52010-11-24 18:12:06 +00007449
cristy3ed852e2009-09-05 21:47:34 +00007450 name=GetNextImageProfile(image);
7451 }
glennrp47b9dd52010-11-24 18:12:06 +00007452
cristy3ed852e2009-09-05 21:47:34 +00007453 return(MagickTrue);
7454}
7455
glennrpb9cfe272010-12-21 15:08:06 +00007456
cristy3ed852e2009-09-05 21:47:34 +00007457/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007458static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +00007459 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007460{
7461 Image
7462 *image;
7463
7464 ImageInfo
7465 *image_info;
7466
cristy3ed852e2009-09-05 21:47:34 +00007467 char
7468 s[2];
7469
7470 const char
7471 *name,
7472 *property,
7473 *value;
7474
7475 const StringInfo
7476 *profile;
7477
cristy3ed852e2009-09-05 21:47:34 +00007478 int
cristy3ed852e2009-09-05 21:47:34 +00007479 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007480 pass;
7481
glennrpe9c26dc2010-05-30 01:56:35 +00007482 png_byte
7483 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007484
glennrp39992b42010-11-14 00:03:43 +00007485 png_color
7486 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007487
glennrp5af765f2010-03-30 11:12:18 +00007488 png_color_16
7489 ping_background,
7490 ping_trans_color;
7491
cristy3ed852e2009-09-05 21:47:34 +00007492 png_info
7493 *ping_info;
7494
7495 png_struct
7496 *ping;
7497
glennrp5af765f2010-03-30 11:12:18 +00007498 png_uint_32
7499 ping_height,
7500 ping_width;
7501
cristybb503372010-05-27 20:51:26 +00007502 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007503 y;
7504
7505 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007506 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007507 logging,
glennrp58e01762011-01-07 15:28:54 +00007508 matte,
7509
glennrpda8f3a72011-02-27 23:54:12 +00007510 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007511 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007512 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007513 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007514 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007515 ping_have_bKGD,
7516 ping_have_pHYs,
7517 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007518
7519 ping_exclude_bKGD,
7520 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007521 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007522 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007523 ping_exclude_gAMA,
7524 ping_exclude_iCCP,
7525 /* ping_exclude_iTXt, */
7526 ping_exclude_oFFs,
7527 ping_exclude_pHYs,
7528 ping_exclude_sRGB,
7529 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007530 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007531 ping_exclude_vpAg,
7532 ping_exclude_zCCP, /* hex-encoded iCCP */
7533 ping_exclude_zTXt,
7534
glennrp8d3d6e52011-04-19 04:39:51 +00007535 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007536 ping_need_colortype_warning,
7537
glennrp82b3c532011-03-22 19:20:54 +00007538 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007539 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007540 tried_333,
7541 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007542
7543 QuantumInfo
7544 *quantum_info;
7545
cristyc82a27b2011-10-21 01:07:16 +00007546 PNGErrorInfo
7547 error_info;
7548
cristybb503372010-05-27 20:51:26 +00007549 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007550 i,
7551 x;
7552
7553 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007554 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007555
glennrp5af765f2010-03-30 11:12:18 +00007556 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007557 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007558 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007559 ping_color_type,
7560 ping_interlace_method,
7561 ping_compression_method,
7562 ping_filter_method,
7563 ping_num_trans;
7564
cristybb503372010-05-27 20:51:26 +00007565 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007566 image_depth,
7567 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007568
cristybb503372010-05-27 20:51:26 +00007569 size_t
cristy3ed852e2009-09-05 21:47:34 +00007570 quality,
7571 rowbytes,
7572 save_image_depth;
7573
glennrpdfd70802010-11-14 01:23:35 +00007574 int
glennrpfd05d622011-02-25 04:10:33 +00007575 j,
glennrpf09bded2011-01-08 01:15:59 +00007576 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007577 number_opaque,
7578 number_semitransparent,
7579 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007580 ping_pHYs_unit_type;
7581
7582 png_uint_32
7583 ping_pHYs_x_resolution,
7584 ping_pHYs_y_resolution;
7585
cristy3ed852e2009-09-05 21:47:34 +00007586 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007587 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007588
cristyc82a27b2011-10-21 01:07:16 +00007589 image = CloneImage(IMimage,0,0,MagickFalse,exception);
glennrpb9cfe272010-12-21 15:08:06 +00007590 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007591 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007592 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007593
cristy3ed852e2009-09-05 21:47:34 +00007594#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007595 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007596#endif
7597
glennrp5af765f2010-03-30 11:12:18 +00007598 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007599 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007600 ping_color_type=0,
7601 ping_interlace_method=0,
7602 ping_compression_method=0,
7603 ping_filter_method=0,
7604 ping_num_trans = 0;
7605
7606 ping_background.red = 0;
7607 ping_background.green = 0;
7608 ping_background.blue = 0;
7609 ping_background.gray = 0;
7610 ping_background.index = 0;
7611
7612 ping_trans_color.red=0;
7613 ping_trans_color.green=0;
7614 ping_trans_color.blue=0;
7615 ping_trans_color.gray=0;
7616
glennrpdfd70802010-11-14 01:23:35 +00007617 ping_pHYs_unit_type = 0;
7618 ping_pHYs_x_resolution = 0;
7619 ping_pHYs_y_resolution = 0;
7620
glennrpda8f3a72011-02-27 23:54:12 +00007621 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007622 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007623 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007624 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007625 ping_have_bKGD=MagickFalse;
7626 ping_have_pHYs=MagickFalse;
7627 ping_have_tRNS=MagickFalse;
7628
glennrp0e8ea192010-12-24 18:00:33 +00007629 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7630 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007631 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007632 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007633 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007634 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7635 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7636 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7637 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7638 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7639 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007640 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007641 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7642 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7643 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7644
glennrp8d3d6e52011-04-19 04:39:51 +00007645 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007646 ping_need_colortype_warning = MagickFalse;
7647
cristy0d57eec2011-09-04 22:13:56 +00007648 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7649 * i.e., eliminate the ICC profile and set image->rendering_intent.
7650 * Note that this will not involve any changes to the actual pixels
7651 * but merely passes information to applications that read the resulting
7652 * PNG image.
7653 */
7654 if (ping_exclude_sRGB == MagickFalse)
7655 {
7656 char
7657 *name;
7658
7659 const StringInfo
7660 *profile;
7661
7662 ResetImageProfileIterator(image);
7663 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7664 {
7665 profile=GetImageProfile(image,name);
7666
7667 if (profile != (StringInfo *) NULL)
7668 {
7669 if ((LocaleCompare(name,"ICC") == 0) ||
glennrp29a106e2011-09-06 17:11:42 +00007670 (LocaleCompare(name,"ICM") == 0))
7671 {
glennrpee7b4c02011-10-04 01:21:09 +00007672 int
7673 icheck;
7674
7675 /* 0: not a known sRGB profile
7676 * 1: HP-Microsoft sRGB v2
7677 * 2: ICC sRGB v4 perceptual
7678 * 3: ICC sRGB v2 perceptual no black-compensation
7679 */
7680 png_uint_32
7681 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7682 check_len[4] = {0, 3144, 60960, 3052};
7683
7684 png_uint_32
7685 length,
7686 profile_crc;
7687
cristy0d57eec2011-09-04 22:13:56 +00007688 unsigned char
7689 *data;
7690
glennrp29a106e2011-09-06 17:11:42 +00007691 length=(png_uint_32) GetStringInfoLength(profile);
7692
glennrpee7b4c02011-10-04 01:21:09 +00007693 for (icheck=3; icheck > 0; icheck--)
cristy0d57eec2011-09-04 22:13:56 +00007694 {
glennrpee7b4c02011-10-04 01:21:09 +00007695 if (length == check_len[icheck])
glennrp29a106e2011-09-06 17:11:42 +00007696 {
glennrpee7b4c02011-10-04 01:21:09 +00007697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7698 " Got a %lu-byte ICC profile (potentially sRGB)",
7699 (unsigned long) length);
glennrp29a106e2011-09-06 17:11:42 +00007700
glennrpee7b4c02011-10-04 01:21:09 +00007701 data=GetStringInfoDatum(profile);
7702 profile_crc=crc32(0,data,length);
glennrp29a106e2011-09-06 17:11:42 +00007703
glennrpee7b4c02011-10-04 01:21:09 +00007704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe54cd4b2011-10-04 01:31:35 +00007705 " with crc=%8x",(unsigned int) profile_crc);
glennrpee7b4c02011-10-04 01:21:09 +00007706
7707 if (profile_crc == check_crc[icheck])
7708 {
7709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7710 " It is sRGB.");
7711 if (image->rendering_intent==UndefinedIntent)
7712 image->rendering_intent=PerceptualIntent;
7713 break;
7714 }
glennrp29a106e2011-09-06 17:11:42 +00007715 }
glennrp29a106e2011-09-06 17:11:42 +00007716 }
glennrpee7b4c02011-10-04 01:21:09 +00007717 if (icheck == 0)
glennrp29a106e2011-09-06 17:11:42 +00007718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpee7b4c02011-10-04 01:21:09 +00007719 " Got a %lu-byte ICC profile",
glennrp29a106e2011-09-06 17:11:42 +00007720 (unsigned long) length);
7721 }
cristy0d57eec2011-09-04 22:13:56 +00007722 }
7723 name=GetNextImageProfile(image);
7724 }
7725 }
7726
glennrp8bb3a022010-12-13 20:40:04 +00007727 number_opaque = 0;
7728 number_semitransparent = 0;
7729 number_transparent = 0;
7730
glennrpfd05d622011-02-25 04:10:33 +00007731 if (logging != MagickFalse)
7732 {
7733 if (image->storage_class == UndefinedClass)
7734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7735 " storage_class=UndefinedClass");
7736 if (image->storage_class == DirectClass)
7737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7738 " storage_class=DirectClass");
7739 if (image->storage_class == PseudoClass)
7740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7741 " storage_class=PseudoClass");
7742 }
glennrp28af3712011-04-06 18:07:30 +00007743
glennrp7e65e932011-08-19 02:31:16 +00007744 if (image->storage_class == PseudoClass &&
7745 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7746 (mng_info->write_png_colortype != 0 &&
7747 mng_info->write_png_colortype != 4)))
7748 {
cristyea1a8aa2011-10-20 13:24:06 +00007749 (void) SyncImage(image,exception);
glennrp7e65e932011-08-19 02:31:16 +00007750 image->storage_class = DirectClass;
7751 }
7752
glennrpc6c391a2011-04-27 02:23:56 +00007753 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007754 {
glennrpc6c391a2011-04-27 02:23:56 +00007755 if (image->storage_class != PseudoClass && image->colormap != NULL)
7756 {
7757 /* Free the bogus colormap; it can cause trouble later */
7758 if (logging != MagickFalse)
7759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7760 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00007761 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00007762 image->colormap=NULL;
7763 }
glennrp28af3712011-04-06 18:07:30 +00007764 }
glennrpbb4f99d2011-05-22 11:13:17 +00007765
cristy510d06a2011-07-06 23:43:54 +00007766 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristye941a752011-10-15 01:52:48 +00007767 (void) TransformImageColorspace(image,RGBColorspace,exception);
glennrp0fe50b42010-11-16 03:52:51 +00007768
glennrp3241bd02010-12-12 04:36:28 +00007769 /*
7770 Sometimes we get PseudoClass images whose RGB values don't match
7771 the colors in the colormap. This code syncs the RGB values.
7772 */
7773 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
cristyea1a8aa2011-10-20 13:24:06 +00007774 (void) SyncImage(image,exception);
glennrp3241bd02010-12-12 04:36:28 +00007775
glennrpa6a06632011-01-19 15:15:34 +00007776#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7777 if (image->depth > 8)
7778 {
7779 if (logging != MagickFalse)
7780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7781 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7782
7783 image->depth=8;
7784 }
7785#endif
7786
glennrp8e58efd2011-05-20 12:16:29 +00007787 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007788 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7789 {
cristy4c08aed2011-07-01 19:47:50 +00007790 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007791 *r;
7792
glennrp8e58efd2011-05-20 12:16:29 +00007793 if (image->depth > 8)
7794 {
7795#if MAGICKCORE_QUANTUM_DEPTH > 16
7796 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007797 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007798
7799 for (y=0; y < (ssize_t) image->rows; y++)
7800 {
cristy8a20fa02011-12-27 15:54:31 +00007801 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007802
cristy4c08aed2011-07-01 19:47:50 +00007803 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007804 break;
7805
7806 for (x=0; x < (ssize_t) image->columns; x++)
7807 {
glennrp54cf7972011-08-06 14:28:09 +00007808 LBR16PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007809 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007810 }
glennrpbb4f99d2011-05-22 11:13:17 +00007811
glennrp8e58efd2011-05-20 12:16:29 +00007812 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7813 break;
7814 }
7815
7816 if (image->storage_class == PseudoClass && image->colormap != NULL)
7817 {
cristy3e08f112011-05-24 13:19:30 +00007818 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007819 {
glennrp91d99252011-06-25 14:30:13 +00007820 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007821 }
7822 }
7823#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7824 }
7825
7826 else if (image->depth > 4)
7827 {
7828#if MAGICKCORE_QUANTUM_DEPTH > 8
7829 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007830 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007831
7832 for (y=0; y < (ssize_t) image->rows; y++)
7833 {
cristyc82a27b2011-10-21 01:07:16 +00007834 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007835
cristy4c08aed2011-07-01 19:47:50 +00007836 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007837 break;
7838
7839 for (x=0; x < (ssize_t) image->columns; x++)
7840 {
glennrp54cf7972011-08-06 14:28:09 +00007841 LBR08PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007842 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007843 }
glennrpbb4f99d2011-05-22 11:13:17 +00007844
glennrp8e58efd2011-05-20 12:16:29 +00007845 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7846 break;
7847 }
7848
7849 if (image->storage_class == PseudoClass && image->colormap != NULL)
7850 {
cristy3e08f112011-05-24 13:19:30 +00007851 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007852 {
glennrp91d99252011-06-25 14:30:13 +00007853 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007854 }
7855 }
7856#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7857 }
7858 else
7859 if (image->depth > 2)
7860 {
7861 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007862 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007863
7864 for (y=0; y < (ssize_t) image->rows; y++)
7865 {
cristy8a20fa02011-12-27 15:54:31 +00007866 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007867
cristy4c08aed2011-07-01 19:47:50 +00007868 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007869 break;
7870
7871 for (x=0; x < (ssize_t) image->columns; x++)
7872 {
glennrp54cf7972011-08-06 14:28:09 +00007873 LBR04PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007874 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007875 }
glennrpbb4f99d2011-05-22 11:13:17 +00007876
glennrp8e58efd2011-05-20 12:16:29 +00007877 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7878 break;
7879 }
7880
7881 if (image->storage_class == PseudoClass && image->colormap != NULL)
7882 {
cristy3e08f112011-05-24 13:19:30 +00007883 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007884 {
glennrp91d99252011-06-25 14:30:13 +00007885 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007886 }
7887 }
7888 }
7889
7890 else if (image->depth > 1)
7891 {
7892 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007893 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007894
7895 for (y=0; y < (ssize_t) image->rows; y++)
7896 {
cristy8a20fa02011-12-27 15:54:31 +00007897 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007898
cristy4c08aed2011-07-01 19:47:50 +00007899 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007900 break;
7901
7902 for (x=0; x < (ssize_t) image->columns; x++)
7903 {
glennrp54cf7972011-08-06 14:28:09 +00007904 LBR02PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007905 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007906 }
glennrpbb4f99d2011-05-22 11:13:17 +00007907
glennrp8e58efd2011-05-20 12:16:29 +00007908 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7909 break;
7910 }
7911
7912 if (image->storage_class == PseudoClass && image->colormap != NULL)
7913 {
cristy3e08f112011-05-24 13:19:30 +00007914 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007915 {
glennrp91d99252011-06-25 14:30:13 +00007916 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007917 }
7918 }
7919 }
7920 else
7921 {
7922 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007923 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007924
7925 for (y=0; y < (ssize_t) image->rows; y++)
7926 {
cristy8a20fa02011-12-27 15:54:31 +00007927 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007928
cristy4c08aed2011-07-01 19:47:50 +00007929 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007930 break;
7931
7932 for (x=0; x < (ssize_t) image->columns; x++)
7933 {
glennrp54cf7972011-08-06 14:28:09 +00007934 LBR01PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007935 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007936 }
glennrpbb4f99d2011-05-22 11:13:17 +00007937
glennrp8e58efd2011-05-20 12:16:29 +00007938 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7939 break;
7940 }
7941
7942 if (image->storage_class == PseudoClass && image->colormap != NULL)
7943 {
cristy3e08f112011-05-24 13:19:30 +00007944 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007945 {
glennrp91d99252011-06-25 14:30:13 +00007946 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007947 }
7948 }
7949 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007950 }
7951
glennrp67b9c1a2011-04-22 18:47:36 +00007952 /* To do: set to next higher multiple of 8 */
7953 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007954 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007955
glennrp2b013e42010-11-24 16:55:50 +00007956#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7957 /* PNG does not handle depths greater than 16 so reduce it even
7958 * if lossy
7959 */
glennrp8e58efd2011-05-20 12:16:29 +00007960 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007961 image->depth=16;
7962#endif
7963
glennrp3faa9a32011-04-23 14:00:25 +00007964#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007965 if (image->depth == 16 && mng_info->write_png_depth != 16)
cristyc82a27b2011-10-21 01:07:16 +00007966 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007967 image->depth = 8;
7968#endif
7969
glennrpc8c2f062011-02-25 19:00:33 +00007970 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007971 * we reduce the transparency to binary and run again, then if there
7972 * 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 +00007973 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7974 * palette. Then (To do) we take care of a final reduction that is only
7975 * needed if there are still 256 colors present and one of them has both
7976 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007977 */
glennrp82b3c532011-03-22 19:20:54 +00007978
glennrp8ca51ad2011-05-12 21:22:32 +00007979 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007980 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007981 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007982
glennrp8ca51ad2011-05-12 21:22:32 +00007983 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007984 {
7985 /* BUILD_PALETTE
7986 *
7987 * Sometimes we get DirectClass images that have 256 colors or fewer.
7988 * This code will build a colormap.
7989 *
7990 * Also, sometimes we get PseudoClass images with an out-of-date
7991 * colormap. This code will replace the colormap with a new one.
7992 * Sometimes we get PseudoClass images that have more than 256 colors.
7993 * This code will delete the colormap and change the image to
7994 * DirectClass.
7995 *
cristy4c08aed2011-07-01 19:47:50 +00007996 * If image->matte is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00007997 * even though it sometimes contains left-over non-opaque values.
7998 *
7999 * Also we gather some information (number of opaque, transparent,
8000 * and semitransparent pixels, and whether the image has any non-gray
8001 * pixels or only black-and-white pixels) that we might need later.
8002 *
8003 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8004 * we need to check for bogus non-opaque values, at least.
8005 */
glennrp3c218112010-11-27 15:31:26 +00008006
glennrpd71e86a2011-02-24 01:28:37 +00008007 int
8008 n;
glennrp3c218112010-11-27 15:31:26 +00008009
cristy101ab702011-10-13 13:06:32 +00008010 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008011 opaque[260],
8012 semitransparent[260],
8013 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00008014
cristy4c08aed2011-07-01 19:47:50 +00008015 register const Quantum
8016 *s;
glennrp8bb3a022010-12-13 20:40:04 +00008017
cristy4c08aed2011-07-01 19:47:50 +00008018 register Quantum
8019 *q,
glennrpfd05d622011-02-25 04:10:33 +00008020 *r;
8021
glennrpd71e86a2011-02-24 01:28:37 +00008022 if (logging != MagickFalse)
8023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8024 " Enter BUILD_PALETTE:");
8025
8026 if (logging != MagickFalse)
8027 {
glennrp03812ae2010-12-24 01:31:34 +00008028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008029 " image->columns=%.20g",(double) image->columns);
8030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8031 " image->rows=%.20g",(double) image->rows);
8032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8033 " image->matte=%.20g",(double) image->matte);
8034 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8035 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008036
glennrpfd05d622011-02-25 04:10:33 +00008037 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008038 {
8039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008040 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008042 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008043
glennrpd71e86a2011-02-24 01:28:37 +00008044 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008045 {
glennrpd71e86a2011-02-24 01:28:37 +00008046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8047 " %d (%d,%d,%d,%d)",
8048 (int) i,
8049 (int) image->colormap[i].red,
8050 (int) image->colormap[i].green,
8051 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008052 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008053 }
glennrp2cc891a2010-12-24 13:44:32 +00008054
glennrpd71e86a2011-02-24 01:28:37 +00008055 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8056 {
8057 if (i > 255)
8058 {
8059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8060 " %d (%d,%d,%d,%d)",
8061 (int) i,
8062 (int) image->colormap[i].red,
8063 (int) image->colormap[i].green,
8064 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008065 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008066 }
8067 }
glennrp03812ae2010-12-24 01:31:34 +00008068 }
glennrp7ddcc222010-12-11 05:01:05 +00008069
glennrpd71e86a2011-02-24 01:28:37 +00008070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8071 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008072
glennrpd71e86a2011-02-24 01:28:37 +00008073 if (image->colors == 0)
8074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8075 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008076
glennrp8d3d6e52011-04-19 04:39:51 +00008077 if (ping_preserve_colormap == MagickFalse)
8078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8079 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008080 }
8081
glennrpd71e86a2011-02-24 01:28:37 +00008082 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008083 number_opaque = 0;
8084 number_semitransparent = 0;
8085 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008086
8087 for (y=0; y < (ssize_t) image->rows; y++)
8088 {
8089 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8090
cristyacd2ed22011-08-30 01:44:23 +00008091 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008092 break;
8093
8094 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008095 {
glennrp4737d522011-04-29 03:33:42 +00008096 if (image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008097 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008098 {
8099 if (number_opaque < 259)
8100 {
8101 if (number_opaque == 0)
8102 {
cristy101ab702011-10-13 13:06:32 +00008103 GetPixelInfoPixel(image, q, opaque);
cristy4c08aed2011-07-01 19:47:50 +00008104 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008105 number_opaque=1;
8106 }
glennrp2cc891a2010-12-24 13:44:32 +00008107
glennrpd71e86a2011-02-24 01:28:37 +00008108 for (i=0; i< (ssize_t) number_opaque; i++)
8109 {
cristy4c08aed2011-07-01 19:47:50 +00008110 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008111 break;
8112 }
glennrp7ddcc222010-12-11 05:01:05 +00008113
glennrpd71e86a2011-02-24 01:28:37 +00008114 if (i == (ssize_t) number_opaque &&
8115 number_opaque < 259)
8116 {
8117 number_opaque++;
cristy101ab702011-10-13 13:06:32 +00008118 GetPixelInfoPixel(image, q, opaque+i);
cristy4c08aed2011-07-01 19:47:50 +00008119 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008120 }
8121 }
8122 }
cristy4c08aed2011-07-01 19:47:50 +00008123 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008124 {
8125 if (number_transparent < 259)
8126 {
8127 if (number_transparent == 0)
8128 {
cristy101ab702011-10-13 13:06:32 +00008129 GetPixelInfoPixel(image, q, transparent);
cristy4c08aed2011-07-01 19:47:50 +00008130 ping_trans_color.red=(unsigned short)
8131 GetPixelRed(image,q);
8132 ping_trans_color.green=(unsigned short)
8133 GetPixelGreen(image,q);
8134 ping_trans_color.blue=(unsigned short)
8135 GetPixelBlue(image,q);
8136 ping_trans_color.gray=(unsigned short)
8137 GetPixelRed(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008138 number_transparent = 1;
8139 }
8140
8141 for (i=0; i< (ssize_t) number_transparent; i++)
8142 {
cristy4c08aed2011-07-01 19:47:50 +00008143 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008144 break;
8145 }
8146
8147 if (i == (ssize_t) number_transparent &&
8148 number_transparent < 259)
8149 {
8150 number_transparent++;
cristy101ab702011-10-13 13:06:32 +00008151 GetPixelInfoPixel(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008152 }
8153 }
8154 }
8155 else
8156 {
8157 if (number_semitransparent < 259)
8158 {
8159 if (number_semitransparent == 0)
8160 {
cristy101ab702011-10-13 13:06:32 +00008161 GetPixelInfoPixel(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008162 number_semitransparent = 1;
8163 }
8164
8165 for (i=0; i< (ssize_t) number_semitransparent; i++)
8166 {
cristy4c08aed2011-07-01 19:47:50 +00008167 if (IsPixelEquivalent(image,q, semitransparent+i)
8168 && GetPixelAlpha(image,q) ==
8169 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008170 break;
8171 }
8172
8173 if (i == (ssize_t) number_semitransparent &&
8174 number_semitransparent < 259)
8175 {
8176 number_semitransparent++;
cristy101ab702011-10-13 13:06:32 +00008177 GetPixelInfoPixel(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008178 }
8179 }
8180 }
cristyed231572011-07-14 02:18:59 +00008181 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008182 }
8183 }
8184
cristy4054bfb2011-08-29 23:41:39 +00008185 if (mng_info->write_png8 == MagickFalse &&
8186 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008187 {
8188 /* Add the background color to the palette, if it
8189 * isn't already there.
8190 */
glennrpc6c391a2011-04-27 02:23:56 +00008191 if (logging != MagickFalse)
8192 {
8193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8194 " Check colormap for background (%d,%d,%d)",
8195 (int) image->background_color.red,
8196 (int) image->background_color.green,
8197 (int) image->background_color.blue);
8198 }
glennrpd71e86a2011-02-24 01:28:37 +00008199 for (i=0; i<number_opaque; i++)
8200 {
glennrpca7ad3a2011-04-26 04:44:54 +00008201 if (opaque[i].red == image->background_color.red &&
8202 opaque[i].green == image->background_color.green &&
8203 opaque[i].blue == image->background_color.blue)
8204 break;
glennrpd71e86a2011-02-24 01:28:37 +00008205 }
glennrpd71e86a2011-02-24 01:28:37 +00008206 if (number_opaque < 259 && i == number_opaque)
8207 {
glennrp8e045c82011-04-27 16:40:27 +00008208 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008209 ping_background.index = i;
8210 if (logging != MagickFalse)
8211 {
8212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8213 " background_color index is %d",(int) i);
8214 }
8215
glennrpd71e86a2011-02-24 01:28:37 +00008216 }
glennrpa080bc32011-03-11 18:03:44 +00008217 else if (logging != MagickFalse)
8218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8219 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008220 }
8221
8222 image_colors=number_opaque+number_transparent+number_semitransparent;
8223
glennrpa080bc32011-03-11 18:03:44 +00008224 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8225 {
8226 /* No room for the background color; remove it. */
8227 number_opaque--;
8228 image_colors--;
8229 }
8230
glennrpd71e86a2011-02-24 01:28:37 +00008231 if (logging != MagickFalse)
8232 {
8233 if (image_colors > 256)
8234 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8235 " image has more than 256 colors");
8236
8237 else
8238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8239 " image has %d colors",image_colors);
8240 }
8241
glennrp8d3d6e52011-04-19 04:39:51 +00008242 if (ping_preserve_colormap != MagickFalse)
8243 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008244
glennrpfd05d622011-02-25 04:10:33 +00008245 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008246 {
8247 ping_have_color=MagickFalse;
8248 ping_have_non_bw=MagickFalse;
8249
8250 if(image_colors > 256)
8251 {
8252 for (y=0; y < (ssize_t) image->rows; y++)
8253 {
8254 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8255
cristyacd2ed22011-08-30 01:44:23 +00008256 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008257 break;
8258
glennrpe5e6b802011-07-20 14:44:40 +00008259 s=q;
8260 for (x=0; x < (ssize_t) image->columns; x++)
8261 {
8262 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8263 GetPixelRed(image,s) != GetPixelBlue(image,s))
8264 {
8265 ping_have_color=MagickTrue;
8266 ping_have_non_bw=MagickTrue;
8267 break;
8268 }
8269 s+=GetPixelChannels(image);
8270 }
8271
8272 if (ping_have_color != MagickFalse)
8273 break;
8274
glennrpd71e86a2011-02-24 01:28:37 +00008275 /* Worst case is black-and-white; we are looking at every
8276 * pixel twice.
8277 */
8278
glennrpd71e86a2011-02-24 01:28:37 +00008279 if (ping_have_non_bw == MagickFalse)
8280 {
8281 s=q;
8282 for (x=0; x < (ssize_t) image->columns; x++)
8283 {
cristy4c08aed2011-07-01 19:47:50 +00008284 if (GetPixelRed(image,s) != 0 &&
8285 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008286 {
8287 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008288 break;
glennrpd71e86a2011-02-24 01:28:37 +00008289 }
cristyed231572011-07-14 02:18:59 +00008290 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008291 }
glennrpe5e6b802011-07-20 14:44:40 +00008292 }
glennrpd71e86a2011-02-24 01:28:37 +00008293 }
glennrpbb4f99d2011-05-22 11:13:17 +00008294 }
8295 }
glennrpd71e86a2011-02-24 01:28:37 +00008296
8297 if (image_colors < 257)
8298 {
cristy101ab702011-10-13 13:06:32 +00008299 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008300 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008301
glennrpd71e86a2011-02-24 01:28:37 +00008302 /*
8303 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008304 */
8305
glennrpd71e86a2011-02-24 01:28:37 +00008306 if (logging != MagickFalse)
8307 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8308 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008309
glennrpd71e86a2011-02-24 01:28:37 +00008310 /* Sort palette, transparent first */;
8311
8312 n = 0;
8313
8314 for (i=0; i<number_transparent; i++)
8315 colormap[n++] = transparent[i];
8316
8317 for (i=0; i<number_semitransparent; i++)
8318 colormap[n++] = semitransparent[i];
8319
8320 for (i=0; i<number_opaque; i++)
8321 colormap[n++] = opaque[i];
8322
glennrpc6c391a2011-04-27 02:23:56 +00008323 ping_background.index +=
8324 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008325
glennrpd71e86a2011-02-24 01:28:37 +00008326 /* image_colors < 257; search the colormap instead of the pixels
8327 * to get ping_have_color and ping_have_non_bw
8328 */
8329 for (i=0; i<n; i++)
8330 {
8331 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008332 {
glennrpd71e86a2011-02-24 01:28:37 +00008333 if (colormap[i].red != colormap[i].green ||
8334 colormap[i].red != colormap[i].blue)
8335 {
8336 ping_have_color=MagickTrue;
8337 ping_have_non_bw=MagickTrue;
8338 break;
8339 }
8340 }
8341
8342 if (ping_have_non_bw == MagickFalse)
8343 {
8344 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008345 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008346 }
glennrp8bb3a022010-12-13 20:40:04 +00008347 }
8348
glennrpd71e86a2011-02-24 01:28:37 +00008349 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8350 (number_transparent == 0 && number_semitransparent == 0)) &&
8351 (((mng_info->write_png_colortype-1) ==
8352 PNG_COLOR_TYPE_PALETTE) ||
8353 (mng_info->write_png_colortype == 0)))
8354 {
glennrp6185c532011-01-14 17:58:40 +00008355 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008356 {
glennrpd71e86a2011-02-24 01:28:37 +00008357 if (n != (ssize_t) image_colors)
8358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8359 " image_colors (%d) and n (%d) don't match",
8360 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008361
glennrpd71e86a2011-02-24 01:28:37 +00008362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8363 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008364 }
glennrp03812ae2010-12-24 01:31:34 +00008365
glennrpd71e86a2011-02-24 01:28:37 +00008366 image->colors = image_colors;
8367
cristy018f07f2011-09-04 21:15:19 +00008368 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008369 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008370 ThrowWriterException(ResourceLimitError,
8371 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008372
8373 for (i=0; i< (ssize_t) image_colors; i++)
8374 image->colormap[i] = colormap[i];
8375
8376 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008377 {
glennrpd71e86a2011-02-24 01:28:37 +00008378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8379 " image->colors=%d (%d)",
8380 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008381
glennrpd71e86a2011-02-24 01:28:37 +00008382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8383 " Update the pixel indexes");
8384 }
glennrp6185c532011-01-14 17:58:40 +00008385
glennrpfd05d622011-02-25 04:10:33 +00008386 /* Sync the pixel indices with the new colormap */
8387
glennrpd71e86a2011-02-24 01:28:37 +00008388 for (y=0; y < (ssize_t) image->rows; y++)
8389 {
8390 q=GetAuthenticPixels(image,0,y,image->columns,1,
8391 exception);
glennrp6185c532011-01-14 17:58:40 +00008392
cristyacd2ed22011-08-30 01:44:23 +00008393 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008394 break;
glennrp6185c532011-01-14 17:58:40 +00008395
glennrpbb4f99d2011-05-22 11:13:17 +00008396
glennrpd71e86a2011-02-24 01:28:37 +00008397 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008398 {
glennrpd71e86a2011-02-24 01:28:37 +00008399 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008400 {
glennrpd71e86a2011-02-24 01:28:37 +00008401 if ((image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008402 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8403 image->colormap[i].red == GetPixelRed(image,q) &&
8404 image->colormap[i].green == GetPixelGreen(image,q) &&
8405 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008406 {
cristy4c08aed2011-07-01 19:47:50 +00008407 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008408 break;
glennrp6185c532011-01-14 17:58:40 +00008409 }
glennrp6185c532011-01-14 17:58:40 +00008410 }
cristyed231572011-07-14 02:18:59 +00008411 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008412 }
glennrp6185c532011-01-14 17:58:40 +00008413
glennrpd71e86a2011-02-24 01:28:37 +00008414 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8415 break;
8416 }
8417 }
8418 }
8419
8420 if (logging != MagickFalse)
8421 {
8422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8423 " image->colors=%d", (int) image->colors);
8424
8425 if (image->colormap != NULL)
8426 {
8427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008428 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008429
8430 for (i=0; i < (ssize_t) image->colors; i++)
8431 {
cristy72988482011-03-29 16:34:38 +00008432 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008433 {
8434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8435 " %d (%d,%d,%d,%d)",
8436 (int) i,
8437 (int) image->colormap[i].red,
8438 (int) image->colormap[i].green,
8439 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008440 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008441 }
glennrp6185c532011-01-14 17:58:40 +00008442 }
8443 }
glennrp03812ae2010-12-24 01:31:34 +00008444
glennrpd71e86a2011-02-24 01:28:37 +00008445 if (number_transparent < 257)
8446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8447 " number_transparent = %d",
8448 number_transparent);
8449 else
glennrp03812ae2010-12-24 01:31:34 +00008450
glennrpd71e86a2011-02-24 01:28:37 +00008451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8452 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008453
glennrpd71e86a2011-02-24 01:28:37 +00008454 if (number_opaque < 257)
8455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8456 " number_opaque = %d",
8457 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008458
glennrpd71e86a2011-02-24 01:28:37 +00008459 else
8460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8461 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008462
glennrpd71e86a2011-02-24 01:28:37 +00008463 if (number_semitransparent < 257)
8464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8465 " number_semitransparent = %d",
8466 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008467
glennrpd71e86a2011-02-24 01:28:37 +00008468 else
8469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8470 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008471
glennrpd71e86a2011-02-24 01:28:37 +00008472 if (ping_have_non_bw == MagickFalse)
8473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8474 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008475
glennrpd71e86a2011-02-24 01:28:37 +00008476 else if (ping_have_color == MagickFalse)
8477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8478 " All pixels and the background are gray");
8479
8480 else
8481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8482 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008483
glennrp03812ae2010-12-24 01:31:34 +00008484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8485 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008486 }
glennrpfd05d622011-02-25 04:10:33 +00008487
glennrpc8c2f062011-02-25 19:00:33 +00008488 if (mng_info->write_png8 == MagickFalse)
8489 break;
glennrpfd05d622011-02-25 04:10:33 +00008490
glennrpc8c2f062011-02-25 19:00:33 +00008491 /* Make any reductions necessary for the PNG8 format */
8492 if (image_colors <= 256 &&
8493 image_colors != 0 && image->colormap != NULL &&
8494 number_semitransparent == 0 &&
8495 number_transparent <= 1)
8496 break;
8497
8498 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008499 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8500 * transparent color so if more than one is transparent we merge
8501 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008502 */
glennrp130fc452011-08-20 03:43:18 +00008503 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008504 {
8505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8506 " Thresholding the alpha channel to binary");
8507
8508 for (y=0; y < (ssize_t) image->rows; y++)
8509 {
cristy8a20fa02011-12-27 15:54:31 +00008510 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008511
cristy4c08aed2011-07-01 19:47:50 +00008512 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008513 break;
8514
8515 for (x=0; x < (ssize_t) image->columns; x++)
8516 {
glennrpf73547f2011-08-20 04:40:26 +00008517 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008518 {
cristy803640d2011-11-17 02:11:32 +00008519 SetPixelInfoPixel(image,&image->background_color,r);
cristy4c08aed2011-07-01 19:47:50 +00008520 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008521 }
8522 else
cristy4c08aed2011-07-01 19:47:50 +00008523 SetPixelAlpha(image,OpaqueAlpha,r);
cristyed231572011-07-14 02:18:59 +00008524 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008525 }
glennrpbb4f99d2011-05-22 11:13:17 +00008526
glennrpc8c2f062011-02-25 19:00:33 +00008527 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8528 break;
8529
8530 if (image_colors != 0 && image_colors <= 256 &&
8531 image->colormap != NULL)
8532 for (i=0; i<image_colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00008533 image->colormap[i].alpha =
8534 (image->colormap[i].alpha > TransparentAlpha/2 ?
8535 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008536 }
8537 continue;
8538 }
8539
8540 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008541 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8542 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8543 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008544 */
glennrpd3371642011-03-22 19:42:23 +00008545 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8546 {
8547 if (logging != MagickFalse)
8548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8549 " Quantizing the background color to 4-4-4");
8550
8551 tried_444 = MagickTrue;
8552
glennrp91d99252011-06-25 14:30:13 +00008553 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008554
8555 if (logging != MagickFalse)
8556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8557 " Quantizing the pixel colors to 4-4-4");
8558
8559 if (image->colormap == NULL)
8560 {
8561 for (y=0; y < (ssize_t) image->rows; y++)
8562 {
cristy8a20fa02011-12-27 15:54:31 +00008563 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpd3371642011-03-22 19:42:23 +00008564
cristy4c08aed2011-07-01 19:47:50 +00008565 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008566 break;
8567
8568 for (x=0; x < (ssize_t) image->columns; x++)
8569 {
cristy4c08aed2011-07-01 19:47:50 +00008570 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008571 LBR04PixelRGB(r);
cristy8a20fa02011-12-27 15:54:31 +00008572 r+=GetPixelChannels(image);
glennrpd3371642011-03-22 19:42:23 +00008573 }
glennrpbb4f99d2011-05-22 11:13:17 +00008574
glennrpd3371642011-03-22 19:42:23 +00008575 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8576 break;
8577 }
8578 }
8579
8580 else /* Should not reach this; colormap already exists and
8581 must be <= 256 */
8582 {
8583 if (logging != MagickFalse)
8584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8585 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008586
glennrpd3371642011-03-22 19:42:23 +00008587 for (i=0; i<image_colors; i++)
8588 {
glennrp91d99252011-06-25 14:30:13 +00008589 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008590 }
8591 }
8592 continue;
8593 }
8594
glennrp82b3c532011-03-22 19:20:54 +00008595 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8596 {
8597 if (logging != MagickFalse)
8598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8599 " Quantizing the background color to 3-3-3");
8600
8601 tried_333 = MagickTrue;
8602
glennrp91d99252011-06-25 14:30:13 +00008603 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008604
8605 if (logging != MagickFalse)
8606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008607 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008608
8609 if (image->colormap == NULL)
8610 {
8611 for (y=0; y < (ssize_t) image->rows; y++)
8612 {
cristy8a20fa02011-12-27 15:54:31 +00008613 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp82b3c532011-03-22 19:20:54 +00008614
cristy4c08aed2011-07-01 19:47:50 +00008615 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008616 break;
8617
8618 for (x=0; x < (ssize_t) image->columns; x++)
8619 {
cristy4c08aed2011-07-01 19:47:50 +00008620 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8621 LBR03RGB(r);
cristy8a20fa02011-12-27 15:54:31 +00008622 r+=GetPixelChannels(image);
glennrp82b3c532011-03-22 19:20:54 +00008623 }
glennrpbb4f99d2011-05-22 11:13:17 +00008624
glennrp82b3c532011-03-22 19:20:54 +00008625 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8626 break;
8627 }
8628 }
8629
8630 else /* Should not reach this; colormap already exists and
8631 must be <= 256 */
8632 {
8633 if (logging != MagickFalse)
8634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008635 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008636 for (i=0; i<image_colors; i++)
8637 {
glennrp91d99252011-06-25 14:30:13 +00008638 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008639 }
glennrpd3371642011-03-22 19:42:23 +00008640 }
8641 continue;
glennrp82b3c532011-03-22 19:20:54 +00008642 }
glennrpc8c2f062011-02-25 19:00:33 +00008643
glennrp8ca51ad2011-05-12 21:22:32 +00008644 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008645 {
8646 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008648 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008649
glennrp8ca51ad2011-05-12 21:22:32 +00008650 tried_332 = MagickTrue;
8651
glennrp3faa9a32011-04-23 14:00:25 +00008652 /* Red and green were already done so we only quantize the blue
8653 * channel
8654 */
8655
glennrp91d99252011-06-25 14:30:13 +00008656 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008657
glennrpc8c2f062011-02-25 19:00:33 +00008658 if (logging != MagickFalse)
8659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008660 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008661
glennrpc8c2f062011-02-25 19:00:33 +00008662 if (image->colormap == NULL)
8663 {
8664 for (y=0; y < (ssize_t) image->rows; y++)
8665 {
cristy8a20fa02011-12-27 15:54:31 +00008666 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008667
cristy4c08aed2011-07-01 19:47:50 +00008668 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008669 break;
8670
8671 for (x=0; x < (ssize_t) image->columns; x++)
8672 {
cristy4c08aed2011-07-01 19:47:50 +00008673 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008674 LBR02PixelBlue(r);
cristy8a20fa02011-12-27 15:54:31 +00008675 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008676 }
glennrpbb4f99d2011-05-22 11:13:17 +00008677
glennrpc8c2f062011-02-25 19:00:33 +00008678 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8679 break;
8680 }
8681 }
glennrpfd05d622011-02-25 04:10:33 +00008682
glennrpc8c2f062011-02-25 19:00:33 +00008683 else /* Should not reach this; colormap already exists and
8684 must be <= 256 */
8685 {
8686 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008688 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008689 for (i=0; i<image_colors; i++)
8690 {
glennrp91d99252011-06-25 14:30:13 +00008691 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008692 }
8693 }
8694 continue;
8695 }
8696 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008697
8698 if (image_colors == 0 || image_colors > 256)
8699 {
8700 /* Take care of special case with 256 colors + 1 transparent
8701 * color. We don't need to quantize to 2-3-2-1; we only need to
8702 * eliminate one color, so we'll merge the two darkest red
8703 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8704 */
8705 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8706 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8707 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8708 {
8709 image->background_color.red=ScaleCharToQuantum(0x24);
8710 }
glennrpbb4f99d2011-05-22 11:13:17 +00008711
glennrp8ca51ad2011-05-12 21:22:32 +00008712 if (image->colormap == NULL)
8713 {
8714 for (y=0; y < (ssize_t) image->rows; y++)
8715 {
cristy8a20fa02011-12-27 15:54:31 +00008716 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008717
cristy4c08aed2011-07-01 19:47:50 +00008718 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008719 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008720
glennrp8ca51ad2011-05-12 21:22:32 +00008721 for (x=0; x < (ssize_t) image->columns; x++)
8722 {
cristy4c08aed2011-07-01 19:47:50 +00008723 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8724 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8725 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8726 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008727 {
cristy4c08aed2011-07-01 19:47:50 +00008728 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008729 }
cristyed231572011-07-14 02:18:59 +00008730 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008731 }
glennrpbb4f99d2011-05-22 11:13:17 +00008732
glennrp8ca51ad2011-05-12 21:22:32 +00008733 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8734 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008735
glennrp8ca51ad2011-05-12 21:22:32 +00008736 }
8737 }
8738
8739 else
8740 {
8741 for (i=0; i<image_colors; i++)
8742 {
8743 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8744 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8745 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8746 {
8747 image->colormap[i].red=ScaleCharToQuantum(0x24);
8748 }
8749 }
8750 }
8751 }
glennrpd71e86a2011-02-24 01:28:37 +00008752 }
cristy8a20fa02011-12-27 15:54:31 +00008753{ ImageInfo *image_info=AcquireImageInfo(); strcpy(image->filename,"test.pnm");
8754WriteImage(image_info,image,exception);
8755}
glennrpfd05d622011-02-25 04:10:33 +00008756 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008757
glennrpfd05d622011-02-25 04:10:33 +00008758 /* If we are excluding the tRNS chunk and there is transparency,
8759 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8760 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008761 */
glennrp0e8ea192010-12-24 18:00:33 +00008762 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8763 (number_transparent != 0 || number_semitransparent != 0))
8764 {
glennrpd17915c2011-04-29 14:24:22 +00008765 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008766
8767 if (ping_have_color == MagickFalse)
8768 mng_info->write_png_colortype = 5;
8769
8770 else
8771 mng_info->write_png_colortype = 7;
8772
glennrp8d579662011-02-23 02:05:02 +00008773 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008774 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008775 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008776
glennrp0e8ea192010-12-24 18:00:33 +00008777 }
8778
glennrpfd05d622011-02-25 04:10:33 +00008779 /* See if cheap transparency is possible. It is only possible
8780 * when there is a single transparent color, no semitransparent
8781 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008782 * as the transparent color. We only need this information if
8783 * we are writing a PNG with colortype 0 or 2, and we have not
8784 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008785 */
glennrp5a39f372011-02-25 04:52:16 +00008786 if (number_transparent == 1 &&
8787 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008788 {
8789 ping_have_cheap_transparency = MagickTrue;
8790
8791 if (number_semitransparent != 0)
8792 ping_have_cheap_transparency = MagickFalse;
8793
8794 else if (image_colors == 0 || image_colors > 256 ||
8795 image->colormap == NULL)
8796 {
cristy4c08aed2011-07-01 19:47:50 +00008797 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008798 *q;
8799
glennrpfd05d622011-02-25 04:10:33 +00008800 for (y=0; y < (ssize_t) image->rows; y++)
8801 {
8802 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8803
cristyacd2ed22011-08-30 01:44:23 +00008804 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008805 break;
8806
8807 for (x=0; x < (ssize_t) image->columns; x++)
8808 {
cristy4c08aed2011-07-01 19:47:50 +00008809 if (GetPixelAlpha(image,q) != TransparentAlpha &&
glennrp847370c2011-07-05 17:37:15 +00008810 (unsigned short) GetPixelRed(image,q) ==
8811 ping_trans_color.red &&
8812 (unsigned short) GetPixelGreen(image,q) ==
8813 ping_trans_color.green &&
8814 (unsigned short) GetPixelBlue(image,q) ==
8815 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008816 {
8817 ping_have_cheap_transparency = MagickFalse;
8818 break;
8819 }
8820
cristyed231572011-07-14 02:18:59 +00008821 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008822 }
glennrpbb4f99d2011-05-22 11:13:17 +00008823
glennrpfd05d622011-02-25 04:10:33 +00008824 if (ping_have_cheap_transparency == MagickFalse)
8825 break;
8826 }
8827 }
8828 else
8829 {
glennrp67b9c1a2011-04-22 18:47:36 +00008830 /* Assuming that image->colormap[0] is the one transparent color
8831 * and that all others are opaque.
8832 */
glennrpfd05d622011-02-25 04:10:33 +00008833 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008834 for (i=1; i<image_colors; i++)
8835 if (image->colormap[i].red == image->colormap[0].red &&
8836 image->colormap[i].green == image->colormap[0].green &&
8837 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008838 {
glennrp67b9c1a2011-04-22 18:47:36 +00008839 ping_have_cheap_transparency = MagickFalse;
8840 break;
glennrpfd05d622011-02-25 04:10:33 +00008841 }
8842 }
glennrpbb4f99d2011-05-22 11:13:17 +00008843
glennrpfd05d622011-02-25 04:10:33 +00008844 if (logging != MagickFalse)
8845 {
8846 if (ping_have_cheap_transparency == MagickFalse)
8847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8848 " Cheap transparency is not possible.");
8849
8850 else
8851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8852 " Cheap transparency is possible.");
8853 }
8854 }
8855 else
8856 ping_have_cheap_transparency = MagickFalse;
8857
glennrp8640fb52010-11-23 15:48:26 +00008858 image_depth=image->depth;
8859
glennrp26c990a2010-11-23 02:23:20 +00008860 quantum_info = (QuantumInfo *) NULL;
8861 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008862 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008863 image_matte=image->matte;
8864
glennrp0fe50b42010-11-16 03:52:51 +00008865 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008866 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008867
glennrp52a479c2011-02-26 21:14:38 +00008868 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8869 (image->colors == 0 || image->colormap == NULL))
8870 {
glennrp52a479c2011-02-26 21:14:38 +00008871 image_info=DestroyImageInfo(image_info);
8872 image=DestroyImage(image);
cristyc82a27b2011-10-21 01:07:16 +00008873 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
glennrp15e01552011-03-06 23:29:17 +00008874 "Cannot write PNG8 or color-type 3; colormap is NULL",
8875 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008876#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8877 UnlockSemaphoreInfo(ping_semaphore);
8878#endif
8879 return(MagickFalse);
8880 }
8881
cristy3ed852e2009-09-05 21:47:34 +00008882 /*
8883 Allocate the PNG structures
8884 */
8885#ifdef PNG_USER_MEM_SUPPORTED
cristyc82a27b2011-10-21 01:07:16 +00008886 error_info.image=image;
8887 error_info.exception=exception;
8888 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008889 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8890 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008891
cristy3ed852e2009-09-05 21:47:34 +00008892#else
cristyc82a27b2011-10-21 01:07:16 +00008893 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008894 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008895
cristy3ed852e2009-09-05 21:47:34 +00008896#endif
8897 if (ping == (png_struct *) NULL)
8898 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008899
cristy3ed852e2009-09-05 21:47:34 +00008900 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008901
cristy3ed852e2009-09-05 21:47:34 +00008902 if (ping_info == (png_info *) NULL)
8903 {
8904 png_destroy_write_struct(&ping,(png_info **) NULL);
8905 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8906 }
glennrp0fe50b42010-11-16 03:52:51 +00008907
cristy3ed852e2009-09-05 21:47:34 +00008908 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008909 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008910
glennrp5af765f2010-03-30 11:12:18 +00008911 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008912 {
8913 /*
8914 PNG write failed.
8915 */
8916#ifdef PNG_DEBUG
8917 if (image_info->verbose)
8918 (void) printf("PNG write has failed.\n");
8919#endif
8920 png_destroy_write_struct(&ping,&ping_info);
8921#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008922 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008923#endif
glennrpda8f3a72011-02-27 23:54:12 +00008924 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008925 (void) CloseBlob(image);
8926 image_info=DestroyImageInfo(image_info);
8927 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008928 return(MagickFalse);
8929 }
8930 /*
8931 Prepare PNG for writing.
8932 */
8933#if defined(PNG_MNG_FEATURES_SUPPORTED)
8934 if (mng_info->write_mng)
8935 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008936
cristy3ed852e2009-09-05 21:47:34 +00008937#else
8938# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8939 if (mng_info->write_mng)
8940 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008941
cristy3ed852e2009-09-05 21:47:34 +00008942# endif
8943#endif
glennrp2b013e42010-11-24 16:55:50 +00008944
cristy3ed852e2009-09-05 21:47:34 +00008945 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008946
cristy4e5bc842010-06-09 13:56:01 +00008947 ping_width=(png_uint_32) image->columns;
8948 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008949
cristy3ed852e2009-09-05 21:47:34 +00008950 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8951 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008952
cristy3ed852e2009-09-05 21:47:34 +00008953 if (mng_info->write_png_depth != 0)
8954 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008955
cristy3ed852e2009-09-05 21:47:34 +00008956 /* Adjust requested depth to next higher valid depth if necessary */
8957 if (image_depth > 8)
8958 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008959
cristy3ed852e2009-09-05 21:47:34 +00008960 if ((image_depth > 4) && (image_depth < 8))
8961 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008962
cristy3ed852e2009-09-05 21:47:34 +00008963 if (image_depth == 3)
8964 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008965
cristy3ed852e2009-09-05 21:47:34 +00008966 if (logging != MagickFalse)
8967 {
8968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008969 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008971 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008973 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008975 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008977 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008978 }
glennrp8640fb52010-11-23 15:48:26 +00008979
cristy3ed852e2009-09-05 21:47:34 +00008980 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008981 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008982
glennrp26f37912010-12-23 16:22:42 +00008983
cristy3ed852e2009-09-05 21:47:34 +00008984#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008985 if (ping_exclude_pHYs == MagickFalse)
8986 {
cristy2a11bef2011-10-28 18:33:11 +00008987 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00008988 (!mng_info->write_mng || !mng_info->equal_physs))
8989 {
glennrp0fe50b42010-11-16 03:52:51 +00008990 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8992 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008993
8994 if (image->units == PixelsPerInchResolution)
8995 {
glennrpdfd70802010-11-14 01:23:35 +00008996 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008997 ping_pHYs_x_resolution=
cristy2a11bef2011-10-28 18:33:11 +00008998 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
glennrp823b55c2011-03-14 18:46:46 +00008999 ping_pHYs_y_resolution=
cristy2a11bef2011-10-28 18:33:11 +00009000 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00009001 }
glennrpdfd70802010-11-14 01:23:35 +00009002
cristy3ed852e2009-09-05 21:47:34 +00009003 else if (image->units == PixelsPerCentimeterResolution)
9004 {
glennrpdfd70802010-11-14 01:23:35 +00009005 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
cristy2a11bef2011-10-28 18:33:11 +00009006 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9007 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00009008 }
glennrp991d11d2010-11-12 21:55:28 +00009009
cristy3ed852e2009-09-05 21:47:34 +00009010 else
9011 {
glennrpdfd70802010-11-14 01:23:35 +00009012 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
cristy2a11bef2011-10-28 18:33:11 +00009013 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9014 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00009015 }
glennrp991d11d2010-11-12 21:55:28 +00009016
glennrp823b55c2011-03-14 18:46:46 +00009017 if (logging != MagickFalse)
9018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9019 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9020 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9021 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009022 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009023 }
glennrp26f37912010-12-23 16:22:42 +00009024 }
cristy3ed852e2009-09-05 21:47:34 +00009025#endif
glennrpa521b2f2010-10-29 04:11:03 +00009026
glennrp26f37912010-12-23 16:22:42 +00009027 if (ping_exclude_bKGD == MagickFalse)
9028 {
glennrpa521b2f2010-10-29 04:11:03 +00009029 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009030 {
glennrpa521b2f2010-10-29 04:11:03 +00009031 unsigned int
9032 mask;
cristy3ed852e2009-09-05 21:47:34 +00009033
glennrpa521b2f2010-10-29 04:11:03 +00009034 mask=0xffff;
9035 if (ping_bit_depth == 8)
9036 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009037
glennrpa521b2f2010-10-29 04:11:03 +00009038 if (ping_bit_depth == 4)
9039 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009040
glennrpa521b2f2010-10-29 04:11:03 +00009041 if (ping_bit_depth == 2)
9042 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009043
glennrpa521b2f2010-10-29 04:11:03 +00009044 if (ping_bit_depth == 1)
9045 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009046
glennrpa521b2f2010-10-29 04:11:03 +00009047 ping_background.red=(png_uint_16)
9048 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009049
glennrpa521b2f2010-10-29 04:11:03 +00009050 ping_background.green=(png_uint_16)
9051 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009052
glennrpa521b2f2010-10-29 04:11:03 +00009053 ping_background.blue=(png_uint_16)
9054 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009055
9056 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009057 }
cristy3ed852e2009-09-05 21:47:34 +00009058
glennrp0fe50b42010-11-16 03:52:51 +00009059 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009060 {
9061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9062 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9064 " background_color index is %d",
9065 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009066
9067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9068 " ping_bit_depth=%d",ping_bit_depth);
9069 }
glennrp0fe50b42010-11-16 03:52:51 +00009070
9071 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009072 }
glennrp0fe50b42010-11-16 03:52:51 +00009073
cristy3ed852e2009-09-05 21:47:34 +00009074 /*
9075 Select the color type.
9076 */
9077 matte=image_matte;
9078 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009079
glennrp1273f7b2011-02-24 03:20:30 +00009080 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009081 {
glennrp0fe50b42010-11-16 03:52:51 +00009082
glennrpfd05d622011-02-25 04:10:33 +00009083 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009084 for reducing the sample depth from 8. */
9085
glennrp0fe50b42010-11-16 03:52:51 +00009086 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009087
glennrp8bb3a022010-12-13 20:40:04 +00009088 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009089
9090 /*
9091 Set image palette.
9092 */
9093 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9094
glennrp0fe50b42010-11-16 03:52:51 +00009095 if (logging != MagickFalse)
9096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9097 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009098 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009099
9100 for (i=0; i < (ssize_t) number_colors; i++)
9101 {
9102 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9103 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9104 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9105 if (logging != MagickFalse)
9106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009107#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009108 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009109#else
9110 " %5ld (%5d,%5d,%5d)",
9111#endif
glennrp0fe50b42010-11-16 03:52:51 +00009112 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9113
9114 }
glennrp2b013e42010-11-24 16:55:50 +00009115
glennrp8bb3a022010-12-13 20:40:04 +00009116 ping_have_PLTE=MagickTrue;
9117 image_depth=ping_bit_depth;
9118 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009119
glennrp58e01762011-01-07 15:28:54 +00009120 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009121 {
glennrp0fe50b42010-11-16 03:52:51 +00009122 /*
9123 Identify which colormap entry is transparent.
9124 */
9125 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009126 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009127
glennrp8bb3a022010-12-13 20:40:04 +00009128 for (i=0; i < (ssize_t) number_transparent; i++)
9129 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009130
glennrp0fe50b42010-11-16 03:52:51 +00009131
glennrp2cc891a2010-12-24 13:44:32 +00009132 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009133 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009134
9135 if (ping_num_trans == 0)
9136 ping_have_tRNS=MagickFalse;
9137
glennrp8bb3a022010-12-13 20:40:04 +00009138 else
9139 ping_have_tRNS=MagickTrue;
9140 }
glennrp0fe50b42010-11-16 03:52:51 +00009141
glennrp1273f7b2011-02-24 03:20:30 +00009142 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009143 {
glennrp1273f7b2011-02-24 03:20:30 +00009144 /*
9145 * Identify which colormap entry is the background color.
9146 */
9147
glennrp4f25bd02011-01-01 18:51:28 +00009148 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9149 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9150 break;
glennrp0fe50b42010-11-16 03:52:51 +00009151
glennrp4f25bd02011-01-01 18:51:28 +00009152 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009153
9154 if (logging != MagickFalse)
9155 {
9156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9157 " background_color index is %d",
9158 (int) ping_background.index);
9159 }
glennrp4f25bd02011-01-01 18:51:28 +00009160 }
cristy3ed852e2009-09-05 21:47:34 +00009161 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009162
glennrp7e65e932011-08-19 02:31:16 +00009163 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009164 {
9165 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009166 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009167 }
glennrp0fe50b42010-11-16 03:52:51 +00009168
glennrp7e65e932011-08-19 02:31:16 +00009169 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009170 {
9171 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009172 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009173 }
glennrp0fe50b42010-11-16 03:52:51 +00009174
glennrp8bb3a022010-12-13 20:40:04 +00009175 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009176 {
glennrp5af765f2010-03-30 11:12:18 +00009177 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009178
glennrp8bb3a022010-12-13 20:40:04 +00009179 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009180 {
glennrp5af765f2010-03-30 11:12:18 +00009181 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009182
glennrp5af765f2010-03-30 11:12:18 +00009183 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9184 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009185 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009186
glennrp8bb3a022010-12-13 20:40:04 +00009187 else
9188 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009189
9190 if (logging != MagickFalse)
9191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9192 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009193 }
glennrp0fe50b42010-11-16 03:52:51 +00009194
glennrp7c4c9e62011-03-21 20:23:32 +00009195 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009196 {
9197 if (logging != MagickFalse)
9198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009199 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009200
glennrpd6bf1612010-12-17 17:28:54 +00009201 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009202 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009203
glennrpd6bf1612010-12-17 17:28:54 +00009204 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009205 {
glennrp5af765f2010-03-30 11:12:18 +00009206 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009207 image_matte=MagickFalse;
9208 }
glennrp0fe50b42010-11-16 03:52:51 +00009209
glennrpd6bf1612010-12-17 17:28:54 +00009210 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009211 {
glennrp5af765f2010-03-30 11:12:18 +00009212 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009213 image_matte=MagickTrue;
9214 }
glennrp0fe50b42010-11-16 03:52:51 +00009215
glennrp5aa37f62011-01-02 03:07:57 +00009216 if (image_info->type == PaletteType ||
9217 image_info->type == PaletteMatteType)
9218 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9219
glennrp7c4c9e62011-03-21 20:23:32 +00009220 if (mng_info->write_png_colortype == 0 &&
9221 (image_info->type == UndefinedType ||
9222 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009223 {
glennrp5aa37f62011-01-02 03:07:57 +00009224 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009225 {
glennrp5aa37f62011-01-02 03:07:57 +00009226 if (image_matte == MagickFalse)
9227 {
9228 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9229 image_matte=MagickFalse;
9230 }
glennrp0fe50b42010-11-16 03:52:51 +00009231
glennrp0b206f52011-01-07 04:55:32 +00009232 else
glennrp5aa37f62011-01-02 03:07:57 +00009233 {
9234 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9235 image_matte=MagickTrue;
9236 }
9237 }
9238 else
glennrp8bb3a022010-12-13 20:40:04 +00009239 {
glennrp5aa37f62011-01-02 03:07:57 +00009240 if (image_matte == MagickFalse)
9241 {
9242 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9243 image_matte=MagickFalse;
9244 }
glennrp8bb3a022010-12-13 20:40:04 +00009245
glennrp0b206f52011-01-07 04:55:32 +00009246 else
glennrp5aa37f62011-01-02 03:07:57 +00009247 {
9248 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9249 image_matte=MagickTrue;
9250 }
9251 }
glennrp0fe50b42010-11-16 03:52:51 +00009252 }
glennrp5aa37f62011-01-02 03:07:57 +00009253
cristy3ed852e2009-09-05 21:47:34 +00009254 }
glennrp0fe50b42010-11-16 03:52:51 +00009255
cristy3ed852e2009-09-05 21:47:34 +00009256 if (logging != MagickFalse)
9257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009258 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009259
glennrp5af765f2010-03-30 11:12:18 +00009260 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009261 {
9262 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9263 ping_color_type == PNG_COLOR_TYPE_RGB ||
9264 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9265 ping_bit_depth=8;
9266 }
cristy3ed852e2009-09-05 21:47:34 +00009267
glennrpd6bf1612010-12-17 17:28:54 +00009268 old_bit_depth=ping_bit_depth;
9269
glennrp5af765f2010-03-30 11:12:18 +00009270 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009271 {
glennrp8d579662011-02-23 02:05:02 +00009272 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9273 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009274 }
glennrp8640fb52010-11-23 15:48:26 +00009275
glennrp5af765f2010-03-30 11:12:18 +00009276 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009277 {
cristy35ef8242010-06-03 16:24:13 +00009278 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009279 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009280
9281 if (image->colors == 0)
9282 {
glennrp0fe50b42010-11-16 03:52:51 +00009283 /* DO SOMETHING */
cristyc82a27b2011-10-21 01:07:16 +00009284 (void) ThrowMagickException(exception,
glennrp0f111982010-07-07 20:18:33 +00009285 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009286 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009287 }
9288
cristy35ef8242010-06-03 16:24:13 +00009289 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009290 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009291 }
glennrp2b013e42010-11-24 16:55:50 +00009292
glennrpd6bf1612010-12-17 17:28:54 +00009293 if (logging != MagickFalse)
9294 {
9295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9296 " Number of colors: %.20g",(double) image_colors);
9297
9298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9299 " Tentative PNG bit depth: %d",ping_bit_depth);
9300 }
9301
9302 if (ping_bit_depth < (int) mng_info->write_png_depth)
9303 ping_bit_depth = mng_info->write_png_depth;
9304 }
glennrp2cc891a2010-12-24 13:44:32 +00009305
glennrp5af765f2010-03-30 11:12:18 +00009306 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009307
cristy3ed852e2009-09-05 21:47:34 +00009308 if (logging != MagickFalse)
9309 {
9310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009311 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009312
cristy3ed852e2009-09-05 21:47:34 +00009313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009314 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009315
cristy3ed852e2009-09-05 21:47:34 +00009316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009317 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009318
cristy3ed852e2009-09-05 21:47:34 +00009319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009320
glennrp8640fb52010-11-23 15:48:26 +00009321 " image->depth: %.20g",(double) image->depth);
9322
9323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009324 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009325 }
9326
glennrp58e01762011-01-07 15:28:54 +00009327 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009328 {
glennrp4f25bd02011-01-01 18:51:28 +00009329 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009330 {
glennrp7c4c9e62011-03-21 20:23:32 +00009331 if (mng_info->write_png_colortype == 0)
9332 {
9333 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009334
glennrp7c4c9e62011-03-21 20:23:32 +00009335 if (ping_have_color != MagickFalse)
9336 ping_color_type=PNG_COLOR_TYPE_RGBA;
9337 }
glennrp4f25bd02011-01-01 18:51:28 +00009338
9339 /*
9340 * Determine if there is any transparent color.
9341 */
9342 if (number_transparent + number_semitransparent == 0)
9343 {
9344 /*
9345 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9346 */
glennrpa6a06632011-01-19 15:15:34 +00009347
glennrp4f25bd02011-01-01 18:51:28 +00009348 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009349
9350 if (mng_info->write_png_colortype == 0)
9351 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009352 }
9353
9354 else
9355 {
9356 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009357 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009358
9359 mask=0xffff;
9360
9361 if (ping_bit_depth == 8)
9362 mask=0x00ff;
9363
9364 if (ping_bit_depth == 4)
9365 mask=0x000f;
9366
9367 if (ping_bit_depth == 2)
9368 mask=0x0003;
9369
9370 if (ping_bit_depth == 1)
9371 mask=0x0001;
9372
9373 ping_trans_color.red=(png_uint_16)
9374 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9375
9376 ping_trans_color.green=(png_uint_16)
9377 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9378
9379 ping_trans_color.blue=(png_uint_16)
9380 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9381
9382 ping_trans_color.gray=(png_uint_16)
cristy101ab702011-10-13 13:06:32 +00009383 (ScaleQuantumToShort(GetPixelInfoIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009384 image->colormap)) & mask);
9385
9386 ping_trans_color.index=(png_byte) 0;
9387
9388 ping_have_tRNS=MagickTrue;
9389 }
9390
9391 if (ping_have_tRNS != MagickFalse)
9392 {
9393 /*
glennrpfd05d622011-02-25 04:10:33 +00009394 * Determine if there is one and only one transparent color
9395 * and if so if it is fully transparent.
9396 */
9397 if (ping_have_cheap_transparency == MagickFalse)
9398 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009399 }
9400
9401 if (ping_have_tRNS != MagickFalse)
9402 {
glennrp7c4c9e62011-03-21 20:23:32 +00009403 if (mng_info->write_png_colortype == 0)
9404 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009405
9406 if (image_depth == 8)
9407 {
9408 ping_trans_color.red&=0xff;
9409 ping_trans_color.green&=0xff;
9410 ping_trans_color.blue&=0xff;
9411 ping_trans_color.gray&=0xff;
9412 }
9413 }
9414 }
cristy3ed852e2009-09-05 21:47:34 +00009415 else
9416 {
cristy3ed852e2009-09-05 21:47:34 +00009417 if (image_depth == 8)
9418 {
glennrp5af765f2010-03-30 11:12:18 +00009419 ping_trans_color.red&=0xff;
9420 ping_trans_color.green&=0xff;
9421 ping_trans_color.blue&=0xff;
9422 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009423 }
9424 }
9425 }
glennrp8640fb52010-11-23 15:48:26 +00009426
cristy3ed852e2009-09-05 21:47:34 +00009427 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009428
glennrp2e09f552010-11-14 00:38:48 +00009429 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009430 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009431
glennrp39992b42010-11-14 00:03:43 +00009432 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009433 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009434 ping_have_color == MagickFalse &&
9435 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009436 {
cristy35ef8242010-06-03 16:24:13 +00009437 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009438
cristy3ed852e2009-09-05 21:47:34 +00009439 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009440 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009441
glennrp7c4c9e62011-03-21 20:23:32 +00009442 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009443 {
glennrp5af765f2010-03-30 11:12:18 +00009444 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009445
cristy3ed852e2009-09-05 21:47:34 +00009446 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009447 {
9448 if (logging != MagickFalse)
9449 {
9450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9451 " Scaling ping_trans_color (0)");
9452 }
9453 ping_trans_color.gray*=0x0101;
9454 }
cristy3ed852e2009-09-05 21:47:34 +00009455 }
glennrp0fe50b42010-11-16 03:52:51 +00009456
cristy3ed852e2009-09-05 21:47:34 +00009457 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9458 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009459
glennrp136ee3a2011-04-27 15:47:45 +00009460 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009461 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009462 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009463
cristy3ed852e2009-09-05 21:47:34 +00009464 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009465 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009466
cristy3ed852e2009-09-05 21:47:34 +00009467 else
9468 {
glennrp5af765f2010-03-30 11:12:18 +00009469 ping_bit_depth=8;
9470 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009471 {
9472 if(!mng_info->write_png_depth)
9473 {
glennrp5af765f2010-03-30 11:12:18 +00009474 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009475
cristy35ef8242010-06-03 16:24:13 +00009476 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009477 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009478 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009479 }
9480 }
glennrp2b013e42010-11-24 16:55:50 +00009481
glennrp0fe50b42010-11-16 03:52:51 +00009482 else if (ping_color_type ==
9483 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009484 mng_info->IsPalette)
9485 {
cristy3ed852e2009-09-05 21:47:34 +00009486 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009487
cristy3ed852e2009-09-05 21:47:34 +00009488 int
9489 depth_4_ok=MagickTrue,
9490 depth_2_ok=MagickTrue,
9491 depth_1_ok=MagickTrue;
9492
cristybb503372010-05-27 20:51:26 +00009493 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009494 {
9495 unsigned char
9496 intensity;
9497
9498 intensity=ScaleQuantumToChar(image->colormap[i].red);
9499
9500 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9501 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9502 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9503 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009504 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009505 depth_1_ok=MagickFalse;
9506 }
glennrp2b013e42010-11-24 16:55:50 +00009507
cristy3ed852e2009-09-05 21:47:34 +00009508 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009509 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009510
cristy3ed852e2009-09-05 21:47:34 +00009511 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009512 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009513
cristy3ed852e2009-09-05 21:47:34 +00009514 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009515 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009516 }
9517 }
glennrp2b013e42010-11-24 16:55:50 +00009518
glennrp5af765f2010-03-30 11:12:18 +00009519 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009520 }
glennrp0fe50b42010-11-16 03:52:51 +00009521
cristy3ed852e2009-09-05 21:47:34 +00009522 else
glennrp0fe50b42010-11-16 03:52:51 +00009523
cristy3ed852e2009-09-05 21:47:34 +00009524 if (mng_info->IsPalette)
9525 {
glennrp17a14852010-05-10 03:01:59 +00009526 number_colors=image_colors;
9527
cristy3ed852e2009-09-05 21:47:34 +00009528 if (image_depth <= 8)
9529 {
cristy3ed852e2009-09-05 21:47:34 +00009530 /*
9531 Set image palette.
9532 */
glennrp5af765f2010-03-30 11:12:18 +00009533 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009534
glennrp58e01762011-01-07 15:28:54 +00009535 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009536 {
glennrp9c1eb072010-06-06 22:19:15 +00009537 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009538
glennrp3b51f0e2010-11-27 18:14:08 +00009539 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9541 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009542 }
glennrp0fe50b42010-11-16 03:52:51 +00009543
cristy3ed852e2009-09-05 21:47:34 +00009544 else
9545 {
cristybb503372010-05-27 20:51:26 +00009546 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009547 {
9548 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9549 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9550 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9551 }
glennrp0fe50b42010-11-16 03:52:51 +00009552
glennrp3b51f0e2010-11-27 18:14:08 +00009553 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009555 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009556 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009557
glennrp39992b42010-11-14 00:03:43 +00009558 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009559 }
glennrp0fe50b42010-11-16 03:52:51 +00009560
cristy3ed852e2009-09-05 21:47:34 +00009561 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009562 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009563 {
cristybefe4d22010-06-07 01:18:58 +00009564 size_t
9565 one;
9566
glennrp5af765f2010-03-30 11:12:18 +00009567 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009568 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009569
cristy94b11832011-09-08 19:46:03 +00009570 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009571 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009572 }
glennrp0fe50b42010-11-16 03:52:51 +00009573
glennrp5af765f2010-03-30 11:12:18 +00009574 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009575
glennrp58e01762011-01-07 15:28:54 +00009576 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009577 {
glennrp0fe50b42010-11-16 03:52:51 +00009578 /*
glennrpd6bf1612010-12-17 17:28:54 +00009579 * Set up trans_colors array.
9580 */
glennrp0fe50b42010-11-16 03:52:51 +00009581 assert(number_colors <= 256);
9582
glennrpd6bf1612010-12-17 17:28:54 +00009583 ping_num_trans=(unsigned short) (number_transparent +
9584 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009585
9586 if (ping_num_trans == 0)
9587 ping_have_tRNS=MagickFalse;
9588
glennrpd6bf1612010-12-17 17:28:54 +00009589 else
glennrp0fe50b42010-11-16 03:52:51 +00009590 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009591 if (logging != MagickFalse)
9592 {
9593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9594 " Scaling ping_trans_color (1)");
9595 }
glennrpd6bf1612010-12-17 17:28:54 +00009596 ping_have_tRNS=MagickTrue;
9597
9598 for (i=0; i < ping_num_trans; i++)
9599 {
cristy4c08aed2011-07-01 19:47:50 +00009600 ping_trans_alpha[i]= (png_byte)
9601 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009602 }
glennrp0fe50b42010-11-16 03:52:51 +00009603 }
9604 }
cristy3ed852e2009-09-05 21:47:34 +00009605 }
9606 }
glennrp0fe50b42010-11-16 03:52:51 +00009607
cristy3ed852e2009-09-05 21:47:34 +00009608 else
9609 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009610
cristy3ed852e2009-09-05 21:47:34 +00009611 if (image_depth < 8)
9612 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009613
cristy3ed852e2009-09-05 21:47:34 +00009614 if ((save_image_depth == 16) && (image_depth == 8))
9615 {
glennrp4f25bd02011-01-01 18:51:28 +00009616 if (logging != MagickFalse)
9617 {
9618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9619 " Scaling ping_trans_color from (%d,%d,%d)",
9620 (int) ping_trans_color.red,
9621 (int) ping_trans_color.green,
9622 (int) ping_trans_color.blue);
9623 }
9624
glennrp5af765f2010-03-30 11:12:18 +00009625 ping_trans_color.red*=0x0101;
9626 ping_trans_color.green*=0x0101;
9627 ping_trans_color.blue*=0x0101;
9628 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009629
9630 if (logging != MagickFalse)
9631 {
9632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9633 " to (%d,%d,%d)",
9634 (int) ping_trans_color.red,
9635 (int) ping_trans_color.green,
9636 (int) ping_trans_color.blue);
9637 }
cristy3ed852e2009-09-05 21:47:34 +00009638 }
9639 }
9640
cristy4383ec82011-01-05 15:42:32 +00009641 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9642 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009643
cristy3ed852e2009-09-05 21:47:34 +00009644 /*
9645 Adjust background and transparency samples in sub-8-bit grayscale files.
9646 */
glennrp5af765f2010-03-30 11:12:18 +00009647 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009648 PNG_COLOR_TYPE_GRAY)
9649 {
9650 png_uint_16
9651 maxval;
9652
cristy35ef8242010-06-03 16:24:13 +00009653 size_t
9654 one=1;
9655
cristy22ffd972010-06-03 16:51:47 +00009656 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009657
glennrp4f25bd02011-01-01 18:51:28 +00009658 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009659 {
cristy3ed852e2009-09-05 21:47:34 +00009660
glennrp9f0fa852011-12-15 12:20:50 +00009661 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9662 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9663 &image->background_color))) +.5)));
cristy3ed852e2009-09-05 21:47:34 +00009664
9665 if (logging != MagickFalse)
9666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009667 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9669 " background_color index is %d",
9670 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009671
glennrp991d11d2010-11-12 21:55:28 +00009672 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009673 }
cristy3ed852e2009-09-05 21:47:34 +00009674
glennrp3e3e20f2011-06-09 04:21:43 +00009675 if (logging != MagickFalse)
9676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9677 " Scaling ping_trans_color.gray from %d",
9678 (int)ping_trans_color.gray);
9679
glennrp9be9b1c2011-06-09 12:21:45 +00009680 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009681 ping_trans_color.gray)+.5);
9682
9683 if (logging != MagickFalse)
9684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9685 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009686 }
glennrp17a14852010-05-10 03:01:59 +00009687
glennrp26f37912010-12-23 16:22:42 +00009688 if (ping_exclude_bKGD == MagickFalse)
9689 {
glennrp1273f7b2011-02-24 03:20:30 +00009690 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009691 {
9692 /*
9693 Identify which colormap entry is the background color.
9694 */
9695
glennrp17a14852010-05-10 03:01:59 +00009696 number_colors=image_colors;
9697
glennrpa521b2f2010-10-29 04:11:03 +00009698 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9699 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009700 break;
9701
9702 ping_background.index=(png_byte) i;
9703
glennrp3b51f0e2010-11-27 18:14:08 +00009704 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009705 {
9706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009707 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009708 }
glennrp0fe50b42010-11-16 03:52:51 +00009709
cristy13d07042010-11-21 20:56:18 +00009710 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009711 {
9712 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009713
9714 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009715 {
9716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9717 " background =(%d,%d,%d)",
9718 (int) ping_background.red,
9719 (int) ping_background.green,
9720 (int) ping_background.blue);
9721 }
9722 }
glennrpa521b2f2010-10-29 04:11:03 +00009723
glennrpd6bf1612010-12-17 17:28:54 +00009724 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009725 {
glennrp3b51f0e2010-11-27 18:14:08 +00009726 if (logging != MagickFalse)
9727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9728 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009729 ping_have_bKGD = MagickFalse;
9730 }
glennrp17a14852010-05-10 03:01:59 +00009731 }
glennrp26f37912010-12-23 16:22:42 +00009732 }
glennrp17a14852010-05-10 03:01:59 +00009733
cristy3ed852e2009-09-05 21:47:34 +00009734 if (logging != MagickFalse)
9735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009736 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009737 /*
9738 Initialize compression level and filtering.
9739 */
9740 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009741 {
9742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9743 " Setting up deflate compression");
9744
9745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9746 " Compression buffer size: 32768");
9747 }
9748
cristy3ed852e2009-09-05 21:47:34 +00009749 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009750
cristy3ed852e2009-09-05 21:47:34 +00009751 if (logging != MagickFalse)
9752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9753 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009754
cristy4054bfb2011-08-29 23:41:39 +00009755 png_set_compression_mem_level(ping, 9);
9756
glennrp10d739e2011-06-29 18:00:52 +00009757 /* Untangle the "-quality" setting:
9758
9759 Undefined is 0; the default is used.
9760 Default is 75
9761
9762 10's digit:
9763
9764 0: Use Z_HUFFMAN_ONLY strategy with the
9765 zlib default compression level
9766
9767 1-9: the zlib compression level
9768
9769 1's digit:
9770
9771 0-4: the PNG filter method
9772
9773 5: libpng adaptive filtering if compression level > 5
9774 libpng filter type "none" if compression level <= 5
9775 or if image is grayscale or palette
9776
9777 6: libpng adaptive filtering
9778
9779 7: "LOCO" filtering (intrapixel differing) if writing
9780 a MNG, othewise "none". Did not work in IM-6.7.0-9
9781 and earlier because of a missing "else".
9782
9783 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009784 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009785
9786 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009787 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009788
9789 Note that using the -quality option, not all combinations of
9790 PNG filter type, zlib compression level, and zlib compression
cristy5e6be1e2011-07-16 01:23:39 +00009791 strategy are possible. This will be addressed soon in a
cristy5d6fc9c2011-12-27 03:10:42 +00009792 release that accomodates "-define png:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +00009793
9794 */
9795
cristy3ed852e2009-09-05 21:47:34 +00009796 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9797 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009798
glennrp18682582011-06-30 18:11:47 +00009799 if (quality <= 9)
9800 {
9801 if (mng_info->write_png_compression_strategy == 0)
9802 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9803 }
9804
9805 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009806 {
9807 int
9808 level;
9809
cristybb503372010-05-27 20:51:26 +00009810 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009811
glennrp18682582011-06-30 18:11:47 +00009812 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009813 }
glennrp0fe50b42010-11-16 03:52:51 +00009814
glennrp18682582011-06-30 18:11:47 +00009815 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009816 {
glennrp18682582011-06-30 18:11:47 +00009817 if ((quality %10) == 8 || (quality %10) == 9)
9818 mng_info->write_png_compression_strategy=Z_RLE;
cristy3ed852e2009-09-05 21:47:34 +00009819 }
glennrp0fe50b42010-11-16 03:52:51 +00009820
glennrp18682582011-06-30 18:11:47 +00009821 if (mng_info->write_png_compression_filter == 0)
9822 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9823
cristy3ed852e2009-09-05 21:47:34 +00009824 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009825 {
glennrp18682582011-06-30 18:11:47 +00009826 if (mng_info->write_png_compression_level)
9827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9828 " Compression level: %d",
9829 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009830
glennrp18682582011-06-30 18:11:47 +00009831 if (mng_info->write_png_compression_strategy)
9832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9833 " Compression strategy: %d",
9834 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009835
glennrp18682582011-06-30 18:11:47 +00009836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9837 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009838
cristy4054bfb2011-08-29 23:41:39 +00009839 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +00009840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9841 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +00009842 else if (mng_info->write_png_compression_filter == 0 ||
9843 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +00009844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9845 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009846 else
9847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9848 " Base filter method: %d",
9849 (int) mng_info->write_png_compression_filter-1);
9850 }
glennrp2b013e42010-11-24 16:55:50 +00009851
glennrp18682582011-06-30 18:11:47 +00009852 if (mng_info->write_png_compression_level != 0)
9853 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9854
9855 if (mng_info->write_png_compression_filter == 6)
9856 {
9857 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9858 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9859 (quality < 50))
9860 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9861 else
9862 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9863 }
cristy4054bfb2011-08-29 23:41:39 +00009864 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +00009865 mng_info->write_png_compression_filter == 10)
9866 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9867
9868 else if (mng_info->write_png_compression_filter == 8)
9869 {
9870#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9871 if (mng_info->write_mng)
9872 {
9873 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9874 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9875 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9876 }
9877#endif
cristy4054bfb2011-08-29 23:41:39 +00009878 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +00009879 }
9880
9881 else if (mng_info->write_png_compression_filter == 9)
9882 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9883
9884 else if (mng_info->write_png_compression_filter != 0)
9885 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9886 mng_info->write_png_compression_filter-1);
9887
9888 if (mng_info->write_png_compression_strategy != 0)
9889 png_set_compression_strategy(ping,
9890 mng_info->write_png_compression_strategy-1);
9891
cristy0d57eec2011-09-04 22:13:56 +00009892 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9893 if (ping_exclude_sRGB != MagickFalse ||
9894 (image->rendering_intent == UndefinedIntent))
9895 {
9896 if ((ping_exclude_tEXt == MagickFalse ||
9897 ping_exclude_zTXt == MagickFalse) &&
9898 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009899 {
9900 ResetImageProfileIterator(image);
9901 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009902 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009903 profile=GetImageProfile(image,name);
9904
9905 if (profile != (StringInfo *) NULL)
9906 {
glennrp5af765f2010-03-30 11:12:18 +00009907#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009908 if ((LocaleCompare(name,"ICC") == 0) ||
9909 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009910 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009911
9912 if (ping_exclude_iCCP == MagickFalse)
9913 {
cristy9f027d12011-09-21 01:17:17 +00009914 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009915#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009916 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009917#else
9918 (png_const_bytep) GetStringInfoDatum(profile),
9919#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009920 (png_uint_32) GetStringInfoLength(profile));
9921 }
glennrp26f37912010-12-23 16:22:42 +00009922 }
glennrp0fe50b42010-11-16 03:52:51 +00009923
glennrpc8cbc5d2011-01-01 00:12:34 +00009924 else
cristy3ed852e2009-09-05 21:47:34 +00009925#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009926 if (ping_exclude_zCCP == MagickFalse)
9927 {
glennrpcf002022011-01-30 02:38:15 +00009928 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009929 (unsigned char *) name,(unsigned char *) name,
9930 GetStringInfoDatum(profile),
9931 (png_uint_32) GetStringInfoLength(profile));
9932 }
9933 }
glennrp0b206f52011-01-07 04:55:32 +00009934
glennrpc8cbc5d2011-01-01 00:12:34 +00009935 if (logging != MagickFalse)
9936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9937 " Setting up text chunk with %s profile",name);
9938
9939 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009940 }
cristy0d57eec2011-09-04 22:13:56 +00009941 }
cristy3ed852e2009-09-05 21:47:34 +00009942 }
9943
9944#if defined(PNG_WRITE_sRGB_SUPPORTED)
9945 if ((mng_info->have_write_global_srgb == 0) &&
9946 ((image->rendering_intent != UndefinedIntent) ||
9947 (image->colorspace == sRGBColorspace)))
9948 {
glennrp26f37912010-12-23 16:22:42 +00009949 if (ping_exclude_sRGB == MagickFalse)
9950 {
9951 /*
9952 Note image rendering intent.
9953 */
9954 if (logging != MagickFalse)
9955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9956 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009957
glennrp26f37912010-12-23 16:22:42 +00009958 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009959 Magick_RenderingIntent_to_PNG_RenderingIntent(
9960 image->rendering_intent)));
glennrp26f37912010-12-23 16:22:42 +00009961 }
cristy3ed852e2009-09-05 21:47:34 +00009962 }
glennrp26f37912010-12-23 16:22:42 +00009963
glennrp5af765f2010-03-30 11:12:18 +00009964 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009965#endif
9966 {
glennrp2cc891a2010-12-24 13:44:32 +00009967 if (ping_exclude_gAMA == MagickFalse &&
9968 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009969 (image->gamma < .45 || image->gamma > .46)))
9970 {
cristy3ed852e2009-09-05 21:47:34 +00009971 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9972 {
9973 /*
9974 Note image gamma.
9975 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9976 */
9977 if (logging != MagickFalse)
9978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9979 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009980
cristy3ed852e2009-09-05 21:47:34 +00009981 png_set_gAMA(ping,ping_info,image->gamma);
9982 }
glennrp26f37912010-12-23 16:22:42 +00009983 }
glennrp2b013e42010-11-24 16:55:50 +00009984
glennrp26f37912010-12-23 16:22:42 +00009985 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009986 {
glennrp26f37912010-12-23 16:22:42 +00009987 if ((mng_info->have_write_global_chrm == 0) &&
9988 (image->chromaticity.red_primary.x != 0.0))
9989 {
9990 /*
9991 Note image chromaticity.
9992 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9993 */
9994 PrimaryInfo
9995 bp,
9996 gp,
9997 rp,
9998 wp;
cristy3ed852e2009-09-05 21:47:34 +00009999
glennrp26f37912010-12-23 16:22:42 +000010000 wp=image->chromaticity.white_point;
10001 rp=image->chromaticity.red_primary;
10002 gp=image->chromaticity.green_primary;
10003 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +000010004
glennrp26f37912010-12-23 16:22:42 +000010005 if (logging != MagickFalse)
10006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10007 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010008
glennrp26f37912010-12-23 16:22:42 +000010009 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10010 bp.x,bp.y);
10011 }
10012 }
cristy3ed852e2009-09-05 21:47:34 +000010013 }
glennrpdfd70802010-11-14 01:23:35 +000010014
glennrp5af765f2010-03-30 11:12:18 +000010015 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +000010016
10017 if (mng_info->write_mng)
10018 png_set_sig_bytes(ping,8);
10019
cristy5d6fc9c2011-12-27 03:10:42 +000010020 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
cristy3ed852e2009-09-05 21:47:34 +000010021
glennrpd6bf1612010-12-17 17:28:54 +000010022 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +000010023 {
10024 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +000010025 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010026 {
glennrp5af765f2010-03-30 11:12:18 +000010027 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +000010028
glennrp5af765f2010-03-30 11:12:18 +000010029 if (ping_bit_depth < 8)
10030 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +000010031 }
glennrp0fe50b42010-11-16 03:52:51 +000010032
cristy3ed852e2009-09-05 21:47:34 +000010033 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +000010034 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +000010035 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +000010036 }
10037
glennrp0e8ea192010-12-24 18:00:33 +000010038 if (ping_need_colortype_warning != MagickFalse ||
10039 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +000010040 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +000010041 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +000010042 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +000010043 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +000010044 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +000010045 {
10046 if (logging != MagickFalse)
10047 {
glennrp0e8ea192010-12-24 18:00:33 +000010048 if (ping_need_colortype_warning != MagickFalse)
10049 {
10050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10051 " Image has transparency but tRNS chunk was excluded");
10052 }
10053
cristy3ed852e2009-09-05 21:47:34 +000010054 if (mng_info->write_png_depth)
10055 {
10056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010057 " Defined png:bit-depth=%u, Computed depth=%u",
cristy3ed852e2009-09-05 21:47:34 +000010058 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +000010059 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +000010060 }
glennrp0e8ea192010-12-24 18:00:33 +000010061
cristy3ed852e2009-09-05 21:47:34 +000010062 if (mng_info->write_png_colortype)
10063 {
10064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010065 " Defined png:color-type=%u, Computed color type=%u",
cristy3ed852e2009-09-05 21:47:34 +000010066 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +000010067 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010068 }
10069 }
glennrp0e8ea192010-12-24 18:00:33 +000010070
glennrp3bd2e412010-08-10 13:34:52 +000010071 png_warning(ping,
cristy5d6fc9c2011-12-27 03:10:42 +000010072 "Cannot write image with defined png:bit-depth or png:color-type.");
cristy3ed852e2009-09-05 21:47:34 +000010073 }
10074
glennrp58e01762011-01-07 15:28:54 +000010075 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010076 {
10077 /* Add an opaque matte channel */
10078 image->matte = MagickTrue;
cristye941a752011-10-15 01:52:48 +000010079 (void) SetImageAlpha(image,OpaqueAlpha,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010080
glennrpb4a13412010-05-05 12:47:19 +000010081 if (logging != MagickFalse)
10082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10083 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +000010084 }
10085
glennrp0e319732011-01-25 21:53:13 +000010086 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +000010087 {
glennrp991d11d2010-11-12 21:55:28 +000010088 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +000010089 {
glennrp991d11d2010-11-12 21:55:28 +000010090 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010091 if (logging != MagickFalse)
10092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10093 " Setting ping_have_tRNS=MagickTrue.");
10094 }
glennrpe9c26dc2010-05-30 01:56:35 +000010095 }
10096
cristy3ed852e2009-09-05 21:47:34 +000010097 if (logging != MagickFalse)
10098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10099 " Writing PNG header chunks");
10100
glennrp5af765f2010-03-30 11:12:18 +000010101 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10102 ping_bit_depth,ping_color_type,
10103 ping_interlace_method,ping_compression_method,
10104 ping_filter_method);
10105
glennrp39992b42010-11-14 00:03:43 +000010106 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10107 {
glennrpf09bded2011-01-08 01:15:59 +000010108 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010109
glennrp3b51f0e2010-11-27 18:14:08 +000010110 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +000010111 {
glennrp8640fb52010-11-23 15:48:26 +000010112 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +000010113 {
glennrpd6bf1612010-12-17 17:28:54 +000010114 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +000010115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010116 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10117 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010118 (int) palette[i].red,
10119 (int) palette[i].green,
10120 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +000010121 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010122 (int) ping_trans_alpha[i]);
10123 else
10124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010125 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010126 (int) i,
10127 (int) palette[i].red,
10128 (int) palette[i].green,
10129 (int) palette[i].blue);
10130 }
glennrp39992b42010-11-14 00:03:43 +000010131 }
glennrp39992b42010-11-14 00:03:43 +000010132 }
10133
glennrp26f37912010-12-23 16:22:42 +000010134 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010135 {
glennrp26f37912010-12-23 16:22:42 +000010136 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010137 {
glennrp26f37912010-12-23 16:22:42 +000010138 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010139 if (logging)
10140 {
10141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10142 " Setting up bKGD chunk");
10143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10144 " background color = (%d,%d,%d)",
10145 (int) ping_background.red,
10146 (int) ping_background.green,
10147 (int) ping_background.blue);
10148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10149 " index = %d, gray=%d",
10150 (int) ping_background.index,
10151 (int) ping_background.gray);
10152 }
10153 }
glennrp26f37912010-12-23 16:22:42 +000010154 }
10155
10156 if (ping_exclude_pHYs == MagickFalse)
10157 {
10158 if (ping_have_pHYs != MagickFalse)
10159 {
10160 png_set_pHYs(ping,ping_info,
10161 ping_pHYs_x_resolution,
10162 ping_pHYs_y_resolution,
10163 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010164
10165 if (logging)
10166 {
10167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10168 " Setting up pHYs chunk");
10169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10170 " x_resolution=%lu",
10171 (unsigned long) ping_pHYs_x_resolution);
10172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10173 " y_resolution=%lu",
10174 (unsigned long) ping_pHYs_y_resolution);
10175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10176 " unit_type=%lu",
10177 (unsigned long) ping_pHYs_unit_type);
10178 }
glennrp26f37912010-12-23 16:22:42 +000010179 }
glennrpdfd70802010-11-14 01:23:35 +000010180 }
10181
10182#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010183 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010184 {
glennrp26f37912010-12-23 16:22:42 +000010185 if (image->page.x || image->page.y)
10186 {
10187 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10188 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010189
glennrp26f37912010-12-23 16:22:42 +000010190 if (logging != MagickFalse)
10191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10192 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10193 (int) image->page.x, (int) image->page.y);
10194 }
glennrpdfd70802010-11-14 01:23:35 +000010195 }
10196#endif
10197
glennrpda8f3a72011-02-27 23:54:12 +000010198 if (mng_info->need_blob != MagickFalse)
10199 {
cristyc82a27b2011-10-21 01:07:16 +000010200 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
glennrpda8f3a72011-02-27 23:54:12 +000010201 MagickFalse)
10202 png_error(ping,"WriteBlob Failed");
10203
10204 ping_have_blob=MagickTrue;
10205 }
10206
cristy3ed852e2009-09-05 21:47:34 +000010207 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010208
glennrp39992b42010-11-14 00:03:43 +000010209 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010210 {
glennrp3b51f0e2010-11-27 18:14:08 +000010211 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010212 {
10213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10214 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10215 }
10216
10217 if (ping_color_type == 3)
10218 (void) png_set_tRNS(ping, ping_info,
10219 ping_trans_alpha,
10220 ping_num_trans,
10221 NULL);
10222
10223 else
10224 {
10225 (void) png_set_tRNS(ping, ping_info,
10226 NULL,
10227 0,
10228 &ping_trans_color);
10229
glennrp3b51f0e2010-11-27 18:14:08 +000010230 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010231 {
10232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010233 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010234 (int) ping_trans_color.red,
10235 (int) ping_trans_color.green,
10236 (int) ping_trans_color.blue);
10237 }
10238 }
glennrp991d11d2010-11-12 21:55:28 +000010239 }
10240
cristy3ed852e2009-09-05 21:47:34 +000010241 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010242 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010243
cristy3ed852e2009-09-05 21:47:34 +000010244 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010245
cristy3ed852e2009-09-05 21:47:34 +000010246 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010247 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010248
glennrp26f37912010-12-23 16:22:42 +000010249 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010250 {
glennrp4f25bd02011-01-01 18:51:28 +000010251 if ((image->page.width != 0 && image->page.width != image->columns) ||
10252 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010253 {
10254 unsigned char
10255 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010256
glennrp26f37912010-12-23 16:22:42 +000010257 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10258 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010259 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010260 PNGLong(chunk+4,(png_uint_32) image->page.width);
10261 PNGLong(chunk+8,(png_uint_32) image->page.height);
10262 chunk[12]=0; /* unit = pixels */
10263 (void) WriteBlob(image,13,chunk);
10264 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10265 }
cristy3ed852e2009-09-05 21:47:34 +000010266 }
10267
10268#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010269 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010270#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010271 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010272#undef PNG_HAVE_IDAT
10273#endif
10274
10275 png_set_packing(ping);
10276 /*
10277 Allocate memory.
10278 */
10279 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010280 if (image_depth > 8)
10281 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010282 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010283 {
glennrpb4a13412010-05-05 12:47:19 +000010284 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010285 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010286 break;
glennrp0fe50b42010-11-16 03:52:51 +000010287
glennrpb4a13412010-05-05 12:47:19 +000010288 case PNG_COLOR_TYPE_GRAY_ALPHA:
10289 rowbytes*=2;
10290 break;
glennrp0fe50b42010-11-16 03:52:51 +000010291
glennrpb4a13412010-05-05 12:47:19 +000010292 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010293 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010294 break;
glennrp0fe50b42010-11-16 03:52:51 +000010295
glennrpb4a13412010-05-05 12:47:19 +000010296 default:
10297 break;
cristy3ed852e2009-09-05 21:47:34 +000010298 }
glennrp3b51f0e2010-11-27 18:14:08 +000010299
10300 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010301 {
10302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10303 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010304
glennrpb4a13412010-05-05 12:47:19 +000010305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010306 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010307 }
glennrpcf002022011-01-30 02:38:15 +000010308 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10309 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010310
glennrpcf002022011-01-30 02:38:15 +000010311 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010312 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010313
cristy3ed852e2009-09-05 21:47:34 +000010314 /*
10315 Initialize image scanlines.
10316 */
glennrp5af765f2010-03-30 11:12:18 +000010317 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010318 {
10319 /*
10320 PNG write failed.
10321 */
10322#ifdef PNG_DEBUG
10323 if (image_info->verbose)
10324 (void) printf("PNG write has failed.\n");
10325#endif
10326 png_destroy_write_struct(&ping,&ping_info);
10327 if (quantum_info != (QuantumInfo *) NULL)
10328 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010329 if (ping_pixels != (unsigned char *) NULL)
10330 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010331#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010332 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010333#endif
glennrpda8f3a72011-02-27 23:54:12 +000010334 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010335 (void) CloseBlob(image);
10336 image_info=DestroyImageInfo(image_info);
10337 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010338 return(MagickFalse);
10339 }
cristyed552522009-10-16 14:04:35 +000010340 quantum_info=AcquireQuantumInfo(image_info,image);
10341 if (quantum_info == (QuantumInfo *) NULL)
10342 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010343 quantum_info->format=UndefinedQuantumFormat;
10344 quantum_info->depth=image_depth;
10345 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010346
cristy3ed852e2009-09-05 21:47:34 +000010347 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010348 !mng_info->write_png32) &&
10349 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010350 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010351 image_matte == MagickFalse &&
10352 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010353 {
glennrp8bb3a022010-12-13 20:40:04 +000010354 /* Palette, Bilevel, or Opaque Monochrome */
cristy4c08aed2011-07-01 19:47:50 +000010355 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010356 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010357
cristy3ed852e2009-09-05 21:47:34 +000010358 quantum_info->depth=8;
10359 for (pass=0; pass < num_passes; pass++)
10360 {
10361 /*
10362 Convert PseudoClass image to a PNG monochrome image.
10363 */
cristybb503372010-05-27 20:51:26 +000010364 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010365 {
glennrpd71e86a2011-02-24 01:28:37 +000010366 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10368 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010369
cristyc82a27b2011-10-21 01:07:16 +000010370 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010371
cristy4c08aed2011-07-01 19:47:50 +000010372 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010373 break;
glennrp0fe50b42010-11-16 03:52:51 +000010374
cristy3ed852e2009-09-05 21:47:34 +000010375 if (mng_info->IsPalette)
10376 {
cristy4c08aed2011-07-01 19:47:50 +000010377 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010378 quantum_info,GrayQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010379 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10380 mng_info->write_png_depth &&
10381 mng_info->write_png_depth != old_bit_depth)
10382 {
10383 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010384 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010385 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010386 >> (8-old_bit_depth));
10387 }
10388 }
glennrp0fe50b42010-11-16 03:52:51 +000010389
cristy3ed852e2009-09-05 21:47:34 +000010390 else
10391 {
cristy4c08aed2011-07-01 19:47:50 +000010392 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010393 quantum_info,RedQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010394 }
glennrp0fe50b42010-11-16 03:52:51 +000010395
cristy3ed852e2009-09-05 21:47:34 +000010396 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010397 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010398 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010399 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010400
glennrp3b51f0e2010-11-27 18:14:08 +000010401 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10403 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010404
glennrpcf002022011-01-30 02:38:15 +000010405 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010406 }
10407 if (image->previous == (Image *) NULL)
10408 {
10409 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10410 if (status == MagickFalse)
10411 break;
10412 }
10413 }
10414 }
glennrp0fe50b42010-11-16 03:52:51 +000010415
glennrp8bb3a022010-12-13 20:40:04 +000010416 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010417 {
glennrp0fe50b42010-11-16 03:52:51 +000010418 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010419 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010420 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010421 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010422 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010423 {
cristy4c08aed2011-07-01 19:47:50 +000010424 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010425 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010426
glennrp8bb3a022010-12-13 20:40:04 +000010427 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010428 {
glennrp8bb3a022010-12-13 20:40:04 +000010429
cristybb503372010-05-27 20:51:26 +000010430 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010431 {
cristyc82a27b2011-10-21 01:07:16 +000010432 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010433
cristy4c08aed2011-07-01 19:47:50 +000010434 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010435 break;
glennrp2cc891a2010-12-24 13:44:32 +000010436
glennrp5af765f2010-03-30 11:12:18 +000010437 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010438 {
glennrp8bb3a022010-12-13 20:40:04 +000010439 if (mng_info->IsPalette)
cristy4c08aed2011-07-01 19:47:50 +000010440 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010441 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010442
glennrp8bb3a022010-12-13 20:40:04 +000010443 else
cristy4c08aed2011-07-01 19:47:50 +000010444 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010445 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010446
glennrp3b51f0e2010-11-27 18:14:08 +000010447 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010449 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010450 }
glennrp2cc891a2010-12-24 13:44:32 +000010451
glennrp8bb3a022010-12-13 20:40:04 +000010452 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10453 {
10454 if (logging != MagickFalse && y == 0)
10455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10456 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010457
cristy4c08aed2011-07-01 19:47:50 +000010458 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010459 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010460 }
glennrp2cc891a2010-12-24 13:44:32 +000010461
glennrp3b51f0e2010-11-27 18:14:08 +000010462 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010464 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010465
glennrpcf002022011-01-30 02:38:15 +000010466 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010467 }
glennrp2cc891a2010-12-24 13:44:32 +000010468
glennrp8bb3a022010-12-13 20:40:04 +000010469 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010470 {
glennrp8bb3a022010-12-13 20:40:04 +000010471 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10472 if (status == MagickFalse)
10473 break;
cristy3ed852e2009-09-05 21:47:34 +000010474 }
cristy3ed852e2009-09-05 21:47:34 +000010475 }
10476 }
glennrp8bb3a022010-12-13 20:40:04 +000010477
10478 else
10479 {
cristy4c08aed2011-07-01 19:47:50 +000010480 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010481 *p;
10482
10483 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010484 {
glennrp8bb3a022010-12-13 20:40:04 +000010485 if ((image_depth > 8) || (mng_info->write_png24 ||
10486 mng_info->write_png32 ||
10487 (!mng_info->write_png8 && !mng_info->IsPalette)))
10488 {
10489 for (y=0; y < (ssize_t) image->rows; y++)
10490 {
10491 p=GetVirtualPixels(image,0,y,image->columns,1,
cristyc82a27b2011-10-21 01:07:16 +000010492 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010493
cristy4c08aed2011-07-01 19:47:50 +000010494 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010495 break;
glennrp2cc891a2010-12-24 13:44:32 +000010496
glennrp8bb3a022010-12-13 20:40:04 +000010497 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10498 {
10499 if (image->storage_class == DirectClass)
cristy4c08aed2011-07-01 19:47:50 +000010500 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010501 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010502
glennrp8bb3a022010-12-13 20:40:04 +000010503 else
cristy4c08aed2011-07-01 19:47:50 +000010504 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010505 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010506 }
glennrp2cc891a2010-12-24 13:44:32 +000010507
glennrp8bb3a022010-12-13 20:40:04 +000010508 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10509 {
cristy4c08aed2011-07-01 19:47:50 +000010510 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010511 quantum_info,GrayAlphaQuantum,ping_pixels,
cristyc82a27b2011-10-21 01:07:16 +000010512 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010513
glennrp8bb3a022010-12-13 20:40:04 +000010514 if (logging != MagickFalse && y == 0)
10515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10516 " Writing GRAY_ALPHA PNG pixels (3)");
10517 }
glennrp2cc891a2010-12-24 13:44:32 +000010518
glennrp8bb3a022010-12-13 20:40:04 +000010519 else if (image_matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +000010520 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010521 quantum_info,RGBAQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010522
glennrp8bb3a022010-12-13 20:40:04 +000010523 else
cristy4c08aed2011-07-01 19:47:50 +000010524 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010525 quantum_info,RGBQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010526
glennrp8bb3a022010-12-13 20:40:04 +000010527 if (logging != MagickFalse && y == 0)
10528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10529 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010530
glennrpcf002022011-01-30 02:38:15 +000010531 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010532 }
10533 }
glennrp2cc891a2010-12-24 13:44:32 +000010534
glennrp8bb3a022010-12-13 20:40:04 +000010535 else
10536 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10537 mng_info->write_png32 ||
10538 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10539 {
10540 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10541 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10542 {
10543 if (logging != MagickFalse)
10544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10545 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010546
glennrp8bb3a022010-12-13 20:40:04 +000010547 quantum_info->depth=8;
10548 image_depth=8;
10549 }
glennrp2cc891a2010-12-24 13:44:32 +000010550
glennrp8bb3a022010-12-13 20:40:04 +000010551 for (y=0; y < (ssize_t) image->rows; y++)
10552 {
10553 if (logging != MagickFalse && y == 0)
10554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10555 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010556
glennrp770d1932011-03-06 22:11:17 +000010557 p=GetVirtualPixels(image,0,y,image->columns,1,
cristyc82a27b2011-10-21 01:07:16 +000010558 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010559
cristy4c08aed2011-07-01 19:47:50 +000010560 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010561 break;
glennrp2cc891a2010-12-24 13:44:32 +000010562
glennrp8bb3a022010-12-13 20:40:04 +000010563 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010564 {
glennrp4bf89732011-03-21 13:48:28 +000010565 quantum_info->depth=image->depth;
10566
cristy4c08aed2011-07-01 19:47:50 +000010567 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010568 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp44757ab2011-03-17 12:57:03 +000010569 }
glennrp2cc891a2010-12-24 13:44:32 +000010570
glennrp8bb3a022010-12-13 20:40:04 +000010571 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10572 {
10573 if (logging != MagickFalse && y == 0)
10574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10575 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010576
cristy4c08aed2011-07-01 19:47:50 +000010577 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010578 quantum_info,GrayAlphaQuantum,ping_pixels,
cristyc82a27b2011-10-21 01:07:16 +000010579 exception);
glennrp8bb3a022010-12-13 20:40:04 +000010580 }
glennrp2cc891a2010-12-24 13:44:32 +000010581
glennrp8bb3a022010-12-13 20:40:04 +000010582 else
glennrp8bb3a022010-12-13 20:40:04 +000010583 {
cristy4c08aed2011-07-01 19:47:50 +000010584 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010585 quantum_info,IndexQuantum,ping_pixels,exception);
glennrp5eae7602011-02-22 15:21:32 +000010586
10587 if (logging != MagickFalse && y <= 2)
10588 {
10589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010590 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010591
10592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10593 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10594 (int)ping_pixels[0],(int)ping_pixels[1]);
10595 }
glennrp8bb3a022010-12-13 20:40:04 +000010596 }
glennrpcf002022011-01-30 02:38:15 +000010597 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010598 }
10599 }
glennrp2cc891a2010-12-24 13:44:32 +000010600
glennrp8bb3a022010-12-13 20:40:04 +000010601 if (image->previous == (Image *) NULL)
10602 {
10603 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10604 if (status == MagickFalse)
10605 break;
10606 }
cristy3ed852e2009-09-05 21:47:34 +000010607 }
glennrp8bb3a022010-12-13 20:40:04 +000010608 }
10609 }
10610
cristyb32b90a2009-09-07 21:45:48 +000010611 if (quantum_info != (QuantumInfo *) NULL)
10612 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010613
10614 if (logging != MagickFalse)
10615 {
10616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010617 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010618
cristy3ed852e2009-09-05 21:47:34 +000010619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010620 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010621
cristy3ed852e2009-09-05 21:47:34 +000010622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010623 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010624
cristy3ed852e2009-09-05 21:47:34 +000010625 if (mng_info->write_png_depth)
10626 {
10627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010628 " Defined png:bit-depth: %d",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010629 }
glennrp0fe50b42010-11-16 03:52:51 +000010630
cristy3ed852e2009-09-05 21:47:34 +000010631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010632 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010633
cristy3ed852e2009-09-05 21:47:34 +000010634 if (mng_info->write_png_colortype)
10635 {
10636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010637 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010638 }
glennrp0fe50b42010-11-16 03:52:51 +000010639
cristy3ed852e2009-09-05 21:47:34 +000010640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010641 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010642
cristy3ed852e2009-09-05 21:47:34 +000010643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010644 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010645 }
10646 /*
glennrpa0ed0092011-04-18 16:36:29 +000010647 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010648 */
glennrp823b55c2011-03-14 18:46:46 +000010649 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010650 {
glennrp26f37912010-12-23 16:22:42 +000010651 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010652 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010653 while (property != (const char *) NULL)
10654 {
10655 png_textp
10656 text;
glennrp2cc891a2010-12-24 13:44:32 +000010657
cristyd15e6592011-10-15 00:13:06 +000010658 value=GetImageProperty(image,property,exception);
glennrpa0ed0092011-04-18 16:36:29 +000010659
10660 /* Don't write any "png:" properties; those are just for "identify" */
10661 if (LocaleNCompare(property,"png:",4) != 0 &&
10662
10663 /* Suppress density and units if we wrote a pHYs chunk */
10664 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010665 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010666 LocaleCompare(property,"units") != 0) &&
10667
10668 /* Suppress the IM-generated Date:create and Date:modify */
10669 (ping_exclude_date == MagickFalse ||
10670 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010671 {
glennrpc70af4a2011-03-07 00:08:23 +000010672 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010673 {
glennrpc70af4a2011-03-07 00:08:23 +000010674 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10675 text[0].key=(char *) property;
10676 text[0].text=(char *) value;
10677 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010678
glennrpc70af4a2011-03-07 00:08:23 +000010679 if (ping_exclude_tEXt != MagickFalse)
10680 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10681
10682 else if (ping_exclude_zTXt != MagickFalse)
10683 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10684
10685 else
glennrp26f37912010-12-23 16:22:42 +000010686 {
glennrpc70af4a2011-03-07 00:08:23 +000010687 text[0].compression=image_info->compression == NoCompression ||
10688 (image_info->compression == UndefinedCompression &&
10689 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10690 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010691 }
glennrp2cc891a2010-12-24 13:44:32 +000010692
glennrpc70af4a2011-03-07 00:08:23 +000010693 if (logging != MagickFalse)
10694 {
10695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10696 " Setting up text chunk");
10697
10698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10699 " keyword: %s",text[0].key);
10700 }
10701
10702 png_set_text(ping,ping_info,text,1);
10703 png_free(ping,text);
10704 }
glennrp26f37912010-12-23 16:22:42 +000010705 }
10706 property=GetNextImageProperty(image);
10707 }
cristy3ed852e2009-09-05 21:47:34 +000010708 }
10709
10710 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010711 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010712
10713 if (logging != MagickFalse)
10714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10715 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010716
cristy3ed852e2009-09-05 21:47:34 +000010717 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010718
cristy3ed852e2009-09-05 21:47:34 +000010719 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10720 {
10721 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010722 (ping_width != mng_info->page.width) ||
10723 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010724 {
10725 unsigned char
10726 chunk[32];
10727
10728 /*
10729 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10730 */
10731 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10732 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010733 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010734 chunk[4]=4;
10735 chunk[5]=0; /* frame name separator (no name) */
10736 chunk[6]=1; /* flag for changing delay, for next frame only */
10737 chunk[7]=0; /* flag for changing frame timeout */
10738 chunk[8]=1; /* flag for changing frame clipping for next frame */
10739 chunk[9]=0; /* flag for changing frame sync_id */
10740 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10741 chunk[14]=0; /* clipping boundaries delta type */
10742 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10743 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010744 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010745 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10746 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010747 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010748 (void) WriteBlob(image,31,chunk);
10749 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10750 mng_info->old_framing_mode=4;
10751 mng_info->framing_mode=1;
10752 }
glennrp0fe50b42010-11-16 03:52:51 +000010753
cristy3ed852e2009-09-05 21:47:34 +000010754 else
10755 mng_info->framing_mode=3;
10756 }
10757 if (mng_info->write_mng && !mng_info->need_fram &&
10758 ((int) image->dispose == 3))
cristyc82a27b2011-10-21 01:07:16 +000010759 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010760 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010761 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010762
cristy3ed852e2009-09-05 21:47:34 +000010763 /*
10764 Free PNG resources.
10765 */
glennrp5af765f2010-03-30 11:12:18 +000010766
cristy3ed852e2009-09-05 21:47:34 +000010767 png_destroy_write_struct(&ping,&ping_info);
10768
glennrpcf002022011-01-30 02:38:15 +000010769 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010770
10771#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010772 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010773#endif
10774
glennrpda8f3a72011-02-27 23:54:12 +000010775 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010776 (void) CloseBlob(image);
10777
10778 image_info=DestroyImageInfo(image_info);
10779 image=DestroyImage(image);
10780
10781 /* Store bit depth actually written */
10782 s[0]=(char) ping_bit_depth;
10783 s[1]='\0';
10784
cristyd15e6592011-10-15 00:13:06 +000010785 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
glennrpb9cfe272010-12-21 15:08:06 +000010786
cristy3ed852e2009-09-05 21:47:34 +000010787 if (logging != MagickFalse)
10788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10789 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010790
cristy3ed852e2009-09-05 21:47:34 +000010791 return(MagickTrue);
10792/* End write one PNG image */
10793}
10794
10795/*
10796%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10797% %
10798% %
10799% %
10800% W r i t e P N G I m a g e %
10801% %
10802% %
10803% %
10804%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10805%
10806% WritePNGImage() writes a Portable Network Graphics (PNG) or
10807% Multiple-image Network Graphics (MNG) image file.
10808%
10809% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10810%
10811% The format of the WritePNGImage method is:
10812%
cristy1e178e72011-08-28 19:44:34 +000010813% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10814% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010815%
10816% A description of each parameter follows:
10817%
10818% o image_info: the image info.
10819%
10820% o image: The image.
10821%
cristy1e178e72011-08-28 19:44:34 +000010822% o exception: return any errors or warnings in this structure.
10823%
cristy3ed852e2009-09-05 21:47:34 +000010824% Returns MagickTrue on success, MagickFalse on failure.
10825%
10826% Communicating with the PNG encoder:
10827%
10828% While the datastream written is always in PNG format and normally would
10829% be given the "png" file extension, this method also writes the following
cristy5d6fc9c2011-12-27 03:10:42 +000010830% pseudo-formats which are subsets of png:
cristy3ed852e2009-09-05 21:47:34 +000010831%
glennrp5a39f372011-02-25 04:52:16 +000010832% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10833% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010834% is present, the tRNS chunk must only have values 0 and 255
10835% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010836% transparent). If other values are present they will be
10837% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010838% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000010839% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10840% of any resulting fully-transparent pixels is changed to
10841% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000010842%
10843% If you want better quantization or dithering of the colors
10844% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010845% PNG encoder. The pixels contain 8-bit indices even if
10846% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010847% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010848% PNG grayscale type might be slightly more efficient. Please
10849% note that writing to the PNG8 format may result in loss
10850% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010851%
10852% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10853% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010854% one of the colors as transparent. The only loss incurred
10855% is reduction of sample depth to 8. If the image has more
10856% than one transparent color, has semitransparent pixels, or
10857% has an opaque pixel with the same RGB components as the
10858% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010859%
10860% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10861% transparency is permitted, i.e., the alpha sample for
10862% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010863% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010864% The only loss in data is the reduction of the sample depth
10865% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010866%
10867% o -define: For more precise control of the PNG output, you can use the
10868% Image options "png:bit-depth" and "png:color-type". These
10869% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010870% from the application programming interfaces. The options
10871% are case-independent and are converted to lowercase before
10872% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010873%
10874% png:color-type can be 0, 2, 3, 4, or 6.
10875%
10876% When png:color-type is 0 (Grayscale), png:bit-depth can
10877% be 1, 2, 4, 8, or 16.
10878%
10879% When png:color-type is 2 (RGB), png:bit-depth can
10880% be 8 or 16.
10881%
10882% When png:color-type is 3 (Indexed), png:bit-depth can
10883% be 1, 2, 4, or 8. This refers to the number of bits
10884% used to store the index. The color samples always have
10885% bit-depth 8 in indexed PNG files.
10886%
10887% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10888% png:bit-depth can be 8 or 16.
10889%
glennrp5a39f372011-02-25 04:52:16 +000010890% If the image cannot be written without loss with the requested bit-depth
10891% and color-type, a PNG file will not be written, and the encoder will
10892% return MagickFalse.
10893%
cristy3ed852e2009-09-05 21:47:34 +000010894% Since image encoders should not be responsible for the "heavy lifting",
10895% the user should make sure that ImageMagick has already reduced the
10896% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010897% transparency prior to attempting to write the image with depth, color,
glennrp54dc0692011-07-01 19:02:13 +000010898% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010899%
cristy3ed852e2009-09-05 21:47:34 +000010900% Note that another definition, "png:bit-depth-written" exists, but it
10901% is not intended for external use. It is only used internally by the
10902% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10903%
10904% It is possible to request that the PNG encoder write previously-formatted
10905% ancillary chunks in the output PNG file, using the "-profile" commandline
10906% option as shown below or by setting the profile via a programming
10907% interface:
10908%
10909% -profile PNG-chunk-x:<file>
10910%
10911% where x is a location flag and <file> is a file containing the chunk
10912% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010913% This encoder will compute the chunk length and CRC, so those must not
10914% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010915%
10916% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10917% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10918% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010919% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010920%
glennrpbb8a7332010-11-13 15:17:35 +000010921% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010922%
glennrp3241bd02010-12-12 04:36:28 +000010923% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010924%
glennrpd6afd542010-11-19 01:53:05 +000010925% o 32-bit depth is reduced to 16.
10926% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10927% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010928% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010929% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010930% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010931% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10932% this can be done without loss and a larger bit depth N was not
cristy5d6fc9c2011-12-27 03:10:42 +000010933% requested via the "-define png:bit-depth=N" option.
glennrpd6afd542010-11-19 01:53:05 +000010934% o If matte channel is present but only one transparent color is
10935% present, RGB+tRNS is written instead of RGBA
10936% o Opaque matte channel is removed (or added, if color-type 4 or 6
10937% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010938%
cristy3ed852e2009-09-05 21:47:34 +000010939%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10940*/
10941static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +000010942 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010943{
10944 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010945 excluding,
10946 logging,
10947 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010948 status;
10949
10950 MngInfo
10951 *mng_info;
10952
10953 const char
10954 *value;
10955
10956 int
glennrp21f0e622011-01-07 16:20:57 +000010957 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010958 source;
10959
cristy3ed852e2009-09-05 21:47:34 +000010960 /*
10961 Open image file.
10962 */
10963 assert(image_info != (const ImageInfo *) NULL);
10964 assert(image_info->signature == MagickSignature);
10965 assert(image != (Image *) NULL);
10966 assert(image->signature == MagickSignature);
10967 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010968 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010969 /*
10970 Allocate a MngInfo structure.
10971 */
10972 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010973 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010974
cristy3ed852e2009-09-05 21:47:34 +000010975 if (mng_info == (MngInfo *) NULL)
10976 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010977
cristy3ed852e2009-09-05 21:47:34 +000010978 /*
10979 Initialize members of the MngInfo structure.
10980 */
10981 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10982 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010983 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010984 have_mng_structure=MagickTrue;
10985
10986 /* See if user has requested a specific PNG subformat */
10987
10988 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10989 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10990 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10991
10992 if (mng_info->write_png8)
10993 {
glennrp9c1eb072010-06-06 22:19:15 +000010994 mng_info->write_png_colortype = /* 3 */ 4;
10995 mng_info->write_png_depth = 8;
10996 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010997 }
10998
10999 if (mng_info->write_png24)
11000 {
glennrp9c1eb072010-06-06 22:19:15 +000011001 mng_info->write_png_colortype = /* 2 */ 3;
11002 mng_info->write_png_depth = 8;
11003 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011004
glennrp9c1eb072010-06-06 22:19:15 +000011005 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011006 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011007
glennrp9c1eb072010-06-06 22:19:15 +000011008 else
cristy018f07f2011-09-04 21:15:19 +000011009 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011010
cristyea1a8aa2011-10-20 13:24:06 +000011011 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011012 }
11013
11014 if (mng_info->write_png32)
11015 {
glennrp9c1eb072010-06-06 22:19:15 +000011016 mng_info->write_png_colortype = /* 6 */ 7;
11017 mng_info->write_png_depth = 8;
11018 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011019
glennrp9c1eb072010-06-06 22:19:15 +000011020 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011021 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011022
glennrp9c1eb072010-06-06 22:19:15 +000011023 else
cristy018f07f2011-09-04 21:15:19 +000011024 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011025
cristyea1a8aa2011-10-20 13:24:06 +000011026 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011027 }
11028
11029 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011030
cristy3ed852e2009-09-05 21:47:34 +000011031 if (value != (char *) NULL)
11032 {
11033 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011034 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011035
cristy3ed852e2009-09-05 21:47:34 +000011036 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011037 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011038
cristy3ed852e2009-09-05 21:47:34 +000011039 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011040 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011041
cristy3ed852e2009-09-05 21:47:34 +000011042 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011043 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011044
cristy3ed852e2009-09-05 21:47:34 +000011045 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011046 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011047
glennrpbb8a7332010-11-13 15:17:35 +000011048 else
cristyc82a27b2011-10-21 01:07:16 +000011049 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011050 GetMagickModule(),CoderWarning,
11051 "ignoring invalid defined png:bit-depth",
11052 "=%s",value);
11053
cristy3ed852e2009-09-05 21:47:34 +000011054 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011056 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011057 }
glennrp0fe50b42010-11-16 03:52:51 +000011058
cristy3ed852e2009-09-05 21:47:34 +000011059 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011060
cristy3ed852e2009-09-05 21:47:34 +000011061 if (value != (char *) NULL)
11062 {
11063 /* We must store colortype+1 because 0 is a valid colortype */
11064 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011065 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011066
cristy3ed852e2009-09-05 21:47:34 +000011067 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011068 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011069
cristy3ed852e2009-09-05 21:47:34 +000011070 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011071 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011072
cristy3ed852e2009-09-05 21:47:34 +000011073 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011074 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011075
cristy3ed852e2009-09-05 21:47:34 +000011076 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011077 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011078
glennrpbb8a7332010-11-13 15:17:35 +000011079 else
cristyc82a27b2011-10-21 01:07:16 +000011080 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011081 GetMagickModule(),CoderWarning,
11082 "ignoring invalid defined png:color-type",
11083 "=%s",value);
11084
cristy3ed852e2009-09-05 21:47:34 +000011085 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011087 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011088 }
11089
glennrp0e8ea192010-12-24 18:00:33 +000011090 /* Check for chunks to be excluded:
11091 *
glennrp0dff56c2011-01-29 19:10:02 +000011092 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011093 * listed in the "unused_chunks" array, above.
11094 *
cristy5d6fc9c2011-12-27 03:10:42 +000011095 * Chunks can be listed for exclusion via a "png:exclude-chunk"
glennrp0e8ea192010-12-24 18:00:33 +000011096 * define (in the image properties or in the image artifacts)
11097 * or via a mng_info member. For convenience, in addition
11098 * to or instead of a comma-separated list of chunks, the
11099 * "exclude-chunk" string can be simply "all" or "none".
11100 *
11101 * The exclude-chunk define takes priority over the mng_info.
11102 *
cristy5d6fc9c2011-12-27 03:10:42 +000011103 * A "png:include-chunk" define takes priority over both the
11104 * mng_info and the "png:exclude-chunk" define. Like the
glennrp0e8ea192010-12-24 18:00:33 +000011105 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011106 * well as a comma-separated list. Chunks that are unknown to
11107 * ImageMagick are always excluded, regardless of their "copy-safe"
11108 * status according to the PNG specification, and even if they
11109 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000011110 *
11111 * Finally, all chunks listed in the "unused_chunks" array are
11112 * automatically excluded, regardless of the other instructions
11113 * or lack thereof.
11114 *
11115 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11116 * will not be written and the gAMA chunk will only be written if it
11117 * is not between .45 and .46, or approximately (1.0/2.2).
11118 *
11119 * If you exclude tRNS and the image has transparency, the colortype
11120 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11121 *
11122 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011123 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011124 */
11125
glennrp26f37912010-12-23 16:22:42 +000011126 mng_info->ping_exclude_bKGD=MagickFalse;
11127 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011128 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011129 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11130 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011131 mng_info->ping_exclude_iCCP=MagickFalse;
11132 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11133 mng_info->ping_exclude_oFFs=MagickFalse;
11134 mng_info->ping_exclude_pHYs=MagickFalse;
11135 mng_info->ping_exclude_sRGB=MagickFalse;
11136 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011137 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011138 mng_info->ping_exclude_vpAg=MagickFalse;
11139 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11140 mng_info->ping_exclude_zTXt=MagickFalse;
11141
glennrp8d3d6e52011-04-19 04:39:51 +000011142 mng_info->ping_preserve_colormap=MagickFalse;
11143
11144 value=GetImageArtifact(image,"png:preserve-colormap");
11145 if (value == NULL)
11146 value=GetImageOption(image_info,"png:preserve-colormap");
11147 if (value != NULL)
11148 mng_info->ping_preserve_colormap=MagickTrue;
11149
glennrp18682582011-06-30 18:11:47 +000011150 /* Thes compression-level, compression-strategy, and compression-filter
11151 * defines take precedence over values from the -quality option.
11152 */
11153 value=GetImageArtifact(image,"png:compression-level");
11154 if (value == NULL)
11155 value=GetImageOption(image_info,"png:compression-level");
11156 if (value != NULL)
11157 {
glennrp18682582011-06-30 18:11:47 +000011158 /* We have to add 1 to everything because 0 is a valid input,
11159 * and we want to use 0 (the default) to mean undefined.
11160 */
11161 if (LocaleCompare(value,"0") == 0)
11162 mng_info->write_png_compression_level = 1;
11163
11164 if (LocaleCompare(value,"1") == 0)
11165 mng_info->write_png_compression_level = 2;
11166
11167 else if (LocaleCompare(value,"2") == 0)
11168 mng_info->write_png_compression_level = 3;
11169
11170 else if (LocaleCompare(value,"3") == 0)
11171 mng_info->write_png_compression_level = 4;
11172
11173 else if (LocaleCompare(value,"4") == 0)
11174 mng_info->write_png_compression_level = 5;
11175
11176 else if (LocaleCompare(value,"5") == 0)
11177 mng_info->write_png_compression_level = 6;
11178
11179 else if (LocaleCompare(value,"6") == 0)
11180 mng_info->write_png_compression_level = 7;
11181
11182 else if (LocaleCompare(value,"7") == 0)
11183 mng_info->write_png_compression_level = 8;
11184
11185 else if (LocaleCompare(value,"8") == 0)
11186 mng_info->write_png_compression_level = 9;
11187
11188 else if (LocaleCompare(value,"9") == 0)
11189 mng_info->write_png_compression_level = 10;
11190
11191 else
cristyc82a27b2011-10-21 01:07:16 +000011192 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011193 GetMagickModule(),CoderWarning,
11194 "ignoring invalid defined png:compression-level",
11195 "=%s",value);
11196 }
11197
11198 value=GetImageArtifact(image,"png:compression-strategy");
11199 if (value == NULL)
11200 value=GetImageOption(image_info,"png:compression-strategy");
11201 if (value != NULL)
11202 {
11203
11204 if (LocaleCompare(value,"0") == 0)
11205 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11206
11207 else if (LocaleCompare(value,"1") == 0)
11208 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11209
11210 else if (LocaleCompare(value,"2") == 0)
11211 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11212
11213 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011214#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011215 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011216#else
11217 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11218#endif
glennrp18682582011-06-30 18:11:47 +000011219
11220 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011221#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011222 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011223#else
11224 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11225#endif
glennrp18682582011-06-30 18:11:47 +000011226
11227 else
cristyc82a27b2011-10-21 01:07:16 +000011228 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011229 GetMagickModule(),CoderWarning,
11230 "ignoring invalid defined png:compression-strategy",
11231 "=%s",value);
11232 }
11233
11234 value=GetImageArtifact(image,"png:compression-filter");
11235 if (value == NULL)
11236 value=GetImageOption(image_info,"png:compression-filter");
11237 if (value != NULL)
11238 {
11239
11240 /* To do: combinations of filters allowed by libpng
11241 * masks 0x08 through 0xf8
11242 *
11243 * Implement this as a comma-separated list of 0,1,2,3,4,5
11244 * where 5 is a special case meaning PNG_ALL_FILTERS.
11245 */
11246
11247 if (LocaleCompare(value,"0") == 0)
11248 mng_info->write_png_compression_filter = 1;
11249
11250 if (LocaleCompare(value,"1") == 0)
11251 mng_info->write_png_compression_filter = 2;
11252
11253 else if (LocaleCompare(value,"2") == 0)
11254 mng_info->write_png_compression_filter = 3;
11255
11256 else if (LocaleCompare(value,"3") == 0)
11257 mng_info->write_png_compression_filter = 4;
11258
11259 else if (LocaleCompare(value,"4") == 0)
11260 mng_info->write_png_compression_filter = 5;
11261
11262 else if (LocaleCompare(value,"5") == 0)
11263 mng_info->write_png_compression_filter = 6;
11264
glennrp18682582011-06-30 18:11:47 +000011265 else
cristyc82a27b2011-10-21 01:07:16 +000011266 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011267 GetMagickModule(),CoderWarning,
11268 "ignoring invalid defined png:compression-filter",
11269 "=%s",value);
11270 }
11271
glennrp03812ae2010-12-24 01:31:34 +000011272 excluding=MagickFalse;
11273
glennrp5c7cf4e2010-12-24 00:30:00 +000011274 for (source=0; source<1; source++)
11275 {
11276 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011277 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011278 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011279
11280 if (value == NULL)
11281 value=GetImageArtifact(image,"png:exclude-chunks");
11282 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011283 else
glennrpacba0042010-12-24 14:27:26 +000011284 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011285 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011286
glennrpacba0042010-12-24 14:27:26 +000011287 if (value == NULL)
11288 value=GetImageOption(image_info,"png:exclude-chunks");
11289 }
11290
glennrp03812ae2010-12-24 01:31:34 +000011291 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011292 {
glennrp03812ae2010-12-24 01:31:34 +000011293
11294 size_t
11295 last;
11296
11297 excluding=MagickTrue;
11298
11299 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011300 {
11301 if (source == 0)
11302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11303 " png:exclude-chunk=%s found in image artifacts.\n", value);
11304 else
11305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11306 " png:exclude-chunk=%s found in image properties.\n", value);
11307 }
glennrp03812ae2010-12-24 01:31:34 +000011308
11309 last=strlen(value);
11310
11311 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011312 {
glennrp03812ae2010-12-24 01:31:34 +000011313
11314 if (LocaleNCompare(value+i,"all",3) == 0)
11315 {
11316 mng_info->ping_exclude_bKGD=MagickTrue;
11317 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011318 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011319 mng_info->ping_exclude_EXIF=MagickTrue;
11320 mng_info->ping_exclude_gAMA=MagickTrue;
11321 mng_info->ping_exclude_iCCP=MagickTrue;
11322 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11323 mng_info->ping_exclude_oFFs=MagickTrue;
11324 mng_info->ping_exclude_pHYs=MagickTrue;
11325 mng_info->ping_exclude_sRGB=MagickTrue;
11326 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011327 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011328 mng_info->ping_exclude_vpAg=MagickTrue;
11329 mng_info->ping_exclude_zCCP=MagickTrue;
11330 mng_info->ping_exclude_zTXt=MagickTrue;
11331 i--;
11332 }
glennrp2cc891a2010-12-24 13:44:32 +000011333
glennrp03812ae2010-12-24 01:31:34 +000011334 if (LocaleNCompare(value+i,"none",4) == 0)
11335 {
11336 mng_info->ping_exclude_bKGD=MagickFalse;
11337 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011338 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011339 mng_info->ping_exclude_EXIF=MagickFalse;
11340 mng_info->ping_exclude_gAMA=MagickFalse;
11341 mng_info->ping_exclude_iCCP=MagickFalse;
11342 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11343 mng_info->ping_exclude_oFFs=MagickFalse;
11344 mng_info->ping_exclude_pHYs=MagickFalse;
11345 mng_info->ping_exclude_sRGB=MagickFalse;
11346 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011347 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011348 mng_info->ping_exclude_vpAg=MagickFalse;
11349 mng_info->ping_exclude_zCCP=MagickFalse;
11350 mng_info->ping_exclude_zTXt=MagickFalse;
11351 }
glennrp2cc891a2010-12-24 13:44:32 +000011352
glennrp03812ae2010-12-24 01:31:34 +000011353 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11354 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011355
glennrp03812ae2010-12-24 01:31:34 +000011356 if (LocaleNCompare(value+i,"chrm",4) == 0)
11357 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011358
glennrpa0ed0092011-04-18 16:36:29 +000011359 if (LocaleNCompare(value+i,"date",4) == 0)
11360 mng_info->ping_exclude_date=MagickTrue;
11361
glennrp03812ae2010-12-24 01:31:34 +000011362 if (LocaleNCompare(value+i,"exif",4) == 0)
11363 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011364
glennrp03812ae2010-12-24 01:31:34 +000011365 if (LocaleNCompare(value+i,"gama",4) == 0)
11366 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011367
glennrp03812ae2010-12-24 01:31:34 +000011368 if (LocaleNCompare(value+i,"iccp",4) == 0)
11369 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011370
glennrp03812ae2010-12-24 01:31:34 +000011371 /*
11372 if (LocaleNCompare(value+i,"itxt",4) == 0)
11373 mng_info->ping_exclude_iTXt=MagickTrue;
11374 */
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,"offs",4) == 0)
11380 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011381
glennrp03812ae2010-12-24 01:31:34 +000011382 if (LocaleNCompare(value+i,"phys",4) == 0)
11383 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011384
glennrpa1e3b7b2010-12-24 16:37:33 +000011385 if (LocaleNCompare(value+i,"srgb",4) == 0)
11386 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011387
glennrp03812ae2010-12-24 01:31:34 +000011388 if (LocaleNCompare(value+i,"text",4) == 0)
11389 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011390
glennrpa1e3b7b2010-12-24 16:37:33 +000011391 if (LocaleNCompare(value+i,"trns",4) == 0)
11392 mng_info->ping_exclude_tRNS=MagickTrue;
11393
glennrp03812ae2010-12-24 01:31:34 +000011394 if (LocaleNCompare(value+i,"vpag",4) == 0)
11395 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011396
glennrp03812ae2010-12-24 01:31:34 +000011397 if (LocaleNCompare(value+i,"zccp",4) == 0)
11398 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011399
glennrp03812ae2010-12-24 01:31:34 +000011400 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11401 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011402
glennrp03812ae2010-12-24 01:31:34 +000011403 }
glennrpce91ed52010-12-23 22:37:49 +000011404 }
glennrp26f37912010-12-23 16:22:42 +000011405 }
11406
glennrp5c7cf4e2010-12-24 00:30:00 +000011407 for (source=0; source<1; source++)
11408 {
11409 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011410 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011411 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011412
11413 if (value == NULL)
11414 value=GetImageArtifact(image,"png:include-chunks");
11415 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011416 else
glennrpacba0042010-12-24 14:27:26 +000011417 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011418 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011419
glennrpacba0042010-12-24 14:27:26 +000011420 if (value == NULL)
11421 value=GetImageOption(image_info,"png:include-chunks");
11422 }
11423
glennrp03812ae2010-12-24 01:31:34 +000011424 if (value != NULL)
11425 {
11426 size_t
11427 last;
glennrp26f37912010-12-23 16:22:42 +000011428
glennrp03812ae2010-12-24 01:31:34 +000011429 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011430
glennrp03812ae2010-12-24 01:31:34 +000011431 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011432 {
11433 if (source == 0)
11434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11435 " png:include-chunk=%s found in image artifacts.\n", value);
11436 else
11437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11438 " png:include-chunk=%s found in image properties.\n", value);
11439 }
glennrp03812ae2010-12-24 01:31:34 +000011440
11441 last=strlen(value);
11442
11443 for (i=0; i<(int) last; i+=5)
11444 {
11445 if (LocaleNCompare(value+i,"all",3) == 0)
11446 {
11447 mng_info->ping_exclude_bKGD=MagickFalse;
11448 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011449 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011450 mng_info->ping_exclude_EXIF=MagickFalse;
11451 mng_info->ping_exclude_gAMA=MagickFalse;
11452 mng_info->ping_exclude_iCCP=MagickFalse;
11453 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11454 mng_info->ping_exclude_oFFs=MagickFalse;
11455 mng_info->ping_exclude_pHYs=MagickFalse;
11456 mng_info->ping_exclude_sRGB=MagickFalse;
11457 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011458 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011459 mng_info->ping_exclude_vpAg=MagickFalse;
11460 mng_info->ping_exclude_zCCP=MagickFalse;
11461 mng_info->ping_exclude_zTXt=MagickFalse;
11462 i--;
11463 }
glennrp2cc891a2010-12-24 13:44:32 +000011464
glennrp03812ae2010-12-24 01:31:34 +000011465 if (LocaleNCompare(value+i,"none",4) == 0)
11466 {
11467 mng_info->ping_exclude_bKGD=MagickTrue;
11468 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011469 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011470 mng_info->ping_exclude_EXIF=MagickTrue;
11471 mng_info->ping_exclude_gAMA=MagickTrue;
11472 mng_info->ping_exclude_iCCP=MagickTrue;
11473 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11474 mng_info->ping_exclude_oFFs=MagickTrue;
11475 mng_info->ping_exclude_pHYs=MagickTrue;
11476 mng_info->ping_exclude_sRGB=MagickTrue;
11477 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011478 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011479 mng_info->ping_exclude_vpAg=MagickTrue;
11480 mng_info->ping_exclude_zCCP=MagickTrue;
11481 mng_info->ping_exclude_zTXt=MagickTrue;
11482 }
glennrp2cc891a2010-12-24 13:44:32 +000011483
glennrp03812ae2010-12-24 01:31:34 +000011484 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11485 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011486
glennrp03812ae2010-12-24 01:31:34 +000011487 if (LocaleNCompare(value+i,"chrm",4) == 0)
11488 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011489
glennrpa0ed0092011-04-18 16:36:29 +000011490 if (LocaleNCompare(value+i,"date",4) == 0)
11491 mng_info->ping_exclude_date=MagickFalse;
11492
glennrp03812ae2010-12-24 01:31:34 +000011493 if (LocaleNCompare(value+i,"exif",4) == 0)
11494 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011495
glennrp03812ae2010-12-24 01:31:34 +000011496 if (LocaleNCompare(value+i,"gama",4) == 0)
11497 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011498
glennrp03812ae2010-12-24 01:31:34 +000011499 if (LocaleNCompare(value+i,"iccp",4) == 0)
11500 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011501
glennrp03812ae2010-12-24 01:31:34 +000011502 /*
11503 if (LocaleNCompare(value+i,"itxt",4) == 0)
11504 mng_info->ping_exclude_iTXt=MagickFalse;
11505 */
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,"offs",4) == 0)
11511 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011512
glennrp03812ae2010-12-24 01:31:34 +000011513 if (LocaleNCompare(value+i,"phys",4) == 0)
11514 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011515
glennrpa1e3b7b2010-12-24 16:37:33 +000011516 if (LocaleNCompare(value+i,"srgb",4) == 0)
11517 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011518
glennrp03812ae2010-12-24 01:31:34 +000011519 if (LocaleNCompare(value+i,"text",4) == 0)
11520 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011521
glennrpa1e3b7b2010-12-24 16:37:33 +000011522 if (LocaleNCompare(value+i,"trns",4) == 0)
11523 mng_info->ping_exclude_tRNS=MagickFalse;
11524
glennrp03812ae2010-12-24 01:31:34 +000011525 if (LocaleNCompare(value+i,"vpag",4) == 0)
11526 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011527
glennrp03812ae2010-12-24 01:31:34 +000011528 if (LocaleNCompare(value+i,"zccp",4) == 0)
11529 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011530
glennrp03812ae2010-12-24 01:31:34 +000011531 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11532 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011533
glennrp03812ae2010-12-24 01:31:34 +000011534 }
glennrpce91ed52010-12-23 22:37:49 +000011535 }
glennrp26f37912010-12-23 16:22:42 +000011536 }
11537
glennrp03812ae2010-12-24 01:31:34 +000011538 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011539 {
11540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011541 " Chunks to be excluded from the output png:");
glennrp26f37912010-12-23 16:22:42 +000011542 if (mng_info->ping_exclude_bKGD != MagickFalse)
11543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11544 " bKGD");
11545 if (mng_info->ping_exclude_cHRM != MagickFalse)
11546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11547 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011548 if (mng_info->ping_exclude_date != MagickFalse)
11549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11550 " date");
glennrp26f37912010-12-23 16:22:42 +000011551 if (mng_info->ping_exclude_EXIF != MagickFalse)
11552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11553 " EXIF");
11554 if (mng_info->ping_exclude_gAMA != MagickFalse)
11555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11556 " gAMA");
11557 if (mng_info->ping_exclude_iCCP != MagickFalse)
11558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11559 " iCCP");
11560/*
11561 if (mng_info->ping_exclude_iTXt != MagickFalse)
11562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11563 " iTXt");
11564*/
11565 if (mng_info->ping_exclude_oFFs != MagickFalse)
11566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11567 " oFFs");
11568 if (mng_info->ping_exclude_pHYs != MagickFalse)
11569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11570 " pHYs");
11571 if (mng_info->ping_exclude_sRGB != MagickFalse)
11572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11573 " sRGB");
11574 if (mng_info->ping_exclude_tEXt != MagickFalse)
11575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11576 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011577 if (mng_info->ping_exclude_tRNS != MagickFalse)
11578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11579 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011580 if (mng_info->ping_exclude_vpAg != MagickFalse)
11581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11582 " vpAg");
11583 if (mng_info->ping_exclude_zCCP != MagickFalse)
11584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11585 " zCCP");
11586 if (mng_info->ping_exclude_zTXt != MagickFalse)
11587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11588 " zTXt");
11589 }
11590
glennrpb9cfe272010-12-21 15:08:06 +000011591 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011592
cristy018f07f2011-09-04 21:15:19 +000011593 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011594
11595 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011596
cristy3ed852e2009-09-05 21:47:34 +000011597 if (logging != MagickFalse)
11598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011599
cristy3ed852e2009-09-05 21:47:34 +000011600 return(status);
11601}
11602
11603#if defined(JNG_SUPPORTED)
11604
11605/* Write one JNG image */
11606static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +000011607 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011608{
11609 Image
11610 *jpeg_image;
11611
11612 ImageInfo
11613 *jpeg_image_info;
11614
11615 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011616 logging,
cristy3ed852e2009-09-05 21:47:34 +000011617 status;
11618
11619 size_t
11620 length;
11621
11622 unsigned char
11623 *blob,
11624 chunk[80],
11625 *p;
11626
11627 unsigned int
11628 jng_alpha_compression_method,
11629 jng_alpha_sample_depth,
11630 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011631 transparent;
11632
cristybb503372010-05-27 20:51:26 +000011633 size_t
cristy3ed852e2009-09-05 21:47:34 +000011634 jng_quality;
11635
11636 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011637 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011638
11639 blob=(unsigned char *) NULL;
11640 jpeg_image=(Image *) NULL;
11641 jpeg_image_info=(ImageInfo *) NULL;
11642
11643 status=MagickTrue;
11644 transparent=image_info->type==GrayscaleMatteType ||
11645 image_info->type==TrueColorMatteType;
11646 jng_color_type=10;
11647 jng_alpha_sample_depth=0;
11648 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11649 jng_alpha_compression_method=0;
11650
11651 if (image->matte != MagickFalse)
11652 {
11653 /* if any pixels are transparent */
11654 transparent=MagickTrue;
11655 if (image_info->compression==JPEGCompression)
11656 jng_alpha_compression_method=8;
11657 }
11658
11659 if (transparent)
11660 {
cristybd5a96c2011-08-21 00:04:26 +000011661 ChannelType
11662 channel_mask;
11663
cristy3ed852e2009-09-05 21:47:34 +000011664 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011665
cristy3ed852e2009-09-05 21:47:34 +000011666 /* Create JPEG blob, image, and image_info */
11667 if (logging != MagickFalse)
11668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +000011669 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011670
cristy3ed852e2009-09-05 21:47:34 +000011671 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011672
cristy3ed852e2009-09-05 21:47:34 +000011673 if (jpeg_image_info == (ImageInfo *) NULL)
11674 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011675
cristy3ed852e2009-09-05 21:47:34 +000011676 if (logging != MagickFalse)
11677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11678 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011679
cristyc82a27b2011-10-21 01:07:16 +000011680 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011681
cristy3ed852e2009-09-05 21:47:34 +000011682 if (jpeg_image == (Image *) NULL)
11683 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011684
cristy3ed852e2009-09-05 21:47:34 +000011685 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristybd5a96c2011-08-21 00:04:26 +000011686 channel_mask=SetPixelChannelMask(jpeg_image,AlphaChannel);
cristye941a752011-10-15 01:52:48 +000011687 status=SeparateImage(jpeg_image,exception);
cristye2a912b2011-12-05 20:02:07 +000011688 (void) SetPixelChannelMapMask(jpeg_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +000011689 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011690
cristy3ed852e2009-09-05 21:47:34 +000011691 if (jng_quality >= 1000)
11692 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000011693
cristy3ed852e2009-09-05 21:47:34 +000011694 else
11695 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000011696
cristy3ed852e2009-09-05 21:47:34 +000011697 jpeg_image_info->type=GrayscaleType;
cristy018f07f2011-09-04 21:15:19 +000011698 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000011699 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011700 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011701 "%s",jpeg_image->filename);
11702 }
11703
11704 /* To do: check bit depth of PNG alpha channel */
11705
11706 /* Check if image is grayscale. */
11707 if (image_info->type != TrueColorMatteType && image_info->type !=
cristyc82a27b2011-10-21 01:07:16 +000011708 TrueColorType && ImageIsGray(image,exception))
cristy3ed852e2009-09-05 21:47:34 +000011709 jng_color_type-=2;
11710
11711 if (transparent)
11712 {
11713 if (jng_alpha_compression_method==0)
11714 {
11715 const char
11716 *value;
11717
cristy4c08aed2011-07-01 19:47:50 +000011718 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011719 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000011720 exception);
cristy3ed852e2009-09-05 21:47:34 +000011721 if (logging != MagickFalse)
11722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11723 " Creating PNG blob.");
11724 length=0;
11725
11726 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11727 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11728 jpeg_image_info->interlace=NoInterlace;
11729
11730 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyc82a27b2011-10-21 01:07:16 +000011731 exception);
cristy3ed852e2009-09-05 21:47:34 +000011732
11733 /* Retrieve sample depth used */
cristyd15e6592011-10-15 00:13:06 +000011734 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
cristy3ed852e2009-09-05 21:47:34 +000011735 if (value != (char *) NULL)
11736 jng_alpha_sample_depth= (unsigned int) value[0];
11737 }
11738 else
11739 {
cristy4c08aed2011-07-01 19:47:50 +000011740 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011741
11742 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000011743 exception);
cristy3ed852e2009-09-05 21:47:34 +000011744
11745 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11746 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11747 jpeg_image_info->interlace=NoInterlace;
11748 if (logging != MagickFalse)
11749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11750 " Creating blob.");
11751 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyc82a27b2011-10-21 01:07:16 +000011752 exception);
cristy3ed852e2009-09-05 21:47:34 +000011753 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011754
cristy3ed852e2009-09-05 21:47:34 +000011755 if (logging != MagickFalse)
11756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011757 " Successfully read jpeg_image into a blob, length=%.20g.",
11758 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011759
11760 }
11761 /* Destroy JPEG image and image_info */
11762 jpeg_image=DestroyImage(jpeg_image);
11763 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11764 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11765 }
11766
11767 /* Write JHDR chunk */
11768 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11769 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011770 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011771 PNGLong(chunk+4,(png_uint_32) image->columns);
11772 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011773 chunk[12]=jng_color_type;
11774 chunk[13]=8; /* sample depth */
11775 chunk[14]=8; /*jng_image_compression_method */
11776 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11777 chunk[16]=jng_alpha_sample_depth;
11778 chunk[17]=jng_alpha_compression_method;
11779 chunk[18]=0; /*jng_alpha_filter_method */
11780 chunk[19]=0; /*jng_alpha_interlace_method */
11781 (void) WriteBlob(image,20,chunk);
11782 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11783 if (logging != MagickFalse)
11784 {
11785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011786 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011787
cristy3ed852e2009-09-05 21:47:34 +000011788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011789 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011790
cristy3ed852e2009-09-05 21:47:34 +000011791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11792 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011793
cristy3ed852e2009-09-05 21:47:34 +000011794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11795 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011796
cristy3ed852e2009-09-05 21:47:34 +000011797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11798 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011799
cristy3ed852e2009-09-05 21:47:34 +000011800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11801 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011802
cristy3ed852e2009-09-05 21:47:34 +000011803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11804 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011805
cristy3ed852e2009-09-05 21:47:34 +000011806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11807 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011808
cristy3ed852e2009-09-05 21:47:34 +000011809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11810 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011811
cristy3ed852e2009-09-05 21:47:34 +000011812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11813 " JNG alpha interlace:%5d",0);
11814 }
11815
glennrp0fe50b42010-11-16 03:52:51 +000011816 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011817 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011818
11819 /*
11820 Write leading ancillary chunks
11821 */
11822
11823 if (transparent)
11824 {
11825 /*
11826 Write JNG bKGD chunk
11827 */
11828
11829 unsigned char
11830 blue,
11831 green,
11832 red;
11833
cristybb503372010-05-27 20:51:26 +000011834 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011835 num_bytes;
11836
11837 if (jng_color_type == 8 || jng_color_type == 12)
11838 num_bytes=6L;
11839 else
11840 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011841 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011842 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011843 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011844 red=ScaleQuantumToChar(image->background_color.red);
11845 green=ScaleQuantumToChar(image->background_color.green);
11846 blue=ScaleQuantumToChar(image->background_color.blue);
11847 *(chunk+4)=0;
11848 *(chunk+5)=red;
11849 *(chunk+6)=0;
11850 *(chunk+7)=green;
11851 *(chunk+8)=0;
11852 *(chunk+9)=blue;
11853 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11854 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11855 }
11856
11857 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11858 {
11859 /*
11860 Write JNG sRGB chunk
11861 */
11862 (void) WriteBlobMSBULong(image,1L);
11863 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011864 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011865
cristy3ed852e2009-09-05 21:47:34 +000011866 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011867 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011868 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011869 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011870
cristy3ed852e2009-09-05 21:47:34 +000011871 else
glennrpe610a072010-08-05 17:08:46 +000011872 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011873 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011874 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011875
cristy3ed852e2009-09-05 21:47:34 +000011876 (void) WriteBlob(image,5,chunk);
11877 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11878 }
11879 else
11880 {
11881 if (image->gamma != 0.0)
11882 {
11883 /*
11884 Write JNG gAMA chunk
11885 */
11886 (void) WriteBlobMSBULong(image,4L);
11887 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011888 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011889 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011890 (void) WriteBlob(image,8,chunk);
11891 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11892 }
glennrp0fe50b42010-11-16 03:52:51 +000011893
cristy3ed852e2009-09-05 21:47:34 +000011894 if ((mng_info->equal_chrms == MagickFalse) &&
11895 (image->chromaticity.red_primary.x != 0.0))
11896 {
11897 PrimaryInfo
11898 primary;
11899
11900 /*
11901 Write JNG cHRM chunk
11902 */
11903 (void) WriteBlobMSBULong(image,32L);
11904 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011905 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011906 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011907 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11908 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011909 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011910 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11911 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011912 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011913 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11914 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011915 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011916 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11917 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011918 (void) WriteBlob(image,36,chunk);
11919 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11920 }
11921 }
glennrp0fe50b42010-11-16 03:52:51 +000011922
cristy2a11bef2011-10-28 18:33:11 +000011923 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000011924 {
11925 /*
11926 Write JNG pHYs chunk
11927 */
11928 (void) WriteBlobMSBULong(image,9L);
11929 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011930 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011931 if (image->units == PixelsPerInchResolution)
11932 {
cristy35ef8242010-06-03 16:24:13 +000011933 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011934 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011935
cristy35ef8242010-06-03 16:24:13 +000011936 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011937 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011938
cristy3ed852e2009-09-05 21:47:34 +000011939 chunk[12]=1;
11940 }
glennrp0fe50b42010-11-16 03:52:51 +000011941
cristy3ed852e2009-09-05 21:47:34 +000011942 else
11943 {
11944 if (image->units == PixelsPerCentimeterResolution)
11945 {
cristy35ef8242010-06-03 16:24:13 +000011946 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011947 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011948
cristy35ef8242010-06-03 16:24:13 +000011949 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011950 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011951
cristy3ed852e2009-09-05 21:47:34 +000011952 chunk[12]=1;
11953 }
glennrp0fe50b42010-11-16 03:52:51 +000011954
cristy3ed852e2009-09-05 21:47:34 +000011955 else
11956 {
cristy2a11bef2011-10-28 18:33:11 +000011957 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
11958 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011959 chunk[12]=0;
11960 }
11961 }
11962 (void) WriteBlob(image,13,chunk);
11963 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11964 }
11965
11966 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11967 {
11968 /*
11969 Write JNG oFFs chunk
11970 */
11971 (void) WriteBlobMSBULong(image,9L);
11972 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011973 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011974 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11975 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011976 chunk[12]=0;
11977 (void) WriteBlob(image,13,chunk);
11978 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11979 }
11980 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11981 {
11982 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11983 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011984 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011985 PNGLong(chunk+4,(png_uint_32) image->page.width);
11986 PNGLong(chunk+8,(png_uint_32) image->page.height);
11987 chunk[12]=0; /* unit = pixels */
11988 (void) WriteBlob(image,13,chunk);
11989 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11990 }
11991
11992
11993 if (transparent)
11994 {
11995 if (jng_alpha_compression_method==0)
11996 {
cristybb503372010-05-27 20:51:26 +000011997 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011998 i;
11999
cristybb503372010-05-27 20:51:26 +000012000 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012001 len;
12002
12003 /* Write IDAT chunk header */
12004 if (logging != MagickFalse)
12005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012006 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000012007 length);
cristy3ed852e2009-09-05 21:47:34 +000012008
12009 /* Copy IDAT chunks */
12010 len=0;
12011 p=blob+8;
cristybb503372010-05-27 20:51:26 +000012012 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000012013 {
12014 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12015 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012016
cristy3ed852e2009-09-05 21:47:34 +000012017 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12018 {
12019 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000012020 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000012021 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000012022 (void) WriteBlob(image,(size_t) len+4,p);
12023 (void) WriteBlobMSBULong(image,
12024 crc32(0,p,(uInt) len+4));
12025 }
glennrp0fe50b42010-11-16 03:52:51 +000012026
cristy3ed852e2009-09-05 21:47:34 +000012027 else
12028 {
12029 if (logging != MagickFalse)
12030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012031 " Skipping %c%c%c%c chunk, length=%.20g.",
12032 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012033 }
12034 p+=(8+len);
12035 }
12036 }
12037 else
12038 {
12039 /* Write JDAA chunk header */
12040 if (logging != MagickFalse)
12041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012042 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012043 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012044 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012045 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012046 /* Write JDAT chunk(s) data */
12047 (void) WriteBlob(image,4,chunk);
12048 (void) WriteBlob(image,length,blob);
12049 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12050 (uInt) length));
12051 }
12052 blob=(unsigned char *) RelinquishMagickMemory(blob);
12053 }
12054
12055 /* Encode image as a JPEG blob */
12056 if (logging != MagickFalse)
12057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12058 " Creating jpeg_image_info.");
12059 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12060 if (jpeg_image_info == (ImageInfo *) NULL)
12061 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12062
12063 if (logging != MagickFalse)
12064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12065 " Creating jpeg_image.");
12066
cristyc82a27b2011-10-21 01:07:16 +000012067 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012068 if (jpeg_image == (Image *) NULL)
12069 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12070 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12071
12072 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012073 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012074 jpeg_image->filename);
12075
12076 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000012077 exception);
cristy3ed852e2009-09-05 21:47:34 +000012078
12079 if (logging != MagickFalse)
12080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012081 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12082 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012083
12084 if (jng_color_type == 8 || jng_color_type == 12)
12085 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012086
cristy3ed852e2009-09-05 21:47:34 +000012087 jpeg_image_info->quality=jng_quality % 1000;
12088 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12089 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012090
cristy3ed852e2009-09-05 21:47:34 +000012091 if (logging != MagickFalse)
12092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12093 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012094
cristyc82a27b2011-10-21 01:07:16 +000012095 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
glennrp0fe50b42010-11-16 03:52:51 +000012096
cristy3ed852e2009-09-05 21:47:34 +000012097 if (logging != MagickFalse)
12098 {
12099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012100 " Successfully read jpeg_image into a blob, length=%.20g.",
12101 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012102
12103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012104 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012105 }
glennrp0fe50b42010-11-16 03:52:51 +000012106
cristy3ed852e2009-09-05 21:47:34 +000012107 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012108 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012109 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012110 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012111 (void) WriteBlob(image,4,chunk);
12112 (void) WriteBlob(image,length,blob);
12113 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12114
12115 jpeg_image=DestroyImage(jpeg_image);
12116 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12117 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12118 blob=(unsigned char *) RelinquishMagickMemory(blob);
12119
12120 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012121 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012122
12123 /* Write IEND chunk */
12124 (void) WriteBlobMSBULong(image,0L);
12125 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012126 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012127 (void) WriteBlob(image,4,chunk);
12128 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12129
12130 if (logging != MagickFalse)
12131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12132 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012133
cristy3ed852e2009-09-05 21:47:34 +000012134 return(status);
12135}
12136
12137
12138/*
12139%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12140% %
12141% %
12142% %
12143% W r i t e J N G I m a g e %
12144% %
12145% %
12146% %
12147%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12148%
12149% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12150%
12151% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12152%
12153% The format of the WriteJNGImage method is:
12154%
cristy1e178e72011-08-28 19:44:34 +000012155% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12156% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012157%
12158% A description of each parameter follows:
12159%
12160% o image_info: the image info.
12161%
12162% o image: The image.
12163%
cristy1e178e72011-08-28 19:44:34 +000012164% o exception: return any errors or warnings in this structure.
12165%
cristy3ed852e2009-09-05 21:47:34 +000012166%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12167*/
cristy1e178e72011-08-28 19:44:34 +000012168static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12169 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012170{
12171 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012172 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012173 logging,
cristy3ed852e2009-09-05 21:47:34 +000012174 status;
12175
12176 MngInfo
12177 *mng_info;
12178
cristy3ed852e2009-09-05 21:47:34 +000012179 /*
12180 Open image file.
12181 */
12182 assert(image_info != (const ImageInfo *) NULL);
12183 assert(image_info->signature == MagickSignature);
12184 assert(image != (Image *) NULL);
12185 assert(image->signature == MagickSignature);
12186 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012187 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristyc82a27b2011-10-21 01:07:16 +000012188 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012189 if (status == MagickFalse)
12190 return(status);
12191
12192 /*
12193 Allocate a MngInfo structure.
12194 */
12195 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012196 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012197 if (mng_info == (MngInfo *) NULL)
12198 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12199 /*
12200 Initialize members of the MngInfo structure.
12201 */
12202 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12203 mng_info->image=image;
12204 have_mng_structure=MagickTrue;
12205
12206 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12207
cristy018f07f2011-09-04 21:15:19 +000012208 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012209 (void) CloseBlob(image);
12210
12211 (void) CatchImageException(image);
12212 MngInfoFreeStruct(mng_info,&have_mng_structure);
12213 if (logging != MagickFalse)
12214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12215 return(status);
12216}
12217#endif
12218
cristy1e178e72011-08-28 19:44:34 +000012219static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12220 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012221{
12222 const char
12223 *option;
12224
12225 Image
12226 *next_image;
12227
12228 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012229 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012230 status;
12231
glennrp03812ae2010-12-24 01:31:34 +000012232 volatile MagickBooleanType
12233 logging;
12234
cristy3ed852e2009-09-05 21:47:34 +000012235 MngInfo
12236 *mng_info;
12237
12238 int
cristy3ed852e2009-09-05 21:47:34 +000012239 image_count,
12240 need_iterations,
12241 need_matte;
12242
12243 volatile int
12244#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12245 defined(PNG_MNG_FEATURES_SUPPORTED)
12246 need_local_plte,
12247#endif
12248 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012249 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012250 use_global_plte;
12251
cristybb503372010-05-27 20:51:26 +000012252 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012253 i;
12254
12255 unsigned char
12256 chunk[800];
12257
12258 volatile unsigned int
12259 write_jng,
12260 write_mng;
12261
cristybb503372010-05-27 20:51:26 +000012262 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012263 scene;
12264
cristybb503372010-05-27 20:51:26 +000012265 size_t
cristy3ed852e2009-09-05 21:47:34 +000012266 final_delay=0,
12267 initial_delay;
12268
glennrpd5045b42010-03-24 12:40:35 +000012269#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012270 if (image_info->verbose)
12271 printf("Your PNG library (libpng-%s) is rather old.\n",
12272 PNG_LIBPNG_VER_STRING);
12273#endif
12274
12275 /*
12276 Open image file.
12277 */
12278 assert(image_info != (const ImageInfo *) NULL);
12279 assert(image_info->signature == MagickSignature);
12280 assert(image != (Image *) NULL);
12281 assert(image->signature == MagickSignature);
12282 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012283 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristyc82a27b2011-10-21 01:07:16 +000012284 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012285 if (status == MagickFalse)
12286 return(status);
12287
12288 /*
12289 Allocate a MngInfo structure.
12290 */
12291 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012292 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012293 if (mng_info == (MngInfo *) NULL)
12294 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12295 /*
12296 Initialize members of the MngInfo structure.
12297 */
12298 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12299 mng_info->image=image;
12300 have_mng_structure=MagickTrue;
12301 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12302
12303 /*
12304 * See if user has requested a specific PNG subformat to be used
12305 * for all of the PNGs in the MNG being written, e.g.,
12306 *
12307 * convert *.png png8:animation.mng
12308 *
12309 * To do: check -define png:bit_depth and png:color_type as well,
12310 * or perhaps use mng:bit_depth and mng:color_type instead for
12311 * global settings.
12312 */
12313
12314 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12315 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12316 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12317
12318 write_jng=MagickFalse;
12319 if (image_info->compression == JPEGCompression)
12320 write_jng=MagickTrue;
12321
12322 mng_info->adjoin=image_info->adjoin &&
12323 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12324
cristy3ed852e2009-09-05 21:47:34 +000012325 if (logging != MagickFalse)
12326 {
12327 /* Log some info about the input */
12328 Image
12329 *p;
12330
12331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12332 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012333
cristy3ed852e2009-09-05 21:47:34 +000012334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012335 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012336
cristy3ed852e2009-09-05 21:47:34 +000012337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12338 " Type: %d",image_info->type);
12339
12340 scene=0;
12341 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12342 {
12343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012344 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012345
cristy3ed852e2009-09-05 21:47:34 +000012346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012347 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012348
cristy3ed852e2009-09-05 21:47:34 +000012349 if (p->matte)
12350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12351 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012352
cristy3ed852e2009-09-05 21:47:34 +000012353 else
12354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12355 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012356
cristy3ed852e2009-09-05 21:47:34 +000012357 if (p->storage_class == PseudoClass)
12358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12359 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012360
cristy3ed852e2009-09-05 21:47:34 +000012361 else
12362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12363 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012364
cristy3ed852e2009-09-05 21:47:34 +000012365 if (p->colors)
12366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012367 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012368
cristy3ed852e2009-09-05 21:47:34 +000012369 else
12370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12371 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012372
cristy3ed852e2009-09-05 21:47:34 +000012373 if (mng_info->adjoin == MagickFalse)
12374 break;
12375 }
12376 }
12377
cristy3ed852e2009-09-05 21:47:34 +000012378 use_global_plte=MagickFalse;
12379 all_images_are_gray=MagickFalse;
12380#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12381 need_local_plte=MagickTrue;
12382#endif
12383 need_defi=MagickFalse;
12384 need_matte=MagickFalse;
12385 mng_info->framing_mode=1;
12386 mng_info->old_framing_mode=1;
12387
12388 if (write_mng)
12389 if (image_info->page != (char *) NULL)
12390 {
12391 /*
12392 Determine image bounding box.
12393 */
12394 SetGeometry(image,&mng_info->page);
12395 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12396 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12397 }
12398 if (write_mng)
12399 {
12400 unsigned int
12401 need_geom;
12402
12403 unsigned short
12404 red,
12405 green,
12406 blue;
12407
12408 mng_info->page=image->page;
12409 need_geom=MagickTrue;
12410 if (mng_info->page.width || mng_info->page.height)
12411 need_geom=MagickFalse;
12412 /*
12413 Check all the scenes.
12414 */
12415 initial_delay=image->delay;
12416 need_iterations=MagickFalse;
12417 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12418 mng_info->equal_physs=MagickTrue,
12419 mng_info->equal_gammas=MagickTrue;
12420 mng_info->equal_srgbs=MagickTrue;
12421 mng_info->equal_backgrounds=MagickTrue;
12422 image_count=0;
12423#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12424 defined(PNG_MNG_FEATURES_SUPPORTED)
12425 all_images_are_gray=MagickTrue;
12426 mng_info->equal_palettes=MagickFalse;
12427 need_local_plte=MagickFalse;
12428#endif
12429 for (next_image=image; next_image != (Image *) NULL; )
12430 {
12431 if (need_geom)
12432 {
12433 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12434 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012435
cristy3ed852e2009-09-05 21:47:34 +000012436 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12437 mng_info->page.height=next_image->rows+next_image->page.y;
12438 }
glennrp0fe50b42010-11-16 03:52:51 +000012439
cristy3ed852e2009-09-05 21:47:34 +000012440 if (next_image->page.x || next_image->page.y)
12441 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012442
cristy3ed852e2009-09-05 21:47:34 +000012443 if (next_image->matte)
12444 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012445
cristy3ed852e2009-09-05 21:47:34 +000012446 if ((int) next_image->dispose >= BackgroundDispose)
12447 if (next_image->matte || next_image->page.x || next_image->page.y ||
12448 ((next_image->columns < mng_info->page.width) &&
12449 (next_image->rows < mng_info->page.height)))
12450 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012451
cristy3ed852e2009-09-05 21:47:34 +000012452 if (next_image->iterations)
12453 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012454
cristy3ed852e2009-09-05 21:47:34 +000012455 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012456
cristy3ed852e2009-09-05 21:47:34 +000012457 if (final_delay != initial_delay || final_delay > 1UL*
12458 next_image->ticks_per_second)
12459 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012460
cristy3ed852e2009-09-05 21:47:34 +000012461#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12462 defined(PNG_MNG_FEATURES_SUPPORTED)
12463 /*
12464 check for global palette possibility.
12465 */
12466 if (image->matte != MagickFalse)
12467 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012468
cristy3ed852e2009-09-05 21:47:34 +000012469 if (need_local_plte == 0)
12470 {
cristyc82a27b2011-10-21 01:07:16 +000012471 if (ImageIsGray(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012472 all_images_are_gray=MagickFalse;
12473 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12474 if (use_global_plte == 0)
12475 use_global_plte=mng_info->equal_palettes;
12476 need_local_plte=!mng_info->equal_palettes;
12477 }
12478#endif
12479 if (GetNextImageInList(next_image) != (Image *) NULL)
12480 {
12481 if (next_image->background_color.red !=
12482 next_image->next->background_color.red ||
12483 next_image->background_color.green !=
12484 next_image->next->background_color.green ||
12485 next_image->background_color.blue !=
12486 next_image->next->background_color.blue)
12487 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012488
cristy3ed852e2009-09-05 21:47:34 +000012489 if (next_image->gamma != next_image->next->gamma)
12490 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012491
cristy3ed852e2009-09-05 21:47:34 +000012492 if (next_image->rendering_intent !=
12493 next_image->next->rendering_intent)
12494 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012495
cristy3ed852e2009-09-05 21:47:34 +000012496 if ((next_image->units != next_image->next->units) ||
cristy2a11bef2011-10-28 18:33:11 +000012497 (next_image->resolution.x != next_image->next->resolution.x) ||
12498 (next_image->resolution.y != next_image->next->resolution.y))
cristy3ed852e2009-09-05 21:47:34 +000012499 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012500
cristy3ed852e2009-09-05 21:47:34 +000012501 if (mng_info->equal_chrms)
12502 {
12503 if (next_image->chromaticity.red_primary.x !=
12504 next_image->next->chromaticity.red_primary.x ||
12505 next_image->chromaticity.red_primary.y !=
12506 next_image->next->chromaticity.red_primary.y ||
12507 next_image->chromaticity.green_primary.x !=
12508 next_image->next->chromaticity.green_primary.x ||
12509 next_image->chromaticity.green_primary.y !=
12510 next_image->next->chromaticity.green_primary.y ||
12511 next_image->chromaticity.blue_primary.x !=
12512 next_image->next->chromaticity.blue_primary.x ||
12513 next_image->chromaticity.blue_primary.y !=
12514 next_image->next->chromaticity.blue_primary.y ||
12515 next_image->chromaticity.white_point.x !=
12516 next_image->next->chromaticity.white_point.x ||
12517 next_image->chromaticity.white_point.y !=
12518 next_image->next->chromaticity.white_point.y)
12519 mng_info->equal_chrms=MagickFalse;
12520 }
12521 }
12522 image_count++;
12523 next_image=GetNextImageInList(next_image);
12524 }
12525 if (image_count < 2)
12526 {
12527 mng_info->equal_backgrounds=MagickFalse;
12528 mng_info->equal_chrms=MagickFalse;
12529 mng_info->equal_gammas=MagickFalse;
12530 mng_info->equal_srgbs=MagickFalse;
12531 mng_info->equal_physs=MagickFalse;
12532 use_global_plte=MagickFalse;
12533#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12534 need_local_plte=MagickTrue;
12535#endif
12536 need_iterations=MagickFalse;
12537 }
glennrp0fe50b42010-11-16 03:52:51 +000012538
cristy3ed852e2009-09-05 21:47:34 +000012539 if (mng_info->need_fram == MagickFalse)
12540 {
12541 /*
12542 Only certain framing rates 100/n are exactly representable without
12543 the FRAM chunk but we'll allow some slop in VLC files
12544 */
12545 if (final_delay == 0)
12546 {
12547 if (need_iterations != MagickFalse)
12548 {
12549 /*
12550 It's probably a GIF with loop; don't run it *too* fast.
12551 */
glennrp02617122010-07-28 13:07:35 +000012552 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012553 {
12554 final_delay=10;
cristyc82a27b2011-10-21 01:07:16 +000012555 (void) ThrowMagickException(exception,GetMagickModule(),
12556 CoderWarning,
glennrpd908de42010-07-28 13:28:27 +000012557 "input has zero delay between all frames; assuming",
12558 " 10 cs `%s'","");
12559 }
cristy3ed852e2009-09-05 21:47:34 +000012560 }
12561 else
12562 mng_info->ticks_per_second=0;
12563 }
12564 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012565 mng_info->ticks_per_second=(png_uint_32)
12566 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012567 if (final_delay > 50)
12568 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012569
cristy3ed852e2009-09-05 21:47:34 +000012570 if (final_delay > 75)
12571 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012572
cristy3ed852e2009-09-05 21:47:34 +000012573 if (final_delay > 125)
12574 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012575
cristy3ed852e2009-09-05 21:47:34 +000012576 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12577 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12578 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12579 1UL*image->ticks_per_second))
12580 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12581 }
glennrp0fe50b42010-11-16 03:52:51 +000012582
cristy3ed852e2009-09-05 21:47:34 +000012583 if (mng_info->need_fram != MagickFalse)
12584 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12585 /*
12586 If pseudocolor, we should also check to see if all the
12587 palettes are identical and write a global PLTE if they are.
12588 ../glennrp Feb 99.
12589 */
12590 /*
12591 Write the MNG version 1.0 signature and MHDR chunk.
12592 */
12593 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12594 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12595 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012596 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012597 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12598 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012599 PNGLong(chunk+12,mng_info->ticks_per_second);
12600 PNGLong(chunk+16,0L); /* layer count=unknown */
12601 PNGLong(chunk+20,0L); /* frame count=unknown */
12602 PNGLong(chunk+24,0L); /* play time=unknown */
12603 if (write_jng)
12604 {
12605 if (need_matte)
12606 {
12607 if (need_defi || mng_info->need_fram || use_global_plte)
12608 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012609
cristy3ed852e2009-09-05 21:47:34 +000012610 else
12611 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12612 }
glennrp0fe50b42010-11-16 03:52:51 +000012613
cristy3ed852e2009-09-05 21:47:34 +000012614 else
12615 {
12616 if (need_defi || mng_info->need_fram || use_global_plte)
12617 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012618
cristy3ed852e2009-09-05 21:47:34 +000012619 else
12620 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12621 }
12622 }
glennrp0fe50b42010-11-16 03:52:51 +000012623
cristy3ed852e2009-09-05 21:47:34 +000012624 else
12625 {
12626 if (need_matte)
12627 {
12628 if (need_defi || mng_info->need_fram || use_global_plte)
12629 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012630
cristy3ed852e2009-09-05 21:47:34 +000012631 else
12632 PNGLong(chunk+28,9L); /* simplicity=VLC */
12633 }
glennrp0fe50b42010-11-16 03:52:51 +000012634
cristy3ed852e2009-09-05 21:47:34 +000012635 else
12636 {
12637 if (need_defi || mng_info->need_fram || use_global_plte)
12638 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012639
cristy3ed852e2009-09-05 21:47:34 +000012640 else
12641 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12642 }
12643 }
12644 (void) WriteBlob(image,32,chunk);
12645 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12646 option=GetImageOption(image_info,"mng:need-cacheoff");
12647 if (option != (const char *) NULL)
12648 {
12649 size_t
12650 length;
12651
12652 /*
12653 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12654 */
12655 PNGType(chunk,mng_nEED);
12656 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012657 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012658 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012659 length+=4;
12660 (void) WriteBlob(image,length,chunk);
12661 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12662 }
12663 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12664 (GetNextImageInList(image) != (Image *) NULL) &&
12665 (image->iterations != 1))
12666 {
12667 /*
12668 Write MNG TERM chunk
12669 */
12670 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12671 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012672 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012673 chunk[4]=3; /* repeat animation */
12674 chunk[5]=0; /* show last frame when done */
12675 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12676 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012677
cristy3ed852e2009-09-05 21:47:34 +000012678 if (image->iterations == 0)
12679 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012680
cristy3ed852e2009-09-05 21:47:34 +000012681 else
12682 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012683
cristy3ed852e2009-09-05 21:47:34 +000012684 if (logging != MagickFalse)
12685 {
12686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012687 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12688 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012689
cristy3ed852e2009-09-05 21:47:34 +000012690 if (image->iterations == 0)
12691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012692 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012693
cristy3ed852e2009-09-05 21:47:34 +000012694 else
12695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012696 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012697 }
12698 (void) WriteBlob(image,14,chunk);
12699 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12700 }
12701 /*
12702 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12703 */
12704 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12705 mng_info->equal_srgbs)
12706 {
12707 /*
12708 Write MNG sRGB chunk
12709 */
12710 (void) WriteBlobMSBULong(image,1L);
12711 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012712 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012713
cristy3ed852e2009-09-05 21:47:34 +000012714 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012715 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012716 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012717 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012718
cristy3ed852e2009-09-05 21:47:34 +000012719 else
glennrpe610a072010-08-05 17:08:46 +000012720 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012721 Magick_RenderingIntent_to_PNG_RenderingIntent(
12722 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012723
cristy3ed852e2009-09-05 21:47:34 +000012724 (void) WriteBlob(image,5,chunk);
12725 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12726 mng_info->have_write_global_srgb=MagickTrue;
12727 }
glennrp0fe50b42010-11-16 03:52:51 +000012728
cristy3ed852e2009-09-05 21:47:34 +000012729 else
12730 {
12731 if (image->gamma && mng_info->equal_gammas)
12732 {
12733 /*
12734 Write MNG gAMA chunk
12735 */
12736 (void) WriteBlobMSBULong(image,4L);
12737 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012738 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012739 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012740 (void) WriteBlob(image,8,chunk);
12741 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12742 mng_info->have_write_global_gama=MagickTrue;
12743 }
12744 if (mng_info->equal_chrms)
12745 {
12746 PrimaryInfo
12747 primary;
12748
12749 /*
12750 Write MNG cHRM chunk
12751 */
12752 (void) WriteBlobMSBULong(image,32L);
12753 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012754 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012755 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012756 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12757 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012758 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012759 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12760 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012761 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012762 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12763 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012764 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012765 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12766 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012767 (void) WriteBlob(image,36,chunk);
12768 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12769 mng_info->have_write_global_chrm=MagickTrue;
12770 }
12771 }
cristy2a11bef2011-10-28 18:33:11 +000012772 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012773 {
12774 /*
12775 Write MNG pHYs chunk
12776 */
12777 (void) WriteBlobMSBULong(image,9L);
12778 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012779 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012780
cristy3ed852e2009-09-05 21:47:34 +000012781 if (image->units == PixelsPerInchResolution)
12782 {
cristy35ef8242010-06-03 16:24:13 +000012783 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012784 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012785
cristy35ef8242010-06-03 16:24:13 +000012786 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012787 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012788
cristy3ed852e2009-09-05 21:47:34 +000012789 chunk[12]=1;
12790 }
glennrp0fe50b42010-11-16 03:52:51 +000012791
cristy3ed852e2009-09-05 21:47:34 +000012792 else
12793 {
12794 if (image->units == PixelsPerCentimeterResolution)
12795 {
cristy35ef8242010-06-03 16:24:13 +000012796 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012797 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012798
cristy35ef8242010-06-03 16:24:13 +000012799 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012800 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012801
cristy3ed852e2009-09-05 21:47:34 +000012802 chunk[12]=1;
12803 }
glennrp0fe50b42010-11-16 03:52:51 +000012804
cristy3ed852e2009-09-05 21:47:34 +000012805 else
12806 {
cristy2a11bef2011-10-28 18:33:11 +000012807 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12808 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012809 chunk[12]=0;
12810 }
12811 }
12812 (void) WriteBlob(image,13,chunk);
12813 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12814 }
12815 /*
12816 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12817 or does not cover the entire frame.
12818 */
12819 if (write_mng && (image->matte || image->page.x > 0 ||
12820 image->page.y > 0 || (image->page.width &&
12821 (image->page.width+image->page.x < mng_info->page.width))
12822 || (image->page.height && (image->page.height+image->page.y
12823 < mng_info->page.height))))
12824 {
12825 (void) WriteBlobMSBULong(image,6L);
12826 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012827 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012828 red=ScaleQuantumToShort(image->background_color.red);
12829 green=ScaleQuantumToShort(image->background_color.green);
12830 blue=ScaleQuantumToShort(image->background_color.blue);
12831 PNGShort(chunk+4,red);
12832 PNGShort(chunk+6,green);
12833 PNGShort(chunk+8,blue);
12834 (void) WriteBlob(image,10,chunk);
12835 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12836 if (mng_info->equal_backgrounds)
12837 {
12838 (void) WriteBlobMSBULong(image,6L);
12839 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012840 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012841 (void) WriteBlob(image,10,chunk);
12842 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12843 }
12844 }
12845
12846#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12847 if ((need_local_plte == MagickFalse) &&
12848 (image->storage_class == PseudoClass) &&
12849 (all_images_are_gray == MagickFalse))
12850 {
cristybb503372010-05-27 20:51:26 +000012851 size_t
cristy3ed852e2009-09-05 21:47:34 +000012852 data_length;
12853
12854 /*
12855 Write MNG PLTE chunk
12856 */
12857 data_length=3*image->colors;
12858 (void) WriteBlobMSBULong(image,data_length);
12859 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012860 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012861
cristybb503372010-05-27 20:51:26 +000012862 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012863 {
cristy5f07f702011-09-26 17:29:10 +000012864 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12865 image->colormap[i].red) & 0xff);
12866 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12867 image->colormap[i].green) & 0xff);
12868 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12869 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000012870 }
glennrp0fe50b42010-11-16 03:52:51 +000012871
cristy3ed852e2009-09-05 21:47:34 +000012872 (void) WriteBlob(image,data_length+4,chunk);
12873 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12874 mng_info->have_write_global_plte=MagickTrue;
12875 }
12876#endif
12877 }
12878 scene=0;
12879 mng_info->delay=0;
12880#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12881 defined(PNG_MNG_FEATURES_SUPPORTED)
12882 mng_info->equal_palettes=MagickFalse;
12883#endif
12884 do
12885 {
12886 if (mng_info->adjoin)
12887 {
12888#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12889 defined(PNG_MNG_FEATURES_SUPPORTED)
12890 /*
12891 If we aren't using a global palette for the entire MNG, check to
12892 see if we can use one for two or more consecutive images.
12893 */
12894 if (need_local_plte && use_global_plte && !all_images_are_gray)
12895 {
12896 if (mng_info->IsPalette)
12897 {
12898 /*
12899 When equal_palettes is true, this image has the same palette
12900 as the previous PseudoClass image
12901 */
12902 mng_info->have_write_global_plte=mng_info->equal_palettes;
12903 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12904 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12905 {
12906 /*
12907 Write MNG PLTE chunk
12908 */
cristybb503372010-05-27 20:51:26 +000012909 size_t
cristy3ed852e2009-09-05 21:47:34 +000012910 data_length;
12911
12912 data_length=3*image->colors;
12913 (void) WriteBlobMSBULong(image,data_length);
12914 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012915 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012916
cristybb503372010-05-27 20:51:26 +000012917 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012918 {
12919 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12920 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12921 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12922 }
glennrp0fe50b42010-11-16 03:52:51 +000012923
cristy3ed852e2009-09-05 21:47:34 +000012924 (void) WriteBlob(image,data_length+4,chunk);
12925 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12926 (uInt) (data_length+4)));
12927 mng_info->have_write_global_plte=MagickTrue;
12928 }
12929 }
12930 else
12931 mng_info->have_write_global_plte=MagickFalse;
12932 }
12933#endif
12934 if (need_defi)
12935 {
cristybb503372010-05-27 20:51:26 +000012936 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012937 previous_x,
12938 previous_y;
12939
12940 if (scene)
12941 {
12942 previous_x=mng_info->page.x;
12943 previous_y=mng_info->page.y;
12944 }
12945 else
12946 {
12947 previous_x=0;
12948 previous_y=0;
12949 }
12950 mng_info->page=image->page;
12951 if ((mng_info->page.x != previous_x) ||
12952 (mng_info->page.y != previous_y))
12953 {
12954 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12955 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012956 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012957 chunk[4]=0; /* object 0 MSB */
12958 chunk[5]=0; /* object 0 LSB */
12959 chunk[6]=0; /* visible */
12960 chunk[7]=0; /* abstract */
12961 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12962 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12963 (void) WriteBlob(image,16,chunk);
12964 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12965 }
12966 }
12967 }
12968
12969 mng_info->write_mng=write_mng;
12970
12971 if ((int) image->dispose >= 3)
12972 mng_info->framing_mode=3;
12973
12974 if (mng_info->need_fram && mng_info->adjoin &&
12975 ((image->delay != mng_info->delay) ||
12976 (mng_info->framing_mode != mng_info->old_framing_mode)))
12977 {
12978 if (image->delay == mng_info->delay)
12979 {
12980 /*
12981 Write a MNG FRAM chunk with the new framing mode.
12982 */
12983 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12984 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012985 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012986 chunk[4]=(unsigned char) mng_info->framing_mode;
12987 (void) WriteBlob(image,5,chunk);
12988 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12989 }
12990 else
12991 {
12992 /*
12993 Write a MNG FRAM chunk with the delay.
12994 */
12995 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12996 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012997 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012998 chunk[4]=(unsigned char) mng_info->framing_mode;
12999 chunk[5]=0; /* frame name separator (no name) */
13000 chunk[6]=2; /* flag for changing default delay */
13001 chunk[7]=0; /* flag for changing frame timeout */
13002 chunk[8]=0; /* flag for changing frame clipping */
13003 chunk[9]=0; /* flag for changing frame sync_id */
13004 PNGLong(chunk+10,(png_uint_32)
13005 ((mng_info->ticks_per_second*
13006 image->delay)/MagickMax(image->ticks_per_second,1)));
13007 (void) WriteBlob(image,14,chunk);
13008 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000013009 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000013010 }
13011 mng_info->old_framing_mode=mng_info->framing_mode;
13012 }
13013
13014#if defined(JNG_SUPPORTED)
13015 if (image_info->compression == JPEGCompression)
13016 {
13017 ImageInfo
13018 *write_info;
13019
13020 if (logging != MagickFalse)
13021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13022 " Writing JNG object.");
13023 /* To do: specify the desired alpha compression method. */
13024 write_info=CloneImageInfo(image_info);
13025 write_info->compression=UndefinedCompression;
cristy018f07f2011-09-04 21:15:19 +000013026 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013027 write_info=DestroyImageInfo(write_info);
13028 }
13029 else
13030#endif
13031 {
13032 if (logging != MagickFalse)
13033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13034 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013035
glennrpb9cfe272010-12-21 15:08:06 +000013036 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013037 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013038
13039 /* We don't want any ancillary chunks written */
13040 mng_info->ping_exclude_bKGD=MagickTrue;
13041 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013042 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013043 mng_info->ping_exclude_EXIF=MagickTrue;
13044 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013045 mng_info->ping_exclude_iCCP=MagickTrue;
13046 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13047 mng_info->ping_exclude_oFFs=MagickTrue;
13048 mng_info->ping_exclude_pHYs=MagickTrue;
13049 mng_info->ping_exclude_sRGB=MagickTrue;
13050 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013051 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013052 mng_info->ping_exclude_vpAg=MagickTrue;
13053 mng_info->ping_exclude_zCCP=MagickTrue;
13054 mng_info->ping_exclude_zTXt=MagickTrue;
13055
cristy018f07f2011-09-04 21:15:19 +000013056 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013057 }
13058
13059 if (status == MagickFalse)
13060 {
13061 MngInfoFreeStruct(mng_info,&have_mng_structure);
13062 (void) CloseBlob(image);
13063 return(MagickFalse);
13064 }
13065 (void) CatchImageException(image);
13066 if (GetNextImageInList(image) == (Image *) NULL)
13067 break;
13068 image=SyncNextImageInList(image);
13069 status=SetImageProgress(image,SaveImagesTag,scene++,
13070 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013071
cristy3ed852e2009-09-05 21:47:34 +000013072 if (status == MagickFalse)
13073 break;
glennrp0fe50b42010-11-16 03:52:51 +000013074
cristy3ed852e2009-09-05 21:47:34 +000013075 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013076
cristy3ed852e2009-09-05 21:47:34 +000013077 if (write_mng)
13078 {
13079 while (GetPreviousImageInList(image) != (Image *) NULL)
13080 image=GetPreviousImageInList(image);
13081 /*
13082 Write the MEND chunk.
13083 */
13084 (void) WriteBlobMSBULong(image,0x00000000L);
13085 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013086 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013087 (void) WriteBlob(image,4,chunk);
13088 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13089 }
13090 /*
13091 Relinquish resources.
13092 */
13093 (void) CloseBlob(image);
13094 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013095
cristy3ed852e2009-09-05 21:47:34 +000013096 if (logging != MagickFalse)
13097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013098
cristy3ed852e2009-09-05 21:47:34 +000013099 return(MagickTrue);
13100}
glennrpd5045b42010-03-24 12:40:35 +000013101#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013102
cristy3ed852e2009-09-05 21:47:34 +000013103static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13104{
glennrp3bd393f2011-12-21 18:54:53 +000013105 (void) image;
cristy3ed852e2009-09-05 21:47:34 +000013106 printf("Your PNG library is too old: You have libpng-%s\n",
13107 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013108
cristy3ed852e2009-09-05 21:47:34 +000013109 ThrowBinaryException(CoderError,"PNG library is too old",
13110 image_info->filename);
13111}
glennrp39992b42010-11-14 00:03:43 +000013112
cristy3ed852e2009-09-05 21:47:34 +000013113static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13114{
13115 return(WritePNGImage(image_info,image));
13116}
glennrpd5045b42010-03-24 12:40:35 +000013117#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013118#endif