blob: cc10247c8101e19b36488ba4c74245bdbc44f59b [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP N N GGGG %
7% P P NN N G %
8% PPPP N N N G GG %
9% P N NN G G %
10% P N N GGG %
11% %
12% %
13% Read/Write Portable Network Graphics Image Format %
14% %
15% Software Design %
16% John Cristy %
17% Glenn Randers-Pehrson %
18% November 1997 %
19% %
20% %
cristy7e41fe82010-12-04 23:12:08 +000021% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000022% dedicated to making software imaging solutions freely available. %
23% %
24% You may not use this file except in compliance with the License. You may %
25% obtain a copy of the License at %
26% %
27% http://www.imagemagick.org/script/license.php %
28% %
29% Unless required by applicable law or agreed to in writing, software %
30% distributed under the License is distributed on an "AS IS" BASIS, %
31% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32% See the License for the specific language governing permissions and %
33% limitations under the License. %
34% %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38*/
39
40
41/*
42 Include declarations.
43*/
cristy4c08aed2011-07-01 19:47:50 +000044#include "MagickCore/studio.h"
45#include "MagickCore/artifact.h"
46#include "MagickCore/attribute.h"
47#include "MagickCore/blob.h"
48#include "MagickCore/blob-private.h"
49#include "MagickCore/cache.h"
50#include "MagickCore/color.h"
51#include "MagickCore/color-private.h"
52#include "MagickCore/colormap.h"
53#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000054#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000055#include "MagickCore/constitute.h"
56#include "MagickCore/enhance.h"
57#include "MagickCore/exception.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/geometry.h"
60#include "MagickCore/histogram.h"
61#include "MagickCore/image.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/layer.h"
64#include "MagickCore/list.h"
65#include "MagickCore/log.h"
66#include "MagickCore/MagickCore.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/module.h"
69#include "MagickCore/monitor.h"
70#include "MagickCore/monitor-private.h"
71#include "MagickCore/option.h"
72#include "MagickCore/pixel.h"
73#include "MagickCore/pixel-accessor.h"
74#include "MagickCore/profile.h"
75#include "MagickCore/property.h"
76#include "MagickCore/quantum-private.h"
77#include "MagickCore/resource_.h"
78#include "MagickCore/semaphore.h"
79#include "MagickCore/quantum-private.h"
80#include "MagickCore/static.h"
81#include "MagickCore/statistic.h"
82#include "MagickCore/string_.h"
83#include "MagickCore/string-private.h"
84#include "MagickCore/transform.h"
85#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000086#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp286a6352009-11-09 02:58:50 +000087
glennrp7ef138c2009-11-10 13:50:20 +000088/* Suppress libpng pedantic warnings that were added in
89 * libpng-1.2.41 and libpng-1.4.0. If you are working on
glennrpfaa852b2010-03-30 12:17:00 +000090 * migration to libpng-1.5, remove these defines and then
glennrp7ef138c2009-11-10 13:50:20 +000091 * fix any code that generates warnings.
92 */
glennrp991e92a2010-01-28 03:09:00 +000093/* #define PNG_DEPRECATED Use of this function is deprecated */
glennrpfaa852b2010-03-30 12:17:00 +000094/* #define PNG_USE_RESULT The result of this function must be checked */
95/* #define PNG_NORETURN This function does not return */
96/* #define PNG_ALLOCATED The result of the function is new memory */
glennrp8371ecc2011-02-19 19:15:38 +000097/* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
glennrp5b927342011-04-14 14:31:48 +000098
99/* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
cristy75cfe702011-04-14 14:27:17 +0000100#define PNG_PTR_NORETURN
glennrp286a6352009-11-09 02:58:50 +0000101
cristy3ed852e2009-09-05 21:47:34 +0000102#include "png.h"
103#include "zlib.h"
104
105/* ImageMagick differences */
106#define first_scene scene
107
glennrpd5045b42010-03-24 12:40:35 +0000108#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000109/*
110 Optional declarations. Define or undefine them as you like.
111*/
112/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
113
114/*
115 Features under construction. Define these to work on them.
116*/
117#undef MNG_OBJECT_BUFFERS
118#undef MNG_BASI_SUPPORTED
119#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
120#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
cristy3ed852e2009-09-05 21:47:34 +0000121#if defined(MAGICKCORE_JPEG_DELEGATE)
122# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
123#endif
124#if !defined(RGBColorMatchExact)
125#define IsPNGColorEqual(color,target) \
glennrp8e045c82011-04-27 16:40:27 +0000126 (((color).red == (target).red) && \
127 ((color).green == (target).green) && \
128 ((color).blue == (target).blue))
cristy3ed852e2009-09-05 21:47:34 +0000129#endif
130
glennrp8e58efd2011-05-20 12:16:29 +0000131/* Macros for left-bit-replication to ensure that pixels
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
639static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
640static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
641static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
642static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
643static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
644static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
645static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
646static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
647static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
648static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
649static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
650static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
651static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
652static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
653static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
654static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
655static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
656static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
657static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
658static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
659static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
660static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
661static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
662static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
663static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
664static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
665static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
666static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
667static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
668static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
669static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
670static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
671static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
672static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
673
674#if defined(JNG_SUPPORTED)
675static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
676static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
677static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
678static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
679static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
680static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
681#endif
682
683/*
684Other known chunks that are not yet supported by ImageMagick:
685static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
686static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
687static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
688static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
689static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
690static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
691static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
692static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
693*/
694
695typedef struct _MngBox
696{
cristy8182b072010-05-30 20:10:53 +0000697 long
cristy3ed852e2009-09-05 21:47:34 +0000698 left,
699 right,
700 top,
701 bottom;
702} MngBox;
703
704typedef struct _MngPair
705{
cristy8182b072010-05-30 20:10:53 +0000706 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000707 a,
708 b;
709} MngPair;
710
711#ifdef MNG_OBJECT_BUFFERS
712typedef struct _MngBuffer
713{
714
cristybb503372010-05-27 20:51:26 +0000715 size_t
cristy3ed852e2009-09-05 21:47:34 +0000716 height,
717 width;
718
719 Image
720 *image;
721
722 png_color
723 plte[256];
724
725 int
726 reference_count;
727
728 unsigned char
729 alpha_sample_depth,
730 compression_method,
731 color_type,
732 concrete,
733 filter_method,
734 frozen,
735 image_type,
736 interlace_method,
737 pixel_sample_depth,
738 plte_length,
739 sample_depth,
740 viewable;
741} MngBuffer;
742#endif
743
744typedef struct _MngInfo
745{
746
747#ifdef MNG_OBJECT_BUFFERS
748 MngBuffer
749 *ob[MNG_MAX_OBJECTS];
750#endif
751
752 Image *
753 image;
754
755 RectangleInfo
756 page;
757
758 int
759 adjoin,
760#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
761 bytes_in_read_buffer,
762 found_empty_plte,
763#endif
764 equal_backgrounds,
765 equal_chrms,
766 equal_gammas,
767#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
768 defined(PNG_MNG_FEATURES_SUPPORTED)
769 equal_palettes,
770#endif
771 equal_physs,
772 equal_srgbs,
773 framing_mode,
774 have_global_bkgd,
775 have_global_chrm,
776 have_global_gama,
777 have_global_phys,
778 have_global_sbit,
779 have_global_srgb,
780 have_saved_bkgd_index,
781 have_write_global_chrm,
782 have_write_global_gama,
783 have_write_global_plte,
784 have_write_global_srgb,
785 need_fram,
786 object_id,
787 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000788 saved_bkgd_index;
789
790 int
791 new_number_colors;
792
cristybb503372010-05-27 20:51:26 +0000793 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000794 image_found,
795 loop_count[256],
796 loop_iteration[256],
797 scenes_found,
798 x_off[MNG_MAX_OBJECTS],
799 y_off[MNG_MAX_OBJECTS];
800
801 MngBox
802 clip,
803 frame,
804 image_box,
805 object_clip[MNG_MAX_OBJECTS];
806
807 unsigned char
808 /* These flags could be combined into one byte */
809 exists[MNG_MAX_OBJECTS],
810 frozen[MNG_MAX_OBJECTS],
811 loop_active[256],
812 invisible[MNG_MAX_OBJECTS],
813 viewable[MNG_MAX_OBJECTS];
814
815 MagickOffsetType
816 loop_jump[256];
817
818 png_colorp
819 global_plte;
820
821 png_color_8
822 global_sbit;
823
824 png_byte
825#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
826 read_buffer[8],
827#endif
828 global_trns[256];
829
830 float
831 global_gamma;
832
833 ChromaticityInfo
834 global_chrm;
835
836 RenderingIntent
837 global_srgb_intent;
838
cristy35ef8242010-06-03 16:24:13 +0000839 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000840 delay,
841 global_plte_length,
842 global_trns_length,
843 global_x_pixels_per_unit,
844 global_y_pixels_per_unit,
845 mng_width,
846 mng_height,
847 ticks_per_second;
848
glennrpb9cfe272010-12-21 15:08:06 +0000849 MagickBooleanType
850 need_blob;
851
cristy3ed852e2009-09-05 21:47:34 +0000852 unsigned int
853 IsPalette,
854 global_phys_unit_type,
855 basi_warning,
856 clon_warning,
857 dhdr_warning,
858 jhdr_warning,
859 magn_warning,
860 past_warning,
861 phyg_warning,
862 phys_warning,
863 sbit_warning,
864 show_warning,
865 mng_type,
866 write_mng,
867 write_png_colortype,
868 write_png_depth,
glennrp18682582011-06-30 18:11:47 +0000869 write_png_compression_level,
870 write_png_compression_strategy,
871 write_png_compression_filter,
cristy3ed852e2009-09-05 21:47:34 +0000872 write_png8,
873 write_png24,
874 write_png32;
875
876#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000877 size_t
cristy3ed852e2009-09-05 21:47:34 +0000878 basi_width,
879 basi_height;
880
881 unsigned int
882 basi_depth,
883 basi_color_type,
884 basi_compression_method,
885 basi_filter_type,
886 basi_interlace_method,
887 basi_red,
888 basi_green,
889 basi_blue,
890 basi_alpha,
891 basi_viewable;
892#endif
893
894 png_uint_16
895 magn_first,
896 magn_last,
897 magn_mb,
898 magn_ml,
899 magn_mr,
900 magn_mt,
901 magn_mx,
902 magn_my,
903 magn_methx,
904 magn_methy;
905
cristy101ab702011-10-13 13:06:32 +0000906 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000907 mng_global_bkgd;
908
glennrp26f37912010-12-23 16:22:42 +0000909 /* Added at version 6.6.6-7 */
910 MagickBooleanType
911 ping_exclude_bKGD,
912 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000913 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000914 ping_exclude_EXIF,
915 ping_exclude_gAMA,
916 ping_exclude_iCCP,
917 /* ping_exclude_iTXt, */
918 ping_exclude_oFFs,
919 ping_exclude_pHYs,
920 ping_exclude_sRGB,
921 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000922 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000923 ping_exclude_vpAg,
924 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000925 ping_exclude_zTXt,
926 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000927
cristy3ed852e2009-09-05 21:47:34 +0000928} MngInfo;
929#endif /* VER */
930
931/*
932 Forward declarations.
933*/
934static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000935 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000936
cristy3ed852e2009-09-05 21:47:34 +0000937static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000938 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000939
cristy3ed852e2009-09-05 21:47:34 +0000940#if defined(JNG_SUPPORTED)
941static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000942 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000943#endif
944
glennrp0c3e06b2010-11-19 13:45:02 +0000945#if PNG_LIBPNG_VER > 10011
946
glennrpfd05d622011-02-25 04:10:33 +0000947
glennrp0c3e06b2010-11-19 13:45:02 +0000948#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
949static MagickBooleanType
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
1794 png_ptr=png_ptr;
1795 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
1802 png_ptr=png_ptr;
1803 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{
1812 png_ptr=png_ptr;
1813 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 ping_rowbytes,
2057 x_resolution,
2058 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002059
cristy3ed852e2009-09-05 21:47:34 +00002060 QuantumInfo
2061 *quantum_info;
2062
2063 unsigned char
glennrpcf002022011-01-30 02:38:15 +00002064 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002065
cristybb503372010-05-27 20:51:26 +00002066 ssize_t
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);
cristyd15e6592011-10-15 00:13:06 +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);
cristyd15e6592011-10-15 00:13:06 +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);
cristyd15e6592011-10-15 00:13:06 +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);
cristyd15e6592011-10-15 00:13:06 +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);
glennrp1a2061f2011-10-18 12:30:45 +00003533 (void) SetImageProperty(image,"PNG:text ",msg,
3534 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);
glennrp1a2061f2011-10-18 12:30:45 +00003541 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg,
3542 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)");
glennrp1a2061f2011-10-18 12:30:45 +00003549 (void) SetImageProperty(image,"PNG:cHRM ",msg,
3550 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)");
glennrp1a2061f2011-10-18 12:30:45 +00003557 (void) SetImageProperty(image,"PNG:bKGD ",msg,
3558 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))
glennrp1a2061f2011-10-18 12:30:45 +00003565 (void) SetImageProperty(image,"PNG:iCCP ",msg,
3566 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))
glennrp1a2061f2011-10-18 12:30:45 +00003569 (void) SetImageProperty(image,"PNG:tRNS ",msg,
3570 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);
glennrp1a2061f2011-10-18 12:30:45 +00003578 (void) SetImageProperty(image,"PNG:sRGB ",msg,
3579 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);
glennrp1a2061f2011-10-18 12:30:45 +00003588 (void) SetImageProperty(image,"PNG:gAMA ",msg,
3589 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);
glennrp1a2061f2011-10-18 12:30:45 +00003598 (void) SetImageProperty(image,"PNG:pHYs ",msg,
3599 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);
glennrp1a2061f2011-10-18 12:30:45 +00003608 (void) SetImageProperty(image,"PNG:oFFs ",msg,
3609 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);
glennrp1a2061f2011-10-18 12:30:45 +00003619 (void) SetImageProperty(image,"PNG:vpAg ",msg,
3620 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;
6300 length=(size_t) image->columns;
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 {
7801 r=GetAuthenticPixels(image,0,y,image->columns,1,
7802 exception);
7803
cristy4c08aed2011-07-01 19:47:50 +00007804 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007805 break;
7806
7807 for (x=0; x < (ssize_t) image->columns; x++)
7808 {
glennrp54cf7972011-08-06 14:28:09 +00007809 LBR16PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007810 r++;
7811 }
glennrpbb4f99d2011-05-22 11:13:17 +00007812
glennrp8e58efd2011-05-20 12:16:29 +00007813 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7814 break;
7815 }
7816
7817 if (image->storage_class == PseudoClass && image->colormap != NULL)
7818 {
cristy3e08f112011-05-24 13:19:30 +00007819 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007820 {
glennrp91d99252011-06-25 14:30:13 +00007821 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007822 }
7823 }
7824#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7825 }
7826
7827 else if (image->depth > 4)
7828 {
7829#if MAGICKCORE_QUANTUM_DEPTH > 8
7830 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007831 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007832
7833 for (y=0; y < (ssize_t) image->rows; y++)
7834 {
cristyc82a27b2011-10-21 01:07:16 +00007835 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007836
cristy4c08aed2011-07-01 19:47:50 +00007837 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007838 break;
7839
7840 for (x=0; x < (ssize_t) image->columns; x++)
7841 {
glennrp54cf7972011-08-06 14:28:09 +00007842 LBR08PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007843 r++;
7844 }
glennrpbb4f99d2011-05-22 11:13:17 +00007845
glennrp8e58efd2011-05-20 12:16:29 +00007846 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7847 break;
7848 }
7849
7850 if (image->storage_class == PseudoClass && image->colormap != NULL)
7851 {
cristy3e08f112011-05-24 13:19:30 +00007852 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007853 {
glennrp91d99252011-06-25 14:30:13 +00007854 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007855 }
7856 }
7857#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7858 }
7859 else
7860 if (image->depth > 2)
7861 {
7862 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007863 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007864
7865 for (y=0; y < (ssize_t) image->rows; y++)
7866 {
7867 r=GetAuthenticPixels(image,0,y,image->columns,1,
7868 exception);
7869
cristy4c08aed2011-07-01 19:47:50 +00007870 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007871 break;
7872
7873 for (x=0; x < (ssize_t) image->columns; x++)
7874 {
glennrp54cf7972011-08-06 14:28:09 +00007875 LBR04PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007876 r++;
7877 }
glennrpbb4f99d2011-05-22 11:13:17 +00007878
glennrp8e58efd2011-05-20 12:16:29 +00007879 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7880 break;
7881 }
7882
7883 if (image->storage_class == PseudoClass && image->colormap != NULL)
7884 {
cristy3e08f112011-05-24 13:19:30 +00007885 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007886 {
glennrp91d99252011-06-25 14:30:13 +00007887 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007888 }
7889 }
7890 }
7891
7892 else if (image->depth > 1)
7893 {
7894 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007895 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007896
7897 for (y=0; y < (ssize_t) image->rows; y++)
7898 {
7899 r=GetAuthenticPixels(image,0,y,image->columns,1,
7900 exception);
7901
cristy4c08aed2011-07-01 19:47:50 +00007902 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007903 break;
7904
7905 for (x=0; x < (ssize_t) image->columns; x++)
7906 {
glennrp54cf7972011-08-06 14:28:09 +00007907 LBR02PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007908 r++;
7909 }
glennrpbb4f99d2011-05-22 11:13:17 +00007910
glennrp8e58efd2011-05-20 12:16:29 +00007911 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7912 break;
7913 }
7914
7915 if (image->storage_class == PseudoClass && image->colormap != NULL)
7916 {
cristy3e08f112011-05-24 13:19:30 +00007917 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007918 {
glennrp91d99252011-06-25 14:30:13 +00007919 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007920 }
7921 }
7922 }
7923 else
7924 {
7925 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007926 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007927
7928 for (y=0; y < (ssize_t) image->rows; y++)
7929 {
7930 r=GetAuthenticPixels(image,0,y,image->columns,1,
7931 exception);
7932
cristy4c08aed2011-07-01 19:47:50 +00007933 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007934 break;
7935
7936 for (x=0; x < (ssize_t) image->columns; x++)
7937 {
glennrp54cf7972011-08-06 14:28:09 +00007938 LBR01PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007939 r++;
7940 }
glennrpbb4f99d2011-05-22 11:13:17 +00007941
glennrp8e58efd2011-05-20 12:16:29 +00007942 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7943 break;
7944 }
7945
7946 if (image->storage_class == PseudoClass && image->colormap != NULL)
7947 {
cristy3e08f112011-05-24 13:19:30 +00007948 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007949 {
glennrp91d99252011-06-25 14:30:13 +00007950 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007951 }
7952 }
7953 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007954 }
7955
glennrp67b9c1a2011-04-22 18:47:36 +00007956 /* To do: set to next higher multiple of 8 */
7957 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007958 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007959
glennrp2b013e42010-11-24 16:55:50 +00007960#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7961 /* PNG does not handle depths greater than 16 so reduce it even
7962 * if lossy
7963 */
glennrp8e58efd2011-05-20 12:16:29 +00007964 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007965 image->depth=16;
7966#endif
7967
glennrp3faa9a32011-04-23 14:00:25 +00007968#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007969 if (image->depth == 16 && mng_info->write_png_depth != 16)
cristyc82a27b2011-10-21 01:07:16 +00007970 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007971 image->depth = 8;
7972#endif
7973
glennrpc8c2f062011-02-25 19:00:33 +00007974 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007975 * we reduce the transparency to binary and run again, then if there
7976 * 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 +00007977 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7978 * palette. Then (To do) we take care of a final reduction that is only
7979 * needed if there are still 256 colors present and one of them has both
7980 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007981 */
glennrp82b3c532011-03-22 19:20:54 +00007982
glennrp8ca51ad2011-05-12 21:22:32 +00007983 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007984 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007985 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007986
glennrp8ca51ad2011-05-12 21:22:32 +00007987 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007988 {
7989 /* BUILD_PALETTE
7990 *
7991 * Sometimes we get DirectClass images that have 256 colors or fewer.
7992 * This code will build a colormap.
7993 *
7994 * Also, sometimes we get PseudoClass images with an out-of-date
7995 * colormap. This code will replace the colormap with a new one.
7996 * Sometimes we get PseudoClass images that have more than 256 colors.
7997 * This code will delete the colormap and change the image to
7998 * DirectClass.
7999 *
cristy4c08aed2011-07-01 19:47:50 +00008000 * If image->matte is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00008001 * even though it sometimes contains left-over non-opaque values.
8002 *
8003 * Also we gather some information (number of opaque, transparent,
8004 * and semitransparent pixels, and whether the image has any non-gray
8005 * pixels or only black-and-white pixels) that we might need later.
8006 *
8007 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8008 * we need to check for bogus non-opaque values, at least.
8009 */
glennrp3c218112010-11-27 15:31:26 +00008010
glennrpd71e86a2011-02-24 01:28:37 +00008011 int
8012 n;
glennrp3c218112010-11-27 15:31:26 +00008013
cristy101ab702011-10-13 13:06:32 +00008014 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008015 opaque[260],
8016 semitransparent[260],
8017 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00008018
cristy4c08aed2011-07-01 19:47:50 +00008019 register const Quantum
8020 *s;
glennrp8bb3a022010-12-13 20:40:04 +00008021
cristy4c08aed2011-07-01 19:47:50 +00008022 register Quantum
8023 *q,
glennrpfd05d622011-02-25 04:10:33 +00008024 *r;
8025
glennrpd71e86a2011-02-24 01:28:37 +00008026 if (logging != MagickFalse)
8027 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8028 " Enter BUILD_PALETTE:");
8029
8030 if (logging != MagickFalse)
8031 {
glennrp03812ae2010-12-24 01:31:34 +00008032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008033 " image->columns=%.20g",(double) image->columns);
8034 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8035 " image->rows=%.20g",(double) image->rows);
8036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8037 " image->matte=%.20g",(double) image->matte);
8038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8039 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008040
glennrpfd05d622011-02-25 04:10:33 +00008041 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008042 {
8043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008044 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008046 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008047
glennrpd71e86a2011-02-24 01:28:37 +00008048 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008049 {
glennrpd71e86a2011-02-24 01:28:37 +00008050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8051 " %d (%d,%d,%d,%d)",
8052 (int) i,
8053 (int) image->colormap[i].red,
8054 (int) image->colormap[i].green,
8055 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008056 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008057 }
glennrp2cc891a2010-12-24 13:44:32 +00008058
glennrpd71e86a2011-02-24 01:28:37 +00008059 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8060 {
8061 if (i > 255)
8062 {
8063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8064 " %d (%d,%d,%d,%d)",
8065 (int) i,
8066 (int) image->colormap[i].red,
8067 (int) image->colormap[i].green,
8068 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008069 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008070 }
8071 }
glennrp03812ae2010-12-24 01:31:34 +00008072 }
glennrp7ddcc222010-12-11 05:01:05 +00008073
glennrpd71e86a2011-02-24 01:28:37 +00008074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8075 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008076
glennrpd71e86a2011-02-24 01:28:37 +00008077 if (image->colors == 0)
8078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8079 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008080
glennrp8d3d6e52011-04-19 04:39:51 +00008081 if (ping_preserve_colormap == MagickFalse)
8082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8083 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008084 }
8085
glennrpd71e86a2011-02-24 01:28:37 +00008086 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008087 number_opaque = 0;
8088 number_semitransparent = 0;
8089 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008090
8091 for (y=0; y < (ssize_t) image->rows; y++)
8092 {
8093 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8094
cristyacd2ed22011-08-30 01:44:23 +00008095 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008096 break;
8097
8098 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008099 {
glennrp4737d522011-04-29 03:33:42 +00008100 if (image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008101 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008102 {
8103 if (number_opaque < 259)
8104 {
8105 if (number_opaque == 0)
8106 {
cristy101ab702011-10-13 13:06:32 +00008107 GetPixelInfoPixel(image, q, opaque);
cristy4c08aed2011-07-01 19:47:50 +00008108 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008109 number_opaque=1;
8110 }
glennrp2cc891a2010-12-24 13:44:32 +00008111
glennrpd71e86a2011-02-24 01:28:37 +00008112 for (i=0; i< (ssize_t) number_opaque; i++)
8113 {
cristy4c08aed2011-07-01 19:47:50 +00008114 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008115 break;
8116 }
glennrp7ddcc222010-12-11 05:01:05 +00008117
glennrpd71e86a2011-02-24 01:28:37 +00008118 if (i == (ssize_t) number_opaque &&
8119 number_opaque < 259)
8120 {
8121 number_opaque++;
cristy101ab702011-10-13 13:06:32 +00008122 GetPixelInfoPixel(image, q, opaque+i);
cristy4c08aed2011-07-01 19:47:50 +00008123 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008124 }
8125 }
8126 }
cristy4c08aed2011-07-01 19:47:50 +00008127 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008128 {
8129 if (number_transparent < 259)
8130 {
8131 if (number_transparent == 0)
8132 {
cristy101ab702011-10-13 13:06:32 +00008133 GetPixelInfoPixel(image, q, transparent);
cristy4c08aed2011-07-01 19:47:50 +00008134 ping_trans_color.red=(unsigned short)
8135 GetPixelRed(image,q);
8136 ping_trans_color.green=(unsigned short)
8137 GetPixelGreen(image,q);
8138 ping_trans_color.blue=(unsigned short)
8139 GetPixelBlue(image,q);
8140 ping_trans_color.gray=(unsigned short)
8141 GetPixelRed(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008142 number_transparent = 1;
8143 }
8144
8145 for (i=0; i< (ssize_t) number_transparent; i++)
8146 {
cristy4c08aed2011-07-01 19:47:50 +00008147 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008148 break;
8149 }
8150
8151 if (i == (ssize_t) number_transparent &&
8152 number_transparent < 259)
8153 {
8154 number_transparent++;
cristy101ab702011-10-13 13:06:32 +00008155 GetPixelInfoPixel(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008156 }
8157 }
8158 }
8159 else
8160 {
8161 if (number_semitransparent < 259)
8162 {
8163 if (number_semitransparent == 0)
8164 {
cristy101ab702011-10-13 13:06:32 +00008165 GetPixelInfoPixel(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008166 number_semitransparent = 1;
8167 }
8168
8169 for (i=0; i< (ssize_t) number_semitransparent; i++)
8170 {
cristy4c08aed2011-07-01 19:47:50 +00008171 if (IsPixelEquivalent(image,q, semitransparent+i)
8172 && GetPixelAlpha(image,q) ==
8173 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008174 break;
8175 }
8176
8177 if (i == (ssize_t) number_semitransparent &&
8178 number_semitransparent < 259)
8179 {
8180 number_semitransparent++;
cristy101ab702011-10-13 13:06:32 +00008181 GetPixelInfoPixel(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008182 }
8183 }
8184 }
cristyed231572011-07-14 02:18:59 +00008185 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008186 }
8187 }
8188
cristy4054bfb2011-08-29 23:41:39 +00008189 if (mng_info->write_png8 == MagickFalse &&
8190 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008191 {
8192 /* Add the background color to the palette, if it
8193 * isn't already there.
8194 */
glennrpc6c391a2011-04-27 02:23:56 +00008195 if (logging != MagickFalse)
8196 {
8197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8198 " Check colormap for background (%d,%d,%d)",
8199 (int) image->background_color.red,
8200 (int) image->background_color.green,
8201 (int) image->background_color.blue);
8202 }
glennrpd71e86a2011-02-24 01:28:37 +00008203 for (i=0; i<number_opaque; i++)
8204 {
glennrpca7ad3a2011-04-26 04:44:54 +00008205 if (opaque[i].red == image->background_color.red &&
8206 opaque[i].green == image->background_color.green &&
8207 opaque[i].blue == image->background_color.blue)
8208 break;
glennrpd71e86a2011-02-24 01:28:37 +00008209 }
glennrpd71e86a2011-02-24 01:28:37 +00008210 if (number_opaque < 259 && i == number_opaque)
8211 {
glennrp8e045c82011-04-27 16:40:27 +00008212 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008213 ping_background.index = i;
8214 if (logging != MagickFalse)
8215 {
8216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8217 " background_color index is %d",(int) i);
8218 }
8219
glennrpd71e86a2011-02-24 01:28:37 +00008220 }
glennrpa080bc32011-03-11 18:03:44 +00008221 else if (logging != MagickFalse)
8222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8223 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008224 }
8225
8226 image_colors=number_opaque+number_transparent+number_semitransparent;
8227
glennrpa080bc32011-03-11 18:03:44 +00008228 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8229 {
8230 /* No room for the background color; remove it. */
8231 number_opaque--;
8232 image_colors--;
8233 }
8234
glennrpd71e86a2011-02-24 01:28:37 +00008235 if (logging != MagickFalse)
8236 {
8237 if (image_colors > 256)
8238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8239 " image has more than 256 colors");
8240
8241 else
8242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8243 " image has %d colors",image_colors);
8244 }
8245
glennrp8d3d6e52011-04-19 04:39:51 +00008246 if (ping_preserve_colormap != MagickFalse)
8247 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008248
glennrpfd05d622011-02-25 04:10:33 +00008249 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008250 {
8251 ping_have_color=MagickFalse;
8252 ping_have_non_bw=MagickFalse;
8253
8254 if(image_colors > 256)
8255 {
8256 for (y=0; y < (ssize_t) image->rows; y++)
8257 {
8258 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8259
cristyacd2ed22011-08-30 01:44:23 +00008260 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008261 break;
8262
glennrpe5e6b802011-07-20 14:44:40 +00008263 s=q;
8264 for (x=0; x < (ssize_t) image->columns; x++)
8265 {
8266 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8267 GetPixelRed(image,s) != GetPixelBlue(image,s))
8268 {
8269 ping_have_color=MagickTrue;
8270 ping_have_non_bw=MagickTrue;
8271 break;
8272 }
8273 s+=GetPixelChannels(image);
8274 }
8275
8276 if (ping_have_color != MagickFalse)
8277 break;
8278
glennrpd71e86a2011-02-24 01:28:37 +00008279 /* Worst case is black-and-white; we are looking at every
8280 * pixel twice.
8281 */
8282
glennrpd71e86a2011-02-24 01:28:37 +00008283 if (ping_have_non_bw == MagickFalse)
8284 {
8285 s=q;
8286 for (x=0; x < (ssize_t) image->columns; x++)
8287 {
cristy4c08aed2011-07-01 19:47:50 +00008288 if (GetPixelRed(image,s) != 0 &&
8289 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008290 {
8291 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008292 break;
glennrpd71e86a2011-02-24 01:28:37 +00008293 }
cristyed231572011-07-14 02:18:59 +00008294 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008295 }
glennrpe5e6b802011-07-20 14:44:40 +00008296 }
glennrpd71e86a2011-02-24 01:28:37 +00008297 }
glennrpbb4f99d2011-05-22 11:13:17 +00008298 }
8299 }
glennrpd71e86a2011-02-24 01:28:37 +00008300
8301 if (image_colors < 257)
8302 {
cristy101ab702011-10-13 13:06:32 +00008303 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008304 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008305
glennrpd71e86a2011-02-24 01:28:37 +00008306 /*
8307 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008308 */
8309
glennrpd71e86a2011-02-24 01:28:37 +00008310 if (logging != MagickFalse)
8311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8312 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008313
glennrpd71e86a2011-02-24 01:28:37 +00008314 /* Sort palette, transparent first */;
8315
8316 n = 0;
8317
8318 for (i=0; i<number_transparent; i++)
8319 colormap[n++] = transparent[i];
8320
8321 for (i=0; i<number_semitransparent; i++)
8322 colormap[n++] = semitransparent[i];
8323
8324 for (i=0; i<number_opaque; i++)
8325 colormap[n++] = opaque[i];
8326
glennrpc6c391a2011-04-27 02:23:56 +00008327 ping_background.index +=
8328 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008329
glennrpd71e86a2011-02-24 01:28:37 +00008330 /* image_colors < 257; search the colormap instead of the pixels
8331 * to get ping_have_color and ping_have_non_bw
8332 */
8333 for (i=0; i<n; i++)
8334 {
8335 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008336 {
glennrpd71e86a2011-02-24 01:28:37 +00008337 if (colormap[i].red != colormap[i].green ||
8338 colormap[i].red != colormap[i].blue)
8339 {
8340 ping_have_color=MagickTrue;
8341 ping_have_non_bw=MagickTrue;
8342 break;
8343 }
8344 }
8345
8346 if (ping_have_non_bw == MagickFalse)
8347 {
8348 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008349 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008350 }
glennrp8bb3a022010-12-13 20:40:04 +00008351 }
8352
glennrpd71e86a2011-02-24 01:28:37 +00008353 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8354 (number_transparent == 0 && number_semitransparent == 0)) &&
8355 (((mng_info->write_png_colortype-1) ==
8356 PNG_COLOR_TYPE_PALETTE) ||
8357 (mng_info->write_png_colortype == 0)))
8358 {
glennrp6185c532011-01-14 17:58:40 +00008359 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008360 {
glennrpd71e86a2011-02-24 01:28:37 +00008361 if (n != (ssize_t) image_colors)
8362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8363 " image_colors (%d) and n (%d) don't match",
8364 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008365
glennrpd71e86a2011-02-24 01:28:37 +00008366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8367 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008368 }
glennrp03812ae2010-12-24 01:31:34 +00008369
glennrpd71e86a2011-02-24 01:28:37 +00008370 image->colors = image_colors;
8371
cristy018f07f2011-09-04 21:15:19 +00008372 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008373 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008374 ThrowWriterException(ResourceLimitError,
8375 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008376
8377 for (i=0; i< (ssize_t) image_colors; i++)
8378 image->colormap[i] = colormap[i];
8379
8380 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008381 {
glennrpd71e86a2011-02-24 01:28:37 +00008382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8383 " image->colors=%d (%d)",
8384 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008385
glennrpd71e86a2011-02-24 01:28:37 +00008386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8387 " Update the pixel indexes");
8388 }
glennrp6185c532011-01-14 17:58:40 +00008389
glennrpfd05d622011-02-25 04:10:33 +00008390 /* Sync the pixel indices with the new colormap */
8391
glennrpd71e86a2011-02-24 01:28:37 +00008392 for (y=0; y < (ssize_t) image->rows; y++)
8393 {
8394 q=GetAuthenticPixels(image,0,y,image->columns,1,
8395 exception);
glennrp6185c532011-01-14 17:58:40 +00008396
cristyacd2ed22011-08-30 01:44:23 +00008397 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008398 break;
glennrp6185c532011-01-14 17:58:40 +00008399
glennrpbb4f99d2011-05-22 11:13:17 +00008400
glennrpd71e86a2011-02-24 01:28:37 +00008401 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008402 {
glennrpd71e86a2011-02-24 01:28:37 +00008403 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008404 {
glennrpd71e86a2011-02-24 01:28:37 +00008405 if ((image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008406 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8407 image->colormap[i].red == GetPixelRed(image,q) &&
8408 image->colormap[i].green == GetPixelGreen(image,q) &&
8409 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008410 {
cristy4c08aed2011-07-01 19:47:50 +00008411 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008412 break;
glennrp6185c532011-01-14 17:58:40 +00008413 }
glennrp6185c532011-01-14 17:58:40 +00008414 }
cristyed231572011-07-14 02:18:59 +00008415 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008416 }
glennrp6185c532011-01-14 17:58:40 +00008417
glennrpd71e86a2011-02-24 01:28:37 +00008418 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8419 break;
8420 }
8421 }
8422 }
8423
8424 if (logging != MagickFalse)
8425 {
8426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8427 " image->colors=%d", (int) image->colors);
8428
8429 if (image->colormap != NULL)
8430 {
8431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008432 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008433
8434 for (i=0; i < (ssize_t) image->colors; i++)
8435 {
cristy72988482011-03-29 16:34:38 +00008436 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008437 {
8438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8439 " %d (%d,%d,%d,%d)",
8440 (int) i,
8441 (int) image->colormap[i].red,
8442 (int) image->colormap[i].green,
8443 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008444 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008445 }
glennrp6185c532011-01-14 17:58:40 +00008446 }
8447 }
glennrp03812ae2010-12-24 01:31:34 +00008448
glennrpd71e86a2011-02-24 01:28:37 +00008449 if (number_transparent < 257)
8450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8451 " number_transparent = %d",
8452 number_transparent);
8453 else
glennrp03812ae2010-12-24 01:31:34 +00008454
glennrpd71e86a2011-02-24 01:28:37 +00008455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8456 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008457
glennrpd71e86a2011-02-24 01:28:37 +00008458 if (number_opaque < 257)
8459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8460 " number_opaque = %d",
8461 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008462
glennrpd71e86a2011-02-24 01:28:37 +00008463 else
8464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8465 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008466
glennrpd71e86a2011-02-24 01:28:37 +00008467 if (number_semitransparent < 257)
8468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8469 " number_semitransparent = %d",
8470 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008471
glennrpd71e86a2011-02-24 01:28:37 +00008472 else
8473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8474 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008475
glennrpd71e86a2011-02-24 01:28:37 +00008476 if (ping_have_non_bw == MagickFalse)
8477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8478 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008479
glennrpd71e86a2011-02-24 01:28:37 +00008480 else if (ping_have_color == MagickFalse)
8481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8482 " All pixels and the background are gray");
8483
8484 else
8485 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8486 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008487
glennrp03812ae2010-12-24 01:31:34 +00008488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8489 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008490 }
glennrpfd05d622011-02-25 04:10:33 +00008491
glennrpc8c2f062011-02-25 19:00:33 +00008492 if (mng_info->write_png8 == MagickFalse)
8493 break;
glennrpfd05d622011-02-25 04:10:33 +00008494
glennrpc8c2f062011-02-25 19:00:33 +00008495 /* Make any reductions necessary for the PNG8 format */
8496 if (image_colors <= 256 &&
8497 image_colors != 0 && image->colormap != NULL &&
8498 number_semitransparent == 0 &&
8499 number_transparent <= 1)
8500 break;
8501
8502 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008503 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8504 * transparent color so if more than one is transparent we merge
8505 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008506 */
glennrp130fc452011-08-20 03:43:18 +00008507 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008508 {
8509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8510 " Thresholding the alpha channel to binary");
8511
8512 for (y=0; y < (ssize_t) image->rows; y++)
8513 {
8514 r=GetAuthenticPixels(image,0,y,image->columns,1,
8515 exception);
8516
cristy4c08aed2011-07-01 19:47:50 +00008517 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008518 break;
8519
8520 for (x=0; x < (ssize_t) image->columns; x++)
8521 {
glennrpf73547f2011-08-20 04:40:26 +00008522 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008523 {
cristy101ab702011-10-13 13:06:32 +00008524 SetPixelPixelInfo(image,&image->background_color,r);
cristy4c08aed2011-07-01 19:47:50 +00008525 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008526 }
8527 else
cristy4c08aed2011-07-01 19:47:50 +00008528 SetPixelAlpha(image,OpaqueAlpha,r);
cristyed231572011-07-14 02:18:59 +00008529 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008530 }
glennrpbb4f99d2011-05-22 11:13:17 +00008531
glennrpc8c2f062011-02-25 19:00:33 +00008532 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8533 break;
8534
8535 if (image_colors != 0 && image_colors <= 256 &&
8536 image->colormap != NULL)
8537 for (i=0; i<image_colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00008538 image->colormap[i].alpha =
8539 (image->colormap[i].alpha > TransparentAlpha/2 ?
8540 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008541 }
8542 continue;
8543 }
8544
8545 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008546 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8547 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8548 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008549 */
glennrpd3371642011-03-22 19:42:23 +00008550 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8551 {
8552 if (logging != MagickFalse)
8553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8554 " Quantizing the background color to 4-4-4");
8555
8556 tried_444 = MagickTrue;
8557
glennrp91d99252011-06-25 14:30:13 +00008558 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008559
8560 if (logging != MagickFalse)
8561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8562 " Quantizing the pixel colors to 4-4-4");
8563
8564 if (image->colormap == NULL)
8565 {
8566 for (y=0; y < (ssize_t) image->rows; y++)
8567 {
8568 r=GetAuthenticPixels(image,0,y,image->columns,1,
8569 exception);
8570
cristy4c08aed2011-07-01 19:47:50 +00008571 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008572 break;
8573
8574 for (x=0; x < (ssize_t) image->columns; x++)
8575 {
cristy4c08aed2011-07-01 19:47:50 +00008576 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008577 LBR04PixelRGB(r);
glennrpd3371642011-03-22 19:42:23 +00008578 r++;
8579 }
glennrpbb4f99d2011-05-22 11:13:17 +00008580
glennrpd3371642011-03-22 19:42:23 +00008581 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8582 break;
8583 }
8584 }
8585
8586 else /* Should not reach this; colormap already exists and
8587 must be <= 256 */
8588 {
8589 if (logging != MagickFalse)
8590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8591 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008592
glennrpd3371642011-03-22 19:42:23 +00008593 for (i=0; i<image_colors; i++)
8594 {
glennrp91d99252011-06-25 14:30:13 +00008595 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008596 }
8597 }
8598 continue;
8599 }
8600
glennrp82b3c532011-03-22 19:20:54 +00008601 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8602 {
8603 if (logging != MagickFalse)
8604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8605 " Quantizing the background color to 3-3-3");
8606
8607 tried_333 = MagickTrue;
8608
glennrp91d99252011-06-25 14:30:13 +00008609 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008610
8611 if (logging != MagickFalse)
8612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008613 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008614
8615 if (image->colormap == NULL)
8616 {
8617 for (y=0; y < (ssize_t) image->rows; y++)
8618 {
8619 r=GetAuthenticPixels(image,0,y,image->columns,1,
8620 exception);
8621
cristy4c08aed2011-07-01 19:47:50 +00008622 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008623 break;
8624
8625 for (x=0; x < (ssize_t) image->columns; x++)
8626 {
cristy4c08aed2011-07-01 19:47:50 +00008627 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8628 LBR03RGB(r);
glennrp82b3c532011-03-22 19:20:54 +00008629 r++;
8630 }
glennrpbb4f99d2011-05-22 11:13:17 +00008631
glennrp82b3c532011-03-22 19:20:54 +00008632 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8633 break;
8634 }
8635 }
8636
8637 else /* Should not reach this; colormap already exists and
8638 must be <= 256 */
8639 {
8640 if (logging != MagickFalse)
8641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008642 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008643 for (i=0; i<image_colors; i++)
8644 {
glennrp91d99252011-06-25 14:30:13 +00008645 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008646 }
glennrpd3371642011-03-22 19:42:23 +00008647 }
8648 continue;
glennrp82b3c532011-03-22 19:20:54 +00008649 }
glennrpc8c2f062011-02-25 19:00:33 +00008650
glennrp8ca51ad2011-05-12 21:22:32 +00008651 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008652 {
8653 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008655 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008656
glennrp8ca51ad2011-05-12 21:22:32 +00008657 tried_332 = MagickTrue;
8658
glennrp3faa9a32011-04-23 14:00:25 +00008659 /* Red and green were already done so we only quantize the blue
8660 * channel
8661 */
8662
glennrp91d99252011-06-25 14:30:13 +00008663 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008664
glennrpc8c2f062011-02-25 19:00:33 +00008665 if (logging != MagickFalse)
8666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008667 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008668
glennrpc8c2f062011-02-25 19:00:33 +00008669 if (image->colormap == NULL)
8670 {
8671 for (y=0; y < (ssize_t) image->rows; y++)
8672 {
8673 r=GetAuthenticPixels(image,0,y,image->columns,1,
8674 exception);
8675
cristy4c08aed2011-07-01 19:47:50 +00008676 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008677 break;
8678
8679 for (x=0; x < (ssize_t) image->columns; x++)
8680 {
cristy4c08aed2011-07-01 19:47:50 +00008681 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008682 LBR02PixelBlue(r);
glennrp52a479c2011-02-26 21:14:38 +00008683 r++;
glennrpc8c2f062011-02-25 19:00:33 +00008684 }
glennrpbb4f99d2011-05-22 11:13:17 +00008685
glennrpc8c2f062011-02-25 19:00:33 +00008686 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8687 break;
8688 }
8689 }
glennrpfd05d622011-02-25 04:10:33 +00008690
glennrpc8c2f062011-02-25 19:00:33 +00008691 else /* Should not reach this; colormap already exists and
8692 must be <= 256 */
8693 {
8694 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008696 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008697 for (i=0; i<image_colors; i++)
8698 {
glennrp91d99252011-06-25 14:30:13 +00008699 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008700 }
8701 }
8702 continue;
8703 }
8704 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008705
8706 if (image_colors == 0 || image_colors > 256)
8707 {
8708 /* Take care of special case with 256 colors + 1 transparent
8709 * color. We don't need to quantize to 2-3-2-1; we only need to
8710 * eliminate one color, so we'll merge the two darkest red
8711 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8712 */
8713 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8714 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8715 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8716 {
8717 image->background_color.red=ScaleCharToQuantum(0x24);
8718 }
glennrpbb4f99d2011-05-22 11:13:17 +00008719
glennrp8ca51ad2011-05-12 21:22:32 +00008720 if (image->colormap == NULL)
8721 {
8722 for (y=0; y < (ssize_t) image->rows; y++)
8723 {
8724 r=GetAuthenticPixels(image,0,y,image->columns,1,
8725 exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008726
cristy4c08aed2011-07-01 19:47:50 +00008727 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008728 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008729
glennrp8ca51ad2011-05-12 21:22:32 +00008730 for (x=0; x < (ssize_t) image->columns; x++)
8731 {
cristy4c08aed2011-07-01 19:47:50 +00008732 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8733 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8734 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8735 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008736 {
cristy4c08aed2011-07-01 19:47:50 +00008737 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008738 }
cristyed231572011-07-14 02:18:59 +00008739 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008740 }
glennrpbb4f99d2011-05-22 11:13:17 +00008741
glennrp8ca51ad2011-05-12 21:22:32 +00008742 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8743 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008744
glennrp8ca51ad2011-05-12 21:22:32 +00008745 }
8746 }
8747
8748 else
8749 {
8750 for (i=0; i<image_colors; i++)
8751 {
8752 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8753 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8754 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8755 {
8756 image->colormap[i].red=ScaleCharToQuantum(0x24);
8757 }
8758 }
8759 }
8760 }
glennrpd71e86a2011-02-24 01:28:37 +00008761 }
glennrpfd05d622011-02-25 04:10:33 +00008762 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008763
glennrpfd05d622011-02-25 04:10:33 +00008764 /* If we are excluding the tRNS chunk and there is transparency,
8765 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8766 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008767 */
glennrp0e8ea192010-12-24 18:00:33 +00008768 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8769 (number_transparent != 0 || number_semitransparent != 0))
8770 {
glennrpd17915c2011-04-29 14:24:22 +00008771 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008772
8773 if (ping_have_color == MagickFalse)
8774 mng_info->write_png_colortype = 5;
8775
8776 else
8777 mng_info->write_png_colortype = 7;
8778
glennrp8d579662011-02-23 02:05:02 +00008779 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008780 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008781 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008782
glennrp0e8ea192010-12-24 18:00:33 +00008783 }
8784
glennrpfd05d622011-02-25 04:10:33 +00008785 /* See if cheap transparency is possible. It is only possible
8786 * when there is a single transparent color, no semitransparent
8787 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008788 * as the transparent color. We only need this information if
8789 * we are writing a PNG with colortype 0 or 2, and we have not
8790 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008791 */
glennrp5a39f372011-02-25 04:52:16 +00008792 if (number_transparent == 1 &&
8793 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008794 {
8795 ping_have_cheap_transparency = MagickTrue;
8796
8797 if (number_semitransparent != 0)
8798 ping_have_cheap_transparency = MagickFalse;
8799
8800 else if (image_colors == 0 || image_colors > 256 ||
8801 image->colormap == NULL)
8802 {
cristy4c08aed2011-07-01 19:47:50 +00008803 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008804 *q;
8805
glennrpfd05d622011-02-25 04:10:33 +00008806 for (y=0; y < (ssize_t) image->rows; y++)
8807 {
8808 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8809
cristyacd2ed22011-08-30 01:44:23 +00008810 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008811 break;
8812
8813 for (x=0; x < (ssize_t) image->columns; x++)
8814 {
cristy4c08aed2011-07-01 19:47:50 +00008815 if (GetPixelAlpha(image,q) != TransparentAlpha &&
glennrp847370c2011-07-05 17:37:15 +00008816 (unsigned short) GetPixelRed(image,q) ==
8817 ping_trans_color.red &&
8818 (unsigned short) GetPixelGreen(image,q) ==
8819 ping_trans_color.green &&
8820 (unsigned short) GetPixelBlue(image,q) ==
8821 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008822 {
8823 ping_have_cheap_transparency = MagickFalse;
8824 break;
8825 }
8826
cristyed231572011-07-14 02:18:59 +00008827 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008828 }
glennrpbb4f99d2011-05-22 11:13:17 +00008829
glennrpfd05d622011-02-25 04:10:33 +00008830 if (ping_have_cheap_transparency == MagickFalse)
8831 break;
8832 }
8833 }
8834 else
8835 {
glennrp67b9c1a2011-04-22 18:47:36 +00008836 /* Assuming that image->colormap[0] is the one transparent color
8837 * and that all others are opaque.
8838 */
glennrpfd05d622011-02-25 04:10:33 +00008839 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008840 for (i=1; i<image_colors; i++)
8841 if (image->colormap[i].red == image->colormap[0].red &&
8842 image->colormap[i].green == image->colormap[0].green &&
8843 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008844 {
glennrp67b9c1a2011-04-22 18:47:36 +00008845 ping_have_cheap_transparency = MagickFalse;
8846 break;
glennrpfd05d622011-02-25 04:10:33 +00008847 }
8848 }
glennrpbb4f99d2011-05-22 11:13:17 +00008849
glennrpfd05d622011-02-25 04:10:33 +00008850 if (logging != MagickFalse)
8851 {
8852 if (ping_have_cheap_transparency == MagickFalse)
8853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8854 " Cheap transparency is not possible.");
8855
8856 else
8857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8858 " Cheap transparency is possible.");
8859 }
8860 }
8861 else
8862 ping_have_cheap_transparency = MagickFalse;
8863
glennrp8640fb52010-11-23 15:48:26 +00008864 image_depth=image->depth;
8865
glennrp26c990a2010-11-23 02:23:20 +00008866 quantum_info = (QuantumInfo *) NULL;
8867 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008868 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008869 image_matte=image->matte;
8870
glennrp0fe50b42010-11-16 03:52:51 +00008871 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008872 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008873
glennrp52a479c2011-02-26 21:14:38 +00008874 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8875 (image->colors == 0 || image->colormap == NULL))
8876 {
glennrp52a479c2011-02-26 21:14:38 +00008877 image_info=DestroyImageInfo(image_info);
8878 image=DestroyImage(image);
cristyc82a27b2011-10-21 01:07:16 +00008879 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
glennrp15e01552011-03-06 23:29:17 +00008880 "Cannot write PNG8 or color-type 3; colormap is NULL",
8881 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008882#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8883 UnlockSemaphoreInfo(ping_semaphore);
8884#endif
8885 return(MagickFalse);
8886 }
8887
cristy3ed852e2009-09-05 21:47:34 +00008888 /*
8889 Allocate the PNG structures
8890 */
8891#ifdef PNG_USER_MEM_SUPPORTED
cristyc82a27b2011-10-21 01:07:16 +00008892 error_info.image=image;
8893 error_info.exception=exception;
8894 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008895 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8896 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008897
cristy3ed852e2009-09-05 21:47:34 +00008898#else
cristyc82a27b2011-10-21 01:07:16 +00008899 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008900 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008901
cristy3ed852e2009-09-05 21:47:34 +00008902#endif
8903 if (ping == (png_struct *) NULL)
8904 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008905
cristy3ed852e2009-09-05 21:47:34 +00008906 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008907
cristy3ed852e2009-09-05 21:47:34 +00008908 if (ping_info == (png_info *) NULL)
8909 {
8910 png_destroy_write_struct(&ping,(png_info **) NULL);
8911 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8912 }
glennrp0fe50b42010-11-16 03:52:51 +00008913
cristy3ed852e2009-09-05 21:47:34 +00008914 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008915 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008916
glennrp5af765f2010-03-30 11:12:18 +00008917 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008918 {
8919 /*
8920 PNG write failed.
8921 */
8922#ifdef PNG_DEBUG
8923 if (image_info->verbose)
8924 (void) printf("PNG write has failed.\n");
8925#endif
8926 png_destroy_write_struct(&ping,&ping_info);
8927#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008928 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008929#endif
glennrpda8f3a72011-02-27 23:54:12 +00008930 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008931 (void) CloseBlob(image);
8932 image_info=DestroyImageInfo(image_info);
8933 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008934 return(MagickFalse);
8935 }
8936 /*
8937 Prepare PNG for writing.
8938 */
8939#if defined(PNG_MNG_FEATURES_SUPPORTED)
8940 if (mng_info->write_mng)
8941 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008942
cristy3ed852e2009-09-05 21:47:34 +00008943#else
8944# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8945 if (mng_info->write_mng)
8946 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008947
cristy3ed852e2009-09-05 21:47:34 +00008948# endif
8949#endif
glennrp2b013e42010-11-24 16:55:50 +00008950
cristy3ed852e2009-09-05 21:47:34 +00008951 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008952
cristy4e5bc842010-06-09 13:56:01 +00008953 ping_width=(png_uint_32) image->columns;
8954 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008955
cristy3ed852e2009-09-05 21:47:34 +00008956 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8957 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008958
cristy3ed852e2009-09-05 21:47:34 +00008959 if (mng_info->write_png_depth != 0)
8960 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008961
cristy3ed852e2009-09-05 21:47:34 +00008962 /* Adjust requested depth to next higher valid depth if necessary */
8963 if (image_depth > 8)
8964 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008965
cristy3ed852e2009-09-05 21:47:34 +00008966 if ((image_depth > 4) && (image_depth < 8))
8967 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008968
cristy3ed852e2009-09-05 21:47:34 +00008969 if (image_depth == 3)
8970 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008971
cristy3ed852e2009-09-05 21:47:34 +00008972 if (logging != MagickFalse)
8973 {
8974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008975 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008977 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008979 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008981 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008983 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008984 }
glennrp8640fb52010-11-23 15:48:26 +00008985
cristy3ed852e2009-09-05 21:47:34 +00008986 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008987 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008988
glennrp26f37912010-12-23 16:22:42 +00008989
cristy3ed852e2009-09-05 21:47:34 +00008990#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008991 if (ping_exclude_pHYs == MagickFalse)
8992 {
cristy2a11bef2011-10-28 18:33:11 +00008993 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00008994 (!mng_info->write_mng || !mng_info->equal_physs))
8995 {
glennrp0fe50b42010-11-16 03:52:51 +00008996 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8998 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008999
9000 if (image->units == PixelsPerInchResolution)
9001 {
glennrpdfd70802010-11-14 01:23:35 +00009002 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00009003 ping_pHYs_x_resolution=
cristy2a11bef2011-10-28 18:33:11 +00009004 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
glennrp823b55c2011-03-14 18:46:46 +00009005 ping_pHYs_y_resolution=
cristy2a11bef2011-10-28 18:33:11 +00009006 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00009007 }
glennrpdfd70802010-11-14 01:23:35 +00009008
cristy3ed852e2009-09-05 21:47:34 +00009009 else if (image->units == PixelsPerCentimeterResolution)
9010 {
glennrpdfd70802010-11-14 01:23:35 +00009011 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
cristy2a11bef2011-10-28 18:33:11 +00009012 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9013 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00009014 }
glennrp991d11d2010-11-12 21:55:28 +00009015
cristy3ed852e2009-09-05 21:47:34 +00009016 else
9017 {
glennrpdfd70802010-11-14 01:23:35 +00009018 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
cristy2a11bef2011-10-28 18:33:11 +00009019 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9020 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00009021 }
glennrp991d11d2010-11-12 21:55:28 +00009022
glennrp823b55c2011-03-14 18:46:46 +00009023 if (logging != MagickFalse)
9024 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9025 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9026 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9027 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009028 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009029 }
glennrp26f37912010-12-23 16:22:42 +00009030 }
cristy3ed852e2009-09-05 21:47:34 +00009031#endif
glennrpa521b2f2010-10-29 04:11:03 +00009032
glennrp26f37912010-12-23 16:22:42 +00009033 if (ping_exclude_bKGD == MagickFalse)
9034 {
glennrpa521b2f2010-10-29 04:11:03 +00009035 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009036 {
glennrpa521b2f2010-10-29 04:11:03 +00009037 unsigned int
9038 mask;
cristy3ed852e2009-09-05 21:47:34 +00009039
glennrpa521b2f2010-10-29 04:11:03 +00009040 mask=0xffff;
9041 if (ping_bit_depth == 8)
9042 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009043
glennrpa521b2f2010-10-29 04:11:03 +00009044 if (ping_bit_depth == 4)
9045 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009046
glennrpa521b2f2010-10-29 04:11:03 +00009047 if (ping_bit_depth == 2)
9048 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009049
glennrpa521b2f2010-10-29 04:11:03 +00009050 if (ping_bit_depth == 1)
9051 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009052
glennrpa521b2f2010-10-29 04:11:03 +00009053 ping_background.red=(png_uint_16)
9054 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009055
glennrpa521b2f2010-10-29 04:11:03 +00009056 ping_background.green=(png_uint_16)
9057 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009058
glennrpa521b2f2010-10-29 04:11:03 +00009059 ping_background.blue=(png_uint_16)
9060 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009061
9062 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009063 }
cristy3ed852e2009-09-05 21:47:34 +00009064
glennrp0fe50b42010-11-16 03:52:51 +00009065 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009066 {
9067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9068 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9070 " background_color index is %d",
9071 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009072
9073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9074 " ping_bit_depth=%d",ping_bit_depth);
9075 }
glennrp0fe50b42010-11-16 03:52:51 +00009076
9077 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009078 }
glennrp0fe50b42010-11-16 03:52:51 +00009079
cristy3ed852e2009-09-05 21:47:34 +00009080 /*
9081 Select the color type.
9082 */
9083 matte=image_matte;
9084 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009085
glennrp1273f7b2011-02-24 03:20:30 +00009086 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009087 {
glennrp0fe50b42010-11-16 03:52:51 +00009088
glennrpfd05d622011-02-25 04:10:33 +00009089 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009090 for reducing the sample depth from 8. */
9091
glennrp0fe50b42010-11-16 03:52:51 +00009092 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009093
glennrp8bb3a022010-12-13 20:40:04 +00009094 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009095
9096 /*
9097 Set image palette.
9098 */
9099 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9100
glennrp0fe50b42010-11-16 03:52:51 +00009101 if (logging != MagickFalse)
9102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9103 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009104 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009105
9106 for (i=0; i < (ssize_t) number_colors; i++)
9107 {
9108 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9109 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9110 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9111 if (logging != MagickFalse)
9112 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009113#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009114 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009115#else
9116 " %5ld (%5d,%5d,%5d)",
9117#endif
glennrp0fe50b42010-11-16 03:52:51 +00009118 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9119
9120 }
glennrp2b013e42010-11-24 16:55:50 +00009121
glennrp8bb3a022010-12-13 20:40:04 +00009122 ping_have_PLTE=MagickTrue;
9123 image_depth=ping_bit_depth;
9124 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009125
glennrp58e01762011-01-07 15:28:54 +00009126 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009127 {
glennrp0fe50b42010-11-16 03:52:51 +00009128 /*
9129 Identify which colormap entry is transparent.
9130 */
9131 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009132 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009133
glennrp8bb3a022010-12-13 20:40:04 +00009134 for (i=0; i < (ssize_t) number_transparent; i++)
9135 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009136
glennrp0fe50b42010-11-16 03:52:51 +00009137
glennrp2cc891a2010-12-24 13:44:32 +00009138 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009139 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009140
9141 if (ping_num_trans == 0)
9142 ping_have_tRNS=MagickFalse;
9143
glennrp8bb3a022010-12-13 20:40:04 +00009144 else
9145 ping_have_tRNS=MagickTrue;
9146 }
glennrp0fe50b42010-11-16 03:52:51 +00009147
glennrp1273f7b2011-02-24 03:20:30 +00009148 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009149 {
glennrp1273f7b2011-02-24 03:20:30 +00009150 /*
9151 * Identify which colormap entry is the background color.
9152 */
9153
glennrp4f25bd02011-01-01 18:51:28 +00009154 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9155 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9156 break;
glennrp0fe50b42010-11-16 03:52:51 +00009157
glennrp4f25bd02011-01-01 18:51:28 +00009158 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009159
9160 if (logging != MagickFalse)
9161 {
9162 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9163 " background_color index is %d",
9164 (int) ping_background.index);
9165 }
glennrp4f25bd02011-01-01 18:51:28 +00009166 }
cristy3ed852e2009-09-05 21:47:34 +00009167 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009168
glennrp7e65e932011-08-19 02:31:16 +00009169 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009170 {
9171 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009172 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009173 }
glennrp0fe50b42010-11-16 03:52:51 +00009174
glennrp7e65e932011-08-19 02:31:16 +00009175 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009176 {
9177 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009178 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009179 }
glennrp0fe50b42010-11-16 03:52:51 +00009180
glennrp8bb3a022010-12-13 20:40:04 +00009181 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009182 {
glennrp5af765f2010-03-30 11:12:18 +00009183 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009184
glennrp8bb3a022010-12-13 20:40:04 +00009185 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009186 {
glennrp5af765f2010-03-30 11:12:18 +00009187 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009188
glennrp5af765f2010-03-30 11:12:18 +00009189 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9190 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009191 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009192
glennrp8bb3a022010-12-13 20:40:04 +00009193 else
9194 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009195
9196 if (logging != MagickFalse)
9197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9198 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009199 }
glennrp0fe50b42010-11-16 03:52:51 +00009200
glennrp7c4c9e62011-03-21 20:23:32 +00009201 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009202 {
9203 if (logging != MagickFalse)
9204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009205 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009206
glennrpd6bf1612010-12-17 17:28:54 +00009207 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009208 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009209
glennrpd6bf1612010-12-17 17:28:54 +00009210 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009211 {
glennrp5af765f2010-03-30 11:12:18 +00009212 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009213 image_matte=MagickFalse;
9214 }
glennrp0fe50b42010-11-16 03:52:51 +00009215
glennrpd6bf1612010-12-17 17:28:54 +00009216 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009217 {
glennrp5af765f2010-03-30 11:12:18 +00009218 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009219 image_matte=MagickTrue;
9220 }
glennrp0fe50b42010-11-16 03:52:51 +00009221
glennrp5aa37f62011-01-02 03:07:57 +00009222 if (image_info->type == PaletteType ||
9223 image_info->type == PaletteMatteType)
9224 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9225
glennrp7c4c9e62011-03-21 20:23:32 +00009226 if (mng_info->write_png_colortype == 0 &&
9227 (image_info->type == UndefinedType ||
9228 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009229 {
glennrp5aa37f62011-01-02 03:07:57 +00009230 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009231 {
glennrp5aa37f62011-01-02 03:07:57 +00009232 if (image_matte == MagickFalse)
9233 {
9234 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9235 image_matte=MagickFalse;
9236 }
glennrp0fe50b42010-11-16 03:52:51 +00009237
glennrp0b206f52011-01-07 04:55:32 +00009238 else
glennrp5aa37f62011-01-02 03:07:57 +00009239 {
9240 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9241 image_matte=MagickTrue;
9242 }
9243 }
9244 else
glennrp8bb3a022010-12-13 20:40:04 +00009245 {
glennrp5aa37f62011-01-02 03:07:57 +00009246 if (image_matte == MagickFalse)
9247 {
9248 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9249 image_matte=MagickFalse;
9250 }
glennrp8bb3a022010-12-13 20:40:04 +00009251
glennrp0b206f52011-01-07 04:55:32 +00009252 else
glennrp5aa37f62011-01-02 03:07:57 +00009253 {
9254 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9255 image_matte=MagickTrue;
9256 }
9257 }
glennrp0fe50b42010-11-16 03:52:51 +00009258 }
glennrp5aa37f62011-01-02 03:07:57 +00009259
cristy3ed852e2009-09-05 21:47:34 +00009260 }
glennrp0fe50b42010-11-16 03:52:51 +00009261
cristy3ed852e2009-09-05 21:47:34 +00009262 if (logging != MagickFalse)
9263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009264 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009265
glennrp5af765f2010-03-30 11:12:18 +00009266 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009267 {
9268 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9269 ping_color_type == PNG_COLOR_TYPE_RGB ||
9270 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9271 ping_bit_depth=8;
9272 }
cristy3ed852e2009-09-05 21:47:34 +00009273
glennrpd6bf1612010-12-17 17:28:54 +00009274 old_bit_depth=ping_bit_depth;
9275
glennrp5af765f2010-03-30 11:12:18 +00009276 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009277 {
glennrp8d579662011-02-23 02:05:02 +00009278 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9279 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009280 }
glennrp8640fb52010-11-23 15:48:26 +00009281
glennrp5af765f2010-03-30 11:12:18 +00009282 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009283 {
cristy35ef8242010-06-03 16:24:13 +00009284 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009285 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009286
9287 if (image->colors == 0)
9288 {
glennrp0fe50b42010-11-16 03:52:51 +00009289 /* DO SOMETHING */
cristyc82a27b2011-10-21 01:07:16 +00009290 (void) ThrowMagickException(exception,
glennrp0f111982010-07-07 20:18:33 +00009291 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009292 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009293 }
9294
cristy35ef8242010-06-03 16:24:13 +00009295 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009296 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009297 }
glennrp2b013e42010-11-24 16:55:50 +00009298
glennrpd6bf1612010-12-17 17:28:54 +00009299 if (logging != MagickFalse)
9300 {
9301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9302 " Number of colors: %.20g",(double) image_colors);
9303
9304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9305 " Tentative PNG bit depth: %d",ping_bit_depth);
9306 }
9307
9308 if (ping_bit_depth < (int) mng_info->write_png_depth)
9309 ping_bit_depth = mng_info->write_png_depth;
9310 }
glennrp2cc891a2010-12-24 13:44:32 +00009311
glennrp5af765f2010-03-30 11:12:18 +00009312 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009313
cristy3ed852e2009-09-05 21:47:34 +00009314 if (logging != MagickFalse)
9315 {
9316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009317 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009318
cristy3ed852e2009-09-05 21:47:34 +00009319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009320 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009321
cristy3ed852e2009-09-05 21:47:34 +00009322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009323 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009324
cristy3ed852e2009-09-05 21:47:34 +00009325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009326
glennrp8640fb52010-11-23 15:48:26 +00009327 " image->depth: %.20g",(double) image->depth);
9328
9329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009330 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009331 }
9332
glennrp58e01762011-01-07 15:28:54 +00009333 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009334 {
glennrp4f25bd02011-01-01 18:51:28 +00009335 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009336 {
glennrp7c4c9e62011-03-21 20:23:32 +00009337 if (mng_info->write_png_colortype == 0)
9338 {
9339 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009340
glennrp7c4c9e62011-03-21 20:23:32 +00009341 if (ping_have_color != MagickFalse)
9342 ping_color_type=PNG_COLOR_TYPE_RGBA;
9343 }
glennrp4f25bd02011-01-01 18:51:28 +00009344
9345 /*
9346 * Determine if there is any transparent color.
9347 */
9348 if (number_transparent + number_semitransparent == 0)
9349 {
9350 /*
9351 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9352 */
glennrpa6a06632011-01-19 15:15:34 +00009353
glennrp4f25bd02011-01-01 18:51:28 +00009354 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009355
9356 if (mng_info->write_png_colortype == 0)
9357 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009358 }
9359
9360 else
9361 {
9362 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009363 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009364
9365 mask=0xffff;
9366
9367 if (ping_bit_depth == 8)
9368 mask=0x00ff;
9369
9370 if (ping_bit_depth == 4)
9371 mask=0x000f;
9372
9373 if (ping_bit_depth == 2)
9374 mask=0x0003;
9375
9376 if (ping_bit_depth == 1)
9377 mask=0x0001;
9378
9379 ping_trans_color.red=(png_uint_16)
9380 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9381
9382 ping_trans_color.green=(png_uint_16)
9383 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9384
9385 ping_trans_color.blue=(png_uint_16)
9386 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9387
9388 ping_trans_color.gray=(png_uint_16)
cristy101ab702011-10-13 13:06:32 +00009389 (ScaleQuantumToShort(GetPixelInfoIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009390 image->colormap)) & mask);
9391
9392 ping_trans_color.index=(png_byte) 0;
9393
9394 ping_have_tRNS=MagickTrue;
9395 }
9396
9397 if (ping_have_tRNS != MagickFalse)
9398 {
9399 /*
glennrpfd05d622011-02-25 04:10:33 +00009400 * Determine if there is one and only one transparent color
9401 * and if so if it is fully transparent.
9402 */
9403 if (ping_have_cheap_transparency == MagickFalse)
9404 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009405 }
9406
9407 if (ping_have_tRNS != MagickFalse)
9408 {
glennrp7c4c9e62011-03-21 20:23:32 +00009409 if (mng_info->write_png_colortype == 0)
9410 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009411
9412 if (image_depth == 8)
9413 {
9414 ping_trans_color.red&=0xff;
9415 ping_trans_color.green&=0xff;
9416 ping_trans_color.blue&=0xff;
9417 ping_trans_color.gray&=0xff;
9418 }
9419 }
9420 }
cristy3ed852e2009-09-05 21:47:34 +00009421 else
9422 {
cristy3ed852e2009-09-05 21:47:34 +00009423 if (image_depth == 8)
9424 {
glennrp5af765f2010-03-30 11:12:18 +00009425 ping_trans_color.red&=0xff;
9426 ping_trans_color.green&=0xff;
9427 ping_trans_color.blue&=0xff;
9428 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009429 }
9430 }
9431 }
glennrp8640fb52010-11-23 15:48:26 +00009432
cristy3ed852e2009-09-05 21:47:34 +00009433 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009434
glennrp2e09f552010-11-14 00:38:48 +00009435 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009436 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009437
glennrp39992b42010-11-14 00:03:43 +00009438 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009439 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009440 ping_have_color == MagickFalse &&
9441 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009442 {
cristy35ef8242010-06-03 16:24:13 +00009443 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009444
cristy3ed852e2009-09-05 21:47:34 +00009445 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009446 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009447
glennrp7c4c9e62011-03-21 20:23:32 +00009448 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009449 {
glennrp5af765f2010-03-30 11:12:18 +00009450 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009451
cristy3ed852e2009-09-05 21:47:34 +00009452 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009453 {
9454 if (logging != MagickFalse)
9455 {
9456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9457 " Scaling ping_trans_color (0)");
9458 }
9459 ping_trans_color.gray*=0x0101;
9460 }
cristy3ed852e2009-09-05 21:47:34 +00009461 }
glennrp0fe50b42010-11-16 03:52:51 +00009462
cristy3ed852e2009-09-05 21:47:34 +00009463 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9464 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009465
glennrp136ee3a2011-04-27 15:47:45 +00009466 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009467 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009468 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009469
cristy3ed852e2009-09-05 21:47:34 +00009470 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009471 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009472
cristy3ed852e2009-09-05 21:47:34 +00009473 else
9474 {
glennrp5af765f2010-03-30 11:12:18 +00009475 ping_bit_depth=8;
9476 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009477 {
9478 if(!mng_info->write_png_depth)
9479 {
glennrp5af765f2010-03-30 11:12:18 +00009480 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009481
cristy35ef8242010-06-03 16:24:13 +00009482 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009483 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009484 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009485 }
9486 }
glennrp2b013e42010-11-24 16:55:50 +00009487
glennrp0fe50b42010-11-16 03:52:51 +00009488 else if (ping_color_type ==
9489 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009490 mng_info->IsPalette)
9491 {
cristy3ed852e2009-09-05 21:47:34 +00009492 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009493
cristy3ed852e2009-09-05 21:47:34 +00009494 int
9495 depth_4_ok=MagickTrue,
9496 depth_2_ok=MagickTrue,
9497 depth_1_ok=MagickTrue;
9498
cristybb503372010-05-27 20:51:26 +00009499 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009500 {
9501 unsigned char
9502 intensity;
9503
9504 intensity=ScaleQuantumToChar(image->colormap[i].red);
9505
9506 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9507 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9508 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9509 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009510 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009511 depth_1_ok=MagickFalse;
9512 }
glennrp2b013e42010-11-24 16:55:50 +00009513
cristy3ed852e2009-09-05 21:47:34 +00009514 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009515 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009516
cristy3ed852e2009-09-05 21:47:34 +00009517 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009518 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009519
cristy3ed852e2009-09-05 21:47:34 +00009520 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009521 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009522 }
9523 }
glennrp2b013e42010-11-24 16:55:50 +00009524
glennrp5af765f2010-03-30 11:12:18 +00009525 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009526 }
glennrp0fe50b42010-11-16 03:52:51 +00009527
cristy3ed852e2009-09-05 21:47:34 +00009528 else
glennrp0fe50b42010-11-16 03:52:51 +00009529
cristy3ed852e2009-09-05 21:47:34 +00009530 if (mng_info->IsPalette)
9531 {
glennrp17a14852010-05-10 03:01:59 +00009532 number_colors=image_colors;
9533
cristy3ed852e2009-09-05 21:47:34 +00009534 if (image_depth <= 8)
9535 {
cristy3ed852e2009-09-05 21:47:34 +00009536 /*
9537 Set image palette.
9538 */
glennrp5af765f2010-03-30 11:12:18 +00009539 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009540
glennrp58e01762011-01-07 15:28:54 +00009541 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009542 {
glennrp9c1eb072010-06-06 22:19:15 +00009543 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009544
glennrp3b51f0e2010-11-27 18:14:08 +00009545 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9547 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009548 }
glennrp0fe50b42010-11-16 03:52:51 +00009549
cristy3ed852e2009-09-05 21:47:34 +00009550 else
9551 {
cristybb503372010-05-27 20:51:26 +00009552 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009553 {
9554 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9555 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9556 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9557 }
glennrp0fe50b42010-11-16 03:52:51 +00009558
glennrp3b51f0e2010-11-27 18:14:08 +00009559 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009561 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009562 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009563
glennrp39992b42010-11-14 00:03:43 +00009564 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009565 }
glennrp0fe50b42010-11-16 03:52:51 +00009566
cristy3ed852e2009-09-05 21:47:34 +00009567 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009568 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009569 {
cristybefe4d22010-06-07 01:18:58 +00009570 size_t
9571 one;
9572
glennrp5af765f2010-03-30 11:12:18 +00009573 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009574 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009575
cristy94b11832011-09-08 19:46:03 +00009576 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009577 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009578 }
glennrp0fe50b42010-11-16 03:52:51 +00009579
glennrp5af765f2010-03-30 11:12:18 +00009580 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009581
glennrp58e01762011-01-07 15:28:54 +00009582 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009583 {
glennrp0fe50b42010-11-16 03:52:51 +00009584 /*
glennrpd6bf1612010-12-17 17:28:54 +00009585 * Set up trans_colors array.
9586 */
glennrp0fe50b42010-11-16 03:52:51 +00009587 assert(number_colors <= 256);
9588
glennrpd6bf1612010-12-17 17:28:54 +00009589 ping_num_trans=(unsigned short) (number_transparent +
9590 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009591
9592 if (ping_num_trans == 0)
9593 ping_have_tRNS=MagickFalse;
9594
glennrpd6bf1612010-12-17 17:28:54 +00009595 else
glennrp0fe50b42010-11-16 03:52:51 +00009596 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009597 if (logging != MagickFalse)
9598 {
9599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9600 " Scaling ping_trans_color (1)");
9601 }
glennrpd6bf1612010-12-17 17:28:54 +00009602 ping_have_tRNS=MagickTrue;
9603
9604 for (i=0; i < ping_num_trans; i++)
9605 {
cristy4c08aed2011-07-01 19:47:50 +00009606 ping_trans_alpha[i]= (png_byte)
9607 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009608 }
glennrp0fe50b42010-11-16 03:52:51 +00009609 }
9610 }
cristy3ed852e2009-09-05 21:47:34 +00009611 }
9612 }
glennrp0fe50b42010-11-16 03:52:51 +00009613
cristy3ed852e2009-09-05 21:47:34 +00009614 else
9615 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009616
cristy3ed852e2009-09-05 21:47:34 +00009617 if (image_depth < 8)
9618 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009619
cristy3ed852e2009-09-05 21:47:34 +00009620 if ((save_image_depth == 16) && (image_depth == 8))
9621 {
glennrp4f25bd02011-01-01 18:51:28 +00009622 if (logging != MagickFalse)
9623 {
9624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9625 " Scaling ping_trans_color from (%d,%d,%d)",
9626 (int) ping_trans_color.red,
9627 (int) ping_trans_color.green,
9628 (int) ping_trans_color.blue);
9629 }
9630
glennrp5af765f2010-03-30 11:12:18 +00009631 ping_trans_color.red*=0x0101;
9632 ping_trans_color.green*=0x0101;
9633 ping_trans_color.blue*=0x0101;
9634 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009635
9636 if (logging != MagickFalse)
9637 {
9638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9639 " to (%d,%d,%d)",
9640 (int) ping_trans_color.red,
9641 (int) ping_trans_color.green,
9642 (int) ping_trans_color.blue);
9643 }
cristy3ed852e2009-09-05 21:47:34 +00009644 }
9645 }
9646
cristy4383ec82011-01-05 15:42:32 +00009647 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9648 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009649
cristy3ed852e2009-09-05 21:47:34 +00009650 /*
9651 Adjust background and transparency samples in sub-8-bit grayscale files.
9652 */
glennrp5af765f2010-03-30 11:12:18 +00009653 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009654 PNG_COLOR_TYPE_GRAY)
9655 {
9656 png_uint_16
9657 maxval;
9658
cristy35ef8242010-06-03 16:24:13 +00009659 size_t
9660 one=1;
9661
cristy22ffd972010-06-03 16:51:47 +00009662 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009663
glennrp4f25bd02011-01-01 18:51:28 +00009664 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009665 {
cristy3ed852e2009-09-05 21:47:34 +00009666
glennrpa521b2f2010-10-29 04:11:03 +00009667 ping_background.gray=(png_uint_16)
cristy101ab702011-10-13 13:06:32 +00009668 ((maxval/255.)*((GetPixelInfoIntensity(&image->background_color)))
glennrp847370c2011-07-05 17:37:15 +00009669 +.5);
cristy3ed852e2009-09-05 21:47:34 +00009670
9671 if (logging != MagickFalse)
9672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009673 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9675 " background_color index is %d",
9676 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009677
glennrp991d11d2010-11-12 21:55:28 +00009678 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009679 }
cristy3ed852e2009-09-05 21:47:34 +00009680
glennrp3e3e20f2011-06-09 04:21:43 +00009681 if (logging != MagickFalse)
9682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9683 " Scaling ping_trans_color.gray from %d",
9684 (int)ping_trans_color.gray);
9685
glennrp9be9b1c2011-06-09 12:21:45 +00009686 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009687 ping_trans_color.gray)+.5);
9688
9689 if (logging != MagickFalse)
9690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9691 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009692 }
glennrp17a14852010-05-10 03:01:59 +00009693
glennrp26f37912010-12-23 16:22:42 +00009694 if (ping_exclude_bKGD == MagickFalse)
9695 {
glennrp1273f7b2011-02-24 03:20:30 +00009696 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009697 {
9698 /*
9699 Identify which colormap entry is the background color.
9700 */
9701
glennrp17a14852010-05-10 03:01:59 +00009702 number_colors=image_colors;
9703
glennrpa521b2f2010-10-29 04:11:03 +00009704 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9705 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009706 break;
9707
9708 ping_background.index=(png_byte) i;
9709
glennrp3b51f0e2010-11-27 18:14:08 +00009710 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009711 {
9712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009713 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009714 }
glennrp0fe50b42010-11-16 03:52:51 +00009715
cristy13d07042010-11-21 20:56:18 +00009716 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009717 {
9718 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009719
9720 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009721 {
9722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9723 " background =(%d,%d,%d)",
9724 (int) ping_background.red,
9725 (int) ping_background.green,
9726 (int) ping_background.blue);
9727 }
9728 }
glennrpa521b2f2010-10-29 04:11:03 +00009729
glennrpd6bf1612010-12-17 17:28:54 +00009730 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009731 {
glennrp3b51f0e2010-11-27 18:14:08 +00009732 if (logging != MagickFalse)
9733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9734 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009735 ping_have_bKGD = MagickFalse;
9736 }
glennrp17a14852010-05-10 03:01:59 +00009737 }
glennrp26f37912010-12-23 16:22:42 +00009738 }
glennrp17a14852010-05-10 03:01:59 +00009739
cristy3ed852e2009-09-05 21:47:34 +00009740 if (logging != MagickFalse)
9741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009742 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009743 /*
9744 Initialize compression level and filtering.
9745 */
9746 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009747 {
9748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9749 " Setting up deflate compression");
9750
9751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9752 " Compression buffer size: 32768");
9753 }
9754
cristy3ed852e2009-09-05 21:47:34 +00009755 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009756
cristy3ed852e2009-09-05 21:47:34 +00009757 if (logging != MagickFalse)
9758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9759 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009760
cristy4054bfb2011-08-29 23:41:39 +00009761 png_set_compression_mem_level(ping, 9);
9762
glennrp10d739e2011-06-29 18:00:52 +00009763 /* Untangle the "-quality" setting:
9764
9765 Undefined is 0; the default is used.
9766 Default is 75
9767
9768 10's digit:
9769
9770 0: Use Z_HUFFMAN_ONLY strategy with the
9771 zlib default compression level
9772
9773 1-9: the zlib compression level
9774
9775 1's digit:
9776
9777 0-4: the PNG filter method
9778
9779 5: libpng adaptive filtering if compression level > 5
9780 libpng filter type "none" if compression level <= 5
9781 or if image is grayscale or palette
9782
9783 6: libpng adaptive filtering
9784
9785 7: "LOCO" filtering (intrapixel differing) if writing
9786 a MNG, othewise "none". Did not work in IM-6.7.0-9
9787 and earlier because of a missing "else".
9788
9789 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009790 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009791
9792 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009793 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009794
9795 Note that using the -quality option, not all combinations of
9796 PNG filter type, zlib compression level, and zlib compression
cristy5e6be1e2011-07-16 01:23:39 +00009797 strategy are possible. This will be addressed soon in a
cristy4054bfb2011-08-29 23:41:39 +00009798 release that accomodates "-define PNG:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +00009799
9800 */
9801
cristy3ed852e2009-09-05 21:47:34 +00009802 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9803 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009804
glennrp18682582011-06-30 18:11:47 +00009805 if (quality <= 9)
9806 {
9807 if (mng_info->write_png_compression_strategy == 0)
9808 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9809 }
9810
9811 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009812 {
9813 int
9814 level;
9815
cristybb503372010-05-27 20:51:26 +00009816 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009817
glennrp18682582011-06-30 18:11:47 +00009818 mng_info->write_png_compression_level = level+1;
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_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009822 {
glennrp18682582011-06-30 18:11:47 +00009823 if ((quality %10) == 8 || (quality %10) == 9)
9824 mng_info->write_png_compression_strategy=Z_RLE;
cristy3ed852e2009-09-05 21:47:34 +00009825 }
glennrp0fe50b42010-11-16 03:52:51 +00009826
glennrp18682582011-06-30 18:11:47 +00009827 if (mng_info->write_png_compression_filter == 0)
9828 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9829
cristy3ed852e2009-09-05 21:47:34 +00009830 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009831 {
glennrp18682582011-06-30 18:11:47 +00009832 if (mng_info->write_png_compression_level)
9833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9834 " Compression level: %d",
9835 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009836
glennrp18682582011-06-30 18:11:47 +00009837 if (mng_info->write_png_compression_strategy)
9838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9839 " Compression strategy: %d",
9840 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009841
glennrp18682582011-06-30 18:11:47 +00009842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9843 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009844
cristy4054bfb2011-08-29 23:41:39 +00009845 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +00009846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9847 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +00009848 else if (mng_info->write_png_compression_filter == 0 ||
9849 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +00009850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9851 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009852 else
9853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9854 " Base filter method: %d",
9855 (int) mng_info->write_png_compression_filter-1);
9856 }
glennrp2b013e42010-11-24 16:55:50 +00009857
glennrp18682582011-06-30 18:11:47 +00009858 if (mng_info->write_png_compression_level != 0)
9859 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9860
9861 if (mng_info->write_png_compression_filter == 6)
9862 {
9863 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9864 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9865 (quality < 50))
9866 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9867 else
9868 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9869 }
cristy4054bfb2011-08-29 23:41:39 +00009870 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +00009871 mng_info->write_png_compression_filter == 10)
9872 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9873
9874 else if (mng_info->write_png_compression_filter == 8)
9875 {
9876#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9877 if (mng_info->write_mng)
9878 {
9879 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9880 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9881 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9882 }
9883#endif
cristy4054bfb2011-08-29 23:41:39 +00009884 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +00009885 }
9886
9887 else if (mng_info->write_png_compression_filter == 9)
9888 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9889
9890 else if (mng_info->write_png_compression_filter != 0)
9891 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9892 mng_info->write_png_compression_filter-1);
9893
9894 if (mng_info->write_png_compression_strategy != 0)
9895 png_set_compression_strategy(ping,
9896 mng_info->write_png_compression_strategy-1);
9897
cristy0d57eec2011-09-04 22:13:56 +00009898 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9899 if (ping_exclude_sRGB != MagickFalse ||
9900 (image->rendering_intent == UndefinedIntent))
9901 {
9902 if ((ping_exclude_tEXt == MagickFalse ||
9903 ping_exclude_zTXt == MagickFalse) &&
9904 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009905 {
9906 ResetImageProfileIterator(image);
9907 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009908 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009909 profile=GetImageProfile(image,name);
9910
9911 if (profile != (StringInfo *) NULL)
9912 {
glennrp5af765f2010-03-30 11:12:18 +00009913#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009914 if ((LocaleCompare(name,"ICC") == 0) ||
9915 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009916 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009917
9918 if (ping_exclude_iCCP == MagickFalse)
9919 {
cristy9f027d12011-09-21 01:17:17 +00009920 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009921#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009922 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009923#else
9924 (png_const_bytep) GetStringInfoDatum(profile),
9925#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009926 (png_uint_32) GetStringInfoLength(profile));
9927 }
glennrp26f37912010-12-23 16:22:42 +00009928 }
glennrp0fe50b42010-11-16 03:52:51 +00009929
glennrpc8cbc5d2011-01-01 00:12:34 +00009930 else
cristy3ed852e2009-09-05 21:47:34 +00009931#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009932 if (ping_exclude_zCCP == MagickFalse)
9933 {
glennrpcf002022011-01-30 02:38:15 +00009934 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009935 (unsigned char *) name,(unsigned char *) name,
9936 GetStringInfoDatum(profile),
9937 (png_uint_32) GetStringInfoLength(profile));
9938 }
9939 }
glennrp0b206f52011-01-07 04:55:32 +00009940
glennrpc8cbc5d2011-01-01 00:12:34 +00009941 if (logging != MagickFalse)
9942 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9943 " Setting up text chunk with %s profile",name);
9944
9945 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009946 }
cristy0d57eec2011-09-04 22:13:56 +00009947 }
cristy3ed852e2009-09-05 21:47:34 +00009948 }
9949
9950#if defined(PNG_WRITE_sRGB_SUPPORTED)
9951 if ((mng_info->have_write_global_srgb == 0) &&
9952 ((image->rendering_intent != UndefinedIntent) ||
9953 (image->colorspace == sRGBColorspace)))
9954 {
glennrp26f37912010-12-23 16:22:42 +00009955 if (ping_exclude_sRGB == MagickFalse)
9956 {
9957 /*
9958 Note image rendering intent.
9959 */
9960 if (logging != MagickFalse)
9961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9962 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009963
glennrp26f37912010-12-23 16:22:42 +00009964 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009965 Magick_RenderingIntent_to_PNG_RenderingIntent(
9966 image->rendering_intent)));
glennrp26f37912010-12-23 16:22:42 +00009967 }
cristy3ed852e2009-09-05 21:47:34 +00009968 }
glennrp26f37912010-12-23 16:22:42 +00009969
glennrp5af765f2010-03-30 11:12:18 +00009970 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009971#endif
9972 {
glennrp2cc891a2010-12-24 13:44:32 +00009973 if (ping_exclude_gAMA == MagickFalse &&
9974 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009975 (image->gamma < .45 || image->gamma > .46)))
9976 {
cristy3ed852e2009-09-05 21:47:34 +00009977 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9978 {
9979 /*
9980 Note image gamma.
9981 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9982 */
9983 if (logging != MagickFalse)
9984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9985 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009986
cristy3ed852e2009-09-05 21:47:34 +00009987 png_set_gAMA(ping,ping_info,image->gamma);
9988 }
glennrp26f37912010-12-23 16:22:42 +00009989 }
glennrp2b013e42010-11-24 16:55:50 +00009990
glennrp26f37912010-12-23 16:22:42 +00009991 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009992 {
glennrp26f37912010-12-23 16:22:42 +00009993 if ((mng_info->have_write_global_chrm == 0) &&
9994 (image->chromaticity.red_primary.x != 0.0))
9995 {
9996 /*
9997 Note image chromaticity.
9998 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9999 */
10000 PrimaryInfo
10001 bp,
10002 gp,
10003 rp,
10004 wp;
cristy3ed852e2009-09-05 21:47:34 +000010005
glennrp26f37912010-12-23 16:22:42 +000010006 wp=image->chromaticity.white_point;
10007 rp=image->chromaticity.red_primary;
10008 gp=image->chromaticity.green_primary;
10009 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +000010010
glennrp26f37912010-12-23 16:22:42 +000010011 if (logging != MagickFalse)
10012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10013 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010014
glennrp26f37912010-12-23 16:22:42 +000010015 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10016 bp.x,bp.y);
10017 }
10018 }
cristy3ed852e2009-09-05 21:47:34 +000010019 }
glennrpdfd70802010-11-14 01:23:35 +000010020
glennrp5af765f2010-03-30 11:12:18 +000010021 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +000010022
10023 if (mng_info->write_mng)
10024 png_set_sig_bytes(ping,8);
10025
10026 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
10027
glennrpd6bf1612010-12-17 17:28:54 +000010028 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +000010029 {
10030 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +000010031 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010032 {
glennrp5af765f2010-03-30 11:12:18 +000010033 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +000010034
glennrp5af765f2010-03-30 11:12:18 +000010035 if (ping_bit_depth < 8)
10036 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +000010037 }
glennrp0fe50b42010-11-16 03:52:51 +000010038
cristy3ed852e2009-09-05 21:47:34 +000010039 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +000010040 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +000010041 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +000010042 }
10043
glennrp0e8ea192010-12-24 18:00:33 +000010044 if (ping_need_colortype_warning != MagickFalse ||
10045 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +000010046 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +000010047 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +000010048 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +000010049 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +000010050 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +000010051 {
10052 if (logging != MagickFalse)
10053 {
glennrp0e8ea192010-12-24 18:00:33 +000010054 if (ping_need_colortype_warning != MagickFalse)
10055 {
10056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10057 " Image has transparency but tRNS chunk was excluded");
10058 }
10059
cristy3ed852e2009-09-05 21:47:34 +000010060 if (mng_info->write_png_depth)
10061 {
10062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10063 " Defined PNG:bit-depth=%u, Computed depth=%u",
10064 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +000010065 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +000010066 }
glennrp0e8ea192010-12-24 18:00:33 +000010067
cristy3ed852e2009-09-05 21:47:34 +000010068 if (mng_info->write_png_colortype)
10069 {
10070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10071 " Defined PNG:color-type=%u, Computed color type=%u",
10072 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +000010073 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010074 }
10075 }
glennrp0e8ea192010-12-24 18:00:33 +000010076
glennrp3bd2e412010-08-10 13:34:52 +000010077 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +000010078 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
10079 }
10080
glennrp58e01762011-01-07 15:28:54 +000010081 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010082 {
10083 /* Add an opaque matte channel */
10084 image->matte = MagickTrue;
cristye941a752011-10-15 01:52:48 +000010085 (void) SetImageAlpha(image,OpaqueAlpha,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010086
glennrpb4a13412010-05-05 12:47:19 +000010087 if (logging != MagickFalse)
10088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10089 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +000010090 }
10091
glennrp0e319732011-01-25 21:53:13 +000010092 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +000010093 {
glennrp991d11d2010-11-12 21:55:28 +000010094 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +000010095 {
glennrp991d11d2010-11-12 21:55:28 +000010096 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010097 if (logging != MagickFalse)
10098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10099 " Setting ping_have_tRNS=MagickTrue.");
10100 }
glennrpe9c26dc2010-05-30 01:56:35 +000010101 }
10102
cristy3ed852e2009-09-05 21:47:34 +000010103 if (logging != MagickFalse)
10104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10105 " Writing PNG header chunks");
10106
glennrp5af765f2010-03-30 11:12:18 +000010107 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10108 ping_bit_depth,ping_color_type,
10109 ping_interlace_method,ping_compression_method,
10110 ping_filter_method);
10111
glennrp39992b42010-11-14 00:03:43 +000010112 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10113 {
glennrpf09bded2011-01-08 01:15:59 +000010114 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010115
glennrp3b51f0e2010-11-27 18:14:08 +000010116 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +000010117 {
glennrp8640fb52010-11-23 15:48:26 +000010118 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +000010119 {
glennrpd6bf1612010-12-17 17:28:54 +000010120 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +000010121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010122 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10123 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010124 (int) palette[i].red,
10125 (int) palette[i].green,
10126 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +000010127 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010128 (int) ping_trans_alpha[i]);
10129 else
10130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010131 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010132 (int) i,
10133 (int) palette[i].red,
10134 (int) palette[i].green,
10135 (int) palette[i].blue);
10136 }
glennrp39992b42010-11-14 00:03:43 +000010137 }
glennrp39992b42010-11-14 00:03:43 +000010138 }
10139
glennrp26f37912010-12-23 16:22:42 +000010140 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010141 {
glennrp26f37912010-12-23 16:22:42 +000010142 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010143 {
glennrp26f37912010-12-23 16:22:42 +000010144 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010145 if (logging)
10146 {
10147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10148 " Setting up bKGD chunk");
10149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10150 " background color = (%d,%d,%d)",
10151 (int) ping_background.red,
10152 (int) ping_background.green,
10153 (int) ping_background.blue);
10154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10155 " index = %d, gray=%d",
10156 (int) ping_background.index,
10157 (int) ping_background.gray);
10158 }
10159 }
glennrp26f37912010-12-23 16:22:42 +000010160 }
10161
10162 if (ping_exclude_pHYs == MagickFalse)
10163 {
10164 if (ping_have_pHYs != MagickFalse)
10165 {
10166 png_set_pHYs(ping,ping_info,
10167 ping_pHYs_x_resolution,
10168 ping_pHYs_y_resolution,
10169 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010170
10171 if (logging)
10172 {
10173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10174 " Setting up pHYs chunk");
10175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10176 " x_resolution=%lu",
10177 (unsigned long) ping_pHYs_x_resolution);
10178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10179 " y_resolution=%lu",
10180 (unsigned long) ping_pHYs_y_resolution);
10181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10182 " unit_type=%lu",
10183 (unsigned long) ping_pHYs_unit_type);
10184 }
glennrp26f37912010-12-23 16:22:42 +000010185 }
glennrpdfd70802010-11-14 01:23:35 +000010186 }
10187
10188#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010189 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010190 {
glennrp26f37912010-12-23 16:22:42 +000010191 if (image->page.x || image->page.y)
10192 {
10193 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10194 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010195
glennrp26f37912010-12-23 16:22:42 +000010196 if (logging != MagickFalse)
10197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10198 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10199 (int) image->page.x, (int) image->page.y);
10200 }
glennrpdfd70802010-11-14 01:23:35 +000010201 }
10202#endif
10203
glennrpda8f3a72011-02-27 23:54:12 +000010204 if (mng_info->need_blob != MagickFalse)
10205 {
cristyc82a27b2011-10-21 01:07:16 +000010206 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
glennrpda8f3a72011-02-27 23:54:12 +000010207 MagickFalse)
10208 png_error(ping,"WriteBlob Failed");
10209
10210 ping_have_blob=MagickTrue;
10211 }
10212
cristy3ed852e2009-09-05 21:47:34 +000010213 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010214
glennrp39992b42010-11-14 00:03:43 +000010215 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010216 {
glennrp3b51f0e2010-11-27 18:14:08 +000010217 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010218 {
10219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10220 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10221 }
10222
10223 if (ping_color_type == 3)
10224 (void) png_set_tRNS(ping, ping_info,
10225 ping_trans_alpha,
10226 ping_num_trans,
10227 NULL);
10228
10229 else
10230 {
10231 (void) png_set_tRNS(ping, ping_info,
10232 NULL,
10233 0,
10234 &ping_trans_color);
10235
glennrp3b51f0e2010-11-27 18:14:08 +000010236 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010237 {
10238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010239 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010240 (int) ping_trans_color.red,
10241 (int) ping_trans_color.green,
10242 (int) ping_trans_color.blue);
10243 }
10244 }
glennrp991d11d2010-11-12 21:55:28 +000010245 }
10246
cristy3ed852e2009-09-05 21:47:34 +000010247 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010248 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010249
cristy3ed852e2009-09-05 21:47:34 +000010250 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010251
cristy3ed852e2009-09-05 21:47:34 +000010252 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010253 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010254
glennrp26f37912010-12-23 16:22:42 +000010255 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010256 {
glennrp4f25bd02011-01-01 18:51:28 +000010257 if ((image->page.width != 0 && image->page.width != image->columns) ||
10258 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010259 {
10260 unsigned char
10261 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010262
glennrp26f37912010-12-23 16:22:42 +000010263 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10264 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010265 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010266 PNGLong(chunk+4,(png_uint_32) image->page.width);
10267 PNGLong(chunk+8,(png_uint_32) image->page.height);
10268 chunk[12]=0; /* unit = pixels */
10269 (void) WriteBlob(image,13,chunk);
10270 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10271 }
cristy3ed852e2009-09-05 21:47:34 +000010272 }
10273
10274#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010275 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010276#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010277 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010278#undef PNG_HAVE_IDAT
10279#endif
10280
10281 png_set_packing(ping);
10282 /*
10283 Allocate memory.
10284 */
10285 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010286 if (image_depth > 8)
10287 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010288 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010289 {
glennrpb4a13412010-05-05 12:47:19 +000010290 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010291 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010292 break;
glennrp0fe50b42010-11-16 03:52:51 +000010293
glennrpb4a13412010-05-05 12:47:19 +000010294 case PNG_COLOR_TYPE_GRAY_ALPHA:
10295 rowbytes*=2;
10296 break;
glennrp0fe50b42010-11-16 03:52:51 +000010297
glennrpb4a13412010-05-05 12:47:19 +000010298 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010299 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010300 break;
glennrp0fe50b42010-11-16 03:52:51 +000010301
glennrpb4a13412010-05-05 12:47:19 +000010302 default:
10303 break;
cristy3ed852e2009-09-05 21:47:34 +000010304 }
glennrp3b51f0e2010-11-27 18:14:08 +000010305
10306 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010307 {
10308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10309 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010310
glennrpb4a13412010-05-05 12:47:19 +000010311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010312 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010313 }
glennrpcf002022011-01-30 02:38:15 +000010314 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10315 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010316
glennrpcf002022011-01-30 02:38:15 +000010317 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010318 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010319
cristy3ed852e2009-09-05 21:47:34 +000010320 /*
10321 Initialize image scanlines.
10322 */
glennrp5af765f2010-03-30 11:12:18 +000010323 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010324 {
10325 /*
10326 PNG write failed.
10327 */
10328#ifdef PNG_DEBUG
10329 if (image_info->verbose)
10330 (void) printf("PNG write has failed.\n");
10331#endif
10332 png_destroy_write_struct(&ping,&ping_info);
10333 if (quantum_info != (QuantumInfo *) NULL)
10334 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010335 if (ping_pixels != (unsigned char *) NULL)
10336 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010337#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010338 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010339#endif
glennrpda8f3a72011-02-27 23:54:12 +000010340 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010341 (void) CloseBlob(image);
10342 image_info=DestroyImageInfo(image_info);
10343 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010344 return(MagickFalse);
10345 }
cristyed552522009-10-16 14:04:35 +000010346 quantum_info=AcquireQuantumInfo(image_info,image);
10347 if (quantum_info == (QuantumInfo *) NULL)
10348 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010349 quantum_info->format=UndefinedQuantumFormat;
10350 quantum_info->depth=image_depth;
10351 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010352
cristy3ed852e2009-09-05 21:47:34 +000010353 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010354 !mng_info->write_png32) &&
10355 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010356 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010357 image_matte == MagickFalse &&
10358 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010359 {
glennrp8bb3a022010-12-13 20:40:04 +000010360 /* Palette, Bilevel, or Opaque Monochrome */
cristy4c08aed2011-07-01 19:47:50 +000010361 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010362 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010363
cristy3ed852e2009-09-05 21:47:34 +000010364 quantum_info->depth=8;
10365 for (pass=0; pass < num_passes; pass++)
10366 {
10367 /*
10368 Convert PseudoClass image to a PNG monochrome image.
10369 */
cristybb503372010-05-27 20:51:26 +000010370 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010371 {
glennrpd71e86a2011-02-24 01:28:37 +000010372 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10374 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010375
cristyc82a27b2011-10-21 01:07:16 +000010376 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010377
cristy4c08aed2011-07-01 19:47:50 +000010378 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010379 break;
glennrp0fe50b42010-11-16 03:52:51 +000010380
cristy3ed852e2009-09-05 21:47:34 +000010381 if (mng_info->IsPalette)
10382 {
cristy4c08aed2011-07-01 19:47:50 +000010383 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010384 quantum_info,GrayQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010385 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10386 mng_info->write_png_depth &&
10387 mng_info->write_png_depth != old_bit_depth)
10388 {
10389 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010390 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010391 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010392 >> (8-old_bit_depth));
10393 }
10394 }
glennrp0fe50b42010-11-16 03:52:51 +000010395
cristy3ed852e2009-09-05 21:47:34 +000010396 else
10397 {
cristy4c08aed2011-07-01 19:47:50 +000010398 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010399 quantum_info,RedQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010400 }
glennrp0fe50b42010-11-16 03:52:51 +000010401
cristy3ed852e2009-09-05 21:47:34 +000010402 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010403 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010404 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010405 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010406
glennrp3b51f0e2010-11-27 18:14:08 +000010407 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10409 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010410
glennrpcf002022011-01-30 02:38:15 +000010411 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010412 }
10413 if (image->previous == (Image *) NULL)
10414 {
10415 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10416 if (status == MagickFalse)
10417 break;
10418 }
10419 }
10420 }
glennrp0fe50b42010-11-16 03:52:51 +000010421
glennrp8bb3a022010-12-13 20:40:04 +000010422 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010423 {
glennrp0fe50b42010-11-16 03:52:51 +000010424 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010425 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010426 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010427 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010428 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010429 {
cristy4c08aed2011-07-01 19:47:50 +000010430 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010431 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010432
glennrp8bb3a022010-12-13 20:40:04 +000010433 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010434 {
glennrp8bb3a022010-12-13 20:40:04 +000010435
cristybb503372010-05-27 20:51:26 +000010436 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010437 {
cristyc82a27b2011-10-21 01:07:16 +000010438 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010439
cristy4c08aed2011-07-01 19:47:50 +000010440 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010441 break;
glennrp2cc891a2010-12-24 13:44:32 +000010442
glennrp5af765f2010-03-30 11:12:18 +000010443 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010444 {
glennrp8bb3a022010-12-13 20:40:04 +000010445 if (mng_info->IsPalette)
cristy4c08aed2011-07-01 19:47:50 +000010446 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010447 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010448
glennrp8bb3a022010-12-13 20:40:04 +000010449 else
cristy4c08aed2011-07-01 19:47:50 +000010450 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010451 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010452
glennrp3b51f0e2010-11-27 18:14:08 +000010453 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010455 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010456 }
glennrp2cc891a2010-12-24 13:44:32 +000010457
glennrp8bb3a022010-12-13 20:40:04 +000010458 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10459 {
10460 if (logging != MagickFalse && y == 0)
10461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10462 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010463
cristy4c08aed2011-07-01 19:47:50 +000010464 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010465 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010466 }
glennrp2cc891a2010-12-24 13:44:32 +000010467
glennrp3b51f0e2010-11-27 18:14:08 +000010468 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010470 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010471
glennrpcf002022011-01-30 02:38:15 +000010472 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010473 }
glennrp2cc891a2010-12-24 13:44:32 +000010474
glennrp8bb3a022010-12-13 20:40:04 +000010475 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010476 {
glennrp8bb3a022010-12-13 20:40:04 +000010477 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10478 if (status == MagickFalse)
10479 break;
cristy3ed852e2009-09-05 21:47:34 +000010480 }
cristy3ed852e2009-09-05 21:47:34 +000010481 }
10482 }
glennrp8bb3a022010-12-13 20:40:04 +000010483
10484 else
10485 {
cristy4c08aed2011-07-01 19:47:50 +000010486 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010487 *p;
10488
10489 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010490 {
glennrp8bb3a022010-12-13 20:40:04 +000010491 if ((image_depth > 8) || (mng_info->write_png24 ||
10492 mng_info->write_png32 ||
10493 (!mng_info->write_png8 && !mng_info->IsPalette)))
10494 {
10495 for (y=0; y < (ssize_t) image->rows; y++)
10496 {
10497 p=GetVirtualPixels(image,0,y,image->columns,1,
cristyc82a27b2011-10-21 01:07:16 +000010498 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010499
cristy4c08aed2011-07-01 19:47:50 +000010500 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010501 break;
glennrp2cc891a2010-12-24 13:44:32 +000010502
glennrp8bb3a022010-12-13 20:40:04 +000010503 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10504 {
10505 if (image->storage_class == DirectClass)
cristy4c08aed2011-07-01 19:47:50 +000010506 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010507 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010508
glennrp8bb3a022010-12-13 20:40:04 +000010509 else
cristy4c08aed2011-07-01 19:47:50 +000010510 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010511 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010512 }
glennrp2cc891a2010-12-24 13:44:32 +000010513
glennrp8bb3a022010-12-13 20:40:04 +000010514 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10515 {
cristy4c08aed2011-07-01 19:47:50 +000010516 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010517 quantum_info,GrayAlphaQuantum,ping_pixels,
cristyc82a27b2011-10-21 01:07:16 +000010518 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010519
glennrp8bb3a022010-12-13 20:40:04 +000010520 if (logging != MagickFalse && y == 0)
10521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10522 " Writing GRAY_ALPHA PNG pixels (3)");
10523 }
glennrp2cc891a2010-12-24 13:44:32 +000010524
glennrp8bb3a022010-12-13 20:40:04 +000010525 else if (image_matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +000010526 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010527 quantum_info,RGBAQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010528
glennrp8bb3a022010-12-13 20:40:04 +000010529 else
cristy4c08aed2011-07-01 19:47:50 +000010530 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010531 quantum_info,RGBQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010532
glennrp8bb3a022010-12-13 20:40:04 +000010533 if (logging != MagickFalse && y == 0)
10534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10535 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010536
glennrpcf002022011-01-30 02:38:15 +000010537 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010538 }
10539 }
glennrp2cc891a2010-12-24 13:44:32 +000010540
glennrp8bb3a022010-12-13 20:40:04 +000010541 else
10542 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10543 mng_info->write_png32 ||
10544 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10545 {
10546 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10547 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10548 {
10549 if (logging != MagickFalse)
10550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10551 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010552
glennrp8bb3a022010-12-13 20:40:04 +000010553 quantum_info->depth=8;
10554 image_depth=8;
10555 }
glennrp2cc891a2010-12-24 13:44:32 +000010556
glennrp8bb3a022010-12-13 20:40:04 +000010557 for (y=0; y < (ssize_t) image->rows; y++)
10558 {
10559 if (logging != MagickFalse && y == 0)
10560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10561 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010562
glennrp770d1932011-03-06 22:11:17 +000010563 p=GetVirtualPixels(image,0,y,image->columns,1,
cristyc82a27b2011-10-21 01:07:16 +000010564 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010565
cristy4c08aed2011-07-01 19:47:50 +000010566 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010567 break;
glennrp2cc891a2010-12-24 13:44:32 +000010568
glennrp8bb3a022010-12-13 20:40:04 +000010569 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010570 {
glennrp4bf89732011-03-21 13:48:28 +000010571 quantum_info->depth=image->depth;
10572
cristy4c08aed2011-07-01 19:47:50 +000010573 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010574 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp44757ab2011-03-17 12:57:03 +000010575 }
glennrp2cc891a2010-12-24 13:44:32 +000010576
glennrp8bb3a022010-12-13 20:40:04 +000010577 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10578 {
10579 if (logging != MagickFalse && y == 0)
10580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10581 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010582
cristy4c08aed2011-07-01 19:47:50 +000010583 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010584 quantum_info,GrayAlphaQuantum,ping_pixels,
cristyc82a27b2011-10-21 01:07:16 +000010585 exception);
glennrp8bb3a022010-12-13 20:40:04 +000010586 }
glennrp2cc891a2010-12-24 13:44:32 +000010587
glennrp8bb3a022010-12-13 20:40:04 +000010588 else
glennrp8bb3a022010-12-13 20:40:04 +000010589 {
cristy4c08aed2011-07-01 19:47:50 +000010590 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010591 quantum_info,IndexQuantum,ping_pixels,exception);
glennrp5eae7602011-02-22 15:21:32 +000010592
10593 if (logging != MagickFalse && y <= 2)
10594 {
10595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010596 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010597
10598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10599 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10600 (int)ping_pixels[0],(int)ping_pixels[1]);
10601 }
glennrp8bb3a022010-12-13 20:40:04 +000010602 }
glennrpcf002022011-01-30 02:38:15 +000010603 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010604 }
10605 }
glennrp2cc891a2010-12-24 13:44:32 +000010606
glennrp8bb3a022010-12-13 20:40:04 +000010607 if (image->previous == (Image *) NULL)
10608 {
10609 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10610 if (status == MagickFalse)
10611 break;
10612 }
cristy3ed852e2009-09-05 21:47:34 +000010613 }
glennrp8bb3a022010-12-13 20:40:04 +000010614 }
10615 }
10616
cristyb32b90a2009-09-07 21:45:48 +000010617 if (quantum_info != (QuantumInfo *) NULL)
10618 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010619
10620 if (logging != MagickFalse)
10621 {
10622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010623 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010624
cristy3ed852e2009-09-05 21:47:34 +000010625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010626 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010627
cristy3ed852e2009-09-05 21:47:34 +000010628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010629 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010630
cristy3ed852e2009-09-05 21:47:34 +000010631 if (mng_info->write_png_depth)
10632 {
10633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10634 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
10635 }
glennrp0fe50b42010-11-16 03:52:51 +000010636
cristy3ed852e2009-09-05 21:47:34 +000010637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010638 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010639
cristy3ed852e2009-09-05 21:47:34 +000010640 if (mng_info->write_png_colortype)
10641 {
10642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10643 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
10644 }
glennrp0fe50b42010-11-16 03:52:51 +000010645
cristy3ed852e2009-09-05 21:47:34 +000010646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010647 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010648
cristy3ed852e2009-09-05 21:47:34 +000010649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010650 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010651 }
10652 /*
glennrpa0ed0092011-04-18 16:36:29 +000010653 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010654 */
glennrp823b55c2011-03-14 18:46:46 +000010655 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010656 {
glennrp26f37912010-12-23 16:22:42 +000010657 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010658 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010659 while (property != (const char *) NULL)
10660 {
10661 png_textp
10662 text;
glennrp2cc891a2010-12-24 13:44:32 +000010663
cristyd15e6592011-10-15 00:13:06 +000010664 value=GetImageProperty(image,property,exception);
glennrpa0ed0092011-04-18 16:36:29 +000010665
10666 /* Don't write any "png:" properties; those are just for "identify" */
10667 if (LocaleNCompare(property,"png:",4) != 0 &&
10668
10669 /* Suppress density and units if we wrote a pHYs chunk */
10670 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010671 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010672 LocaleCompare(property,"units") != 0) &&
10673
10674 /* Suppress the IM-generated Date:create and Date:modify */
10675 (ping_exclude_date == MagickFalse ||
10676 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010677 {
glennrpc70af4a2011-03-07 00:08:23 +000010678 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010679 {
glennrpc70af4a2011-03-07 00:08:23 +000010680 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10681 text[0].key=(char *) property;
10682 text[0].text=(char *) value;
10683 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010684
glennrpc70af4a2011-03-07 00:08:23 +000010685 if (ping_exclude_tEXt != MagickFalse)
10686 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10687
10688 else if (ping_exclude_zTXt != MagickFalse)
10689 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10690
10691 else
glennrp26f37912010-12-23 16:22:42 +000010692 {
glennrpc70af4a2011-03-07 00:08:23 +000010693 text[0].compression=image_info->compression == NoCompression ||
10694 (image_info->compression == UndefinedCompression &&
10695 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10696 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010697 }
glennrp2cc891a2010-12-24 13:44:32 +000010698
glennrpc70af4a2011-03-07 00:08:23 +000010699 if (logging != MagickFalse)
10700 {
10701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10702 " Setting up text chunk");
10703
10704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10705 " keyword: %s",text[0].key);
10706 }
10707
10708 png_set_text(ping,ping_info,text,1);
10709 png_free(ping,text);
10710 }
glennrp26f37912010-12-23 16:22:42 +000010711 }
10712 property=GetNextImageProperty(image);
10713 }
cristy3ed852e2009-09-05 21:47:34 +000010714 }
10715
10716 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010717 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010718
10719 if (logging != MagickFalse)
10720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10721 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010722
cristy3ed852e2009-09-05 21:47:34 +000010723 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010724
cristy3ed852e2009-09-05 21:47:34 +000010725 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10726 {
10727 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010728 (ping_width != mng_info->page.width) ||
10729 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010730 {
10731 unsigned char
10732 chunk[32];
10733
10734 /*
10735 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10736 */
10737 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10738 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010739 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010740 chunk[4]=4;
10741 chunk[5]=0; /* frame name separator (no name) */
10742 chunk[6]=1; /* flag for changing delay, for next frame only */
10743 chunk[7]=0; /* flag for changing frame timeout */
10744 chunk[8]=1; /* flag for changing frame clipping for next frame */
10745 chunk[9]=0; /* flag for changing frame sync_id */
10746 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10747 chunk[14]=0; /* clipping boundaries delta type */
10748 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10749 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010750 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010751 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10752 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010753 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010754 (void) WriteBlob(image,31,chunk);
10755 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10756 mng_info->old_framing_mode=4;
10757 mng_info->framing_mode=1;
10758 }
glennrp0fe50b42010-11-16 03:52:51 +000010759
cristy3ed852e2009-09-05 21:47:34 +000010760 else
10761 mng_info->framing_mode=3;
10762 }
10763 if (mng_info->write_mng && !mng_info->need_fram &&
10764 ((int) image->dispose == 3))
cristyc82a27b2011-10-21 01:07:16 +000010765 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010766 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010767 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010768
cristy3ed852e2009-09-05 21:47:34 +000010769 /*
10770 Free PNG resources.
10771 */
glennrp5af765f2010-03-30 11:12:18 +000010772
cristy3ed852e2009-09-05 21:47:34 +000010773 png_destroy_write_struct(&ping,&ping_info);
10774
glennrpcf002022011-01-30 02:38:15 +000010775 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010776
10777#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010778 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010779#endif
10780
glennrpda8f3a72011-02-27 23:54:12 +000010781 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010782 (void) CloseBlob(image);
10783
10784 image_info=DestroyImageInfo(image_info);
10785 image=DestroyImage(image);
10786
10787 /* Store bit depth actually written */
10788 s[0]=(char) ping_bit_depth;
10789 s[1]='\0';
10790
cristyd15e6592011-10-15 00:13:06 +000010791 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
glennrpb9cfe272010-12-21 15:08:06 +000010792
cristy3ed852e2009-09-05 21:47:34 +000010793 if (logging != MagickFalse)
10794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10795 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010796
cristy3ed852e2009-09-05 21:47:34 +000010797 return(MagickTrue);
10798/* End write one PNG image */
10799}
10800
10801/*
10802%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10803% %
10804% %
10805% %
10806% W r i t e P N G I m a g e %
10807% %
10808% %
10809% %
10810%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10811%
10812% WritePNGImage() writes a Portable Network Graphics (PNG) or
10813% Multiple-image Network Graphics (MNG) image file.
10814%
10815% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10816%
10817% The format of the WritePNGImage method is:
10818%
cristy1e178e72011-08-28 19:44:34 +000010819% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10820% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010821%
10822% A description of each parameter follows:
10823%
10824% o image_info: the image info.
10825%
10826% o image: The image.
10827%
cristy1e178e72011-08-28 19:44:34 +000010828% o exception: return any errors or warnings in this structure.
10829%
cristy3ed852e2009-09-05 21:47:34 +000010830% Returns MagickTrue on success, MagickFalse on failure.
10831%
10832% Communicating with the PNG encoder:
10833%
10834% While the datastream written is always in PNG format and normally would
10835% be given the "png" file extension, this method also writes the following
10836% pseudo-formats which are subsets of PNG:
10837%
glennrp5a39f372011-02-25 04:52:16 +000010838% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10839% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010840% is present, the tRNS chunk must only have values 0 and 255
10841% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010842% transparent). If other values are present they will be
10843% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010844% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000010845% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10846% of any resulting fully-transparent pixels is changed to
10847% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000010848%
10849% If you want better quantization or dithering of the colors
10850% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010851% PNG encoder. The pixels contain 8-bit indices even if
10852% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010853% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010854% PNG grayscale type might be slightly more efficient. Please
10855% note that writing to the PNG8 format may result in loss
10856% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010857%
10858% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10859% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010860% one of the colors as transparent. The only loss incurred
10861% is reduction of sample depth to 8. If the image has more
10862% than one transparent color, has semitransparent pixels, or
10863% has an opaque pixel with the same RGB components as the
10864% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010865%
10866% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10867% transparency is permitted, i.e., the alpha sample for
10868% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010869% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010870% The only loss in data is the reduction of the sample depth
10871% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010872%
10873% o -define: For more precise control of the PNG output, you can use the
10874% Image options "png:bit-depth" and "png:color-type". These
10875% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010876% from the application programming interfaces. The options
10877% are case-independent and are converted to lowercase before
10878% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010879%
10880% png:color-type can be 0, 2, 3, 4, or 6.
10881%
10882% When png:color-type is 0 (Grayscale), png:bit-depth can
10883% be 1, 2, 4, 8, or 16.
10884%
10885% When png:color-type is 2 (RGB), png:bit-depth can
10886% be 8 or 16.
10887%
10888% When png:color-type is 3 (Indexed), png:bit-depth can
10889% be 1, 2, 4, or 8. This refers to the number of bits
10890% used to store the index. The color samples always have
10891% bit-depth 8 in indexed PNG files.
10892%
10893% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10894% png:bit-depth can be 8 or 16.
10895%
glennrp5a39f372011-02-25 04:52:16 +000010896% If the image cannot be written without loss with the requested bit-depth
10897% and color-type, a PNG file will not be written, and the encoder will
10898% return MagickFalse.
10899%
cristy3ed852e2009-09-05 21:47:34 +000010900% Since image encoders should not be responsible for the "heavy lifting",
10901% the user should make sure that ImageMagick has already reduced the
10902% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010903% transparency prior to attempting to write the image with depth, color,
glennrp54dc0692011-07-01 19:02:13 +000010904% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010905%
cristy3ed852e2009-09-05 21:47:34 +000010906% Note that another definition, "png:bit-depth-written" exists, but it
10907% is not intended for external use. It is only used internally by the
10908% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10909%
10910% It is possible to request that the PNG encoder write previously-formatted
10911% ancillary chunks in the output PNG file, using the "-profile" commandline
10912% option as shown below or by setting the profile via a programming
10913% interface:
10914%
10915% -profile PNG-chunk-x:<file>
10916%
10917% where x is a location flag and <file> is a file containing the chunk
10918% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010919% This encoder will compute the chunk length and CRC, so those must not
10920% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010921%
10922% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10923% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10924% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010925% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010926%
glennrpbb8a7332010-11-13 15:17:35 +000010927% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010928%
glennrp3241bd02010-12-12 04:36:28 +000010929% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010930%
glennrpd6afd542010-11-19 01:53:05 +000010931% o 32-bit depth is reduced to 16.
10932% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10933% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010934% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010935% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010936% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010937% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10938% this can be done without loss and a larger bit depth N was not
10939% requested via the "-define PNG:bit-depth=N" option.
10940% o If matte channel is present but only one transparent color is
10941% present, RGB+tRNS is written instead of RGBA
10942% o Opaque matte channel is removed (or added, if color-type 4 or 6
10943% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010944%
cristy3ed852e2009-09-05 21:47:34 +000010945%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10946*/
10947static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +000010948 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010949{
10950 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010951 excluding,
10952 logging,
10953 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010954 status;
10955
10956 MngInfo
10957 *mng_info;
10958
10959 const char
10960 *value;
10961
10962 int
glennrp21f0e622011-01-07 16:20:57 +000010963 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010964 source;
10965
cristy3ed852e2009-09-05 21:47:34 +000010966 /*
10967 Open image file.
10968 */
10969 assert(image_info != (const ImageInfo *) NULL);
10970 assert(image_info->signature == MagickSignature);
10971 assert(image != (Image *) NULL);
10972 assert(image->signature == MagickSignature);
10973 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010974 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010975 /*
10976 Allocate a MngInfo structure.
10977 */
10978 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010979 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010980
cristy3ed852e2009-09-05 21:47:34 +000010981 if (mng_info == (MngInfo *) NULL)
10982 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010983
cristy3ed852e2009-09-05 21:47:34 +000010984 /*
10985 Initialize members of the MngInfo structure.
10986 */
10987 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10988 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010989 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010990 have_mng_structure=MagickTrue;
10991
10992 /* See if user has requested a specific PNG subformat */
10993
10994 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10995 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10996 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10997
10998 if (mng_info->write_png8)
10999 {
glennrp9c1eb072010-06-06 22:19:15 +000011000 mng_info->write_png_colortype = /* 3 */ 4;
11001 mng_info->write_png_depth = 8;
11002 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000011003 }
11004
11005 if (mng_info->write_png24)
11006 {
glennrp9c1eb072010-06-06 22:19:15 +000011007 mng_info->write_png_colortype = /* 2 */ 3;
11008 mng_info->write_png_depth = 8;
11009 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011010
glennrp9c1eb072010-06-06 22:19:15 +000011011 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011012 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011013
glennrp9c1eb072010-06-06 22:19:15 +000011014 else
cristy018f07f2011-09-04 21:15:19 +000011015 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011016
cristyea1a8aa2011-10-20 13:24:06 +000011017 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011018 }
11019
11020 if (mng_info->write_png32)
11021 {
glennrp9c1eb072010-06-06 22:19:15 +000011022 mng_info->write_png_colortype = /* 6 */ 7;
11023 mng_info->write_png_depth = 8;
11024 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011025
glennrp9c1eb072010-06-06 22:19:15 +000011026 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011027 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011028
glennrp9c1eb072010-06-06 22:19:15 +000011029 else
cristy018f07f2011-09-04 21:15:19 +000011030 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011031
cristyea1a8aa2011-10-20 13:24:06 +000011032 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011033 }
11034
11035 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011036
cristy3ed852e2009-09-05 21:47:34 +000011037 if (value != (char *) NULL)
11038 {
11039 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011040 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011041
cristy3ed852e2009-09-05 21:47:34 +000011042 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011043 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011044
cristy3ed852e2009-09-05 21:47:34 +000011045 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011046 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011047
cristy3ed852e2009-09-05 21:47:34 +000011048 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011049 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011050
cristy3ed852e2009-09-05 21:47:34 +000011051 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011052 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011053
glennrpbb8a7332010-11-13 15:17:35 +000011054 else
cristyc82a27b2011-10-21 01:07:16 +000011055 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011056 GetMagickModule(),CoderWarning,
11057 "ignoring invalid defined png:bit-depth",
11058 "=%s",value);
11059
cristy3ed852e2009-09-05 21:47:34 +000011060 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011062 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011063 }
glennrp0fe50b42010-11-16 03:52:51 +000011064
cristy3ed852e2009-09-05 21:47:34 +000011065 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011066
cristy3ed852e2009-09-05 21:47:34 +000011067 if (value != (char *) NULL)
11068 {
11069 /* We must store colortype+1 because 0 is a valid colortype */
11070 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011071 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011072
cristy3ed852e2009-09-05 21:47:34 +000011073 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011074 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011075
cristy3ed852e2009-09-05 21:47:34 +000011076 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011077 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011078
cristy3ed852e2009-09-05 21:47:34 +000011079 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011080 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011081
cristy3ed852e2009-09-05 21:47:34 +000011082 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011083 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011084
glennrpbb8a7332010-11-13 15:17:35 +000011085 else
cristyc82a27b2011-10-21 01:07:16 +000011086 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011087 GetMagickModule(),CoderWarning,
11088 "ignoring invalid defined png:color-type",
11089 "=%s",value);
11090
cristy3ed852e2009-09-05 21:47:34 +000011091 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011093 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011094 }
11095
glennrp0e8ea192010-12-24 18:00:33 +000011096 /* Check for chunks to be excluded:
11097 *
glennrp0dff56c2011-01-29 19:10:02 +000011098 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011099 * listed in the "unused_chunks" array, above.
11100 *
11101 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
11102 * define (in the image properties or in the image artifacts)
11103 * or via a mng_info member. For convenience, in addition
11104 * to or instead of a comma-separated list of chunks, the
11105 * "exclude-chunk" string can be simply "all" or "none".
11106 *
11107 * The exclude-chunk define takes priority over the mng_info.
11108 *
11109 * A "PNG:include-chunk" define takes priority over both the
11110 * mng_info and the "PNG:exclude-chunk" define. Like the
11111 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011112 * well as a comma-separated list. Chunks that are unknown to
11113 * ImageMagick are always excluded, regardless of their "copy-safe"
11114 * status according to the PNG specification, and even if they
11115 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000011116 *
11117 * Finally, all chunks listed in the "unused_chunks" array are
11118 * automatically excluded, regardless of the other instructions
11119 * or lack thereof.
11120 *
11121 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11122 * will not be written and the gAMA chunk will only be written if it
11123 * is not between .45 and .46, or approximately (1.0/2.2).
11124 *
11125 * If you exclude tRNS and the image has transparency, the colortype
11126 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11127 *
11128 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011129 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011130 */
11131
glennrp26f37912010-12-23 16:22:42 +000011132 mng_info->ping_exclude_bKGD=MagickFalse;
11133 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011134 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011135 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11136 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011137 mng_info->ping_exclude_iCCP=MagickFalse;
11138 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11139 mng_info->ping_exclude_oFFs=MagickFalse;
11140 mng_info->ping_exclude_pHYs=MagickFalse;
11141 mng_info->ping_exclude_sRGB=MagickFalse;
11142 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011143 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011144 mng_info->ping_exclude_vpAg=MagickFalse;
11145 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11146 mng_info->ping_exclude_zTXt=MagickFalse;
11147
glennrp8d3d6e52011-04-19 04:39:51 +000011148 mng_info->ping_preserve_colormap=MagickFalse;
11149
11150 value=GetImageArtifact(image,"png:preserve-colormap");
11151 if (value == NULL)
11152 value=GetImageOption(image_info,"png:preserve-colormap");
11153 if (value != NULL)
11154 mng_info->ping_preserve_colormap=MagickTrue;
11155
glennrp18682582011-06-30 18:11:47 +000011156 /* Thes compression-level, compression-strategy, and compression-filter
11157 * defines take precedence over values from the -quality option.
11158 */
11159 value=GetImageArtifact(image,"png:compression-level");
11160 if (value == NULL)
11161 value=GetImageOption(image_info,"png:compression-level");
11162 if (value != NULL)
11163 {
glennrp18682582011-06-30 18:11:47 +000011164 /* We have to add 1 to everything because 0 is a valid input,
11165 * and we want to use 0 (the default) to mean undefined.
11166 */
11167 if (LocaleCompare(value,"0") == 0)
11168 mng_info->write_png_compression_level = 1;
11169
11170 if (LocaleCompare(value,"1") == 0)
11171 mng_info->write_png_compression_level = 2;
11172
11173 else if (LocaleCompare(value,"2") == 0)
11174 mng_info->write_png_compression_level = 3;
11175
11176 else if (LocaleCompare(value,"3") == 0)
11177 mng_info->write_png_compression_level = 4;
11178
11179 else if (LocaleCompare(value,"4") == 0)
11180 mng_info->write_png_compression_level = 5;
11181
11182 else if (LocaleCompare(value,"5") == 0)
11183 mng_info->write_png_compression_level = 6;
11184
11185 else if (LocaleCompare(value,"6") == 0)
11186 mng_info->write_png_compression_level = 7;
11187
11188 else if (LocaleCompare(value,"7") == 0)
11189 mng_info->write_png_compression_level = 8;
11190
11191 else if (LocaleCompare(value,"8") == 0)
11192 mng_info->write_png_compression_level = 9;
11193
11194 else if (LocaleCompare(value,"9") == 0)
11195 mng_info->write_png_compression_level = 10;
11196
11197 else
cristyc82a27b2011-10-21 01:07:16 +000011198 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011199 GetMagickModule(),CoderWarning,
11200 "ignoring invalid defined png:compression-level",
11201 "=%s",value);
11202 }
11203
11204 value=GetImageArtifact(image,"png:compression-strategy");
11205 if (value == NULL)
11206 value=GetImageOption(image_info,"png:compression-strategy");
11207 if (value != NULL)
11208 {
11209
11210 if (LocaleCompare(value,"0") == 0)
11211 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11212
11213 else if (LocaleCompare(value,"1") == 0)
11214 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11215
11216 else if (LocaleCompare(value,"2") == 0)
11217 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11218
11219 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011220#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011221 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011222#else
11223 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11224#endif
glennrp18682582011-06-30 18:11:47 +000011225
11226 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011227#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011228 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011229#else
11230 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11231#endif
glennrp18682582011-06-30 18:11:47 +000011232
11233 else
cristyc82a27b2011-10-21 01:07:16 +000011234 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011235 GetMagickModule(),CoderWarning,
11236 "ignoring invalid defined png:compression-strategy",
11237 "=%s",value);
11238 }
11239
11240 value=GetImageArtifact(image,"png:compression-filter");
11241 if (value == NULL)
11242 value=GetImageOption(image_info,"png:compression-filter");
11243 if (value != NULL)
11244 {
11245
11246 /* To do: combinations of filters allowed by libpng
11247 * masks 0x08 through 0xf8
11248 *
11249 * Implement this as a comma-separated list of 0,1,2,3,4,5
11250 * where 5 is a special case meaning PNG_ALL_FILTERS.
11251 */
11252
11253 if (LocaleCompare(value,"0") == 0)
11254 mng_info->write_png_compression_filter = 1;
11255
11256 if (LocaleCompare(value,"1") == 0)
11257 mng_info->write_png_compression_filter = 2;
11258
11259 else if (LocaleCompare(value,"2") == 0)
11260 mng_info->write_png_compression_filter = 3;
11261
11262 else if (LocaleCompare(value,"3") == 0)
11263 mng_info->write_png_compression_filter = 4;
11264
11265 else if (LocaleCompare(value,"4") == 0)
11266 mng_info->write_png_compression_filter = 5;
11267
11268 else if (LocaleCompare(value,"5") == 0)
11269 mng_info->write_png_compression_filter = 6;
11270
glennrp18682582011-06-30 18:11:47 +000011271 else
cristyc82a27b2011-10-21 01:07:16 +000011272 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011273 GetMagickModule(),CoderWarning,
11274 "ignoring invalid defined png:compression-filter",
11275 "=%s",value);
11276 }
11277
glennrp03812ae2010-12-24 01:31:34 +000011278 excluding=MagickFalse;
11279
glennrp5c7cf4e2010-12-24 00:30:00 +000011280 for (source=0; source<1; source++)
11281 {
11282 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011283 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011284 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011285
11286 if (value == NULL)
11287 value=GetImageArtifact(image,"png:exclude-chunks");
11288 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011289 else
glennrpacba0042010-12-24 14:27:26 +000011290 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011291 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011292
glennrpacba0042010-12-24 14:27:26 +000011293 if (value == NULL)
11294 value=GetImageOption(image_info,"png:exclude-chunks");
11295 }
11296
glennrp03812ae2010-12-24 01:31:34 +000011297 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011298 {
glennrp03812ae2010-12-24 01:31:34 +000011299
11300 size_t
11301 last;
11302
11303 excluding=MagickTrue;
11304
11305 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011306 {
11307 if (source == 0)
11308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11309 " png:exclude-chunk=%s found in image artifacts.\n", value);
11310 else
11311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11312 " png:exclude-chunk=%s found in image properties.\n", value);
11313 }
glennrp03812ae2010-12-24 01:31:34 +000011314
11315 last=strlen(value);
11316
11317 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011318 {
glennrp03812ae2010-12-24 01:31:34 +000011319
11320 if (LocaleNCompare(value+i,"all",3) == 0)
11321 {
11322 mng_info->ping_exclude_bKGD=MagickTrue;
11323 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011324 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011325 mng_info->ping_exclude_EXIF=MagickTrue;
11326 mng_info->ping_exclude_gAMA=MagickTrue;
11327 mng_info->ping_exclude_iCCP=MagickTrue;
11328 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11329 mng_info->ping_exclude_oFFs=MagickTrue;
11330 mng_info->ping_exclude_pHYs=MagickTrue;
11331 mng_info->ping_exclude_sRGB=MagickTrue;
11332 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011333 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011334 mng_info->ping_exclude_vpAg=MagickTrue;
11335 mng_info->ping_exclude_zCCP=MagickTrue;
11336 mng_info->ping_exclude_zTXt=MagickTrue;
11337 i--;
11338 }
glennrp2cc891a2010-12-24 13:44:32 +000011339
glennrp03812ae2010-12-24 01:31:34 +000011340 if (LocaleNCompare(value+i,"none",4) == 0)
11341 {
11342 mng_info->ping_exclude_bKGD=MagickFalse;
11343 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011344 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011345 mng_info->ping_exclude_EXIF=MagickFalse;
11346 mng_info->ping_exclude_gAMA=MagickFalse;
11347 mng_info->ping_exclude_iCCP=MagickFalse;
11348 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11349 mng_info->ping_exclude_oFFs=MagickFalse;
11350 mng_info->ping_exclude_pHYs=MagickFalse;
11351 mng_info->ping_exclude_sRGB=MagickFalse;
11352 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011353 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011354 mng_info->ping_exclude_vpAg=MagickFalse;
11355 mng_info->ping_exclude_zCCP=MagickFalse;
11356 mng_info->ping_exclude_zTXt=MagickFalse;
11357 }
glennrp2cc891a2010-12-24 13:44:32 +000011358
glennrp03812ae2010-12-24 01:31:34 +000011359 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11360 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011361
glennrp03812ae2010-12-24 01:31:34 +000011362 if (LocaleNCompare(value+i,"chrm",4) == 0)
11363 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011364
glennrpa0ed0092011-04-18 16:36:29 +000011365 if (LocaleNCompare(value+i,"date",4) == 0)
11366 mng_info->ping_exclude_date=MagickTrue;
11367
glennrp03812ae2010-12-24 01:31:34 +000011368 if (LocaleNCompare(value+i,"exif",4) == 0)
11369 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011370
glennrp03812ae2010-12-24 01:31:34 +000011371 if (LocaleNCompare(value+i,"gama",4) == 0)
11372 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011373
glennrp03812ae2010-12-24 01:31:34 +000011374 if (LocaleNCompare(value+i,"iccp",4) == 0)
11375 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011376
glennrp03812ae2010-12-24 01:31:34 +000011377 /*
11378 if (LocaleNCompare(value+i,"itxt",4) == 0)
11379 mng_info->ping_exclude_iTXt=MagickTrue;
11380 */
glennrp2cc891a2010-12-24 13:44:32 +000011381
glennrp03812ae2010-12-24 01:31:34 +000011382 if (LocaleNCompare(value+i,"gama",4) == 0)
11383 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011384
glennrp03812ae2010-12-24 01:31:34 +000011385 if (LocaleNCompare(value+i,"offs",4) == 0)
11386 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011387
glennrp03812ae2010-12-24 01:31:34 +000011388 if (LocaleNCompare(value+i,"phys",4) == 0)
11389 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011390
glennrpa1e3b7b2010-12-24 16:37:33 +000011391 if (LocaleNCompare(value+i,"srgb",4) == 0)
11392 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011393
glennrp03812ae2010-12-24 01:31:34 +000011394 if (LocaleNCompare(value+i,"text",4) == 0)
11395 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011396
glennrpa1e3b7b2010-12-24 16:37:33 +000011397 if (LocaleNCompare(value+i,"trns",4) == 0)
11398 mng_info->ping_exclude_tRNS=MagickTrue;
11399
glennrp03812ae2010-12-24 01:31:34 +000011400 if (LocaleNCompare(value+i,"vpag",4) == 0)
11401 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011402
glennrp03812ae2010-12-24 01:31:34 +000011403 if (LocaleNCompare(value+i,"zccp",4) == 0)
11404 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011405
glennrp03812ae2010-12-24 01:31:34 +000011406 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11407 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011408
glennrp03812ae2010-12-24 01:31:34 +000011409 }
glennrpce91ed52010-12-23 22:37:49 +000011410 }
glennrp26f37912010-12-23 16:22:42 +000011411 }
11412
glennrp5c7cf4e2010-12-24 00:30:00 +000011413 for (source=0; source<1; source++)
11414 {
11415 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011416 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011417 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011418
11419 if (value == NULL)
11420 value=GetImageArtifact(image,"png:include-chunks");
11421 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011422 else
glennrpacba0042010-12-24 14:27:26 +000011423 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011424 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011425
glennrpacba0042010-12-24 14:27:26 +000011426 if (value == NULL)
11427 value=GetImageOption(image_info,"png:include-chunks");
11428 }
11429
glennrp03812ae2010-12-24 01:31:34 +000011430 if (value != NULL)
11431 {
11432 size_t
11433 last;
glennrp26f37912010-12-23 16:22:42 +000011434
glennrp03812ae2010-12-24 01:31:34 +000011435 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011436
glennrp03812ae2010-12-24 01:31:34 +000011437 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011438 {
11439 if (source == 0)
11440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11441 " png:include-chunk=%s found in image artifacts.\n", value);
11442 else
11443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11444 " png:include-chunk=%s found in image properties.\n", value);
11445 }
glennrp03812ae2010-12-24 01:31:34 +000011446
11447 last=strlen(value);
11448
11449 for (i=0; i<(int) last; i+=5)
11450 {
11451 if (LocaleNCompare(value+i,"all",3) == 0)
11452 {
11453 mng_info->ping_exclude_bKGD=MagickFalse;
11454 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011455 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011456 mng_info->ping_exclude_EXIF=MagickFalse;
11457 mng_info->ping_exclude_gAMA=MagickFalse;
11458 mng_info->ping_exclude_iCCP=MagickFalse;
11459 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11460 mng_info->ping_exclude_oFFs=MagickFalse;
11461 mng_info->ping_exclude_pHYs=MagickFalse;
11462 mng_info->ping_exclude_sRGB=MagickFalse;
11463 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011464 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011465 mng_info->ping_exclude_vpAg=MagickFalse;
11466 mng_info->ping_exclude_zCCP=MagickFalse;
11467 mng_info->ping_exclude_zTXt=MagickFalse;
11468 i--;
11469 }
glennrp2cc891a2010-12-24 13:44:32 +000011470
glennrp03812ae2010-12-24 01:31:34 +000011471 if (LocaleNCompare(value+i,"none",4) == 0)
11472 {
11473 mng_info->ping_exclude_bKGD=MagickTrue;
11474 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011475 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011476 mng_info->ping_exclude_EXIF=MagickTrue;
11477 mng_info->ping_exclude_gAMA=MagickTrue;
11478 mng_info->ping_exclude_iCCP=MagickTrue;
11479 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11480 mng_info->ping_exclude_oFFs=MagickTrue;
11481 mng_info->ping_exclude_pHYs=MagickTrue;
11482 mng_info->ping_exclude_sRGB=MagickTrue;
11483 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011484 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011485 mng_info->ping_exclude_vpAg=MagickTrue;
11486 mng_info->ping_exclude_zCCP=MagickTrue;
11487 mng_info->ping_exclude_zTXt=MagickTrue;
11488 }
glennrp2cc891a2010-12-24 13:44:32 +000011489
glennrp03812ae2010-12-24 01:31:34 +000011490 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11491 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011492
glennrp03812ae2010-12-24 01:31:34 +000011493 if (LocaleNCompare(value+i,"chrm",4) == 0)
11494 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011495
glennrpa0ed0092011-04-18 16:36:29 +000011496 if (LocaleNCompare(value+i,"date",4) == 0)
11497 mng_info->ping_exclude_date=MagickFalse;
11498
glennrp03812ae2010-12-24 01:31:34 +000011499 if (LocaleNCompare(value+i,"exif",4) == 0)
11500 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011501
glennrp03812ae2010-12-24 01:31:34 +000011502 if (LocaleNCompare(value+i,"gama",4) == 0)
11503 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011504
glennrp03812ae2010-12-24 01:31:34 +000011505 if (LocaleNCompare(value+i,"iccp",4) == 0)
11506 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011507
glennrp03812ae2010-12-24 01:31:34 +000011508 /*
11509 if (LocaleNCompare(value+i,"itxt",4) == 0)
11510 mng_info->ping_exclude_iTXt=MagickFalse;
11511 */
glennrp2cc891a2010-12-24 13:44:32 +000011512
glennrp03812ae2010-12-24 01:31:34 +000011513 if (LocaleNCompare(value+i,"gama",4) == 0)
11514 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011515
glennrp03812ae2010-12-24 01:31:34 +000011516 if (LocaleNCompare(value+i,"offs",4) == 0)
11517 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011518
glennrp03812ae2010-12-24 01:31:34 +000011519 if (LocaleNCompare(value+i,"phys",4) == 0)
11520 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011521
glennrpa1e3b7b2010-12-24 16:37:33 +000011522 if (LocaleNCompare(value+i,"srgb",4) == 0)
11523 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011524
glennrp03812ae2010-12-24 01:31:34 +000011525 if (LocaleNCompare(value+i,"text",4) == 0)
11526 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011527
glennrpa1e3b7b2010-12-24 16:37:33 +000011528 if (LocaleNCompare(value+i,"trns",4) == 0)
11529 mng_info->ping_exclude_tRNS=MagickFalse;
11530
glennrp03812ae2010-12-24 01:31:34 +000011531 if (LocaleNCompare(value+i,"vpag",4) == 0)
11532 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011533
glennrp03812ae2010-12-24 01:31:34 +000011534 if (LocaleNCompare(value+i,"zccp",4) == 0)
11535 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011536
glennrp03812ae2010-12-24 01:31:34 +000011537 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11538 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011539
glennrp03812ae2010-12-24 01:31:34 +000011540 }
glennrpce91ed52010-12-23 22:37:49 +000011541 }
glennrp26f37912010-12-23 16:22:42 +000011542 }
11543
glennrp03812ae2010-12-24 01:31:34 +000011544 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011545 {
11546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11547 " Chunks to be excluded from the output PNG:");
11548 if (mng_info->ping_exclude_bKGD != MagickFalse)
11549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11550 " bKGD");
11551 if (mng_info->ping_exclude_cHRM != MagickFalse)
11552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11553 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011554 if (mng_info->ping_exclude_date != MagickFalse)
11555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11556 " date");
glennrp26f37912010-12-23 16:22:42 +000011557 if (mng_info->ping_exclude_EXIF != MagickFalse)
11558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11559 " EXIF");
11560 if (mng_info->ping_exclude_gAMA != MagickFalse)
11561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11562 " gAMA");
11563 if (mng_info->ping_exclude_iCCP != MagickFalse)
11564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11565 " iCCP");
11566/*
11567 if (mng_info->ping_exclude_iTXt != MagickFalse)
11568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11569 " iTXt");
11570*/
11571 if (mng_info->ping_exclude_oFFs != MagickFalse)
11572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11573 " oFFs");
11574 if (mng_info->ping_exclude_pHYs != MagickFalse)
11575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11576 " pHYs");
11577 if (mng_info->ping_exclude_sRGB != MagickFalse)
11578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11579 " sRGB");
11580 if (mng_info->ping_exclude_tEXt != MagickFalse)
11581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11582 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011583 if (mng_info->ping_exclude_tRNS != MagickFalse)
11584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11585 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011586 if (mng_info->ping_exclude_vpAg != MagickFalse)
11587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11588 " vpAg");
11589 if (mng_info->ping_exclude_zCCP != MagickFalse)
11590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11591 " zCCP");
11592 if (mng_info->ping_exclude_zTXt != MagickFalse)
11593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11594 " zTXt");
11595 }
11596
glennrpb9cfe272010-12-21 15:08:06 +000011597 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011598
cristy018f07f2011-09-04 21:15:19 +000011599 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011600
11601 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011602
cristy3ed852e2009-09-05 21:47:34 +000011603 if (logging != MagickFalse)
11604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011605
cristy3ed852e2009-09-05 21:47:34 +000011606 return(status);
11607}
11608
11609#if defined(JNG_SUPPORTED)
11610
11611/* Write one JNG image */
11612static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +000011613 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011614{
11615 Image
11616 *jpeg_image;
11617
11618 ImageInfo
11619 *jpeg_image_info;
11620
11621 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011622 logging,
cristy3ed852e2009-09-05 21:47:34 +000011623 status;
11624
11625 size_t
11626 length;
11627
11628 unsigned char
11629 *blob,
11630 chunk[80],
11631 *p;
11632
11633 unsigned int
11634 jng_alpha_compression_method,
11635 jng_alpha_sample_depth,
11636 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011637 transparent;
11638
cristybb503372010-05-27 20:51:26 +000011639 size_t
cristy3ed852e2009-09-05 21:47:34 +000011640 jng_quality;
11641
11642 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011643 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011644
11645 blob=(unsigned char *) NULL;
11646 jpeg_image=(Image *) NULL;
11647 jpeg_image_info=(ImageInfo *) NULL;
11648
11649 status=MagickTrue;
11650 transparent=image_info->type==GrayscaleMatteType ||
11651 image_info->type==TrueColorMatteType;
11652 jng_color_type=10;
11653 jng_alpha_sample_depth=0;
11654 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11655 jng_alpha_compression_method=0;
11656
11657 if (image->matte != MagickFalse)
11658 {
11659 /* if any pixels are transparent */
11660 transparent=MagickTrue;
11661 if (image_info->compression==JPEGCompression)
11662 jng_alpha_compression_method=8;
11663 }
11664
11665 if (transparent)
11666 {
cristybd5a96c2011-08-21 00:04:26 +000011667 ChannelType
11668 channel_mask;
11669
cristy3ed852e2009-09-05 21:47:34 +000011670 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011671
cristy3ed852e2009-09-05 21:47:34 +000011672 /* Create JPEG blob, image, and image_info */
11673 if (logging != MagickFalse)
11674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +000011675 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011676
cristy3ed852e2009-09-05 21:47:34 +000011677 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011678
cristy3ed852e2009-09-05 21:47:34 +000011679 if (jpeg_image_info == (ImageInfo *) NULL)
11680 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011681
cristy3ed852e2009-09-05 21:47:34 +000011682 if (logging != MagickFalse)
11683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11684 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011685
cristyc82a27b2011-10-21 01:07:16 +000011686 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011687
cristy3ed852e2009-09-05 21:47:34 +000011688 if (jpeg_image == (Image *) NULL)
11689 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011690
cristy3ed852e2009-09-05 21:47:34 +000011691 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristybd5a96c2011-08-21 00:04:26 +000011692 channel_mask=SetPixelChannelMask(jpeg_image,AlphaChannel);
cristye941a752011-10-15 01:52:48 +000011693 status=SeparateImage(jpeg_image,exception);
cristybd5a96c2011-08-21 00:04:26 +000011694 (void) SetPixelChannelMap(jpeg_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +000011695 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011696
cristy3ed852e2009-09-05 21:47:34 +000011697 if (jng_quality >= 1000)
11698 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000011699
cristy3ed852e2009-09-05 21:47:34 +000011700 else
11701 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000011702
cristy3ed852e2009-09-05 21:47:34 +000011703 jpeg_image_info->type=GrayscaleType;
cristy018f07f2011-09-04 21:15:19 +000011704 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000011705 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011706 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011707 "%s",jpeg_image->filename);
11708 }
11709
11710 /* To do: check bit depth of PNG alpha channel */
11711
11712 /* Check if image is grayscale. */
11713 if (image_info->type != TrueColorMatteType && image_info->type !=
cristyc82a27b2011-10-21 01:07:16 +000011714 TrueColorType && ImageIsGray(image,exception))
cristy3ed852e2009-09-05 21:47:34 +000011715 jng_color_type-=2;
11716
11717 if (transparent)
11718 {
11719 if (jng_alpha_compression_method==0)
11720 {
11721 const char
11722 *value;
11723
cristy4c08aed2011-07-01 19:47:50 +000011724 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011725 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000011726 exception);
cristy3ed852e2009-09-05 21:47:34 +000011727 if (logging != MagickFalse)
11728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11729 " Creating PNG blob.");
11730 length=0;
11731
11732 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11733 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11734 jpeg_image_info->interlace=NoInterlace;
11735
11736 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyc82a27b2011-10-21 01:07:16 +000011737 exception);
cristy3ed852e2009-09-05 21:47:34 +000011738
11739 /* Retrieve sample depth used */
cristyd15e6592011-10-15 00:13:06 +000011740 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
cristy3ed852e2009-09-05 21:47:34 +000011741 if (value != (char *) NULL)
11742 jng_alpha_sample_depth= (unsigned int) value[0];
11743 }
11744 else
11745 {
cristy4c08aed2011-07-01 19:47:50 +000011746 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011747
11748 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000011749 exception);
cristy3ed852e2009-09-05 21:47:34 +000011750
11751 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11752 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11753 jpeg_image_info->interlace=NoInterlace;
11754 if (logging != MagickFalse)
11755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11756 " Creating blob.");
11757 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyc82a27b2011-10-21 01:07:16 +000011758 exception);
cristy3ed852e2009-09-05 21:47:34 +000011759 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011760
cristy3ed852e2009-09-05 21:47:34 +000011761 if (logging != MagickFalse)
11762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011763 " Successfully read jpeg_image into a blob, length=%.20g.",
11764 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011765
11766 }
11767 /* Destroy JPEG image and image_info */
11768 jpeg_image=DestroyImage(jpeg_image);
11769 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11770 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11771 }
11772
11773 /* Write JHDR chunk */
11774 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11775 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011776 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011777 PNGLong(chunk+4,(png_uint_32) image->columns);
11778 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011779 chunk[12]=jng_color_type;
11780 chunk[13]=8; /* sample depth */
11781 chunk[14]=8; /*jng_image_compression_method */
11782 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11783 chunk[16]=jng_alpha_sample_depth;
11784 chunk[17]=jng_alpha_compression_method;
11785 chunk[18]=0; /*jng_alpha_filter_method */
11786 chunk[19]=0; /*jng_alpha_interlace_method */
11787 (void) WriteBlob(image,20,chunk);
11788 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11789 if (logging != MagickFalse)
11790 {
11791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011792 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011793
cristy3ed852e2009-09-05 21:47:34 +000011794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011795 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011796
cristy3ed852e2009-09-05 21:47:34 +000011797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11798 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011799
cristy3ed852e2009-09-05 21:47:34 +000011800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11801 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011802
cristy3ed852e2009-09-05 21:47:34 +000011803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11804 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011805
cristy3ed852e2009-09-05 21:47:34 +000011806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11807 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011808
cristy3ed852e2009-09-05 21:47:34 +000011809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11810 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011811
cristy3ed852e2009-09-05 21:47:34 +000011812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11813 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011814
cristy3ed852e2009-09-05 21:47:34 +000011815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11816 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011817
cristy3ed852e2009-09-05 21:47:34 +000011818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11819 " JNG alpha interlace:%5d",0);
11820 }
11821
glennrp0fe50b42010-11-16 03:52:51 +000011822 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011823 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011824
11825 /*
11826 Write leading ancillary chunks
11827 */
11828
11829 if (transparent)
11830 {
11831 /*
11832 Write JNG bKGD chunk
11833 */
11834
11835 unsigned char
11836 blue,
11837 green,
11838 red;
11839
cristybb503372010-05-27 20:51:26 +000011840 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011841 num_bytes;
11842
11843 if (jng_color_type == 8 || jng_color_type == 12)
11844 num_bytes=6L;
11845 else
11846 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011847 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011848 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011849 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011850 red=ScaleQuantumToChar(image->background_color.red);
11851 green=ScaleQuantumToChar(image->background_color.green);
11852 blue=ScaleQuantumToChar(image->background_color.blue);
11853 *(chunk+4)=0;
11854 *(chunk+5)=red;
11855 *(chunk+6)=0;
11856 *(chunk+7)=green;
11857 *(chunk+8)=0;
11858 *(chunk+9)=blue;
11859 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11860 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11861 }
11862
11863 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11864 {
11865 /*
11866 Write JNG sRGB chunk
11867 */
11868 (void) WriteBlobMSBULong(image,1L);
11869 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011870 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011871
cristy3ed852e2009-09-05 21:47:34 +000011872 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011873 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011874 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011875 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011876
cristy3ed852e2009-09-05 21:47:34 +000011877 else
glennrpe610a072010-08-05 17:08:46 +000011878 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011879 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011880 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011881
cristy3ed852e2009-09-05 21:47:34 +000011882 (void) WriteBlob(image,5,chunk);
11883 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11884 }
11885 else
11886 {
11887 if (image->gamma != 0.0)
11888 {
11889 /*
11890 Write JNG gAMA chunk
11891 */
11892 (void) WriteBlobMSBULong(image,4L);
11893 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011894 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011895 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011896 (void) WriteBlob(image,8,chunk);
11897 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11898 }
glennrp0fe50b42010-11-16 03:52:51 +000011899
cristy3ed852e2009-09-05 21:47:34 +000011900 if ((mng_info->equal_chrms == MagickFalse) &&
11901 (image->chromaticity.red_primary.x != 0.0))
11902 {
11903 PrimaryInfo
11904 primary;
11905
11906 /*
11907 Write JNG cHRM chunk
11908 */
11909 (void) WriteBlobMSBULong(image,32L);
11910 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011911 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011912 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011913 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11914 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011915 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011916 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11917 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011918 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011919 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11920 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011921 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011922 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11923 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011924 (void) WriteBlob(image,36,chunk);
11925 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11926 }
11927 }
glennrp0fe50b42010-11-16 03:52:51 +000011928
cristy2a11bef2011-10-28 18:33:11 +000011929 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000011930 {
11931 /*
11932 Write JNG pHYs chunk
11933 */
11934 (void) WriteBlobMSBULong(image,9L);
11935 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011936 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011937 if (image->units == PixelsPerInchResolution)
11938 {
cristy35ef8242010-06-03 16:24:13 +000011939 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011940 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011941
cristy35ef8242010-06-03 16:24:13 +000011942 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011943 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011944
cristy3ed852e2009-09-05 21:47:34 +000011945 chunk[12]=1;
11946 }
glennrp0fe50b42010-11-16 03:52:51 +000011947
cristy3ed852e2009-09-05 21:47:34 +000011948 else
11949 {
11950 if (image->units == PixelsPerCentimeterResolution)
11951 {
cristy35ef8242010-06-03 16:24:13 +000011952 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011953 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011954
cristy35ef8242010-06-03 16:24:13 +000011955 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011956 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011957
cristy3ed852e2009-09-05 21:47:34 +000011958 chunk[12]=1;
11959 }
glennrp0fe50b42010-11-16 03:52:51 +000011960
cristy3ed852e2009-09-05 21:47:34 +000011961 else
11962 {
cristy2a11bef2011-10-28 18:33:11 +000011963 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
11964 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011965 chunk[12]=0;
11966 }
11967 }
11968 (void) WriteBlob(image,13,chunk);
11969 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11970 }
11971
11972 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11973 {
11974 /*
11975 Write JNG oFFs chunk
11976 */
11977 (void) WriteBlobMSBULong(image,9L);
11978 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011979 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011980 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11981 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011982 chunk[12]=0;
11983 (void) WriteBlob(image,13,chunk);
11984 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11985 }
11986 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11987 {
11988 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11989 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011990 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011991 PNGLong(chunk+4,(png_uint_32) image->page.width);
11992 PNGLong(chunk+8,(png_uint_32) image->page.height);
11993 chunk[12]=0; /* unit = pixels */
11994 (void) WriteBlob(image,13,chunk);
11995 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11996 }
11997
11998
11999 if (transparent)
12000 {
12001 if (jng_alpha_compression_method==0)
12002 {
cristybb503372010-05-27 20:51:26 +000012003 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012004 i;
12005
cristybb503372010-05-27 20:51:26 +000012006 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012007 len;
12008
12009 /* Write IDAT chunk header */
12010 if (logging != MagickFalse)
12011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012012 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000012013 length);
cristy3ed852e2009-09-05 21:47:34 +000012014
12015 /* Copy IDAT chunks */
12016 len=0;
12017 p=blob+8;
cristybb503372010-05-27 20:51:26 +000012018 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000012019 {
12020 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12021 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012022
cristy3ed852e2009-09-05 21:47:34 +000012023 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12024 {
12025 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000012026 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000012027 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000012028 (void) WriteBlob(image,(size_t) len+4,p);
12029 (void) WriteBlobMSBULong(image,
12030 crc32(0,p,(uInt) len+4));
12031 }
glennrp0fe50b42010-11-16 03:52:51 +000012032
cristy3ed852e2009-09-05 21:47:34 +000012033 else
12034 {
12035 if (logging != MagickFalse)
12036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012037 " Skipping %c%c%c%c chunk, length=%.20g.",
12038 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012039 }
12040 p+=(8+len);
12041 }
12042 }
12043 else
12044 {
12045 /* Write JDAA chunk header */
12046 if (logging != MagickFalse)
12047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012048 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012049 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012050 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012051 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012052 /* Write JDAT chunk(s) data */
12053 (void) WriteBlob(image,4,chunk);
12054 (void) WriteBlob(image,length,blob);
12055 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12056 (uInt) length));
12057 }
12058 blob=(unsigned char *) RelinquishMagickMemory(blob);
12059 }
12060
12061 /* Encode image as a JPEG blob */
12062 if (logging != MagickFalse)
12063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12064 " Creating jpeg_image_info.");
12065 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12066 if (jpeg_image_info == (ImageInfo *) NULL)
12067 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12068
12069 if (logging != MagickFalse)
12070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12071 " Creating jpeg_image.");
12072
cristyc82a27b2011-10-21 01:07:16 +000012073 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012074 if (jpeg_image == (Image *) NULL)
12075 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12076 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12077
12078 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012079 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012080 jpeg_image->filename);
12081
12082 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000012083 exception);
cristy3ed852e2009-09-05 21:47:34 +000012084
12085 if (logging != MagickFalse)
12086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012087 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12088 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012089
12090 if (jng_color_type == 8 || jng_color_type == 12)
12091 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012092
cristy3ed852e2009-09-05 21:47:34 +000012093 jpeg_image_info->quality=jng_quality % 1000;
12094 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12095 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012096
cristy3ed852e2009-09-05 21:47:34 +000012097 if (logging != MagickFalse)
12098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12099 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012100
cristyc82a27b2011-10-21 01:07:16 +000012101 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
glennrp0fe50b42010-11-16 03:52:51 +000012102
cristy3ed852e2009-09-05 21:47:34 +000012103 if (logging != MagickFalse)
12104 {
12105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012106 " Successfully read jpeg_image into a blob, length=%.20g.",
12107 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012108
12109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012110 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012111 }
glennrp0fe50b42010-11-16 03:52:51 +000012112
cristy3ed852e2009-09-05 21:47:34 +000012113 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012114 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012115 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012116 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012117 (void) WriteBlob(image,4,chunk);
12118 (void) WriteBlob(image,length,blob);
12119 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12120
12121 jpeg_image=DestroyImage(jpeg_image);
12122 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12123 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12124 blob=(unsigned char *) RelinquishMagickMemory(blob);
12125
12126 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012127 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012128
12129 /* Write IEND chunk */
12130 (void) WriteBlobMSBULong(image,0L);
12131 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012132 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012133 (void) WriteBlob(image,4,chunk);
12134 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12135
12136 if (logging != MagickFalse)
12137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12138 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012139
cristy3ed852e2009-09-05 21:47:34 +000012140 return(status);
12141}
12142
12143
12144/*
12145%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12146% %
12147% %
12148% %
12149% W r i t e J N G I m a g e %
12150% %
12151% %
12152% %
12153%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12154%
12155% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12156%
12157% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12158%
12159% The format of the WriteJNGImage method is:
12160%
cristy1e178e72011-08-28 19:44:34 +000012161% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12162% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012163%
12164% A description of each parameter follows:
12165%
12166% o image_info: the image info.
12167%
12168% o image: The image.
12169%
cristy1e178e72011-08-28 19:44:34 +000012170% o exception: return any errors or warnings in this structure.
12171%
cristy3ed852e2009-09-05 21:47:34 +000012172%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12173*/
cristy1e178e72011-08-28 19:44:34 +000012174static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12175 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012176{
12177 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012178 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012179 logging,
cristy3ed852e2009-09-05 21:47:34 +000012180 status;
12181
12182 MngInfo
12183 *mng_info;
12184
cristy3ed852e2009-09-05 21:47:34 +000012185 /*
12186 Open image file.
12187 */
12188 assert(image_info != (const ImageInfo *) NULL);
12189 assert(image_info->signature == MagickSignature);
12190 assert(image != (Image *) NULL);
12191 assert(image->signature == MagickSignature);
12192 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012193 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristyc82a27b2011-10-21 01:07:16 +000012194 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012195 if (status == MagickFalse)
12196 return(status);
12197
12198 /*
12199 Allocate a MngInfo structure.
12200 */
12201 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012202 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012203 if (mng_info == (MngInfo *) NULL)
12204 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12205 /*
12206 Initialize members of the MngInfo structure.
12207 */
12208 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12209 mng_info->image=image;
12210 have_mng_structure=MagickTrue;
12211
12212 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12213
cristy018f07f2011-09-04 21:15:19 +000012214 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012215 (void) CloseBlob(image);
12216
12217 (void) CatchImageException(image);
12218 MngInfoFreeStruct(mng_info,&have_mng_structure);
12219 if (logging != MagickFalse)
12220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12221 return(status);
12222}
12223#endif
12224
cristy1e178e72011-08-28 19:44:34 +000012225static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12226 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012227{
12228 const char
12229 *option;
12230
12231 Image
12232 *next_image;
12233
12234 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012235 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012236 status;
12237
glennrp03812ae2010-12-24 01:31:34 +000012238 volatile MagickBooleanType
12239 logging;
12240
cristy3ed852e2009-09-05 21:47:34 +000012241 MngInfo
12242 *mng_info;
12243
12244 int
cristy3ed852e2009-09-05 21:47:34 +000012245 image_count,
12246 need_iterations,
12247 need_matte;
12248
12249 volatile int
12250#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12251 defined(PNG_MNG_FEATURES_SUPPORTED)
12252 need_local_plte,
12253#endif
12254 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012255 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012256 use_global_plte;
12257
cristybb503372010-05-27 20:51:26 +000012258 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012259 i;
12260
12261 unsigned char
12262 chunk[800];
12263
12264 volatile unsigned int
12265 write_jng,
12266 write_mng;
12267
cristybb503372010-05-27 20:51:26 +000012268 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012269 scene;
12270
cristybb503372010-05-27 20:51:26 +000012271 size_t
cristy3ed852e2009-09-05 21:47:34 +000012272 final_delay=0,
12273 initial_delay;
12274
glennrpd5045b42010-03-24 12:40:35 +000012275#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012276 if (image_info->verbose)
12277 printf("Your PNG library (libpng-%s) is rather old.\n",
12278 PNG_LIBPNG_VER_STRING);
12279#endif
12280
12281 /*
12282 Open image file.
12283 */
12284 assert(image_info != (const ImageInfo *) NULL);
12285 assert(image_info->signature == MagickSignature);
12286 assert(image != (Image *) NULL);
12287 assert(image->signature == MagickSignature);
12288 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012289 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristyc82a27b2011-10-21 01:07:16 +000012290 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012291 if (status == MagickFalse)
12292 return(status);
12293
12294 /*
12295 Allocate a MngInfo structure.
12296 */
12297 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012298 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012299 if (mng_info == (MngInfo *) NULL)
12300 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12301 /*
12302 Initialize members of the MngInfo structure.
12303 */
12304 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12305 mng_info->image=image;
12306 have_mng_structure=MagickTrue;
12307 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12308
12309 /*
12310 * See if user has requested a specific PNG subformat to be used
12311 * for all of the PNGs in the MNG being written, e.g.,
12312 *
12313 * convert *.png png8:animation.mng
12314 *
12315 * To do: check -define png:bit_depth and png:color_type as well,
12316 * or perhaps use mng:bit_depth and mng:color_type instead for
12317 * global settings.
12318 */
12319
12320 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12321 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12322 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12323
12324 write_jng=MagickFalse;
12325 if (image_info->compression == JPEGCompression)
12326 write_jng=MagickTrue;
12327
12328 mng_info->adjoin=image_info->adjoin &&
12329 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12330
cristy3ed852e2009-09-05 21:47:34 +000012331 if (logging != MagickFalse)
12332 {
12333 /* Log some info about the input */
12334 Image
12335 *p;
12336
12337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12338 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012339
cristy3ed852e2009-09-05 21:47:34 +000012340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012341 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012342
cristy3ed852e2009-09-05 21:47:34 +000012343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12344 " Type: %d",image_info->type);
12345
12346 scene=0;
12347 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12348 {
12349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012350 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012351
cristy3ed852e2009-09-05 21:47:34 +000012352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012353 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012354
cristy3ed852e2009-09-05 21:47:34 +000012355 if (p->matte)
12356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12357 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012358
cristy3ed852e2009-09-05 21:47:34 +000012359 else
12360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12361 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012362
cristy3ed852e2009-09-05 21:47:34 +000012363 if (p->storage_class == PseudoClass)
12364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12365 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012366
cristy3ed852e2009-09-05 21:47:34 +000012367 else
12368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12369 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012370
cristy3ed852e2009-09-05 21:47:34 +000012371 if (p->colors)
12372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012373 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012374
cristy3ed852e2009-09-05 21:47:34 +000012375 else
12376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12377 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012378
cristy3ed852e2009-09-05 21:47:34 +000012379 if (mng_info->adjoin == MagickFalse)
12380 break;
12381 }
12382 }
12383
cristy3ed852e2009-09-05 21:47:34 +000012384 use_global_plte=MagickFalse;
12385 all_images_are_gray=MagickFalse;
12386#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12387 need_local_plte=MagickTrue;
12388#endif
12389 need_defi=MagickFalse;
12390 need_matte=MagickFalse;
12391 mng_info->framing_mode=1;
12392 mng_info->old_framing_mode=1;
12393
12394 if (write_mng)
12395 if (image_info->page != (char *) NULL)
12396 {
12397 /*
12398 Determine image bounding box.
12399 */
12400 SetGeometry(image,&mng_info->page);
12401 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12402 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12403 }
12404 if (write_mng)
12405 {
12406 unsigned int
12407 need_geom;
12408
12409 unsigned short
12410 red,
12411 green,
12412 blue;
12413
12414 mng_info->page=image->page;
12415 need_geom=MagickTrue;
12416 if (mng_info->page.width || mng_info->page.height)
12417 need_geom=MagickFalse;
12418 /*
12419 Check all the scenes.
12420 */
12421 initial_delay=image->delay;
12422 need_iterations=MagickFalse;
12423 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12424 mng_info->equal_physs=MagickTrue,
12425 mng_info->equal_gammas=MagickTrue;
12426 mng_info->equal_srgbs=MagickTrue;
12427 mng_info->equal_backgrounds=MagickTrue;
12428 image_count=0;
12429#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12430 defined(PNG_MNG_FEATURES_SUPPORTED)
12431 all_images_are_gray=MagickTrue;
12432 mng_info->equal_palettes=MagickFalse;
12433 need_local_plte=MagickFalse;
12434#endif
12435 for (next_image=image; next_image != (Image *) NULL; )
12436 {
12437 if (need_geom)
12438 {
12439 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12440 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012441
cristy3ed852e2009-09-05 21:47:34 +000012442 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12443 mng_info->page.height=next_image->rows+next_image->page.y;
12444 }
glennrp0fe50b42010-11-16 03:52:51 +000012445
cristy3ed852e2009-09-05 21:47:34 +000012446 if (next_image->page.x || next_image->page.y)
12447 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012448
cristy3ed852e2009-09-05 21:47:34 +000012449 if (next_image->matte)
12450 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012451
cristy3ed852e2009-09-05 21:47:34 +000012452 if ((int) next_image->dispose >= BackgroundDispose)
12453 if (next_image->matte || next_image->page.x || next_image->page.y ||
12454 ((next_image->columns < mng_info->page.width) &&
12455 (next_image->rows < mng_info->page.height)))
12456 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012457
cristy3ed852e2009-09-05 21:47:34 +000012458 if (next_image->iterations)
12459 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012460
cristy3ed852e2009-09-05 21:47:34 +000012461 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012462
cristy3ed852e2009-09-05 21:47:34 +000012463 if (final_delay != initial_delay || final_delay > 1UL*
12464 next_image->ticks_per_second)
12465 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012466
cristy3ed852e2009-09-05 21:47:34 +000012467#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12468 defined(PNG_MNG_FEATURES_SUPPORTED)
12469 /*
12470 check for global palette possibility.
12471 */
12472 if (image->matte != MagickFalse)
12473 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012474
cristy3ed852e2009-09-05 21:47:34 +000012475 if (need_local_plte == 0)
12476 {
cristyc82a27b2011-10-21 01:07:16 +000012477 if (ImageIsGray(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012478 all_images_are_gray=MagickFalse;
12479 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12480 if (use_global_plte == 0)
12481 use_global_plte=mng_info->equal_palettes;
12482 need_local_plte=!mng_info->equal_palettes;
12483 }
12484#endif
12485 if (GetNextImageInList(next_image) != (Image *) NULL)
12486 {
12487 if (next_image->background_color.red !=
12488 next_image->next->background_color.red ||
12489 next_image->background_color.green !=
12490 next_image->next->background_color.green ||
12491 next_image->background_color.blue !=
12492 next_image->next->background_color.blue)
12493 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012494
cristy3ed852e2009-09-05 21:47:34 +000012495 if (next_image->gamma != next_image->next->gamma)
12496 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012497
cristy3ed852e2009-09-05 21:47:34 +000012498 if (next_image->rendering_intent !=
12499 next_image->next->rendering_intent)
12500 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012501
cristy3ed852e2009-09-05 21:47:34 +000012502 if ((next_image->units != next_image->next->units) ||
cristy2a11bef2011-10-28 18:33:11 +000012503 (next_image->resolution.x != next_image->next->resolution.x) ||
12504 (next_image->resolution.y != next_image->next->resolution.y))
cristy3ed852e2009-09-05 21:47:34 +000012505 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012506
cristy3ed852e2009-09-05 21:47:34 +000012507 if (mng_info->equal_chrms)
12508 {
12509 if (next_image->chromaticity.red_primary.x !=
12510 next_image->next->chromaticity.red_primary.x ||
12511 next_image->chromaticity.red_primary.y !=
12512 next_image->next->chromaticity.red_primary.y ||
12513 next_image->chromaticity.green_primary.x !=
12514 next_image->next->chromaticity.green_primary.x ||
12515 next_image->chromaticity.green_primary.y !=
12516 next_image->next->chromaticity.green_primary.y ||
12517 next_image->chromaticity.blue_primary.x !=
12518 next_image->next->chromaticity.blue_primary.x ||
12519 next_image->chromaticity.blue_primary.y !=
12520 next_image->next->chromaticity.blue_primary.y ||
12521 next_image->chromaticity.white_point.x !=
12522 next_image->next->chromaticity.white_point.x ||
12523 next_image->chromaticity.white_point.y !=
12524 next_image->next->chromaticity.white_point.y)
12525 mng_info->equal_chrms=MagickFalse;
12526 }
12527 }
12528 image_count++;
12529 next_image=GetNextImageInList(next_image);
12530 }
12531 if (image_count < 2)
12532 {
12533 mng_info->equal_backgrounds=MagickFalse;
12534 mng_info->equal_chrms=MagickFalse;
12535 mng_info->equal_gammas=MagickFalse;
12536 mng_info->equal_srgbs=MagickFalse;
12537 mng_info->equal_physs=MagickFalse;
12538 use_global_plte=MagickFalse;
12539#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12540 need_local_plte=MagickTrue;
12541#endif
12542 need_iterations=MagickFalse;
12543 }
glennrp0fe50b42010-11-16 03:52:51 +000012544
cristy3ed852e2009-09-05 21:47:34 +000012545 if (mng_info->need_fram == MagickFalse)
12546 {
12547 /*
12548 Only certain framing rates 100/n are exactly representable without
12549 the FRAM chunk but we'll allow some slop in VLC files
12550 */
12551 if (final_delay == 0)
12552 {
12553 if (need_iterations != MagickFalse)
12554 {
12555 /*
12556 It's probably a GIF with loop; don't run it *too* fast.
12557 */
glennrp02617122010-07-28 13:07:35 +000012558 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012559 {
12560 final_delay=10;
cristyc82a27b2011-10-21 01:07:16 +000012561 (void) ThrowMagickException(exception,GetMagickModule(),
12562 CoderWarning,
glennrpd908de42010-07-28 13:28:27 +000012563 "input has zero delay between all frames; assuming",
12564 " 10 cs `%s'","");
12565 }
cristy3ed852e2009-09-05 21:47:34 +000012566 }
12567 else
12568 mng_info->ticks_per_second=0;
12569 }
12570 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012571 mng_info->ticks_per_second=(png_uint_32)
12572 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012573 if (final_delay > 50)
12574 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012575
cristy3ed852e2009-09-05 21:47:34 +000012576 if (final_delay > 75)
12577 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012578
cristy3ed852e2009-09-05 21:47:34 +000012579 if (final_delay > 125)
12580 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012581
cristy3ed852e2009-09-05 21:47:34 +000012582 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12583 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12584 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12585 1UL*image->ticks_per_second))
12586 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12587 }
glennrp0fe50b42010-11-16 03:52:51 +000012588
cristy3ed852e2009-09-05 21:47:34 +000012589 if (mng_info->need_fram != MagickFalse)
12590 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12591 /*
12592 If pseudocolor, we should also check to see if all the
12593 palettes are identical and write a global PLTE if they are.
12594 ../glennrp Feb 99.
12595 */
12596 /*
12597 Write the MNG version 1.0 signature and MHDR chunk.
12598 */
12599 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12600 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12601 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012602 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012603 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12604 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012605 PNGLong(chunk+12,mng_info->ticks_per_second);
12606 PNGLong(chunk+16,0L); /* layer count=unknown */
12607 PNGLong(chunk+20,0L); /* frame count=unknown */
12608 PNGLong(chunk+24,0L); /* play time=unknown */
12609 if (write_jng)
12610 {
12611 if (need_matte)
12612 {
12613 if (need_defi || mng_info->need_fram || use_global_plte)
12614 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012615
cristy3ed852e2009-09-05 21:47:34 +000012616 else
12617 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12618 }
glennrp0fe50b42010-11-16 03:52:51 +000012619
cristy3ed852e2009-09-05 21:47:34 +000012620 else
12621 {
12622 if (need_defi || mng_info->need_fram || use_global_plte)
12623 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012624
cristy3ed852e2009-09-05 21:47:34 +000012625 else
12626 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12627 }
12628 }
glennrp0fe50b42010-11-16 03:52:51 +000012629
cristy3ed852e2009-09-05 21:47:34 +000012630 else
12631 {
12632 if (need_matte)
12633 {
12634 if (need_defi || mng_info->need_fram || use_global_plte)
12635 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012636
cristy3ed852e2009-09-05 21:47:34 +000012637 else
12638 PNGLong(chunk+28,9L); /* simplicity=VLC */
12639 }
glennrp0fe50b42010-11-16 03:52:51 +000012640
cristy3ed852e2009-09-05 21:47:34 +000012641 else
12642 {
12643 if (need_defi || mng_info->need_fram || use_global_plte)
12644 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012645
cristy3ed852e2009-09-05 21:47:34 +000012646 else
12647 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12648 }
12649 }
12650 (void) WriteBlob(image,32,chunk);
12651 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12652 option=GetImageOption(image_info,"mng:need-cacheoff");
12653 if (option != (const char *) NULL)
12654 {
12655 size_t
12656 length;
12657
12658 /*
12659 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12660 */
12661 PNGType(chunk,mng_nEED);
12662 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012663 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012664 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012665 length+=4;
12666 (void) WriteBlob(image,length,chunk);
12667 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12668 }
12669 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12670 (GetNextImageInList(image) != (Image *) NULL) &&
12671 (image->iterations != 1))
12672 {
12673 /*
12674 Write MNG TERM chunk
12675 */
12676 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12677 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012678 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012679 chunk[4]=3; /* repeat animation */
12680 chunk[5]=0; /* show last frame when done */
12681 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12682 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012683
cristy3ed852e2009-09-05 21:47:34 +000012684 if (image->iterations == 0)
12685 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012686
cristy3ed852e2009-09-05 21:47:34 +000012687 else
12688 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012689
cristy3ed852e2009-09-05 21:47:34 +000012690 if (logging != MagickFalse)
12691 {
12692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012693 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12694 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012695
cristy3ed852e2009-09-05 21:47:34 +000012696 if (image->iterations == 0)
12697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012698 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012699
cristy3ed852e2009-09-05 21:47:34 +000012700 else
12701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012702 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012703 }
12704 (void) WriteBlob(image,14,chunk);
12705 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12706 }
12707 /*
12708 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12709 */
12710 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12711 mng_info->equal_srgbs)
12712 {
12713 /*
12714 Write MNG sRGB chunk
12715 */
12716 (void) WriteBlobMSBULong(image,1L);
12717 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012718 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012719
cristy3ed852e2009-09-05 21:47:34 +000012720 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012721 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012722 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012723 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012724
cristy3ed852e2009-09-05 21:47:34 +000012725 else
glennrpe610a072010-08-05 17:08:46 +000012726 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012727 Magick_RenderingIntent_to_PNG_RenderingIntent(
12728 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012729
cristy3ed852e2009-09-05 21:47:34 +000012730 (void) WriteBlob(image,5,chunk);
12731 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12732 mng_info->have_write_global_srgb=MagickTrue;
12733 }
glennrp0fe50b42010-11-16 03:52:51 +000012734
cristy3ed852e2009-09-05 21:47:34 +000012735 else
12736 {
12737 if (image->gamma && mng_info->equal_gammas)
12738 {
12739 /*
12740 Write MNG gAMA chunk
12741 */
12742 (void) WriteBlobMSBULong(image,4L);
12743 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012744 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012745 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012746 (void) WriteBlob(image,8,chunk);
12747 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12748 mng_info->have_write_global_gama=MagickTrue;
12749 }
12750 if (mng_info->equal_chrms)
12751 {
12752 PrimaryInfo
12753 primary;
12754
12755 /*
12756 Write MNG cHRM chunk
12757 */
12758 (void) WriteBlobMSBULong(image,32L);
12759 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012760 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012761 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012762 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12763 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012764 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012765 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12766 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012767 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012768 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12769 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012770 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012771 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12772 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012773 (void) WriteBlob(image,36,chunk);
12774 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12775 mng_info->have_write_global_chrm=MagickTrue;
12776 }
12777 }
cristy2a11bef2011-10-28 18:33:11 +000012778 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012779 {
12780 /*
12781 Write MNG pHYs chunk
12782 */
12783 (void) WriteBlobMSBULong(image,9L);
12784 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012785 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012786
cristy3ed852e2009-09-05 21:47:34 +000012787 if (image->units == PixelsPerInchResolution)
12788 {
cristy35ef8242010-06-03 16:24:13 +000012789 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012790 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012791
cristy35ef8242010-06-03 16:24:13 +000012792 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012793 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012794
cristy3ed852e2009-09-05 21:47:34 +000012795 chunk[12]=1;
12796 }
glennrp0fe50b42010-11-16 03:52:51 +000012797
cristy3ed852e2009-09-05 21:47:34 +000012798 else
12799 {
12800 if (image->units == PixelsPerCentimeterResolution)
12801 {
cristy35ef8242010-06-03 16:24:13 +000012802 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012803 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012804
cristy35ef8242010-06-03 16:24:13 +000012805 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012806 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012807
cristy3ed852e2009-09-05 21:47:34 +000012808 chunk[12]=1;
12809 }
glennrp0fe50b42010-11-16 03:52:51 +000012810
cristy3ed852e2009-09-05 21:47:34 +000012811 else
12812 {
cristy2a11bef2011-10-28 18:33:11 +000012813 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12814 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012815 chunk[12]=0;
12816 }
12817 }
12818 (void) WriteBlob(image,13,chunk);
12819 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12820 }
12821 /*
12822 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12823 or does not cover the entire frame.
12824 */
12825 if (write_mng && (image->matte || image->page.x > 0 ||
12826 image->page.y > 0 || (image->page.width &&
12827 (image->page.width+image->page.x < mng_info->page.width))
12828 || (image->page.height && (image->page.height+image->page.y
12829 < mng_info->page.height))))
12830 {
12831 (void) WriteBlobMSBULong(image,6L);
12832 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012833 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012834 red=ScaleQuantumToShort(image->background_color.red);
12835 green=ScaleQuantumToShort(image->background_color.green);
12836 blue=ScaleQuantumToShort(image->background_color.blue);
12837 PNGShort(chunk+4,red);
12838 PNGShort(chunk+6,green);
12839 PNGShort(chunk+8,blue);
12840 (void) WriteBlob(image,10,chunk);
12841 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12842 if (mng_info->equal_backgrounds)
12843 {
12844 (void) WriteBlobMSBULong(image,6L);
12845 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012846 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012847 (void) WriteBlob(image,10,chunk);
12848 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12849 }
12850 }
12851
12852#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12853 if ((need_local_plte == MagickFalse) &&
12854 (image->storage_class == PseudoClass) &&
12855 (all_images_are_gray == MagickFalse))
12856 {
cristybb503372010-05-27 20:51:26 +000012857 size_t
cristy3ed852e2009-09-05 21:47:34 +000012858 data_length;
12859
12860 /*
12861 Write MNG PLTE chunk
12862 */
12863 data_length=3*image->colors;
12864 (void) WriteBlobMSBULong(image,data_length);
12865 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012866 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012867
cristybb503372010-05-27 20:51:26 +000012868 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012869 {
cristy5f07f702011-09-26 17:29:10 +000012870 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12871 image->colormap[i].red) & 0xff);
12872 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12873 image->colormap[i].green) & 0xff);
12874 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12875 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000012876 }
glennrp0fe50b42010-11-16 03:52:51 +000012877
cristy3ed852e2009-09-05 21:47:34 +000012878 (void) WriteBlob(image,data_length+4,chunk);
12879 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12880 mng_info->have_write_global_plte=MagickTrue;
12881 }
12882#endif
12883 }
12884 scene=0;
12885 mng_info->delay=0;
12886#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12887 defined(PNG_MNG_FEATURES_SUPPORTED)
12888 mng_info->equal_palettes=MagickFalse;
12889#endif
12890 do
12891 {
12892 if (mng_info->adjoin)
12893 {
12894#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12895 defined(PNG_MNG_FEATURES_SUPPORTED)
12896 /*
12897 If we aren't using a global palette for the entire MNG, check to
12898 see if we can use one for two or more consecutive images.
12899 */
12900 if (need_local_plte && use_global_plte && !all_images_are_gray)
12901 {
12902 if (mng_info->IsPalette)
12903 {
12904 /*
12905 When equal_palettes is true, this image has the same palette
12906 as the previous PseudoClass image
12907 */
12908 mng_info->have_write_global_plte=mng_info->equal_palettes;
12909 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12910 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12911 {
12912 /*
12913 Write MNG PLTE chunk
12914 */
cristybb503372010-05-27 20:51:26 +000012915 size_t
cristy3ed852e2009-09-05 21:47:34 +000012916 data_length;
12917
12918 data_length=3*image->colors;
12919 (void) WriteBlobMSBULong(image,data_length);
12920 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012921 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012922
cristybb503372010-05-27 20:51:26 +000012923 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012924 {
12925 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12926 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12927 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12928 }
glennrp0fe50b42010-11-16 03:52:51 +000012929
cristy3ed852e2009-09-05 21:47:34 +000012930 (void) WriteBlob(image,data_length+4,chunk);
12931 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12932 (uInt) (data_length+4)));
12933 mng_info->have_write_global_plte=MagickTrue;
12934 }
12935 }
12936 else
12937 mng_info->have_write_global_plte=MagickFalse;
12938 }
12939#endif
12940 if (need_defi)
12941 {
cristybb503372010-05-27 20:51:26 +000012942 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012943 previous_x,
12944 previous_y;
12945
12946 if (scene)
12947 {
12948 previous_x=mng_info->page.x;
12949 previous_y=mng_info->page.y;
12950 }
12951 else
12952 {
12953 previous_x=0;
12954 previous_y=0;
12955 }
12956 mng_info->page=image->page;
12957 if ((mng_info->page.x != previous_x) ||
12958 (mng_info->page.y != previous_y))
12959 {
12960 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12961 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012962 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012963 chunk[4]=0; /* object 0 MSB */
12964 chunk[5]=0; /* object 0 LSB */
12965 chunk[6]=0; /* visible */
12966 chunk[7]=0; /* abstract */
12967 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12968 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12969 (void) WriteBlob(image,16,chunk);
12970 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12971 }
12972 }
12973 }
12974
12975 mng_info->write_mng=write_mng;
12976
12977 if ((int) image->dispose >= 3)
12978 mng_info->framing_mode=3;
12979
12980 if (mng_info->need_fram && mng_info->adjoin &&
12981 ((image->delay != mng_info->delay) ||
12982 (mng_info->framing_mode != mng_info->old_framing_mode)))
12983 {
12984 if (image->delay == mng_info->delay)
12985 {
12986 /*
12987 Write a MNG FRAM chunk with the new framing mode.
12988 */
12989 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12990 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012991 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012992 chunk[4]=(unsigned char) mng_info->framing_mode;
12993 (void) WriteBlob(image,5,chunk);
12994 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12995 }
12996 else
12997 {
12998 /*
12999 Write a MNG FRAM chunk with the delay.
13000 */
13001 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13002 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013003 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013004 chunk[4]=(unsigned char) mng_info->framing_mode;
13005 chunk[5]=0; /* frame name separator (no name) */
13006 chunk[6]=2; /* flag for changing default delay */
13007 chunk[7]=0; /* flag for changing frame timeout */
13008 chunk[8]=0; /* flag for changing frame clipping */
13009 chunk[9]=0; /* flag for changing frame sync_id */
13010 PNGLong(chunk+10,(png_uint_32)
13011 ((mng_info->ticks_per_second*
13012 image->delay)/MagickMax(image->ticks_per_second,1)));
13013 (void) WriteBlob(image,14,chunk);
13014 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000013015 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000013016 }
13017 mng_info->old_framing_mode=mng_info->framing_mode;
13018 }
13019
13020#if defined(JNG_SUPPORTED)
13021 if (image_info->compression == JPEGCompression)
13022 {
13023 ImageInfo
13024 *write_info;
13025
13026 if (logging != MagickFalse)
13027 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13028 " Writing JNG object.");
13029 /* To do: specify the desired alpha compression method. */
13030 write_info=CloneImageInfo(image_info);
13031 write_info->compression=UndefinedCompression;
cristy018f07f2011-09-04 21:15:19 +000013032 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013033 write_info=DestroyImageInfo(write_info);
13034 }
13035 else
13036#endif
13037 {
13038 if (logging != MagickFalse)
13039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13040 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013041
glennrpb9cfe272010-12-21 15:08:06 +000013042 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013043 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013044
13045 /* We don't want any ancillary chunks written */
13046 mng_info->ping_exclude_bKGD=MagickTrue;
13047 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013048 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013049 mng_info->ping_exclude_EXIF=MagickTrue;
13050 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013051 mng_info->ping_exclude_iCCP=MagickTrue;
13052 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13053 mng_info->ping_exclude_oFFs=MagickTrue;
13054 mng_info->ping_exclude_pHYs=MagickTrue;
13055 mng_info->ping_exclude_sRGB=MagickTrue;
13056 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013057 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013058 mng_info->ping_exclude_vpAg=MagickTrue;
13059 mng_info->ping_exclude_zCCP=MagickTrue;
13060 mng_info->ping_exclude_zTXt=MagickTrue;
13061
cristy018f07f2011-09-04 21:15:19 +000013062 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013063 }
13064
13065 if (status == MagickFalse)
13066 {
13067 MngInfoFreeStruct(mng_info,&have_mng_structure);
13068 (void) CloseBlob(image);
13069 return(MagickFalse);
13070 }
13071 (void) CatchImageException(image);
13072 if (GetNextImageInList(image) == (Image *) NULL)
13073 break;
13074 image=SyncNextImageInList(image);
13075 status=SetImageProgress(image,SaveImagesTag,scene++,
13076 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013077
cristy3ed852e2009-09-05 21:47:34 +000013078 if (status == MagickFalse)
13079 break;
glennrp0fe50b42010-11-16 03:52:51 +000013080
cristy3ed852e2009-09-05 21:47:34 +000013081 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013082
cristy3ed852e2009-09-05 21:47:34 +000013083 if (write_mng)
13084 {
13085 while (GetPreviousImageInList(image) != (Image *) NULL)
13086 image=GetPreviousImageInList(image);
13087 /*
13088 Write the MEND chunk.
13089 */
13090 (void) WriteBlobMSBULong(image,0x00000000L);
13091 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013092 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013093 (void) WriteBlob(image,4,chunk);
13094 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13095 }
13096 /*
13097 Relinquish resources.
13098 */
13099 (void) CloseBlob(image);
13100 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013101
cristy3ed852e2009-09-05 21:47:34 +000013102 if (logging != MagickFalse)
13103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013104
cristy3ed852e2009-09-05 21:47:34 +000013105 return(MagickTrue);
13106}
glennrpd5045b42010-03-24 12:40:35 +000013107#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013108
cristy3ed852e2009-09-05 21:47:34 +000013109static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13110{
13111 image=image;
13112 printf("Your PNG library is too old: You have libpng-%s\n",
13113 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013114
cristy3ed852e2009-09-05 21:47:34 +000013115 ThrowBinaryException(CoderError,"PNG library is too old",
13116 image_info->filename);
13117}
glennrp39992b42010-11-14 00:03:43 +000013118
cristy3ed852e2009-09-05 21:47:34 +000013119static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13120{
13121 return(WritePNGImage(image_info,image));
13122}
glennrpd5045b42010-03-24 12:40:35 +000013123#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013124#endif