blob: bd5782b3ca4954827da03e4dfcd38bad7cda4b49 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP N N GGGG %
7% P P NN N G %
8% PPPP N N N G GG %
9% P N NN G G %
10% P N N GGG %
11% %
12% %
13% Read/Write Portable Network Graphics Image Format %
14% %
15% Software Design %
16% John Cristy %
17% Glenn Randers-Pehrson %
18% November 1997 %
19% %
20% %
cristy7e41fe82010-12-04 23:12:08 +000021% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000022% dedicated to making software imaging solutions freely available. %
23% %
24% You may not use this file except in compliance with the License. You may %
25% obtain a copy of the License at %
26% %
27% http://www.imagemagick.org/script/license.php %
28% %
29% Unless required by applicable law or agreed to in writing, software %
30% distributed under the License is distributed on an "AS IS" BASIS, %
31% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32% See the License for the specific language governing permissions and %
33% limitations under the License. %
34% %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38*/
39
40
41/*
42 Include declarations.
43*/
cristy4c08aed2011-07-01 19:47:50 +000044#include "MagickCore/studio.h"
45#include "MagickCore/artifact.h"
46#include "MagickCore/attribute.h"
47#include "MagickCore/blob.h"
48#include "MagickCore/blob-private.h"
49#include "MagickCore/cache.h"
50#include "MagickCore/color.h"
51#include "MagickCore/color-private.h"
52#include "MagickCore/colormap.h"
53#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000054#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000055#include "MagickCore/constitute.h"
56#include "MagickCore/enhance.h"
57#include "MagickCore/exception.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/geometry.h"
60#include "MagickCore/histogram.h"
61#include "MagickCore/image.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/layer.h"
64#include "MagickCore/list.h"
65#include "MagickCore/log.h"
66#include "MagickCore/MagickCore.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/module.h"
69#include "MagickCore/monitor.h"
70#include "MagickCore/monitor-private.h"
71#include "MagickCore/option.h"
72#include "MagickCore/pixel.h"
73#include "MagickCore/pixel-accessor.h"
74#include "MagickCore/profile.h"
75#include "MagickCore/property.h"
76#include "MagickCore/quantum-private.h"
77#include "MagickCore/resource_.h"
78#include "MagickCore/semaphore.h"
79#include "MagickCore/quantum-private.h"
80#include "MagickCore/static.h"
81#include "MagickCore/statistic.h"
82#include "MagickCore/string_.h"
83#include "MagickCore/string-private.h"
84#include "MagickCore/transform.h"
85#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000086#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp286a6352009-11-09 02:58:50 +000087
glennrp7ef138c2009-11-10 13:50:20 +000088/* Suppress libpng pedantic warnings that were added in
89 * libpng-1.2.41 and libpng-1.4.0. If you are working on
glennrpfaa852b2010-03-30 12:17:00 +000090 * migration to libpng-1.5, remove these defines and then
glennrp7ef138c2009-11-10 13:50:20 +000091 * fix any code that generates warnings.
92 */
glennrp991e92a2010-01-28 03:09:00 +000093/* #define PNG_DEPRECATED Use of this function is deprecated */
glennrpfaa852b2010-03-30 12:17:00 +000094/* #define PNG_USE_RESULT The result of this function must be checked */
95/* #define PNG_NORETURN This function does not return */
96/* #define PNG_ALLOCATED The result of the function is new memory */
glennrp8371ecc2011-02-19 19:15:38 +000097/* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
glennrp5b927342011-04-14 14:31:48 +000098
99/* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
cristy75cfe702011-04-14 14:27:17 +0000100#define PNG_PTR_NORETURN
glennrp286a6352009-11-09 02:58:50 +0000101
cristy3ed852e2009-09-05 21:47:34 +0000102#include "png.h"
103#include "zlib.h"
104
105/* ImageMagick differences */
106#define first_scene scene
107
glennrpd5045b42010-03-24 12:40:35 +0000108#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000109/*
110 Optional declarations. Define or undefine them as you like.
111*/
112/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
113
114/*
115 Features under construction. Define these to work on them.
116*/
117#undef MNG_OBJECT_BUFFERS
118#undef MNG_BASI_SUPPORTED
119#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
120#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
cristy3ed852e2009-09-05 21:47:34 +0000121#if defined(MAGICKCORE_JPEG_DELEGATE)
122# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
123#endif
124#if !defined(RGBColorMatchExact)
125#define IsPNGColorEqual(color,target) \
glennrp8e045c82011-04-27 16:40:27 +0000126 (((color).red == (target).red) && \
127 ((color).green == (target).green) && \
128 ((color).blue == (target).blue))
cristy3ed852e2009-09-05 21:47:34 +0000129#endif
130
glennrp8e58efd2011-05-20 12:16:29 +0000131/* Macros for left-bit-replication to ensure that pixels
132 * and PixelPackets all have the image->depth, and for use
133 * in PNG8 quantization.
134 */
135
136
137/* LBR01: Replicate top bit */
138
glennrp05001c32011-08-06 13:04:16 +0000139#define LBR01PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000140 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
141 0 : QuantumRange);
142
glennrp91d99252011-06-25 14:30:13 +0000143#define LBR01PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000144 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
145 0 : QuantumRange);
146
glennrp91d99252011-06-25 14:30:13 +0000147#define LBR01PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000148 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
149 0 : QuantumRange);
150
cristy4c08aed2011-07-01 19:47:50 +0000151#define LBR01PacketAlpha(pixelpacket) \
152 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000153 0 : QuantumRange);
154
glennrp91d99252011-06-25 14:30:13 +0000155#define LBR01PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000156 { \
glennrp05001c32011-08-06 13:04:16 +0000157 LBR01PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000158 LBR01PacketGreen((pixelpacket)); \
159 LBR01PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000160 }
glennrp8e58efd2011-05-20 12:16:29 +0000161
glennrp91d99252011-06-25 14:30:13 +0000162#define LBR01PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000163 { \
glennrp91d99252011-06-25 14:30:13 +0000164 LBR01PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000165 LBR01PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000166 }
glennrp8e58efd2011-05-20 12:16:29 +0000167
cristyef618312011-06-25 12:26:44 +0000168#define LBR01PixelRed(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000169 (ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000170 0 : QuantumRange);
171
glennrp54cf7972011-08-06 14:28:09 +0000172#define LBR01PixelGreen(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000173 (ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000174 0 : QuantumRange);
175
glennrp54cf7972011-08-06 14:28:09 +0000176#define LBR01PixelBlue(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000177 (ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000178 0 : QuantumRange);
179
glennrp54cf7972011-08-06 14:28:09 +0000180#define LBR01PixelAlpha(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000181 (ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000182 0 : QuantumRange);
183
glennrp54cf7972011-08-06 14:28:09 +0000184#define LBR01PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000185 { \
cristyef618312011-06-25 12:26:44 +0000186 LBR01PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000187 LBR01PixelGreen((pixel)); \
188 LBR01PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000189 }
glennrp8e58efd2011-05-20 12:16:29 +0000190
glennrp54cf7972011-08-06 14:28:09 +0000191#define LBR01PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000192 { \
glennrp54cf7972011-08-06 14:28:09 +0000193 LBR01PixelRGB((pixel)); \
194 LBR01PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000195 }
glennrp8e58efd2011-05-20 12:16:29 +0000196
197/* LBR02: Replicate top 2 bits */
198
glennrp05001c32011-08-06 13:04:16 +0000199#define LBR02PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000200 { \
201 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
202 (pixelpacket).red=ScaleCharToQuantum( \
203 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
204 }
glennrp91d99252011-06-25 14:30:13 +0000205#define LBR02PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000206 { \
207 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
208 (pixelpacket).green=ScaleCharToQuantum( \
209 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
210 }
glennrp91d99252011-06-25 14:30:13 +0000211#define LBR02PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000212 { \
213 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
214 (pixelpacket).blue=ScaleCharToQuantum( \
215 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
216 }
cristy4c08aed2011-07-01 19:47:50 +0000217#define LBR02PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000218 { \
cristy4c08aed2011-07-01 19:47:50 +0000219 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
220 (pixelpacket).alpha=ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000221 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
222 }
223
glennrp91d99252011-06-25 14:30:13 +0000224#define LBR02PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000225 { \
glennrp05001c32011-08-06 13:04:16 +0000226 LBR02PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000227 LBR02PacketGreen((pixelpacket)); \
228 LBR02PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000229 }
glennrp8e58efd2011-05-20 12:16:29 +0000230
glennrp91d99252011-06-25 14:30:13 +0000231#define LBR02PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000232 { \
glennrp91d99252011-06-25 14:30:13 +0000233 LBR02PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000234 LBR02PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000235 }
glennrp8e58efd2011-05-20 12:16:29 +0000236
cristyef618312011-06-25 12:26:44 +0000237#define LBR02PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000238 { \
cristy4c08aed2011-07-01 19:47:50 +0000239 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000240 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000241 SetPixelRed(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000242 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
243 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000244 }
glennrp54cf7972011-08-06 14:28:09 +0000245#define LBR02PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000246 { \
cristy4c08aed2011-07-01 19:47:50 +0000247 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000248 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000249 SetPixelGreen(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000250 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
251 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000252 }
glennrp54cf7972011-08-06 14:28:09 +0000253#define LBR02PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000254 { \
255 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000256 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
257 SetPixelBlue(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000258 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
259 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000260 }
glennrp54cf7972011-08-06 14:28:09 +0000261#define LBR02PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000262 { \
263 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000264 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
265 SetPixelAlpha(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000266 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
267 (pixel) ); \
glennrp8e58efd2011-05-20 12:16:29 +0000268 }
269
glennrp54cf7972011-08-06 14:28:09 +0000270#define LBR02PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000271 { \
cristyef618312011-06-25 12:26:44 +0000272 LBR02PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000273 LBR02PixelGreen((pixel)); \
274 LBR02PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000275 }
glennrp8e58efd2011-05-20 12:16:29 +0000276
glennrp54cf7972011-08-06 14:28:09 +0000277#define LBR02PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000278 { \
glennrp54cf7972011-08-06 14:28:09 +0000279 LBR02PixelRGB((pixel)); \
280 LBR02PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000281 }
glennrp8e58efd2011-05-20 12:16:29 +0000282
283/* LBR03: Replicate top 3 bits (only used with opaque pixels during
284 PNG8 quantization) */
285
glennrp05001c32011-08-06 13:04:16 +0000286#define LBR03PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000287 { \
288 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
289 (pixelpacket).red=ScaleCharToQuantum( \
290 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
291 }
glennrp91d99252011-06-25 14:30:13 +0000292#define LBR03PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000293 { \
294 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
295 (pixelpacket).green=ScaleCharToQuantum( \
296 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
297 }
glennrp91d99252011-06-25 14:30:13 +0000298#define LBR03PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000299 { \
300 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
301 (pixelpacket).blue=ScaleCharToQuantum( \
302 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
303 }
304
glennrp91d99252011-06-25 14:30:13 +0000305#define LBR03PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000306 { \
glennrp05001c32011-08-06 13:04:16 +0000307 LBR03PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000308 LBR03PacketGreen((pixelpacket)); \
309 LBR03PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000310 }
glennrp8e58efd2011-05-20 12:16:29 +0000311
cristyef618312011-06-25 12:26:44 +0000312#define LBR03PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000313 { \
cristy4c08aed2011-07-01 19:47:50 +0000314 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000315 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000316 SetPixelRed(image, ScaleCharToQuantum( \
317 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000318 }
cristy4c08aed2011-07-01 19:47:50 +0000319#define LBR03Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000320 { \
cristy4c08aed2011-07-01 19:47:50 +0000321 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000322 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000323 SetPixelGreen(image, ScaleCharToQuantum( \
324 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000325 }
cristy4c08aed2011-07-01 19:47:50 +0000326#define LBR03Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000327 { \
cristy4c08aed2011-07-01 19:47:50 +0000328 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000329 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000330 SetPixelBlue(image, ScaleCharToQuantum( \
331 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000332 }
333
cristy4c08aed2011-07-01 19:47:50 +0000334#define LBR03RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000335 { \
cristyef618312011-06-25 12:26:44 +0000336 LBR03PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000337 LBR03Green((pixel)); \
338 LBR03Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000339 }
glennrp8e58efd2011-05-20 12:16:29 +0000340
341/* LBR04: Replicate top 4 bits */
342
glennrp05001c32011-08-06 13:04:16 +0000343#define LBR04PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000344 { \
345 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
346 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
347 }
glennrp91d99252011-06-25 14:30:13 +0000348#define LBR04PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000349 { \
350 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
351 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
352 }
glennrp91d99252011-06-25 14:30:13 +0000353#define LBR04PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000354 { \
355 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
356 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
357 }
cristy4c08aed2011-07-01 19:47:50 +0000358#define LBR04PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000359 { \
cristy4c08aed2011-07-01 19:47:50 +0000360 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
361 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
glennrp8e58efd2011-05-20 12:16:29 +0000362 }
363
glennrp91d99252011-06-25 14:30:13 +0000364#define LBR04PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000365 { \
glennrp05001c32011-08-06 13:04:16 +0000366 LBR04PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000367 LBR04PacketGreen((pixelpacket)); \
368 LBR04PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000369 }
glennrp8e58efd2011-05-20 12:16:29 +0000370
glennrp91d99252011-06-25 14:30:13 +0000371#define LBR04PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000372 { \
glennrp91d99252011-06-25 14:30:13 +0000373 LBR04PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000374 LBR04PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000375 }
glennrp8e58efd2011-05-20 12:16:29 +0000376
cristyef618312011-06-25 12:26:44 +0000377#define LBR04PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000378 { \
cristy4c08aed2011-07-01 19:47:50 +0000379 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000380 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000381 SetPixelRed(image,\
382 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000383 }
glennrp54cf7972011-08-06 14:28:09 +0000384#define LBR04PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000385 { \
cristy4c08aed2011-07-01 19:47:50 +0000386 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000387 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000388 SetPixelGreen(image,\
389 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000390 }
glennrp54cf7972011-08-06 14:28:09 +0000391#define LBR04PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000392 { \
393 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000394 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
395 SetPixelBlue(image,\
396 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000397 }
glennrp54cf7972011-08-06 14:28:09 +0000398#define LBR04PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000399 { \
400 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000401 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
402 SetPixelAlpha(image,\
403 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000404 }
405
glennrp54cf7972011-08-06 14:28:09 +0000406#define LBR04PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000407 { \
cristyef618312011-06-25 12:26:44 +0000408 LBR04PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000409 LBR04PixelGreen((pixel)); \
410 LBR04PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000411 }
glennrp8e58efd2011-05-20 12:16:29 +0000412
glennrp54cf7972011-08-06 14:28:09 +0000413#define LBR04PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000414 { \
glennrp54cf7972011-08-06 14:28:09 +0000415 LBR04PixelRGB((pixel)); \
416 LBR04PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000417 }
glennrp8e58efd2011-05-20 12:16:29 +0000418
419
420/* LBR08: Replicate top 8 bits */
421
glennrp05001c32011-08-06 13:04:16 +0000422#define LBR08PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000423 { \
424 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
425 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
426 }
glennrp91d99252011-06-25 14:30:13 +0000427#define LBR08PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000428 { \
429 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
430 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
431 }
glennrp91d99252011-06-25 14:30:13 +0000432#define LBR08PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000433 { \
434 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
435 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
436 }
cristy4c08aed2011-07-01 19:47:50 +0000437#define LBR08PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000438 { \
cristy4c08aed2011-07-01 19:47:50 +0000439 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
440 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000441 }
442
glennrp91d99252011-06-25 14:30:13 +0000443#define LBR08PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000444 { \
glennrp05001c32011-08-06 13:04:16 +0000445 LBR08PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000446 LBR08PacketGreen((pixelpacket)); \
447 LBR08PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000448 }
glennrp8e58efd2011-05-20 12:16:29 +0000449
glennrp91d99252011-06-25 14:30:13 +0000450#define LBR08PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000451 { \
glennrp91d99252011-06-25 14:30:13 +0000452 LBR08PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000453 LBR08PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000454 }
glennrp8e58efd2011-05-20 12:16:29 +0000455
cristyef618312011-06-25 12:26:44 +0000456#define LBR08PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000457 { \
458 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000459 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
460 SetPixelRed(image,\
461 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000462 }
glennrp54cf7972011-08-06 14:28:09 +0000463#define LBR08PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000464 { \
465 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000466 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
467 SetPixelGreen(image,\
468 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000469 }
glennrp54cf7972011-08-06 14:28:09 +0000470#define LBR08PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000471 { \
472 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000473 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
474 SetPixelBlue(image,\
475 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000476 }
glennrp54cf7972011-08-06 14:28:09 +0000477#define LBR08PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000478 { \
479 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000480 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
481 SetPixelAlpha(image,\
482 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000483 }
484
glennrp54cf7972011-08-06 14:28:09 +0000485#define LBR08PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000486 { \
cristyef618312011-06-25 12:26:44 +0000487 LBR08PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000488 LBR08PixelGreen((pixel)); \
489 LBR08PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000490 }
glennrp8e58efd2011-05-20 12:16:29 +0000491
glennrp54cf7972011-08-06 14:28:09 +0000492#define LBR08PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000493 { \
glennrp54cf7972011-08-06 14:28:09 +0000494 LBR08PixelRGB((pixel)); \
495 LBR08PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000496 }
glennrp8e58efd2011-05-20 12:16:29 +0000497
498
499/* LBR16: Replicate top 16 bits */
500
glennrp05001c32011-08-06 13:04:16 +0000501#define LBR16PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000502 { \
503 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
504 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
505 }
glennrp91d99252011-06-25 14:30:13 +0000506#define LBR16PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000507 { \
508 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
509 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
510 }
glennrp91d99252011-06-25 14:30:13 +0000511#define LBR16PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000512 { \
513 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
514 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
515 }
cristy4c08aed2011-07-01 19:47:50 +0000516#define LBR16PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000517 { \
cristy4c08aed2011-07-01 19:47:50 +0000518 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
519 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000520 }
521
glennrp91d99252011-06-25 14:30:13 +0000522#define LBR16PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000523 { \
glennrp05001c32011-08-06 13:04:16 +0000524 LBR16PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000525 LBR16PacketGreen((pixelpacket)); \
526 LBR16PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000527 }
glennrp8e58efd2011-05-20 12:16:29 +0000528
glennrp91d99252011-06-25 14:30:13 +0000529#define LBR16PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000530 { \
glennrp91d99252011-06-25 14:30:13 +0000531 LBR16PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000532 LBR16PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000533 }
glennrp8e58efd2011-05-20 12:16:29 +0000534
cristyef618312011-06-25 12:26:44 +0000535#define LBR16PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000536 { \
537 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000538 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
539 SetPixelRed(image,\
540 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000541 }
glennrp54cf7972011-08-06 14:28:09 +0000542#define LBR16PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000543 { \
544 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000545 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
546 SetPixelGreen(image,\
547 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000548 }
glennrp54cf7972011-08-06 14:28:09 +0000549#define LBR16PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000550 { \
551 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000552 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
553 SetPixelBlue(image,\
554 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000555 }
glennrp54cf7972011-08-06 14:28:09 +0000556#define LBR16PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000557 { \
558 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000559 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
560 SetPixelAlpha(image,\
561 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000562 }
563
glennrp54cf7972011-08-06 14:28:09 +0000564#define LBR16PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000565 { \
cristyef618312011-06-25 12:26:44 +0000566 LBR16PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000567 LBR16PixelGreen((pixel)); \
568 LBR16PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000569 }
glennrp8e58efd2011-05-20 12:16:29 +0000570
glennrp54cf7972011-08-06 14:28:09 +0000571#define LBR16PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000572 { \
glennrp54cf7972011-08-06 14:28:09 +0000573 LBR16PixelRGB((pixel)); \
574 LBR16PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000575 }
glennrp8e58efd2011-05-20 12:16:29 +0000576
cristy3ed852e2009-09-05 21:47:34 +0000577/*
578 Establish thread safety.
579 setjmp/longjmp is claimed to be safe on these platforms:
580 setjmp/longjmp is alleged to be unsafe on these platforms:
581*/
582#ifndef SETJMP_IS_THREAD_SAFE
583#define PNG_SETJMP_NOT_THREAD_SAFE
584#endif
585
586#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
587static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000588 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000589#endif
590
591/*
592 This temporary until I set up malloc'ed object attributes array.
593 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
594 waste more memory.
595*/
596#define MNG_MAX_OBJECTS 256
597
598/*
599 If this not defined, spec is interpreted strictly. If it is
600 defined, an attempt will be made to recover from some errors,
601 including
602 o global PLTE too short
603*/
604#undef MNG_LOOSE
605
606/*
607 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
608 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
609 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
610 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
611 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
612 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
613 will be enabled by default in libpng-1.2.0.
614*/
cristy3ed852e2009-09-05 21:47:34 +0000615#ifdef PNG_MNG_FEATURES_SUPPORTED
616# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
617# define PNG_READ_EMPTY_PLTE_SUPPORTED
618# endif
619# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
620# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
621# endif
622#endif
623
624/*
cristybb503372010-05-27 20:51:26 +0000625 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000626 This macro is only defined in libpng-1.0.3 and later.
627 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
628*/
629#ifndef PNG_UINT_31_MAX
630#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
631#endif
632
633/*
634 Constant strings for known chunk types. If you need to add a chunk,
635 add a string holding the name here. To make the code more
636 portable, we use ASCII numbers like this, not characters.
637*/
638
639static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
640static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
641static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
642static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
643static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
644static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
645static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
646static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
647static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
648static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
649static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
650static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
651static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
652static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
653static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
654static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
655static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
656static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
657static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
658static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
659static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
660static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
661static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
662static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
663static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
664static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
665static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
666static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
667static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
668static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
669static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
670static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
671static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
672static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
673
674#if defined(JNG_SUPPORTED)
675static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
676static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
677static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
678static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
679static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
680static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
681#endif
682
683/*
684Other known chunks that are not yet supported by ImageMagick:
685static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
686static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
687static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
688static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
689static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
690static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
691static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
692static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
693*/
694
695typedef struct _MngBox
696{
cristy8182b072010-05-30 20:10:53 +0000697 long
cristy3ed852e2009-09-05 21:47:34 +0000698 left,
699 right,
700 top,
701 bottom;
702} MngBox;
703
704typedef struct _MngPair
705{
cristy8182b072010-05-30 20:10:53 +0000706 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000707 a,
708 b;
709} MngPair;
710
711#ifdef MNG_OBJECT_BUFFERS
712typedef struct _MngBuffer
713{
714
cristybb503372010-05-27 20:51:26 +0000715 size_t
cristy3ed852e2009-09-05 21:47:34 +0000716 height,
717 width;
718
719 Image
720 *image;
721
722 png_color
723 plte[256];
724
725 int
726 reference_count;
727
728 unsigned char
729 alpha_sample_depth,
730 compression_method,
731 color_type,
732 concrete,
733 filter_method,
734 frozen,
735 image_type,
736 interlace_method,
737 pixel_sample_depth,
738 plte_length,
739 sample_depth,
740 viewable;
741} MngBuffer;
742#endif
743
744typedef struct _MngInfo
745{
746
747#ifdef MNG_OBJECT_BUFFERS
748 MngBuffer
749 *ob[MNG_MAX_OBJECTS];
750#endif
751
752 Image *
753 image;
754
755 RectangleInfo
756 page;
757
758 int
759 adjoin,
760#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
761 bytes_in_read_buffer,
762 found_empty_plte,
763#endif
764 equal_backgrounds,
765 equal_chrms,
766 equal_gammas,
767#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
768 defined(PNG_MNG_FEATURES_SUPPORTED)
769 equal_palettes,
770#endif
771 equal_physs,
772 equal_srgbs,
773 framing_mode,
774 have_global_bkgd,
775 have_global_chrm,
776 have_global_gama,
777 have_global_phys,
778 have_global_sbit,
779 have_global_srgb,
780 have_saved_bkgd_index,
781 have_write_global_chrm,
782 have_write_global_gama,
783 have_write_global_plte,
784 have_write_global_srgb,
785 need_fram,
786 object_id,
787 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000788 saved_bkgd_index;
789
790 int
791 new_number_colors;
792
cristybb503372010-05-27 20:51:26 +0000793 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000794 image_found,
795 loop_count[256],
796 loop_iteration[256],
797 scenes_found,
798 x_off[MNG_MAX_OBJECTS],
799 y_off[MNG_MAX_OBJECTS];
800
801 MngBox
802 clip,
803 frame,
804 image_box,
805 object_clip[MNG_MAX_OBJECTS];
806
807 unsigned char
808 /* These flags could be combined into one byte */
809 exists[MNG_MAX_OBJECTS],
810 frozen[MNG_MAX_OBJECTS],
811 loop_active[256],
812 invisible[MNG_MAX_OBJECTS],
813 viewable[MNG_MAX_OBJECTS];
814
815 MagickOffsetType
816 loop_jump[256];
817
818 png_colorp
819 global_plte;
820
821 png_color_8
822 global_sbit;
823
824 png_byte
825#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
826 read_buffer[8],
827#endif
828 global_trns[256];
829
830 float
831 global_gamma;
832
833 ChromaticityInfo
834 global_chrm;
835
836 RenderingIntent
837 global_srgb_intent;
838
cristy35ef8242010-06-03 16:24:13 +0000839 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000840 delay,
841 global_plte_length,
842 global_trns_length,
843 global_x_pixels_per_unit,
844 global_y_pixels_per_unit,
845 mng_width,
846 mng_height,
847 ticks_per_second;
848
glennrpb9cfe272010-12-21 15:08:06 +0000849 MagickBooleanType
850 need_blob;
851
cristy3ed852e2009-09-05 21:47:34 +0000852 unsigned int
853 IsPalette,
854 global_phys_unit_type,
855 basi_warning,
856 clon_warning,
857 dhdr_warning,
858 jhdr_warning,
859 magn_warning,
860 past_warning,
861 phyg_warning,
862 phys_warning,
863 sbit_warning,
864 show_warning,
865 mng_type,
866 write_mng,
867 write_png_colortype,
868 write_png_depth,
glennrp18682582011-06-30 18:11:47 +0000869 write_png_compression_level,
870 write_png_compression_strategy,
871 write_png_compression_filter,
cristy3ed852e2009-09-05 21:47:34 +0000872 write_png8,
873 write_png24,
874 write_png32;
875
876#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000877 size_t
cristy3ed852e2009-09-05 21:47:34 +0000878 basi_width,
879 basi_height;
880
881 unsigned int
882 basi_depth,
883 basi_color_type,
884 basi_compression_method,
885 basi_filter_type,
886 basi_interlace_method,
887 basi_red,
888 basi_green,
889 basi_blue,
890 basi_alpha,
891 basi_viewable;
892#endif
893
894 png_uint_16
895 magn_first,
896 magn_last,
897 magn_mb,
898 magn_ml,
899 magn_mr,
900 magn_mt,
901 magn_mx,
902 magn_my,
903 magn_methx,
904 magn_methy;
905
906 PixelPacket
907 mng_global_bkgd;
908
glennrp26f37912010-12-23 16:22:42 +0000909 /* Added at version 6.6.6-7 */
910 MagickBooleanType
911 ping_exclude_bKGD,
912 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000913 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000914 ping_exclude_EXIF,
915 ping_exclude_gAMA,
916 ping_exclude_iCCP,
917 /* ping_exclude_iTXt, */
918 ping_exclude_oFFs,
919 ping_exclude_pHYs,
920 ping_exclude_sRGB,
921 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000922 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000923 ping_exclude_vpAg,
924 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000925 ping_exclude_zTXt,
926 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000927
cristy3ed852e2009-09-05 21:47:34 +0000928} MngInfo;
929#endif /* VER */
930
931/*
932 Forward declarations.
933*/
934static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000935 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000936
cristy3ed852e2009-09-05 21:47:34 +0000937static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000938 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000939
cristy3ed852e2009-09-05 21:47:34 +0000940#if defined(JNG_SUPPORTED)
941static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000942 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000943#endif
944
glennrp0c3e06b2010-11-19 13:45:02 +0000945#if PNG_LIBPNG_VER > 10011
946
glennrpfd05d622011-02-25 04:10:33 +0000947
glennrp0c3e06b2010-11-19 13:45:02 +0000948#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
949static MagickBooleanType
glennrpfd05d622011-02-25 04:10:33 +0000950LosslessReduceDepthOK(Image *image)
glennrp0c3e06b2010-11-19 13:45:02 +0000951{
glennrp67b9c1a2011-04-22 18:47:36 +0000952 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
953 *
954 * This is true if the high byte and the next highest byte of
955 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000956 * are equal to each other. We check this by seeing if the samples
957 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000958 *
959 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000960 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000961 */
962
glennrp3faa9a32011-04-23 14:00:25 +0000963#define QuantumToCharToQuantumEqQuantum(quantum) \
964 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
965
glennrp0c3e06b2010-11-19 13:45:02 +0000966 MagickBooleanType
967 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000968
glennrp03e11f62011-04-22 13:30:16 +0000969 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000970 {
971
cristy4c08aed2011-07-01 19:47:50 +0000972 const Quantum
glennrp0c3e06b2010-11-19 13:45:02 +0000973 *p;
974
975 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000976 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
977 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
978 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
979 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000980
981 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
982 {
983 int indx;
984
985 for (indx=0; indx < (ssize_t) image->colors; indx++)
986 {
glennrp3faa9a32011-04-23 14:00:25 +0000987 ok_to_reduce=(
988 QuantumToCharToQuantumEqQuantum(
989 image->colormap[indx].red) &&
990 QuantumToCharToQuantumEqQuantum(
991 image->colormap[indx].green) &&
992 QuantumToCharToQuantumEqQuantum(
993 image->colormap[indx].blue)) ?
994 MagickTrue : MagickFalse;
995
glennrp0c3e06b2010-11-19 13:45:02 +0000996 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000997 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000998 }
999 }
1000
1001 if ((ok_to_reduce != MagickFalse) &&
1002 (image->storage_class != PseudoClass))
1003 {
1004 ssize_t
1005 y;
1006
1007 register ssize_t
1008 x;
1009
1010 for (y=0; y < (ssize_t) image->rows; y++)
1011 {
1012 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1013
cristy4c08aed2011-07-01 19:47:50 +00001014 if (p == (const Quantum *) NULL)
glennrp0c3e06b2010-11-19 13:45:02 +00001015 {
1016 ok_to_reduce = MagickFalse;
1017 break;
1018 }
1019
1020 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1021 {
glennrp3faa9a32011-04-23 14:00:25 +00001022 ok_to_reduce=
cristy4c08aed2011-07-01 19:47:50 +00001023 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1024 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1025 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
glennrp3faa9a32011-04-23 14:00:25 +00001026 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001027
1028 if (ok_to_reduce == MagickFalse)
1029 break;
1030
cristyed231572011-07-14 02:18:59 +00001031 p+=GetPixelChannels(image);
glennrp0c3e06b2010-11-19 13:45:02 +00001032 }
glennrp8640fb52010-11-23 15:48:26 +00001033 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +00001034 break;
1035 }
1036 }
1037
1038 if (ok_to_reduce != MagickFalse)
1039 {
glennrp0c3e06b2010-11-19 13:45:02 +00001040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001041 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +00001042 }
glennrpa6a06632011-01-19 15:15:34 +00001043 else
1044 {
1045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001046 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +00001047 }
glennrp0c3e06b2010-11-19 13:45:02 +00001048 }
1049
1050 return ok_to_reduce;
1051}
1052#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1053
glennrpe610a072010-08-05 17:08:46 +00001054static int
glennrpcf002022011-01-30 02:38:15 +00001055Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +00001056{
glennrpe610a072010-08-05 17:08:46 +00001057 switch (intent)
1058 {
1059 case PerceptualIntent:
1060 return 0;
glennrp0fe50b42010-11-16 03:52:51 +00001061
glennrpe610a072010-08-05 17:08:46 +00001062 case RelativeIntent:
1063 return 1;
glennrp0fe50b42010-11-16 03:52:51 +00001064
glennrpe610a072010-08-05 17:08:46 +00001065 case SaturationIntent:
1066 return 2;
glennrp0fe50b42010-11-16 03:52:51 +00001067
glennrpe610a072010-08-05 17:08:46 +00001068 case AbsoluteIntent:
1069 return 3;
glennrp0fe50b42010-11-16 03:52:51 +00001070
glennrpe610a072010-08-05 17:08:46 +00001071 default:
1072 return -1;
1073 }
1074}
1075
1076static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +00001077Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +00001078{
glennrpcf002022011-01-30 02:38:15 +00001079 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001080 {
1081 case 0:
1082 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001083
glennrpe610a072010-08-05 17:08:46 +00001084 case 1:
1085 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001086
glennrpe610a072010-08-05 17:08:46 +00001087 case 2:
1088 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001089
glennrpe610a072010-08-05 17:08:46 +00001090 case 3:
1091 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001092
glennrpe610a072010-08-05 17:08:46 +00001093 default:
1094 return UndefinedIntent;
1095 }
1096}
1097
cristybb503372010-05-27 20:51:26 +00001098static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001099{
1100 if (x > y)
1101 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001102
cristy3ed852e2009-09-05 21:47:34 +00001103 return(y);
1104}
glennrp0c3e06b2010-11-19 13:45:02 +00001105
cristybb503372010-05-27 20:51:26 +00001106static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001107{
1108 if (x < y)
1109 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001110
cristy3ed852e2009-09-05 21:47:34 +00001111 return(y);
1112}
glennrp0c3e06b2010-11-19 13:45:02 +00001113
cristy3ed852e2009-09-05 21:47:34 +00001114
1115/*
1116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1117% %
1118% %
1119% %
1120% I m a g e I s G r a y %
1121% %
1122% %
1123% %
1124%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1125% %
cristy4c08aed2011-07-01 19:47:50 +00001126% Like IsImageGray except does not change DirectClass to PseudoClass %
cristy3ed852e2009-09-05 21:47:34 +00001127% %
1128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1129*/
1130static MagickBooleanType ImageIsGray(Image *image)
1131{
cristy4c08aed2011-07-01 19:47:50 +00001132 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001133 *p;
1134
cristybb503372010-05-27 20:51:26 +00001135 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001136 i,
1137 x,
1138 y;
1139
1140 assert(image != (Image *) NULL);
1141 assert(image->signature == MagickSignature);
1142 if (image->debug != MagickFalse)
1143 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1144
1145 if (image->storage_class == PseudoClass)
1146 {
cristybb503372010-05-27 20:51:26 +00001147 for (i=0; i < (ssize_t) image->colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00001148 if (IsPixelPacketGray(image->colormap+i) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001149 return(MagickFalse);
1150 return(MagickTrue);
1151 }
cristybb503372010-05-27 20:51:26 +00001152 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001153 {
1154 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00001155 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001156 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +00001157 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001158 {
cristy4c08aed2011-07-01 19:47:50 +00001159 if (IsPixelGray(image,p) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001160 return(MagickFalse);
cristyed231572011-07-14 02:18:59 +00001161 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001162 }
1163 }
1164 return(MagickTrue);
1165}
glennrpd5045b42010-03-24 12:40:35 +00001166#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001167#endif /* MAGICKCORE_PNG_DELEGATE */
1168
1169/*
1170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1171% %
1172% %
1173% %
1174% I s M N G %
1175% %
1176% %
1177% %
1178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1179%
1180% IsMNG() returns MagickTrue if the image format type, identified by the
1181% magick string, is MNG.
1182%
1183% The format of the IsMNG method is:
1184%
1185% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1186%
1187% A description of each parameter follows:
1188%
1189% o magick: compare image format pattern against these bytes.
1190%
1191% o length: Specifies the length of the magick string.
1192%
1193%
1194*/
1195static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1196{
1197 if (length < 8)
1198 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001199
cristy3ed852e2009-09-05 21:47:34 +00001200 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1201 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001202
cristy3ed852e2009-09-05 21:47:34 +00001203 return(MagickFalse);
1204}
1205
1206/*
1207%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1208% %
1209% %
1210% %
1211% I s J N G %
1212% %
1213% %
1214% %
1215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1216%
1217% IsJNG() returns MagickTrue if the image format type, identified by the
1218% magick string, is JNG.
1219%
1220% The format of the IsJNG method is:
1221%
1222% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1223%
1224% A description of each parameter follows:
1225%
1226% o magick: compare image format pattern against these bytes.
1227%
1228% o length: Specifies the length of the magick string.
1229%
1230%
1231*/
1232static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1233{
1234 if (length < 8)
1235 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001236
cristy3ed852e2009-09-05 21:47:34 +00001237 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1238 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001239
cristy3ed852e2009-09-05 21:47:34 +00001240 return(MagickFalse);
1241}
1242
1243/*
1244%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245% %
1246% %
1247% %
1248% I s P N G %
1249% %
1250% %
1251% %
1252%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1253%
1254% IsPNG() returns MagickTrue if the image format type, identified by the
1255% magick string, is PNG.
1256%
1257% The format of the IsPNG method is:
1258%
1259% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1260%
1261% A description of each parameter follows:
1262%
1263% o magick: compare image format pattern against these bytes.
1264%
1265% o length: Specifies the length of the magick string.
1266%
1267*/
1268static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1269{
1270 if (length < 8)
1271 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001272
cristy3ed852e2009-09-05 21:47:34 +00001273 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1274 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001275
cristy3ed852e2009-09-05 21:47:34 +00001276 return(MagickFalse);
1277}
1278
1279#if defined(MAGICKCORE_PNG_DELEGATE)
1280#if defined(__cplusplus) || defined(c_plusplus)
1281extern "C" {
1282#endif
1283
glennrpd5045b42010-03-24 12:40:35 +00001284#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001285static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001286{
1287 unsigned char
1288 buffer[4];
1289
1290 assert(image != (Image *) NULL);
1291 assert(image->signature == MagickSignature);
1292 buffer[0]=(unsigned char) (value >> 24);
1293 buffer[1]=(unsigned char) (value >> 16);
1294 buffer[2]=(unsigned char) (value >> 8);
1295 buffer[3]=(unsigned char) value;
1296 return((size_t) WriteBlob(image,4,buffer));
1297}
1298
1299static void PNGLong(png_bytep p,png_uint_32 value)
1300{
1301 *p++=(png_byte) ((value >> 24) & 0xff);
1302 *p++=(png_byte) ((value >> 16) & 0xff);
1303 *p++=(png_byte) ((value >> 8) & 0xff);
1304 *p++=(png_byte) (value & 0xff);
1305}
1306
glennrpa521b2f2010-10-29 04:11:03 +00001307#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001308static void PNGsLong(png_bytep p,png_int_32 value)
1309{
1310 *p++=(png_byte) ((value >> 24) & 0xff);
1311 *p++=(png_byte) ((value >> 16) & 0xff);
1312 *p++=(png_byte) ((value >> 8) & 0xff);
1313 *p++=(png_byte) (value & 0xff);
1314}
glennrpa521b2f2010-10-29 04:11:03 +00001315#endif
cristy3ed852e2009-09-05 21:47:34 +00001316
1317static void PNGShort(png_bytep p,png_uint_16 value)
1318{
1319 *p++=(png_byte) ((value >> 8) & 0xff);
1320 *p++=(png_byte) (value & 0xff);
1321}
1322
1323static void PNGType(png_bytep p,png_bytep type)
1324{
1325 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1326}
1327
glennrp03812ae2010-12-24 01:31:34 +00001328static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1329 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001330{
1331 if (logging != MagickFalse)
1332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001333 " Writing %c%c%c%c chunk, length: %.20g",
1334 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001335}
glennrpd5045b42010-03-24 12:40:35 +00001336#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001337
1338#if defined(__cplusplus) || defined(c_plusplus)
1339}
1340#endif
1341
glennrpd5045b42010-03-24 12:40:35 +00001342#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001343/*
1344%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1345% %
1346% %
1347% %
1348% R e a d P N G I m a g e %
1349% %
1350% %
1351% %
1352%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1353%
1354% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1355% Multiple-image Network Graphics (MNG) image file and returns it. It
1356% allocates the memory necessary for the new Image structure and returns a
1357% pointer to the new image or set of images.
1358%
1359% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1360%
1361% The format of the ReadPNGImage method is:
1362%
1363% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1364%
1365% A description of each parameter follows:
1366%
1367% o image_info: the image info.
1368%
1369% o exception: return any errors or warnings in this structure.
1370%
1371% To do, more or less in chronological order (as of version 5.5.2,
1372% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1373%
1374% Get 16-bit cheap transparency working.
1375%
1376% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1377%
1378% Preserve all unknown and not-yet-handled known chunks found in input
1379% PNG file and copy them into output PNG files according to the PNG
1380% copying rules.
1381%
1382% (At this point, PNG encoding should be in full MNG compliance)
1383%
1384% Provide options for choice of background to use when the MNG BACK
1385% chunk is not present or is not mandatory (i.e., leave transparent,
1386% user specified, MNG BACK, PNG bKGD)
1387%
1388% Implement LOOP/ENDL [done, but could do discretionary loops more
1389% efficiently by linking in the duplicate frames.].
1390%
1391% Decode and act on the MHDR simplicity profile (offer option to reject
1392% files or attempt to process them anyway when the profile isn't LC or VLC).
1393%
1394% Upgrade to full MNG without Delta-PNG.
1395%
1396% o BACK [done a while ago except for background image ID]
1397% o MOVE [done 15 May 1999]
1398% o CLIP [done 15 May 1999]
1399% o DISC [done 19 May 1999]
1400% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1401% o SEEK [partially done 19 May 1999 (discard function only)]
1402% o SHOW
1403% o PAST
1404% o BASI
1405% o MNG-level tEXt/iTXt/zTXt
1406% o pHYg
1407% o pHYs
1408% o sBIT
1409% o bKGD
1410% o iTXt (wait for libpng implementation).
1411%
1412% Use the scene signature to discover when an identical scene is
1413% being reused, and just point to the original image->exception instead
1414% of storing another set of pixels. This not specific to MNG
1415% but could be applied generally.
1416%
1417% Upgrade to full MNG with Delta-PNG.
1418%
1419% JNG tEXt/iTXt/zTXt
1420%
1421% We will not attempt to read files containing the CgBI chunk.
1422% They are really Xcode files meant for display on the iPhone.
1423% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001424% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001425% since irretrievable loss of color data has occurred due to the
1426% use of premultiplied alpha.
1427*/
1428
1429#if defined(__cplusplus) || defined(c_plusplus)
1430extern "C" {
1431#endif
1432
1433/*
1434 This the function that does the actual reading of data. It is
1435 the same as the one supplied in libpng, except that it receives the
1436 datastream from the ReadBlob() function instead of standard input.
1437*/
1438static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1439{
1440 Image
1441 *image;
1442
1443 image=(Image *) png_get_io_ptr(png_ptr);
1444 if (length)
1445 {
1446 png_size_t
1447 check;
1448
1449 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1450 if (check != length)
1451 {
1452 char
1453 msg[MaxTextExtent];
1454
cristy3b6fd2e2011-05-20 12:53:50 +00001455 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001456 "Expected %.20g bytes; found %.20g bytes",(double) length,
1457 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001458 png_warning(png_ptr,msg);
1459 png_error(png_ptr,"Read Exception");
1460 }
1461 }
1462}
1463
1464#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1465 !defined(PNG_MNG_FEATURES_SUPPORTED)
1466/* We use mng_get_data() instead of png_get_data() if we have a libpng
1467 * older than libpng-1.0.3a, which was the first to allow the empty
1468 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1469 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1470 * encountered after an empty PLTE, so we have to look ahead for bKGD
1471 * chunks and remove them from the datastream that is passed to libpng,
1472 * and store their contents for later use.
1473 */
1474static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1475{
1476 MngInfo
1477 *mng_info;
1478
1479 Image
1480 *image;
1481
1482 png_size_t
1483 check;
1484
cristybb503372010-05-27 20:51:26 +00001485 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001486 i;
1487
1488 i=0;
1489 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1490 image=(Image *) mng_info->image;
1491 while (mng_info->bytes_in_read_buffer && length)
1492 {
1493 data[i]=mng_info->read_buffer[i];
1494 mng_info->bytes_in_read_buffer--;
1495 length--;
1496 i++;
1497 }
1498 if (length)
1499 {
1500 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001501
cristy3ed852e2009-09-05 21:47:34 +00001502 if (check != length)
1503 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001504
cristy3ed852e2009-09-05 21:47:34 +00001505 if (length == 4)
1506 {
1507 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1508 (data[3] == 0))
1509 {
1510 check=(png_size_t) ReadBlob(image,(size_t) length,
1511 (char *) mng_info->read_buffer);
1512 mng_info->read_buffer[4]=0;
1513 mng_info->bytes_in_read_buffer=4;
1514 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1515 mng_info->found_empty_plte=MagickTrue;
1516 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1517 {
1518 mng_info->found_empty_plte=MagickFalse;
1519 mng_info->have_saved_bkgd_index=MagickFalse;
1520 }
1521 }
glennrp0fe50b42010-11-16 03:52:51 +00001522
cristy3ed852e2009-09-05 21:47:34 +00001523 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1524 (data[3] == 1))
1525 {
1526 check=(png_size_t) ReadBlob(image,(size_t) length,
1527 (char *) mng_info->read_buffer);
1528 mng_info->read_buffer[4]=0;
1529 mng_info->bytes_in_read_buffer=4;
1530 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1531 if (mng_info->found_empty_plte)
1532 {
1533 /*
1534 Skip the bKGD data byte and CRC.
1535 */
1536 check=(png_size_t)
1537 ReadBlob(image,5,(char *) mng_info->read_buffer);
1538 check=(png_size_t) ReadBlob(image,(size_t) length,
1539 (char *) mng_info->read_buffer);
1540 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1541 mng_info->have_saved_bkgd_index=MagickTrue;
1542 mng_info->bytes_in_read_buffer=0;
1543 }
1544 }
1545 }
1546 }
1547}
1548#endif
1549
1550static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1551{
1552 Image
1553 *image;
1554
1555 image=(Image *) png_get_io_ptr(png_ptr);
1556 if (length)
1557 {
1558 png_size_t
1559 check;
1560
cristybb503372010-05-27 20:51:26 +00001561 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001562
cristy3ed852e2009-09-05 21:47:34 +00001563 if (check != length)
1564 png_error(png_ptr,"WriteBlob Failed");
1565 }
1566}
1567
1568static void png_flush_data(png_structp png_ptr)
1569{
1570 (void) png_ptr;
1571}
1572
1573#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1574static int PalettesAreEqual(Image *a,Image *b)
1575{
cristybb503372010-05-27 20:51:26 +00001576 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001577 i;
1578
1579 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1580 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001581
cristy3ed852e2009-09-05 21:47:34 +00001582 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1583 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001584
cristy3ed852e2009-09-05 21:47:34 +00001585 if (a->colors != b->colors)
1586 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001587
cristybb503372010-05-27 20:51:26 +00001588 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001589 {
1590 if ((a->colormap[i].red != b->colormap[i].red) ||
1591 (a->colormap[i].green != b->colormap[i].green) ||
1592 (a->colormap[i].blue != b->colormap[i].blue))
1593 return((int) MagickFalse);
1594 }
glennrp0fe50b42010-11-16 03:52:51 +00001595
cristy3ed852e2009-09-05 21:47:34 +00001596 return((int) MagickTrue);
1597}
1598#endif
1599
1600static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1601{
1602 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1603 mng_info->exists[i] && !mng_info->frozen[i])
1604 {
1605#ifdef MNG_OBJECT_BUFFERS
1606 if (mng_info->ob[i] != (MngBuffer *) NULL)
1607 {
1608 if (mng_info->ob[i]->reference_count > 0)
1609 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001610
cristy3ed852e2009-09-05 21:47:34 +00001611 if (mng_info->ob[i]->reference_count == 0)
1612 {
1613 if (mng_info->ob[i]->image != (Image *) NULL)
1614 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001615
cristy3ed852e2009-09-05 21:47:34 +00001616 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1617 }
1618 }
1619 mng_info->ob[i]=(MngBuffer *) NULL;
1620#endif
1621 mng_info->exists[i]=MagickFalse;
1622 mng_info->invisible[i]=MagickFalse;
1623 mng_info->viewable[i]=MagickFalse;
1624 mng_info->frozen[i]=MagickFalse;
1625 mng_info->x_off[i]=0;
1626 mng_info->y_off[i]=0;
1627 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001628 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001629 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001630 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001631 }
1632}
1633
glennrp21f0e622011-01-07 16:20:57 +00001634static void MngInfoFreeStruct(MngInfo *mng_info,
1635 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001636{
glennrp21f0e622011-01-07 16:20:57 +00001637 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001638 {
cristybb503372010-05-27 20:51:26 +00001639 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001640 i;
1641
1642 for (i=1; i < MNG_MAX_OBJECTS; i++)
1643 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001644
cristy3ed852e2009-09-05 21:47:34 +00001645 if (mng_info->global_plte != (png_colorp) NULL)
1646 mng_info->global_plte=(png_colorp)
1647 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001648
cristy3ed852e2009-09-05 21:47:34 +00001649 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1650 *have_mng_structure=MagickFalse;
1651 }
1652}
1653
1654static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1655{
1656 MngBox
1657 box;
1658
1659 box=box1;
1660 if (box.left < box2.left)
1661 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001662
cristy3ed852e2009-09-05 21:47:34 +00001663 if (box.top < box2.top)
1664 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001665
cristy3ed852e2009-09-05 21:47:34 +00001666 if (box.right > box2.right)
1667 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001668
cristy3ed852e2009-09-05 21:47:34 +00001669 if (box.bottom > box2.bottom)
1670 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001671
cristy3ed852e2009-09-05 21:47:34 +00001672 return box;
1673}
1674
1675static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1676{
1677 MngBox
1678 box;
1679
1680 /*
1681 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1682 */
cristybb503372010-05-27 20:51:26 +00001683 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1684 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1685 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1686 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001687 if (delta_type != 0)
1688 {
1689 box.left+=previous_box.left;
1690 box.right+=previous_box.right;
1691 box.top+=previous_box.top;
1692 box.bottom+=previous_box.bottom;
1693 }
glennrp0fe50b42010-11-16 03:52:51 +00001694
cristy3ed852e2009-09-05 21:47:34 +00001695 return(box);
1696}
1697
1698static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1699 unsigned char *p)
1700{
1701 MngPair
1702 pair;
1703 /*
cristybb503372010-05-27 20:51:26 +00001704 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001705 */
cristy8182b072010-05-30 20:10:53 +00001706 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1707 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001708
cristy3ed852e2009-09-05 21:47:34 +00001709 if (delta_type != 0)
1710 {
1711 pair.a+=previous_pair.a;
1712 pair.b+=previous_pair.b;
1713 }
glennrp0fe50b42010-11-16 03:52:51 +00001714
cristy3ed852e2009-09-05 21:47:34 +00001715 return(pair);
1716}
1717
cristy8182b072010-05-30 20:10:53 +00001718static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001719{
cristy8182b072010-05-30 20:10:53 +00001720 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001721}
1722
glennrpcf002022011-01-30 02:38:15 +00001723static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001724{
1725 Image
1726 *image;
1727
1728 image=(Image *) png_get_error_ptr(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001729
cristy3ed852e2009-09-05 21:47:34 +00001730 if (image->debug != MagickFalse)
1731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1732 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001733
cristy3ed852e2009-09-05 21:47:34 +00001734 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1735 message,"`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001736
glennrpe4017e32011-01-08 17:16:09 +00001737#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001738 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1739 * are building with libpng-1.4.x and can be ignored.
1740 */
cristy3ed852e2009-09-05 21:47:34 +00001741 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001742#else
1743 png_longjmp(ping,1);
1744#endif
cristy3ed852e2009-09-05 21:47:34 +00001745}
1746
glennrpcf002022011-01-30 02:38:15 +00001747static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001748{
1749 Image
1750 *image;
1751
1752 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1753 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001754
cristy3ed852e2009-09-05 21:47:34 +00001755 image=(Image *) png_get_error_ptr(ping);
1756 if (image->debug != MagickFalse)
1757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristycc23b9a2010-05-09 22:37:43 +00001758 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001759
cristy3ed852e2009-09-05 21:47:34 +00001760 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1761 message,"`%s'",image->filename);
1762}
1763
1764#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001765static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001766{
1767#if (PNG_LIBPNG_VER < 10011)
1768 png_voidp
1769 ret;
1770
1771 png_ptr=png_ptr;
1772 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001773
cristy3ed852e2009-09-05 21:47:34 +00001774 if (ret == NULL)
1775 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001776
cristy3ed852e2009-09-05 21:47:34 +00001777 return(ret);
1778#else
1779 png_ptr=png_ptr;
1780 return((png_voidp) AcquireMagickMemory((size_t) size));
1781#endif
1782}
1783
1784/*
1785 Free a pointer. It is removed from the list at the same time.
1786*/
glennrpcf002022011-01-30 02:38:15 +00001787static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001788{
1789 png_ptr=png_ptr;
1790 ptr=RelinquishMagickMemory(ptr);
1791 return((png_free_ptr) NULL);
1792}
1793#endif
1794
1795#if defined(__cplusplus) || defined(c_plusplus)
1796}
1797#endif
1798
1799static int
glennrpcf002022011-01-30 02:38:15 +00001800Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristy3ed852e2009-09-05 21:47:34 +00001801 png_textp text,int ii)
1802{
cristybb503372010-05-27 20:51:26 +00001803 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001804 i;
1805
1806 register unsigned char
1807 *dp;
1808
1809 register png_charp
1810 sp;
1811
1812 png_uint_32
1813 length,
1814 nibbles;
1815
1816 StringInfo
1817 *profile;
1818
glennrp0c3e06b2010-11-19 13:45:02 +00001819 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001820 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1821 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1822 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1823 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1824 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1825 13,14,15};
1826
1827 sp=text[ii].text+1;
1828 /* look for newline */
1829 while (*sp != '\n')
1830 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001831
cristy3ed852e2009-09-05 21:47:34 +00001832 /* look for length */
1833 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1834 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001835
cristyf2f27272009-12-17 14:48:46 +00001836 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001837
glennrp97f90e22011-02-22 05:47:58 +00001838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1839 " length: %lu",(unsigned long) length);
1840
cristy3ed852e2009-09-05 21:47:34 +00001841 while (*sp != ' ' && *sp != '\n')
1842 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001843
cristy3ed852e2009-09-05 21:47:34 +00001844 /* allocate space */
1845 if (length == 0)
1846 {
1847 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1848 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1849 return(MagickFalse);
1850 }
glennrp0fe50b42010-11-16 03:52:51 +00001851
cristy3ed852e2009-09-05 21:47:34 +00001852 profile=AcquireStringInfo(length);
glennrp0fe50b42010-11-16 03:52:51 +00001853
cristy3ed852e2009-09-05 21:47:34 +00001854 if (profile == (StringInfo *) NULL)
1855 {
1856 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1857 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1858 "unable to copy profile");
1859 return(MagickFalse);
1860 }
glennrp0fe50b42010-11-16 03:52:51 +00001861
cristy3ed852e2009-09-05 21:47:34 +00001862 /* copy profile, skipping white space and column 1 "=" signs */
1863 dp=GetStringInfoDatum(profile);
1864 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001865
cristybb503372010-05-27 20:51:26 +00001866 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001867 {
1868 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1869 {
1870 if (*sp == '\0')
1871 {
1872 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1873 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1874 profile=DestroyStringInfo(profile);
1875 return(MagickFalse);
1876 }
1877 sp++;
1878 }
glennrp0fe50b42010-11-16 03:52:51 +00001879
cristy3ed852e2009-09-05 21:47:34 +00001880 if (i%2 == 0)
1881 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001882
cristy3ed852e2009-09-05 21:47:34 +00001883 else
1884 (*dp++)+=unhex[(int) *sp++];
1885 }
1886 /*
1887 We have already read "Raw profile type.
1888 */
1889 (void) SetImageProfile(image,&text[ii].key[17],profile);
1890 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001891
cristy3ed852e2009-09-05 21:47:34 +00001892 if (image_info->verbose)
1893 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001894
cristy3ed852e2009-09-05 21:47:34 +00001895 return MagickTrue;
1896}
1897
1898#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1899static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1900{
1901 Image
1902 *image;
1903
1904
1905 /* The unknown chunk structure contains the chunk data:
1906 png_byte name[5];
1907 png_byte *data;
1908 png_size_t size;
1909
1910 Note that libpng has already taken care of the CRC handling.
1911 */
1912
1913
1914 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1915 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1916 return(0); /* Did not recognize */
1917
1918 /* recognized vpAg */
1919
1920 if (chunk->size != 9)
1921 return(-1); /* Error return */
1922
1923 if (chunk->data[8] != 0)
1924 return(0); /* ImageMagick requires pixel units */
1925
1926 image=(Image *) png_get_user_chunk_ptr(ping);
1927
cristybb503372010-05-27 20:51:26 +00001928 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001929 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001930
cristybb503372010-05-27 20:51:26 +00001931 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001932 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1933
1934 /* Return one of the following: */
1935 /* return(-n); chunk had an error */
1936 /* return(0); did not recognize */
1937 /* return(n); success */
1938
1939 return(1);
1940
1941}
1942#endif
1943
1944/*
1945%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1946% %
1947% %
1948% %
1949% R e a d O n e P N G I m a g e %
1950% %
1951% %
1952% %
1953%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1954%
1955% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1956% (minus the 8-byte signature) and returns it. It allocates the memory
1957% necessary for the new Image structure and returns a pointer to the new
1958% image.
1959%
1960% The format of the ReadOnePNGImage method is:
1961%
1962% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1963% ExceptionInfo *exception)
1964%
1965% A description of each parameter follows:
1966%
1967% o mng_info: Specifies a pointer to a MngInfo structure.
1968%
1969% o image_info: the image info.
1970%
1971% o exception: return any errors or warnings in this structure.
1972%
1973*/
1974static Image *ReadOnePNGImage(MngInfo *mng_info,
1975 const ImageInfo *image_info, ExceptionInfo *exception)
1976{
1977 /* Read one PNG image */
1978
glennrpcc95c3f2011-04-18 16:46:48 +00001979 /* To do: Read the tIME chunk into the date:modify property */
1980 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1981
cristy3ed852e2009-09-05 21:47:34 +00001982 Image
1983 *image;
1984
1985 int
glennrp4eb39312011-03-30 21:34:55 +00001986 intent,
glennrpcb395ac2011-03-30 19:50:23 +00001987 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00001988 num_text,
glennrp4eb39312011-03-30 21:34:55 +00001989 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00001990 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00001991 pass,
1992 ping_bit_depth,
1993 ping_color_type,
1994 ping_interlace_method,
1995 ping_compression_method,
1996 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00001997 ping_num_trans,
1998 unit_type;
1999
2000 double
2001 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002002
glennrpa6a06632011-01-19 15:15:34 +00002003 LongPixelPacket
2004 transparent_color;
2005
cristy3ed852e2009-09-05 21:47:34 +00002006 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002007 logging,
cristy3ed852e2009-09-05 21:47:34 +00002008 status;
2009
glennrpfaa852b2010-03-30 12:17:00 +00002010 png_bytep
2011 ping_trans_alpha;
2012
2013 png_color_16p
2014 ping_background,
2015 ping_trans_color;
2016
cristy3ed852e2009-09-05 21:47:34 +00002017 png_info
2018 *end_info,
2019 *ping_info;
2020
2021 png_struct
2022 *ping;
2023
2024 png_textp
2025 text;
2026
glennrpfaa852b2010-03-30 12:17:00 +00002027 png_uint_32
2028 ping_height,
2029 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002030 ping_rowbytes,
2031 x_resolution,
2032 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002033
cristy3ed852e2009-09-05 21:47:34 +00002034 QuantumInfo
2035 *quantum_info;
2036
2037 unsigned char
glennrpcf002022011-01-30 02:38:15 +00002038 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002039
cristybb503372010-05-27 20:51:26 +00002040 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002041 y;
2042
2043 register unsigned char
2044 *p;
2045
cristybb503372010-05-27 20:51:26 +00002046 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002047 i,
2048 x;
2049
cristy4c08aed2011-07-01 19:47:50 +00002050 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002051 *q;
2052
2053 size_t
glennrp39992b42010-11-14 00:03:43 +00002054 length,
cristy3ed852e2009-09-05 21:47:34 +00002055 row_offset;
2056
cristyeb3b22a2011-03-31 20:16:11 +00002057 ssize_t
2058 j;
2059
cristy3ed852e2009-09-05 21:47:34 +00002060#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2061 png_byte unused_chunks[]=
2062 {
2063 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2064 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2065 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2066 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2067 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2068 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2069 };
2070#endif
2071
2072 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002073 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002074
2075#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002076 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002077#endif
2078
glennrp25c1e2b2010-03-25 01:39:56 +00002079#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002080 if (image_info->verbose)
2081 printf("Your PNG library (libpng-%s) is rather old.\n",
2082 PNG_LIBPNG_VER_STRING);
2083#endif
2084
glennrp61b4c952009-11-10 20:40:41 +00002085#if (PNG_LIBPNG_VER >= 10400)
2086# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2087 if (image_info->verbose)
2088 {
2089 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2090 PNG_LIBPNG_VER_STRING);
2091 printf("Please update it.\n");
2092 }
2093# endif
2094#endif
2095
2096
cristyed552522009-10-16 14:04:35 +00002097 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002098 image=mng_info->image;
2099
glennrpa6a06632011-01-19 15:15:34 +00002100 if (logging != MagickFalse)
2101 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2102 " image->matte=%d",(int) image->matte);
2103
glennrp0e319732011-01-25 21:53:13 +00002104 /* Set to an out-of-range color unless tRNS chunk is present */
2105 transparent_color.red=65537;
2106 transparent_color.green=65537;
2107 transparent_color.blue=65537;
cristy4c08aed2011-07-01 19:47:50 +00002108 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002109
glennrpcb395ac2011-03-30 19:50:23 +00002110 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002111 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002112 num_raw_profiles = 0;
2113
cristy3ed852e2009-09-05 21:47:34 +00002114 /*
2115 Allocate the PNG structures
2116 */
2117#ifdef PNG_USER_MEM_SUPPORTED
2118 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
glennrpcf002022011-01-30 02:38:15 +00002119 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2120 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002121#else
2122 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00002123 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002124#endif
2125 if (ping == (png_struct *) NULL)
2126 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002127
cristy3ed852e2009-09-05 21:47:34 +00002128 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002129
cristy3ed852e2009-09-05 21:47:34 +00002130 if (ping_info == (png_info *) NULL)
2131 {
2132 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2133 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2134 }
glennrp0fe50b42010-11-16 03:52:51 +00002135
cristy3ed852e2009-09-05 21:47:34 +00002136 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002137
cristy3ed852e2009-09-05 21:47:34 +00002138 if (end_info == (png_info *) NULL)
2139 {
2140 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2141 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2142 }
glennrp0fe50b42010-11-16 03:52:51 +00002143
glennrpcf002022011-01-30 02:38:15 +00002144 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002145
glennrpfaa852b2010-03-30 12:17:00 +00002146 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002147 {
2148 /*
2149 PNG image is corrupt.
2150 */
2151 png_destroy_read_struct(&ping,&ping_info,&end_info);
2152#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002153 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002154#endif
2155 if (logging != MagickFalse)
2156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2157 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002158
cristy3ed852e2009-09-05 21:47:34 +00002159 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002160 {
2161 InheritException(exception,&image->exception);
2162 image->columns=0;
2163 }
glennrp0fe50b42010-11-16 03:52:51 +00002164
cristy3ed852e2009-09-05 21:47:34 +00002165 return(GetFirstImageInList(image));
2166 }
2167 /*
2168 Prepare PNG for reading.
2169 */
glennrpfaa852b2010-03-30 12:17:00 +00002170
cristy3ed852e2009-09-05 21:47:34 +00002171 mng_info->image_found++;
2172 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002173
cristy3ed852e2009-09-05 21:47:34 +00002174 if (LocaleCompare(image_info->magick,"MNG") == 0)
2175 {
2176#if defined(PNG_MNG_FEATURES_SUPPORTED)
2177 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2178 png_set_read_fn(ping,image,png_get_data);
2179#else
2180#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2181 png_permit_empty_plte(ping,MagickTrue);
2182 png_set_read_fn(ping,image,png_get_data);
2183#else
2184 mng_info->image=image;
2185 mng_info->bytes_in_read_buffer=0;
2186 mng_info->found_empty_plte=MagickFalse;
2187 mng_info->have_saved_bkgd_index=MagickFalse;
2188 png_set_read_fn(ping,mng_info,mng_get_data);
2189#endif
2190#endif
2191 }
glennrp0fe50b42010-11-16 03:52:51 +00002192
cristy3ed852e2009-09-05 21:47:34 +00002193 else
2194 png_set_read_fn(ping,image,png_get_data);
2195
2196#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2197 /* Ignore unused chunks and all unknown chunks except for vpAg */
2198 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2199 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2200 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2201 (int)sizeof(unused_chunks)/5);
2202 /* Callback for other unknown chunks */
2203 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2204#endif
2205
glennrp991e92a2010-01-28 03:09:00 +00002206#if (PNG_LIBPNG_VER < 10400)
2207# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2208 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002209 /* Disable thread-unsafe features of pnggccrd */
2210 if (png_access_version_number() >= 10200)
2211 {
2212 png_uint_32 mmx_disable_mask=0;
2213 png_uint_32 asm_flags;
2214
2215 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2216 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2217 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2218 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2219 asm_flags=png_get_asm_flags(ping);
2220 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2221 }
glennrp991e92a2010-01-28 03:09:00 +00002222# endif
cristy3ed852e2009-09-05 21:47:34 +00002223#endif
2224
2225 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002226
2227 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2228 &ping_bit_depth,&ping_color_type,
2229 &ping_interlace_method,&ping_compression_method,
2230 &ping_filter_method);
2231
2232 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2233 &ping_trans_color);
2234
2235 (void) png_get_bKGD(ping, ping_info, &ping_background);
2236
2237 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002238 {
glennrpfaa852b2010-03-30 12:17:00 +00002239 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2240 {
2241 png_set_packing(ping);
2242 ping_bit_depth = 8;
2243 }
cristy3ed852e2009-09-05 21:47:34 +00002244 }
glennrpfaa852b2010-03-30 12:17:00 +00002245
2246 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002247 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002248 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00002249 if (logging != MagickFalse)
2250 {
2251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002252 " PNG width: %.20g, height: %.20g",
2253 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002254
cristy3ed852e2009-09-05 21:47:34 +00002255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2256 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002257 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002258
cristy3ed852e2009-09-05 21:47:34 +00002259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2260 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002261 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002262
cristy3ed852e2009-09-05 21:47:34 +00002263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2264 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002265 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002266 }
2267
glennrpfaa852b2010-03-30 12:17:00 +00002268#ifdef PNG_READ_iCCP_SUPPORTED
2269 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002270 {
2271 int
2272 compression;
2273
glennrpe4017e32011-01-08 17:16:09 +00002274#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002275 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002276 info;
2277#else
2278 png_bytep
2279 info;
2280#endif
2281
2282 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002283 name;
2284
2285 png_uint_32
2286 profile_length;
2287
2288 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2289 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002290
cristy3ed852e2009-09-05 21:47:34 +00002291 if (profile_length != 0)
2292 {
2293 StringInfo
2294 *profile;
2295
2296 if (logging != MagickFalse)
2297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2298 " Reading PNG iCCP chunk.");
2299 profile=AcquireStringInfo(profile_length);
2300 SetStringInfoDatum(profile,(const unsigned char *) info);
2301 (void) SetImageProfile(image,"icc",profile);
2302 profile=DestroyStringInfo(profile);
2303 }
2304 }
2305#endif
2306#if defined(PNG_READ_sRGB_SUPPORTED)
2307 {
cristy3ed852e2009-09-05 21:47:34 +00002308 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00002309 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2310 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00002311
cristy3ed852e2009-09-05 21:47:34 +00002312 if (png_get_sRGB(ping,ping_info,&intent))
2313 {
glennrpcf002022011-01-30 02:38:15 +00002314 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2315 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002316
cristy3ed852e2009-09-05 21:47:34 +00002317 if (logging != MagickFalse)
2318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002319 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002320 }
2321 }
2322#endif
2323 {
glennrpfaa852b2010-03-30 12:17:00 +00002324 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2325 if (mng_info->have_global_gama)
2326 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002327
cristy3ed852e2009-09-05 21:47:34 +00002328 if (png_get_gAMA(ping,ping_info,&file_gamma))
2329 {
2330 image->gamma=(float) file_gamma;
2331 if (logging != MagickFalse)
2332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2333 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2334 }
2335 }
glennrpfaa852b2010-03-30 12:17:00 +00002336 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2337 {
2338 if (mng_info->have_global_chrm != MagickFalse)
2339 {
2340 (void) png_set_cHRM(ping,ping_info,
2341 mng_info->global_chrm.white_point.x,
2342 mng_info->global_chrm.white_point.y,
2343 mng_info->global_chrm.red_primary.x,
2344 mng_info->global_chrm.red_primary.y,
2345 mng_info->global_chrm.green_primary.x,
2346 mng_info->global_chrm.green_primary.y,
2347 mng_info->global_chrm.blue_primary.x,
2348 mng_info->global_chrm.blue_primary.y);
2349 }
2350 }
glennrp0fe50b42010-11-16 03:52:51 +00002351
glennrpfaa852b2010-03-30 12:17:00 +00002352 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002353 {
2354 (void) png_get_cHRM(ping,ping_info,
2355 &image->chromaticity.white_point.x,
2356 &image->chromaticity.white_point.y,
2357 &image->chromaticity.red_primary.x,
2358 &image->chromaticity.red_primary.y,
2359 &image->chromaticity.green_primary.x,
2360 &image->chromaticity.green_primary.y,
2361 &image->chromaticity.blue_primary.x,
2362 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002363
cristy3ed852e2009-09-05 21:47:34 +00002364 if (logging != MagickFalse)
2365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2366 " Reading PNG cHRM chunk.");
2367 }
glennrp0fe50b42010-11-16 03:52:51 +00002368
glennrpe610a072010-08-05 17:08:46 +00002369 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002370 {
glennrpe610a072010-08-05 17:08:46 +00002371 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002372 Magick_RenderingIntent_to_PNG_RenderingIntent
2373 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00002374 png_set_gAMA(ping,ping_info,0.45455f);
2375 png_set_cHRM(ping,ping_info,
2376 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2377 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002378 }
cristy3ed852e2009-09-05 21:47:34 +00002379#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002380 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002381 {
cristy905ef802011-02-23 00:29:18 +00002382 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2383 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002384
cristy3ed852e2009-09-05 21:47:34 +00002385 if (logging != MagickFalse)
2386 if (image->page.x || image->page.y)
2387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002388 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2389 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002390 }
2391#endif
2392#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002393 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2394 {
2395 if (mng_info->have_global_phys)
2396 {
2397 png_set_pHYs(ping,ping_info,
2398 mng_info->global_x_pixels_per_unit,
2399 mng_info->global_y_pixels_per_unit,
2400 mng_info->global_phys_unit_type);
2401 }
2402 }
2403
2404 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002405 {
cristy3ed852e2009-09-05 21:47:34 +00002406 /*
2407 Set image resolution.
2408 */
2409 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002410 &unit_type);
2411 image->x_resolution=(double) x_resolution;
2412 image->y_resolution=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002413
cristy3ed852e2009-09-05 21:47:34 +00002414 if (unit_type == PNG_RESOLUTION_METER)
2415 {
2416 image->units=PixelsPerCentimeterResolution;
2417 image->x_resolution=(double) x_resolution/100.0;
2418 image->y_resolution=(double) y_resolution/100.0;
2419 }
glennrp0fe50b42010-11-16 03:52:51 +00002420
cristy3ed852e2009-09-05 21:47:34 +00002421 if (logging != MagickFalse)
2422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002423 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2424 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002425 }
cristy3ed852e2009-09-05 21:47:34 +00002426#endif
glennrp823b55c2011-03-14 18:46:46 +00002427
glennrpfaa852b2010-03-30 12:17:00 +00002428 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002429 {
2430 int
2431 number_colors;
2432
2433 png_colorp
2434 palette;
2435
2436 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002437
cristy3ed852e2009-09-05 21:47:34 +00002438 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002439 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002440 {
2441 if (mng_info->global_plte_length)
2442 {
2443 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2444 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002445
glennrpfaa852b2010-03-30 12:17:00 +00002446 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002447 if (mng_info->global_trns_length)
2448 {
2449 if (mng_info->global_trns_length >
2450 mng_info->global_plte_length)
2451 (void) ThrowMagickException(&image->exception,
2452 GetMagickModule(),CoderError,
2453 "global tRNS has more entries than global PLTE",
2454 "`%s'",image_info->filename);
2455 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2456 (int) mng_info->global_trns_length,NULL);
2457 }
glennrpbfd9e612011-04-22 14:02:20 +00002458#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002459 if (
2460#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2461 mng_info->have_saved_bkgd_index ||
2462#endif
glennrpfaa852b2010-03-30 12:17:00 +00002463 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002464 {
2465 png_color_16
2466 background;
2467
2468#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2469 if (mng_info->have_saved_bkgd_index)
2470 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002471#endif
glennrpfaa852b2010-03-30 12:17:00 +00002472 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2473 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002474
cristy3ed852e2009-09-05 21:47:34 +00002475 background.red=(png_uint_16)
2476 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002477
cristy3ed852e2009-09-05 21:47:34 +00002478 background.green=(png_uint_16)
2479 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002480
cristy3ed852e2009-09-05 21:47:34 +00002481 background.blue=(png_uint_16)
2482 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002483
glennrpc6c391a2011-04-27 02:23:56 +00002484 background.gray=(png_uint_16)
2485 mng_info->global_plte[background.index].green;
2486
cristy3ed852e2009-09-05 21:47:34 +00002487 png_set_bKGD(ping,ping_info,&background);
2488 }
2489#endif
2490 }
2491 else
2492 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2493 CoderError,"No global PLTE in file","`%s'",
2494 image_info->filename);
2495 }
2496 }
2497
glennrpbfd9e612011-04-22 14:02:20 +00002498#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002499 if (mng_info->have_global_bkgd &&
2500 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002501 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002502
glennrpfaa852b2010-03-30 12:17:00 +00002503 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002504 {
glennrpbfd9e612011-04-22 14:02:20 +00002505 unsigned int
2506 bkgd_scale;
2507
cristy3ed852e2009-09-05 21:47:34 +00002508 /*
2509 Set image background color.
2510 */
2511 if (logging != MagickFalse)
2512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2513 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002514
glennrpbfd9e612011-04-22 14:02:20 +00002515 /* Scale background components to 16-bit, then scale
2516 * to quantum depth
2517 */
2518 if (logging != MagickFalse)
2519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2520 " raw ping_background=(%d,%d,%d).",ping_background->red,
2521 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002522
glennrpbfd9e612011-04-22 14:02:20 +00002523 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002524
glennrpbfd9e612011-04-22 14:02:20 +00002525 if (ping_bit_depth == 1)
2526 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002527
glennrpbfd9e612011-04-22 14:02:20 +00002528 else if (ping_bit_depth == 2)
2529 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002530
glennrpbfd9e612011-04-22 14:02:20 +00002531 else if (ping_bit_depth == 4)
2532 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002533
glennrpbfd9e612011-04-22 14:02:20 +00002534 if (ping_bit_depth <= 8)
2535 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002536
glennrpbfd9e612011-04-22 14:02:20 +00002537 ping_background->red *= bkgd_scale;
2538 ping_background->green *= bkgd_scale;
2539 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002540
glennrpbfd9e612011-04-22 14:02:20 +00002541 if (logging != MagickFalse)
2542 {
glennrp2cbb4482010-06-02 04:37:24 +00002543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2544 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002545
glennrp2cbb4482010-06-02 04:37:24 +00002546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2547 " ping_background=(%d,%d,%d).",ping_background->red,
2548 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002549 }
glennrp2cbb4482010-06-02 04:37:24 +00002550
glennrpbfd9e612011-04-22 14:02:20 +00002551 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002552 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002553
glennrpbfd9e612011-04-22 14:02:20 +00002554 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002555 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002556
glennrpbfd9e612011-04-22 14:02:20 +00002557 image->background_color.blue=
2558 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002559
cristy4c08aed2011-07-01 19:47:50 +00002560 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002561
glennrpbfd9e612011-04-22 14:02:20 +00002562 if (logging != MagickFalse)
2563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2564 " image->background_color=(%.20g,%.20g,%.20g).",
2565 (double) image->background_color.red,
2566 (double) image->background_color.green,
2567 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002568 }
glennrpbfd9e612011-04-22 14:02:20 +00002569#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002570
glennrpfaa852b2010-03-30 12:17:00 +00002571 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002572 {
2573 /*
glennrpa6a06632011-01-19 15:15:34 +00002574 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002575 */
2576 int
2577 max_sample;
2578
cristy35ef8242010-06-03 16:24:13 +00002579 size_t
2580 one=1;
2581
cristy3ed852e2009-09-05 21:47:34 +00002582 if (logging != MagickFalse)
2583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2584 " Reading PNG tRNS chunk.");
2585
cristyf9cca6a2010-06-04 23:49:28 +00002586 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002587
glennrpfaa852b2010-03-30 12:17:00 +00002588 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2589 (int)ping_trans_color->gray > max_sample) ||
2590 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2591 ((int)ping_trans_color->red > max_sample ||
2592 (int)ping_trans_color->green > max_sample ||
2593 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002594 {
2595 if (logging != MagickFalse)
2596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2597 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002598 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002599 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002600 image->matte=MagickFalse;
2601 }
2602 else
2603 {
glennrpa6a06632011-01-19 15:15:34 +00002604 int
2605 scale_to_short;
2606
2607 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2608
2609 /* Scale transparent_color to short */
2610 transparent_color.red= scale_to_short*ping_trans_color->red;
2611 transparent_color.green= scale_to_short*ping_trans_color->green;
2612 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy4c08aed2011-07-01 19:47:50 +00002613 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002614
glennrpfaa852b2010-03-30 12:17:00 +00002615 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002616 {
glennrp0f111982010-07-07 20:18:33 +00002617 if (logging != MagickFalse)
2618 {
2619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2620 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002621
glennrp0f111982010-07-07 20:18:33 +00002622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00002623 " scaled graylevel is %d.",transparent_color.alpha);
glennrp0f111982010-07-07 20:18:33 +00002624 }
cristy4c08aed2011-07-01 19:47:50 +00002625 transparent_color.red=transparent_color.alpha;
2626 transparent_color.green=transparent_color.alpha;
2627 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002628 }
2629 }
2630 }
2631#if defined(PNG_READ_sBIT_SUPPORTED)
2632 if (mng_info->have_global_sbit)
2633 {
glennrpfaa852b2010-03-30 12:17:00 +00002634 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002635 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2636 }
2637#endif
2638 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002639
cristy3ed852e2009-09-05 21:47:34 +00002640 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002641
2642 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2643
cristy3ed852e2009-09-05 21:47:34 +00002644 /*
2645 Initialize image structure.
2646 */
2647 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002648 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002649 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002650 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002651 if (mng_info->mng_type == 0)
2652 {
glennrpfaa852b2010-03-30 12:17:00 +00002653 mng_info->mng_width=ping_width;
2654 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002655 mng_info->frame=mng_info->image_box;
2656 mng_info->clip=mng_info->image_box;
2657 }
glennrp0fe50b42010-11-16 03:52:51 +00002658
cristy3ed852e2009-09-05 21:47:34 +00002659 else
2660 {
2661 image->page.y=mng_info->y_off[mng_info->object_id];
2662 }
glennrp0fe50b42010-11-16 03:52:51 +00002663
cristy3ed852e2009-09-05 21:47:34 +00002664 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002665 image->columns=ping_width;
2666 image->rows=ping_height;
2667 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002668 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002669 {
cristybefe4d22010-06-07 01:18:58 +00002670 size_t
2671 one;
2672
cristy3ed852e2009-09-05 21:47:34 +00002673 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002674 one=1;
2675 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002676#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2677 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002678 image->colors=256;
2679#else
2680 if (image->colors > 65536L)
2681 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002682#endif
glennrpfaa852b2010-03-30 12:17:00 +00002683 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002684 {
2685 int
2686 number_colors;
2687
2688 png_colorp
2689 palette;
2690
2691 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002692 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002693
cristy3ed852e2009-09-05 21:47:34 +00002694 if (logging != MagickFalse)
2695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2696 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2697 }
2698 }
2699
2700 if (image->storage_class == PseudoClass)
2701 {
2702 /*
2703 Initialize image colormap.
2704 */
2705 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2706 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002707
glennrpfaa852b2010-03-30 12:17:00 +00002708 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002709 {
2710 int
2711 number_colors;
2712
2713 png_colorp
2714 palette;
2715
2716 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002717
glennrp6af6cf12011-04-22 13:05:16 +00002718 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002719 {
2720 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2721 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2722 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2723 }
glennrp6af6cf12011-04-22 13:05:16 +00002724
glennrp67b9c1a2011-04-22 18:47:36 +00002725 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002726 {
2727 image->colormap[i].red=0;
2728 image->colormap[i].green=0;
2729 image->colormap[i].blue=0;
2730 }
cristy3ed852e2009-09-05 21:47:34 +00002731 }
glennrp0fe50b42010-11-16 03:52:51 +00002732
cristy3ed852e2009-09-05 21:47:34 +00002733 else
2734 {
cristybb503372010-05-27 20:51:26 +00002735 size_t
cristy3ed852e2009-09-05 21:47:34 +00002736 scale;
2737
glennrpfaa852b2010-03-30 12:17:00 +00002738 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002739
cristy3ed852e2009-09-05 21:47:34 +00002740 if (scale < 1)
2741 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002742
cristybb503372010-05-27 20:51:26 +00002743 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002744 {
2745 image->colormap[i].red=(Quantum) (i*scale);
2746 image->colormap[i].green=(Quantum) (i*scale);
2747 image->colormap[i].blue=(Quantum) (i*scale);
2748 }
2749 }
2750 }
glennrp147bc912011-03-30 18:47:21 +00002751
glennrpcb395ac2011-03-30 19:50:23 +00002752 /* Set some properties for reporting by "identify" */
2753 {
glennrp147bc912011-03-30 18:47:21 +00002754 char
2755 msg[MaxTextExtent];
2756
2757 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2758 ping_interlace_method in value */
2759
cristy3b6fd2e2011-05-20 12:53:50 +00002760 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002761 "%d, %d",(int) ping_width, (int) ping_height);
2762 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
glennrp147bc912011-03-30 18:47:21 +00002763
cristy3b6fd2e2011-05-20 12:53:50 +00002764 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
glennrp147bc912011-03-30 18:47:21 +00002765 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2766
cristy3b6fd2e2011-05-20 12:53:50 +00002767 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
glennrp147bc912011-03-30 18:47:21 +00002768 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2769
cristy3b6fd2e2011-05-20 12:53:50 +00002770 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002771 (int) ping_interlace_method);
2772 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
glennrpcb395ac2011-03-30 19:50:23 +00002773 }
glennrp147bc912011-03-30 18:47:21 +00002774
cristy3ed852e2009-09-05 21:47:34 +00002775 /*
2776 Read image scanlines.
2777 */
2778 if (image->delay != 0)
2779 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002780
glennrp0ca69b12010-07-26 01:57:52 +00002781 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002782 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2783 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002784 {
2785 if (logging != MagickFalse)
2786 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002787 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002788 mng_info->scenes_found-1);
2789 png_destroy_read_struct(&ping,&ping_info,&end_info);
2790#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002791 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002792#endif
2793 if (logging != MagickFalse)
2794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2795 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002796
cristy3ed852e2009-09-05 21:47:34 +00002797 return(image);
2798 }
glennrp0fe50b42010-11-16 03:52:51 +00002799
cristy3ed852e2009-09-05 21:47:34 +00002800 if (logging != MagickFalse)
2801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2802 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002803
cristy3ed852e2009-09-05 21:47:34 +00002804 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002805 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2806 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002807
cristy3ed852e2009-09-05 21:47:34 +00002808 else
glennrpcf002022011-01-30 02:38:15 +00002809 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2810 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002811
glennrpcf002022011-01-30 02:38:15 +00002812 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002813 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002814
cristy3ed852e2009-09-05 21:47:34 +00002815 if (logging != MagickFalse)
2816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2817 " Converting PNG pixels to pixel packets");
2818 /*
2819 Convert PNG pixels to pixel packets.
2820 */
glennrpfaa852b2010-03-30 12:17:00 +00002821 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002822 {
2823 /*
2824 PNG image is corrupt.
2825 */
2826 png_destroy_read_struct(&ping,&ping_info,&end_info);
2827#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002828 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002829#endif
2830 if (quantum_info != (QuantumInfo *) NULL)
2831 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002832
glennrpcf002022011-01-30 02:38:15 +00002833 if (ping_pixels != (unsigned char *) NULL)
2834 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002835
cristy3ed852e2009-09-05 21:47:34 +00002836 if (logging != MagickFalse)
2837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2838 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002839
cristy3ed852e2009-09-05 21:47:34 +00002840 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002841 {
2842 InheritException(exception,&image->exception);
2843 image->columns=0;
2844 }
glennrp0fe50b42010-11-16 03:52:51 +00002845
cristy3ed852e2009-09-05 21:47:34 +00002846 return(GetFirstImageInList(image));
2847 }
glennrp0fe50b42010-11-16 03:52:51 +00002848
cristyed552522009-10-16 14:04:35 +00002849 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002850
cristyed552522009-10-16 14:04:35 +00002851 if (quantum_info == (QuantumInfo *) NULL)
2852 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002853
glennrpc8cbc5d2011-01-01 00:12:34 +00002854 {
2855
2856 MagickBooleanType
2857 found_transparent_pixel;
2858
2859 found_transparent_pixel=MagickFalse;
2860
cristy3ed852e2009-09-05 21:47:34 +00002861 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002862 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002863 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002864 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002865 /*
2866 Convert image to DirectClass pixel packets.
2867 */
glennrp67b9c1a2011-04-22 18:47:36 +00002868#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2869 int
2870 depth;
2871
2872 depth=(ssize_t) ping_bit_depth;
2873#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002874 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2875 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2876 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2877 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002878
glennrpc8cbc5d2011-01-01 00:12:34 +00002879 for (y=0; y < (ssize_t) image->rows; y++)
2880 {
2881 if (num_passes > 1)
2882 row_offset=ping_rowbytes*y;
2883
2884 else
2885 row_offset=0;
2886
glennrpcf002022011-01-30 02:38:15 +00002887 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002888 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2889
cristy4c08aed2011-07-01 19:47:50 +00002890 if (q == (const Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00002891 break;
2892
glennrpc8cbc5d2011-01-01 00:12:34 +00002893 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2894 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002895 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002896
2897 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2898 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002899 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002900
2901 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2902 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002903 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002904
2905 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2906 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002907 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002908
2909 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2910 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002911 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002912
glennrpc8cbc5d2011-01-01 00:12:34 +00002913 if (found_transparent_pixel == MagickFalse)
2914 {
2915 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002916 if (y== 0 && logging != MagickFalse)
2917 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2918 " Looking for cheap transparent pixel");
2919
glennrpc8cbc5d2011-01-01 00:12:34 +00002920 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2921 {
glennrp5aa37f62011-01-02 03:07:57 +00002922 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2923 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy4c08aed2011-07-01 19:47:50 +00002924 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00002925 {
glennrpa6a06632011-01-19 15:15:34 +00002926 if (logging != MagickFalse)
2927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2928 " ...got one.");
2929
glennrpc8cbc5d2011-01-01 00:12:34 +00002930 found_transparent_pixel = MagickTrue;
2931 break;
2932 }
glennrp4f25bd02011-01-01 18:51:28 +00002933 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2934 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp847370c2011-07-05 17:37:15 +00002935 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2936 transparent_color.red &&
2937 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2938 transparent_color.green &&
2939 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2940 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002941 {
glennrpa6a06632011-01-19 15:15:34 +00002942 if (logging != MagickFalse)
2943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2944 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002945 found_transparent_pixel = MagickTrue;
2946 break;
2947 }
cristyed231572011-07-14 02:18:59 +00002948 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00002949 }
2950 }
2951
2952 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2953 {
2954 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2955 image->rows);
2956
2957 if (status == MagickFalse)
2958 break;
2959 }
2960 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2961 break;
2962 }
2963
2964 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2965 {
2966 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00002967 if (status == MagickFalse)
2968 break;
2969 }
cristy3ed852e2009-09-05 21:47:34 +00002970 }
cristy3ed852e2009-09-05 21:47:34 +00002971 }
glennrp0fe50b42010-11-16 03:52:51 +00002972
cristy3ed852e2009-09-05 21:47:34 +00002973 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00002974
cristy3ed852e2009-09-05 21:47:34 +00002975 for (pass=0; pass < num_passes; pass++)
2976 {
2977 Quantum
2978 *quantum_scanline;
2979
2980 register Quantum
2981 *r;
2982
2983 /*
2984 Convert grayscale image to PseudoClass pixel packets.
2985 */
glennrpc17d96f2011-06-27 01:20:11 +00002986 if (logging != MagickFalse)
2987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2988 " Converting grayscale pixels to pixel packets");
cristy4c08aed2011-07-01 19:47:50 +00002989
glennrpfaa852b2010-03-30 12:17:00 +00002990 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00002991 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002992
cristy3ed852e2009-09-05 21:47:34 +00002993 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2994 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00002995
cristy3ed852e2009-09-05 21:47:34 +00002996 if (quantum_scanline == (Quantum *) NULL)
2997 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002998
cristybb503372010-05-27 20:51:26 +00002999 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003000 {
3001 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003002 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003003
cristy3ed852e2009-09-05 21:47:34 +00003004 else
3005 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003006
glennrpcf002022011-01-30 02:38:15 +00003007 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00003008 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003009
cristy4c08aed2011-07-01 19:47:50 +00003010 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003011 break;
glennrp0fe50b42010-11-16 03:52:51 +00003012
glennrpcf002022011-01-30 02:38:15 +00003013 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003014 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003015
glennrpfaa852b2010-03-30 12:17:00 +00003016 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003017 {
3018 case 1:
3019 {
cristybb503372010-05-27 20:51:26 +00003020 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003021 bit;
3022
cristybb503372010-05-27 20:51:26 +00003023 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003024 {
3025 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003026 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003027 p++;
3028 }
glennrp0fe50b42010-11-16 03:52:51 +00003029
cristy3ed852e2009-09-05 21:47:34 +00003030 if ((image->columns % 8) != 0)
3031 {
cristybb503372010-05-27 20:51:26 +00003032 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003033 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003034 }
glennrp0fe50b42010-11-16 03:52:51 +00003035
cristy3ed852e2009-09-05 21:47:34 +00003036 break;
3037 }
glennrp47b9dd52010-11-24 18:12:06 +00003038
cristy3ed852e2009-09-05 21:47:34 +00003039 case 2:
3040 {
cristybb503372010-05-27 20:51:26 +00003041 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003042 {
glennrpa18d5bc2011-04-23 14:51:34 +00003043 *r++=(*p >> 6) & 0x03;
3044 *r++=(*p >> 4) & 0x03;
3045 *r++=(*p >> 2) & 0x03;
3046 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003047 }
glennrp0fe50b42010-11-16 03:52:51 +00003048
cristy3ed852e2009-09-05 21:47:34 +00003049 if ((image->columns % 4) != 0)
3050 {
cristybb503372010-05-27 20:51:26 +00003051 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003052 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003053 }
glennrp0fe50b42010-11-16 03:52:51 +00003054
cristy3ed852e2009-09-05 21:47:34 +00003055 break;
3056 }
glennrp47b9dd52010-11-24 18:12:06 +00003057
cristy3ed852e2009-09-05 21:47:34 +00003058 case 4:
3059 {
cristybb503372010-05-27 20:51:26 +00003060 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003061 {
glennrpa18d5bc2011-04-23 14:51:34 +00003062 *r++=(*p >> 4) & 0x0f;
3063 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003064 }
glennrp0fe50b42010-11-16 03:52:51 +00003065
cristy3ed852e2009-09-05 21:47:34 +00003066 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003067 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003068
cristy3ed852e2009-09-05 21:47:34 +00003069 break;
3070 }
glennrp47b9dd52010-11-24 18:12:06 +00003071
cristy3ed852e2009-09-05 21:47:34 +00003072 case 8:
3073 {
glennrpfaa852b2010-03-30 12:17:00 +00003074 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003075 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003076 {
glennrpa18d5bc2011-04-23 14:51:34 +00003077 *r++=*p++;
cristy4c08aed2011-07-01 19:47:50 +00003078 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3079 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003080 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003081 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003082 }
glennrp0fe50b42010-11-16 03:52:51 +00003083
cristy3ed852e2009-09-05 21:47:34 +00003084 else
cristybb503372010-05-27 20:51:26 +00003085 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003086 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003087
cristy3ed852e2009-09-05 21:47:34 +00003088 break;
3089 }
glennrp47b9dd52010-11-24 18:12:06 +00003090
cristy3ed852e2009-09-05 21:47:34 +00003091 case 16:
3092 {
cristybb503372010-05-27 20:51:26 +00003093 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003094 {
glennrpc17d96f2011-06-27 01:20:11 +00003095#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003096 size_t
3097 quantum;
3098
3099 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003100 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003101
3102 else
glennrpc17d96f2011-06-27 01:20:11 +00003103 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003104
glennrp58f77c72011-04-23 14:09:09 +00003105 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003106 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003107 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003108
3109 if (ping_color_type == 4)
3110 {
glennrpc17d96f2011-06-27 01:20:11 +00003111 if (image->colors > 256)
3112 quantum=((*p++) << 8);
3113 else
3114 quantum=0;
3115
glennrp58f77c72011-04-23 14:09:09 +00003116 quantum|=(*p++);
cristy4c08aed2011-07-01 19:47:50 +00003117 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3118 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003119 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003120 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003121 }
glennrp58f77c72011-04-23 14:09:09 +00003122
3123#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3124 *r++=(*p++);
3125 p++; /* strip low byte */
3126
3127 if (ping_color_type == 4)
3128 {
cristy4c08aed2011-07-01 19:47:50 +00003129 SetPixelAlpha(image,*p++,q);
3130 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003131 found_transparent_pixel = MagickTrue;
3132 p++;
cristyed231572011-07-14 02:18:59 +00003133 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003134 }
cristy3ed852e2009-09-05 21:47:34 +00003135#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003136 }
glennrp47b9dd52010-11-24 18:12:06 +00003137
cristy3ed852e2009-09-05 21:47:34 +00003138 break;
3139 }
glennrp47b9dd52010-11-24 18:12:06 +00003140
cristy3ed852e2009-09-05 21:47:34 +00003141 default:
3142 break;
3143 }
glennrp3faa9a32011-04-23 14:00:25 +00003144
cristy3ed852e2009-09-05 21:47:34 +00003145 /*
3146 Transfer image scanline.
3147 */
3148 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003149
cristy4c08aed2011-07-01 19:47:50 +00003150 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3151
3152 if (q == (const Quantum *) NULL)
3153 break;
cristybb503372010-05-27 20:51:26 +00003154 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00003155 {
3156 SetPixelIndex(image,*r++,q);
cristyed231572011-07-14 02:18:59 +00003157 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00003158 }
glennrp0fe50b42010-11-16 03:52:51 +00003159
cristy3ed852e2009-09-05 21:47:34 +00003160 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3161 break;
glennrp0fe50b42010-11-16 03:52:51 +00003162
cristy7a287bf2010-02-14 02:18:09 +00003163 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3164 {
cristycee97112010-05-28 00:44:52 +00003165 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003166 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003167
cristy7a287bf2010-02-14 02:18:09 +00003168 if (status == MagickFalse)
3169 break;
3170 }
cristy3ed852e2009-09-05 21:47:34 +00003171 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003172
cristy7a287bf2010-02-14 02:18:09 +00003173 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003174 {
3175 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003176
cristy3ed852e2009-09-05 21:47:34 +00003177 if (status == MagickFalse)
3178 break;
3179 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003180
cristy3ed852e2009-09-05 21:47:34 +00003181 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3182 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003183
3184 image->matte=found_transparent_pixel;
3185
3186 if (logging != MagickFalse)
3187 {
3188 if (found_transparent_pixel != MagickFalse)
3189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3190 " Found transparent pixel");
3191 else
glennrp5aa37f62011-01-02 03:07:57 +00003192 {
3193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3194 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003195
glennrp5aa37f62011-01-02 03:07:57 +00003196 ping_color_type&=0x03;
3197 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003198 }
3199 }
3200
cristyb32b90a2009-09-07 21:45:48 +00003201 if (quantum_info != (QuantumInfo *) NULL)
3202 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00003203
cristy5c6f7892010-05-05 22:53:29 +00003204 if (image->storage_class == PseudoClass)
3205 {
cristyaeb2cbc2010-05-07 13:28:58 +00003206 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003207 matte;
3208
3209 matte=image->matte;
3210 image->matte=MagickFalse;
3211 (void) SyncImage(image);
cristyaeb2cbc2010-05-07 13:28:58 +00003212 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003213 }
glennrp47b9dd52010-11-24 18:12:06 +00003214
glennrp4eb39312011-03-30 21:34:55 +00003215 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003216
3217 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003218 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003219 {
3220 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003221 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003222 image->colors=2;
3223 (void) SetImageBackgroundColor(image);
3224#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003225 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003226#endif
3227 if (logging != MagickFalse)
3228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3229 " exit ReadOnePNGImage() early.");
3230 return(image);
3231 }
glennrp47b9dd52010-11-24 18:12:06 +00003232
glennrpfaa852b2010-03-30 12:17:00 +00003233 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003234 {
3235 ClassType
3236 storage_class;
3237
3238 /*
3239 Image has a transparent background.
3240 */
3241 storage_class=image->storage_class;
3242 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003243
glennrp3c218112010-11-27 15:31:26 +00003244/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003245
glennrp0fe50b42010-11-16 03:52:51 +00003246 if (storage_class == PseudoClass)
3247 {
3248 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003249 {
glennrp0fe50b42010-11-16 03:52:51 +00003250 for (x=0; x < ping_num_trans; x++)
3251 {
cristy4c08aed2011-07-01 19:47:50 +00003252 image->colormap[x].alpha =
3253 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003254 }
glennrpc11cf6a2010-03-20 16:46:19 +00003255 }
glennrp47b9dd52010-11-24 18:12:06 +00003256
glennrp0fe50b42010-11-16 03:52:51 +00003257 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3258 {
3259 for (x=0; x < (int) image->colors; x++)
3260 {
3261 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy4c08aed2011-07-01 19:47:50 +00003262 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003263 {
cristy4c08aed2011-07-01 19:47:50 +00003264 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003265 }
3266 }
3267 }
3268 (void) SyncImage(image);
3269 }
glennrp47b9dd52010-11-24 18:12:06 +00003270
glennrpa6a06632011-01-19 15:15:34 +00003271#if 1 /* Should have already been done above, but glennrp problem P10
3272 * needs this.
3273 */
glennrp0fe50b42010-11-16 03:52:51 +00003274 else
3275 {
3276 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003277 {
glennrp0fe50b42010-11-16 03:52:51 +00003278 image->storage_class=storage_class;
3279 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3280
cristy4c08aed2011-07-01 19:47:50 +00003281 if (q == (const Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003282 break;
3283
glennrp0fe50b42010-11-16 03:52:51 +00003284
glennrpa6a06632011-01-19 15:15:34 +00003285 /* Caution: on a Q8 build, this does not distinguish between
3286 * 16-bit colors that differ only in the low byte
3287 */
glennrp0fe50b42010-11-16 03:52:51 +00003288 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3289 {
glennrp847370c2011-07-05 17:37:15 +00003290 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3291 transparent_color.red &&
3292 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3293 transparent_color.green &&
3294 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3295 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003296 {
cristy4c08aed2011-07-01 19:47:50 +00003297 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003298 }
glennrp0fe50b42010-11-16 03:52:51 +00003299
glennrp67b9c1a2011-04-22 18:47:36 +00003300#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003301 else
glennrp4f25bd02011-01-01 18:51:28 +00003302 {
cristy4c08aed2011-07-01 19:47:50 +00003303 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003304 }
glennrpa6a06632011-01-19 15:15:34 +00003305#endif
glennrp0fe50b42010-11-16 03:52:51 +00003306
cristyed231572011-07-14 02:18:59 +00003307 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003308 }
3309
3310 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3311 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003312 }
glennrp0fe50b42010-11-16 03:52:51 +00003313 }
glennrpa6a06632011-01-19 15:15:34 +00003314#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003315
cristy3ed852e2009-09-05 21:47:34 +00003316 image->storage_class=DirectClass;
3317 }
glennrp3c218112010-11-27 15:31:26 +00003318
cristyb40fc462010-08-08 00:49:49 +00003319 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3320 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3321 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00003322
cristyeb3b22a2011-03-31 20:16:11 +00003323 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003324 {
3325 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003326 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3327 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003328 else
glennrpa0ed0092011-04-18 16:36:29 +00003329 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3330 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003331
glennrp4eb39312011-03-30 21:34:55 +00003332 if (status != MagickFalse)
3333 for (i=0; i < (ssize_t) num_text; i++)
3334 {
3335 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003336
glennrp4eb39312011-03-30 21:34:55 +00003337 if (logging != MagickFalse)
3338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3339 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003340
glennrp4eb39312011-03-30 21:34:55 +00003341 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003342 {
glennrp4eb39312011-03-30 21:34:55 +00003343 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
3344 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003345 }
glennrp0fe50b42010-11-16 03:52:51 +00003346
glennrp4eb39312011-03-30 21:34:55 +00003347 else
3348 {
3349 char
3350 *value;
3351
3352 length=text[i].text_length;
3353 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3354 sizeof(*value));
3355 if (value == (char *) NULL)
3356 {
3357 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3358 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3359 image->filename);
3360 break;
3361 }
3362 *value='\0';
3363 (void) ConcatenateMagickString(value,text[i].text,length+2);
3364
3365 /* Don't save "density" or "units" property if we have a pHYs
3366 * chunk
3367 */
3368 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3369 (LocaleCompare(text[i].key,"density") != 0 &&
3370 LocaleCompare(text[i].key,"units") != 0))
3371 (void) SetImageProperty(image,text[i].key,value);
3372
3373 if (logging != MagickFalse)
3374 {
3375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3376 " length: %lu",(unsigned long) length);
3377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3378 " Keyword: %s",text[i].key);
3379 }
3380
3381 value=DestroyString(value);
3382 }
3383 }
3384 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003385 }
glennrp3c218112010-11-27 15:31:26 +00003386
cristy3ed852e2009-09-05 21:47:34 +00003387#ifdef MNG_OBJECT_BUFFERS
3388 /*
3389 Store the object if necessary.
3390 */
3391 if (object_id && !mng_info->frozen[object_id])
3392 {
3393 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3394 {
3395 /*
3396 create a new object buffer.
3397 */
3398 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003399 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003400
cristy3ed852e2009-09-05 21:47:34 +00003401 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3402 {
3403 mng_info->ob[object_id]->image=(Image *) NULL;
3404 mng_info->ob[object_id]->reference_count=1;
3405 }
3406 }
glennrp47b9dd52010-11-24 18:12:06 +00003407
cristy3ed852e2009-09-05 21:47:34 +00003408 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3409 mng_info->ob[object_id]->frozen)
3410 {
3411 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3412 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3413 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3414 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003415
cristy3ed852e2009-09-05 21:47:34 +00003416 if (mng_info->ob[object_id]->frozen)
3417 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3418 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3419 "`%s'",image->filename);
3420 }
glennrp0fe50b42010-11-16 03:52:51 +00003421
cristy3ed852e2009-09-05 21:47:34 +00003422 else
3423 {
cristy3ed852e2009-09-05 21:47:34 +00003424
3425 if (mng_info->ob[object_id]->image != (Image *) NULL)
3426 mng_info->ob[object_id]->image=DestroyImage
3427 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003428
cristy3ed852e2009-09-05 21:47:34 +00003429 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3430 &image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00003431
cristy3ed852e2009-09-05 21:47:34 +00003432 if (mng_info->ob[object_id]->image != (Image *) NULL)
3433 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003434
cristy3ed852e2009-09-05 21:47:34 +00003435 else
3436 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3437 ResourceLimitError,"Cloning image for object buffer failed",
3438 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003439
glennrpfaa852b2010-03-30 12:17:00 +00003440 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003441 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003442
glennrpfaa852b2010-03-30 12:17:00 +00003443 mng_info->ob[object_id]->width=ping_width;
3444 mng_info->ob[object_id]->height=ping_height;
3445 mng_info->ob[object_id]->color_type=ping_color_type;
3446 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3447 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3448 mng_info->ob[object_id]->compression_method=
3449 ping_compression_method;
3450 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003451
glennrpfaa852b2010-03-30 12:17:00 +00003452 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003453 {
3454 int
3455 number_colors;
3456
3457 png_colorp
3458 plte;
3459
3460 /*
3461 Copy the PLTE to the object buffer.
3462 */
3463 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3464 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003465
cristy3ed852e2009-09-05 21:47:34 +00003466 for (i=0; i < number_colors; i++)
3467 {
3468 mng_info->ob[object_id]->plte[i]=plte[i];
3469 }
3470 }
glennrp47b9dd52010-11-24 18:12:06 +00003471
cristy3ed852e2009-09-05 21:47:34 +00003472 else
3473 mng_info->ob[object_id]->plte_length=0;
3474 }
3475 }
3476#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003477
3478 /* Set image->matte to MagickTrue if the input colortype supports
3479 * alpha or if a valid tRNS chunk is present, no matter whether there
3480 * is actual transparency present.
3481 */
3482 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3483 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3484 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3485 MagickTrue : MagickFalse;
3486
glennrpcb395ac2011-03-30 19:50:23 +00003487 /* Set more properties for identify to retrieve */
3488 {
3489 char
3490 msg[MaxTextExtent];
3491
glennrp4eb39312011-03-30 21:34:55 +00003492 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003493 {
3494 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003495 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003496 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrpcb395ac2011-03-30 19:50:23 +00003497 (void) SetImageProperty(image,"PNG:text ",msg);
3498 }
3499
3500 if (num_raw_profiles != 0)
3501 {
cristy3b6fd2e2011-05-20 12:53:50 +00003502 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003503 "%d were found", num_raw_profiles);
3504 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3505 }
3506
glennrpcb395ac2011-03-30 19:50:23 +00003507 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003508 {
cristy3b6fd2e2011-05-20 12:53:50 +00003509 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003510 "chunk was found (see Chromaticity, above)");
3511 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3512 }
glennrpcb395ac2011-03-30 19:50:23 +00003513
3514 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003515 {
cristy3b6fd2e2011-05-20 12:53:50 +00003516 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003517 "chunk was found (see Background color, above)");
3518 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3519 }
3520
cristy3b6fd2e2011-05-20 12:53:50 +00003521 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003522 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003523
3524 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3525 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3526
glennrpcb395ac2011-03-30 19:50:23 +00003527 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3528 (void) SetImageProperty(image,"PNG:tRNS ",msg);
glennrp4eb39312011-03-30 21:34:55 +00003529
3530#if defined(PNG_sRGB_SUPPORTED)
3531 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3532 {
cristy3b6fd2e2011-05-20 12:53:50 +00003533 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003534 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003535 (int) intent);
3536 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3537 }
3538#endif
3539
3540 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3541 {
cristy3b6fd2e2011-05-20 12:53:50 +00003542 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003543 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003544 file_gamma);
3545 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3546 }
3547
3548#if defined(PNG_pHYs_SUPPORTED)
3549 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3550 {
cristy3b6fd2e2011-05-20 12:53:50 +00003551 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003552 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003553 (double) x_resolution,(double) y_resolution, unit_type);
3554 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3555 }
3556#endif
3557
3558#if defined(PNG_oFFs_SUPPORTED)
3559 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3560 {
cristy3b6fd2e2011-05-20 12:53:50 +00003561 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003562 (double) image->page.x,(double) image->page.y);
3563 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3564 }
3565#endif
3566
glennrp07523c72011-03-31 18:12:10 +00003567 if ((image->page.width != 0 && image->page.width != image->columns) ||
3568 (image->page.height != 0 && image->page.height != image->rows))
3569 {
cristy3b6fd2e2011-05-20 12:53:50 +00003570 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003571 "width=%.20g, height=%.20g",
3572 (double) image->page.width,(double) image->page.height);
3573 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3574 }
glennrpcb395ac2011-03-30 19:50:23 +00003575 }
3576
cristy3ed852e2009-09-05 21:47:34 +00003577 /*
3578 Relinquish resources.
3579 */
3580 png_destroy_read_struct(&ping,&ping_info,&end_info);
3581
glennrpcf002022011-01-30 02:38:15 +00003582 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003583#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003584 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003585#endif
3586
3587 if (logging != MagickFalse)
3588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3589 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003590
cristy3ed852e2009-09-05 21:47:34 +00003591 return(image);
3592
3593/* end of reading one PNG image */
3594}
3595
3596static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3597{
3598 Image
3599 *image,
3600 *previous;
3601
3602 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003603 have_mng_structure,
3604 logging,
cristy3ed852e2009-09-05 21:47:34 +00003605 status;
3606
3607 MngInfo
3608 *mng_info;
3609
3610 char
3611 magic_number[MaxTextExtent];
3612
cristy3ed852e2009-09-05 21:47:34 +00003613 ssize_t
3614 count;
3615
3616 /*
3617 Open image file.
3618 */
3619 assert(image_info != (const ImageInfo *) NULL);
3620 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003621
cristy3ed852e2009-09-05 21:47:34 +00003622 if (image_info->debug != MagickFalse)
3623 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3624 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003625
cristy3ed852e2009-09-05 21:47:34 +00003626 assert(exception != (ExceptionInfo *) NULL);
3627 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003628 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003629 image=AcquireImage(image_info);
3630 mng_info=(MngInfo *) NULL;
3631 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003632
cristy3ed852e2009-09-05 21:47:34 +00003633 if (status == MagickFalse)
3634 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003635
cristy3ed852e2009-09-05 21:47:34 +00003636 /*
3637 Verify PNG signature.
3638 */
3639 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003640
glennrpdde35db2011-02-21 12:06:32 +00003641 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003642 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003643
cristy3ed852e2009-09-05 21:47:34 +00003644 /*
3645 Allocate a MngInfo structure.
3646 */
3647 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003648 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003649
cristy3ed852e2009-09-05 21:47:34 +00003650 if (mng_info == (MngInfo *) NULL)
3651 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003652
cristy3ed852e2009-09-05 21:47:34 +00003653 /*
3654 Initialize members of the MngInfo structure.
3655 */
3656 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3657 mng_info->image=image;
3658 have_mng_structure=MagickTrue;
3659
3660 previous=image;
3661 image=ReadOnePNGImage(mng_info,image_info,exception);
3662 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003663
cristy3ed852e2009-09-05 21:47:34 +00003664 if (image == (Image *) NULL)
3665 {
3666 if (previous != (Image *) NULL)
3667 {
3668 if (previous->signature != MagickSignature)
3669 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003670
cristy3ed852e2009-09-05 21:47:34 +00003671 (void) CloseBlob(previous);
3672 (void) DestroyImageList(previous);
3673 }
glennrp0fe50b42010-11-16 03:52:51 +00003674
cristy3ed852e2009-09-05 21:47:34 +00003675 if (logging != MagickFalse)
3676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3677 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003678
cristy3ed852e2009-09-05 21:47:34 +00003679 return((Image *) NULL);
3680 }
glennrp47b9dd52010-11-24 18:12:06 +00003681
cristy3ed852e2009-09-05 21:47:34 +00003682 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003683
cristy3ed852e2009-09-05 21:47:34 +00003684 if ((image->columns == 0) || (image->rows == 0))
3685 {
3686 if (logging != MagickFalse)
3687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3688 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003689
cristy3ed852e2009-09-05 21:47:34 +00003690 ThrowReaderException(CorruptImageError,"CorruptImage");
3691 }
glennrp47b9dd52010-11-24 18:12:06 +00003692
cristy3ed852e2009-09-05 21:47:34 +00003693 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3694 {
3695 (void) SetImageType(image,TrueColorType);
3696 image->matte=MagickFalse;
3697 }
glennrp0fe50b42010-11-16 03:52:51 +00003698
cristy3ed852e2009-09-05 21:47:34 +00003699 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3700 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +00003701
cristy3ed852e2009-09-05 21:47:34 +00003702 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3704 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3705 (double) image->page.width,(double) image->page.height,
3706 (double) image->page.x,(double) image->page.y);
3707
3708 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003710
cristy3ed852e2009-09-05 21:47:34 +00003711 return(image);
3712}
3713
3714
3715
3716#if defined(JNG_SUPPORTED)
3717/*
3718%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3719% %
3720% %
3721% %
3722% R e a d O n e J N G I m a g e %
3723% %
3724% %
3725% %
3726%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3727%
3728% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3729% (minus the 8-byte signature) and returns it. It allocates the memory
3730% necessary for the new Image structure and returns a pointer to the new
3731% image.
3732%
3733% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3734%
3735% The format of the ReadOneJNGImage method is:
3736%
3737% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3738% ExceptionInfo *exception)
3739%
3740% A description of each parameter follows:
3741%
3742% o mng_info: Specifies a pointer to a MngInfo structure.
3743%
3744% o image_info: the image info.
3745%
3746% o exception: return any errors or warnings in this structure.
3747%
3748*/
3749static Image *ReadOneJNGImage(MngInfo *mng_info,
3750 const ImageInfo *image_info, ExceptionInfo *exception)
3751{
3752 Image
3753 *alpha_image,
3754 *color_image,
3755 *image,
3756 *jng_image;
3757
3758 ImageInfo
3759 *alpha_image_info,
3760 *color_image_info;
3761
cristy4383ec82011-01-05 15:42:32 +00003762 MagickBooleanType
3763 logging;
3764
cristybb503372010-05-27 20:51:26 +00003765 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003766 y;
3767
3768 MagickBooleanType
3769 status;
3770
3771 png_uint_32
3772 jng_height,
3773 jng_width;
3774
3775 png_byte
3776 jng_color_type,
3777 jng_image_sample_depth,
3778 jng_image_compression_method,
3779 jng_image_interlace_method,
3780 jng_alpha_sample_depth,
3781 jng_alpha_compression_method,
3782 jng_alpha_filter_method,
3783 jng_alpha_interlace_method;
3784
cristy4c08aed2011-07-01 19:47:50 +00003785 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003786 *s;
3787
cristybb503372010-05-27 20:51:26 +00003788 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003789 i,
3790 x;
3791
cristy4c08aed2011-07-01 19:47:50 +00003792 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003793 *q;
3794
3795 register unsigned char
3796 *p;
3797
3798 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003799 read_JSEP,
3800 reading_idat,
3801 skip_to_iend;
3802
cristybb503372010-05-27 20:51:26 +00003803 size_t
cristy3ed852e2009-09-05 21:47:34 +00003804 length;
3805
3806 jng_alpha_compression_method=0;
3807 jng_alpha_sample_depth=8;
3808 jng_color_type=0;
3809 jng_height=0;
3810 jng_width=0;
3811 alpha_image=(Image *) NULL;
3812 color_image=(Image *) NULL;
3813 alpha_image_info=(ImageInfo *) NULL;
3814 color_image_info=(ImageInfo *) NULL;
3815
3816 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003817 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003818
3819 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003820
cristy4c08aed2011-07-01 19:47:50 +00003821 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003822 {
3823 /*
3824 Allocate next image structure.
3825 */
3826 if (logging != MagickFalse)
3827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3828 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003829
cristy3ed852e2009-09-05 21:47:34 +00003830 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00003831
cristy3ed852e2009-09-05 21:47:34 +00003832 if (GetNextImageInList(image) == (Image *) NULL)
3833 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003834
cristy3ed852e2009-09-05 21:47:34 +00003835 image=SyncNextImageInList(image);
3836 }
3837 mng_info->image=image;
3838
3839 /*
3840 Signature bytes have already been read.
3841 */
3842
3843 read_JSEP=MagickFalse;
3844 reading_idat=MagickFalse;
3845 skip_to_iend=MagickFalse;
3846 for (;;)
3847 {
3848 char
3849 type[MaxTextExtent];
3850
3851 unsigned char
3852 *chunk;
3853
3854 unsigned int
3855 count;
3856
3857 /*
3858 Read a new JNG chunk.
3859 */
3860 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3861 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003862
cristy3ed852e2009-09-05 21:47:34 +00003863 if (status == MagickFalse)
3864 break;
glennrp0fe50b42010-11-16 03:52:51 +00003865
cristy3ed852e2009-09-05 21:47:34 +00003866 type[0]='\0';
3867 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3868 length=ReadBlobMSBLong(image);
3869 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3870
3871 if (logging != MagickFalse)
3872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003873 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3874 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003875
3876 if (length > PNG_UINT_31_MAX || count == 0)
3877 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003878
cristy3ed852e2009-09-05 21:47:34 +00003879 p=NULL;
3880 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003881
cristy3ed852e2009-09-05 21:47:34 +00003882 if (length)
3883 {
3884 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003885
cristy3ed852e2009-09-05 21:47:34 +00003886 if (chunk == (unsigned char *) NULL)
3887 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003888
cristybb503372010-05-27 20:51:26 +00003889 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003890 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003891
cristy3ed852e2009-09-05 21:47:34 +00003892 p=chunk;
3893 }
glennrp47b9dd52010-11-24 18:12:06 +00003894
cristy3ed852e2009-09-05 21:47:34 +00003895 (void) ReadBlobMSBLong(image); /* read crc word */
3896
3897 if (skip_to_iend)
3898 {
3899 if (length)
3900 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003901
cristy3ed852e2009-09-05 21:47:34 +00003902 continue;
3903 }
3904
3905 if (memcmp(type,mng_JHDR,4) == 0)
3906 {
3907 if (length == 16)
3908 {
cristybb503372010-05-27 20:51:26 +00003909 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003910 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003911 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003912 (p[6] << 8) | p[7]);
3913 jng_color_type=p[8];
3914 jng_image_sample_depth=p[9];
3915 jng_image_compression_method=p[10];
3916 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003917
cristy3ed852e2009-09-05 21:47:34 +00003918 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3919 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003920
cristy3ed852e2009-09-05 21:47:34 +00003921 jng_alpha_sample_depth=p[12];
3922 jng_alpha_compression_method=p[13];
3923 jng_alpha_filter_method=p[14];
3924 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003925
cristy3ed852e2009-09-05 21:47:34 +00003926 if (logging != MagickFalse)
3927 {
3928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003929 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003930
cristy3ed852e2009-09-05 21:47:34 +00003931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003932 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003933
cristy3ed852e2009-09-05 21:47:34 +00003934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3935 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003936
cristy3ed852e2009-09-05 21:47:34 +00003937 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3938 " jng_image_sample_depth: %3d",
3939 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003940
cristy3ed852e2009-09-05 21:47:34 +00003941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3942 " jng_image_compression_method:%3d",
3943 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003944
cristy3ed852e2009-09-05 21:47:34 +00003945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3946 " jng_image_interlace_method: %3d",
3947 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003948
cristy3ed852e2009-09-05 21:47:34 +00003949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3950 " jng_alpha_sample_depth: %3d",
3951 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003952
cristy3ed852e2009-09-05 21:47:34 +00003953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3954 " jng_alpha_compression_method:%3d",
3955 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003956
cristy3ed852e2009-09-05 21:47:34 +00003957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3958 " jng_alpha_filter_method: %3d",
3959 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00003960
cristy3ed852e2009-09-05 21:47:34 +00003961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3962 " jng_alpha_interlace_method: %3d",
3963 jng_alpha_interlace_method);
3964 }
3965 }
glennrp47b9dd52010-11-24 18:12:06 +00003966
cristy3ed852e2009-09-05 21:47:34 +00003967 if (length)
3968 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003969
cristy3ed852e2009-09-05 21:47:34 +00003970 continue;
3971 }
3972
3973
3974 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3975 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3976 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3977 {
3978 /*
3979 o create color_image
3980 o open color_blob, attached to color_image
3981 o if (color type has alpha)
3982 open alpha_blob, attached to alpha_image
3983 */
3984
cristy73bd4a52010-10-05 11:24:23 +00003985 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003986
cristy3ed852e2009-09-05 21:47:34 +00003987 if (color_image_info == (ImageInfo *) NULL)
3988 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003989
cristy3ed852e2009-09-05 21:47:34 +00003990 GetImageInfo(color_image_info);
3991 color_image=AcquireImage(color_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003992
cristy3ed852e2009-09-05 21:47:34 +00003993 if (color_image == (Image *) NULL)
3994 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3995
3996 if (logging != MagickFalse)
3997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3998 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003999
cristy3ed852e2009-09-05 21:47:34 +00004000 (void) AcquireUniqueFilename(color_image->filename);
4001 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4002 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004003
cristy3ed852e2009-09-05 21:47:34 +00004004 if (status == MagickFalse)
4005 return((Image *) NULL);
4006
4007 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4008 {
4009 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004010 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004011
cristy3ed852e2009-09-05 21:47:34 +00004012 if (alpha_image_info == (ImageInfo *) NULL)
4013 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004014
cristy3ed852e2009-09-05 21:47:34 +00004015 GetImageInfo(alpha_image_info);
4016 alpha_image=AcquireImage(alpha_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00004017
cristy3ed852e2009-09-05 21:47:34 +00004018 if (alpha_image == (Image *) NULL)
4019 {
4020 alpha_image=DestroyImage(alpha_image);
4021 ThrowReaderException(ResourceLimitError,
4022 "MemoryAllocationFailed");
4023 }
glennrp0fe50b42010-11-16 03:52:51 +00004024
cristy3ed852e2009-09-05 21:47:34 +00004025 if (logging != MagickFalse)
4026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4027 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004028
cristy3ed852e2009-09-05 21:47:34 +00004029 (void) AcquireUniqueFilename(alpha_image->filename);
4030 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4031 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004032
cristy3ed852e2009-09-05 21:47:34 +00004033 if (status == MagickFalse)
4034 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004035
cristy3ed852e2009-09-05 21:47:34 +00004036 if (jng_alpha_compression_method == 0)
4037 {
4038 unsigned char
4039 data[18];
4040
4041 if (logging != MagickFalse)
4042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4043 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004044
cristy3ed852e2009-09-05 21:47:34 +00004045 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4046 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004047
cristy3ed852e2009-09-05 21:47:34 +00004048 (void) WriteBlobMSBULong(alpha_image,13L);
4049 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004050 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004051 PNGLong(data+4,jng_width);
4052 PNGLong(data+8,jng_height);
4053 data[12]=jng_alpha_sample_depth;
4054 data[13]=0; /* color_type gray */
4055 data[14]=0; /* compression method 0 */
4056 data[15]=0; /* filter_method 0 */
4057 data[16]=0; /* interlace_method 0 */
4058 (void) WriteBlob(alpha_image,17,data);
4059 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4060 }
4061 }
4062 reading_idat=MagickTrue;
4063 }
4064
4065 if (memcmp(type,mng_JDAT,4) == 0)
4066 {
glennrp47b9dd52010-11-24 18:12:06 +00004067 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004068
4069 if (logging != MagickFalse)
4070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4071 " Copying JDAT chunk data to color_blob.");
4072
4073 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004074
cristy3ed852e2009-09-05 21:47:34 +00004075 if (length)
4076 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004077
cristy3ed852e2009-09-05 21:47:34 +00004078 continue;
4079 }
4080
4081 if (memcmp(type,mng_IDAT,4) == 0)
4082 {
4083 png_byte
4084 data[5];
4085
glennrp47b9dd52010-11-24 18:12:06 +00004086 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004087
4088 if (image_info->ping == MagickFalse)
4089 {
4090 if (logging != MagickFalse)
4091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4092 " Copying IDAT chunk data to alpha_blob.");
4093
cristybb503372010-05-27 20:51:26 +00004094 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004095 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004096 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004097 (void) WriteBlob(alpha_image,4,data);
4098 (void) WriteBlob(alpha_image,length,chunk);
4099 (void) WriteBlobMSBULong(alpha_image,
4100 crc32(crc32(0,data,4),chunk,(uInt) length));
4101 }
glennrp0fe50b42010-11-16 03:52:51 +00004102
cristy3ed852e2009-09-05 21:47:34 +00004103 if (length)
4104 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004105
cristy3ed852e2009-09-05 21:47:34 +00004106 continue;
4107 }
4108
4109 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4110 {
glennrp47b9dd52010-11-24 18:12:06 +00004111 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004112
4113 if (image_info->ping == MagickFalse)
4114 {
4115 if (logging != MagickFalse)
4116 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4117 " Copying JDAA chunk data to alpha_blob.");
4118
4119 (void) WriteBlob(alpha_image,length,chunk);
4120 }
glennrp0fe50b42010-11-16 03:52:51 +00004121
cristy3ed852e2009-09-05 21:47:34 +00004122 if (length)
4123 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004124
cristy3ed852e2009-09-05 21:47:34 +00004125 continue;
4126 }
4127
4128 if (memcmp(type,mng_JSEP,4) == 0)
4129 {
4130 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004131
cristy3ed852e2009-09-05 21:47:34 +00004132 if (length)
4133 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004134
cristy3ed852e2009-09-05 21:47:34 +00004135 continue;
4136 }
4137
4138 if (memcmp(type,mng_bKGD,4) == 0)
4139 {
4140 if (length == 2)
4141 {
4142 image->background_color.red=ScaleCharToQuantum(p[1]);
4143 image->background_color.green=image->background_color.red;
4144 image->background_color.blue=image->background_color.red;
4145 }
glennrp0fe50b42010-11-16 03:52:51 +00004146
cristy3ed852e2009-09-05 21:47:34 +00004147 if (length == 6)
4148 {
4149 image->background_color.red=ScaleCharToQuantum(p[1]);
4150 image->background_color.green=ScaleCharToQuantum(p[3]);
4151 image->background_color.blue=ScaleCharToQuantum(p[5]);
4152 }
glennrp0fe50b42010-11-16 03:52:51 +00004153
cristy3ed852e2009-09-05 21:47:34 +00004154 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4155 continue;
4156 }
4157
4158 if (memcmp(type,mng_gAMA,4) == 0)
4159 {
4160 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004161 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004162
cristy3ed852e2009-09-05 21:47:34 +00004163 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4164 continue;
4165 }
4166
4167 if (memcmp(type,mng_cHRM,4) == 0)
4168 {
4169 if (length == 32)
4170 {
cristy8182b072010-05-30 20:10:53 +00004171 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4172 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4173 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4174 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4175 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4176 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4177 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4178 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004179 }
glennrp47b9dd52010-11-24 18:12:06 +00004180
cristy3ed852e2009-09-05 21:47:34 +00004181 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4182 continue;
4183 }
4184
4185 if (memcmp(type,mng_sRGB,4) == 0)
4186 {
4187 if (length == 1)
4188 {
glennrpe610a072010-08-05 17:08:46 +00004189 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004190 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004191 image->gamma=0.45455f;
4192 image->chromaticity.red_primary.x=0.6400f;
4193 image->chromaticity.red_primary.y=0.3300f;
4194 image->chromaticity.green_primary.x=0.3000f;
4195 image->chromaticity.green_primary.y=0.6000f;
4196 image->chromaticity.blue_primary.x=0.1500f;
4197 image->chromaticity.blue_primary.y=0.0600f;
4198 image->chromaticity.white_point.x=0.3127f;
4199 image->chromaticity.white_point.y=0.3290f;
4200 }
glennrp47b9dd52010-11-24 18:12:06 +00004201
cristy3ed852e2009-09-05 21:47:34 +00004202 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4203 continue;
4204 }
4205
4206 if (memcmp(type,mng_oFFs,4) == 0)
4207 {
4208 if (length > 8)
4209 {
glennrp5eae7602011-02-22 15:21:32 +00004210 image->page.x=(ssize_t) mng_get_long(p);
4211 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004212
cristy3ed852e2009-09-05 21:47:34 +00004213 if ((int) p[8] != 0)
4214 {
4215 image->page.x/=10000;
4216 image->page.y/=10000;
4217 }
4218 }
glennrp47b9dd52010-11-24 18:12:06 +00004219
cristy3ed852e2009-09-05 21:47:34 +00004220 if (length)
4221 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004222
cristy3ed852e2009-09-05 21:47:34 +00004223 continue;
4224 }
4225
4226 if (memcmp(type,mng_pHYs,4) == 0)
4227 {
4228 if (length > 8)
4229 {
cristy8182b072010-05-30 20:10:53 +00004230 image->x_resolution=(double) mng_get_long(p);
4231 image->y_resolution=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004232 if ((int) p[8] == PNG_RESOLUTION_METER)
4233 {
4234 image->units=PixelsPerCentimeterResolution;
4235 image->x_resolution=image->x_resolution/100.0f;
4236 image->y_resolution=image->y_resolution/100.0f;
4237 }
4238 }
glennrp0fe50b42010-11-16 03:52:51 +00004239
cristy3ed852e2009-09-05 21:47:34 +00004240 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4241 continue;
4242 }
4243
4244#if 0
4245 if (memcmp(type,mng_iCCP,4) == 0)
4246 {
glennrpfd05d622011-02-25 04:10:33 +00004247 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004248 if (length)
4249 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004250
cristy3ed852e2009-09-05 21:47:34 +00004251 continue;
4252 }
4253#endif
4254
4255 if (length)
4256 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4257
4258 if (memcmp(type,mng_IEND,4))
4259 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004260
cristy3ed852e2009-09-05 21:47:34 +00004261 break;
4262 }
4263
4264
4265 /* IEND found */
4266
4267 /*
4268 Finish up reading image data:
4269
4270 o read main image from color_blob.
4271
4272 o close color_blob.
4273
4274 o if (color_type has alpha)
4275 if alpha_encoding is PNG
4276 read secondary image from alpha_blob via ReadPNG
4277 if alpha_encoding is JPEG
4278 read secondary image from alpha_blob via ReadJPEG
4279
4280 o close alpha_blob.
4281
4282 o copy intensity of secondary image into
cristy4c08aed2011-07-01 19:47:50 +00004283 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004284
4285 o destroy the secondary image.
4286 */
4287
4288 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004289
cristy3ed852e2009-09-05 21:47:34 +00004290 if (logging != MagickFalse)
4291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4292 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004293
cristy3b6fd2e2011-05-20 12:53:50 +00004294 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004295 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004296
cristy3ed852e2009-09-05 21:47:34 +00004297 color_image_info->ping=MagickFalse; /* To do: avoid this */
4298 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004299
cristy3ed852e2009-09-05 21:47:34 +00004300 if (jng_image == (Image *) NULL)
4301 return((Image *) NULL);
4302
4303 (void) RelinquishUniqueFileResource(color_image->filename);
4304 color_image=DestroyImage(color_image);
4305 color_image_info=DestroyImageInfo(color_image_info);
4306
4307 if (jng_image == (Image *) NULL)
4308 return((Image *) NULL);
4309
4310 if (logging != MagickFalse)
4311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4312 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004313
cristy3ed852e2009-09-05 21:47:34 +00004314 image->rows=jng_height;
4315 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004316
cristybb503372010-05-27 20:51:26 +00004317 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004318 {
4319 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
4320 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004321 for (x=(ssize_t) image->columns; x != 0; x--)
4322 {
4323 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4324 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4325 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004326 q+=GetPixelChannels(image);
4327 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004328 }
glennrp47b9dd52010-11-24 18:12:06 +00004329
cristy3ed852e2009-09-05 21:47:34 +00004330 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4331 break;
4332 }
glennrp0fe50b42010-11-16 03:52:51 +00004333
cristy3ed852e2009-09-05 21:47:34 +00004334 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004335
cristy3ed852e2009-09-05 21:47:34 +00004336 if (image_info->ping == MagickFalse)
4337 {
4338 if (jng_color_type >= 12)
4339 {
4340 if (jng_alpha_compression_method == 0)
4341 {
4342 png_byte
4343 data[5];
4344 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4345 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004346 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004347 (void) WriteBlob(alpha_image,4,data);
4348 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4349 }
glennrp0fe50b42010-11-16 03:52:51 +00004350
cristy3ed852e2009-09-05 21:47:34 +00004351 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004352
cristy3ed852e2009-09-05 21:47:34 +00004353 if (logging != MagickFalse)
4354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00004355 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004356
cristy3b6fd2e2011-05-20 12:53:50 +00004357 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004358 "%s",alpha_image->filename);
4359
4360 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004361
cristy3ed852e2009-09-05 21:47:34 +00004362 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004363 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004364 {
4365 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristy4c08aed2011-07-01 19:47:50 +00004366 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +00004367 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004368
cristy3ed852e2009-09-05 21:47:34 +00004369 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00004370 for (x=(ssize_t) image->columns; x != 0; x--)
4371 {
4372 SetPixelAlpha(image,GetPixelRed(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 }
glennrp0fe50b42010-11-16 03:52:51 +00004376
cristy3ed852e2009-09-05 21:47:34 +00004377 else
cristy4c08aed2011-07-01 19:47:50 +00004378 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004379 {
cristy4c08aed2011-07-01 19:47:50 +00004380 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4381 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004382 image->matte=MagickTrue;
cristyed231572011-07-14 02:18:59 +00004383 q+=GetPixelChannels(image);
4384 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004385 }
glennrp0fe50b42010-11-16 03:52:51 +00004386
cristy3ed852e2009-09-05 21:47:34 +00004387 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4388 break;
4389 }
4390 (void) RelinquishUniqueFileResource(alpha_image->filename);
4391 alpha_image=DestroyImage(alpha_image);
4392 alpha_image_info=DestroyImageInfo(alpha_image_info);
4393 if (jng_image != (Image *) NULL)
4394 jng_image=DestroyImage(jng_image);
4395 }
4396 }
4397
glennrp47b9dd52010-11-24 18:12:06 +00004398 /* Read the JNG image. */
4399
cristy3ed852e2009-09-05 21:47:34 +00004400 if (mng_info->mng_type == 0)
4401 {
4402 mng_info->mng_width=jng_width;
4403 mng_info->mng_height=jng_height;
4404 }
glennrp0fe50b42010-11-16 03:52:51 +00004405
cristy3ed852e2009-09-05 21:47:34 +00004406 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004407 {
4408 image->page.width=jng_width;
4409 image->page.height=jng_height;
4410 }
4411
cristy3ed852e2009-09-05 21:47:34 +00004412 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004413 {
4414 image->page.x=mng_info->x_off[mng_info->object_id];
4415 image->page.y=mng_info->y_off[mng_info->object_id];
4416 }
4417
cristy3ed852e2009-09-05 21:47:34 +00004418 else
glennrp0fe50b42010-11-16 03:52:51 +00004419 {
4420 image->page.y=mng_info->y_off[mng_info->object_id];
4421 }
4422
cristy3ed852e2009-09-05 21:47:34 +00004423 mng_info->image_found++;
4424 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4425 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004426
cristy3ed852e2009-09-05 21:47:34 +00004427 if (logging != MagickFalse)
4428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4429 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004430
cristy3ed852e2009-09-05 21:47:34 +00004431 return(image);
4432}
4433
4434/*
4435%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4436% %
4437% %
4438% %
4439% R e a d J N G I m a g e %
4440% %
4441% %
4442% %
4443%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4444%
4445% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4446% (including the 8-byte signature) and returns it. It allocates the memory
4447% necessary for the new Image structure and returns a pointer to the new
4448% image.
4449%
4450% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4451%
4452% The format of the ReadJNGImage method is:
4453%
4454% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4455% *exception)
4456%
4457% A description of each parameter follows:
4458%
4459% o image_info: the image info.
4460%
4461% o exception: return any errors or warnings in this structure.
4462%
4463*/
4464
4465static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4466{
4467 Image
4468 *image,
4469 *previous;
4470
4471 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004472 have_mng_structure,
4473 logging,
cristy3ed852e2009-09-05 21:47:34 +00004474 status;
4475
4476 MngInfo
4477 *mng_info;
4478
4479 char
4480 magic_number[MaxTextExtent];
4481
cristy3ed852e2009-09-05 21:47:34 +00004482 size_t
4483 count;
4484
4485 /*
4486 Open image file.
4487 */
4488 assert(image_info != (const ImageInfo *) NULL);
4489 assert(image_info->signature == MagickSignature);
4490 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4491 assert(exception != (ExceptionInfo *) NULL);
4492 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004493 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004494 image=AcquireImage(image_info);
4495 mng_info=(MngInfo *) NULL;
4496 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004497
cristy3ed852e2009-09-05 21:47:34 +00004498 if (status == MagickFalse)
4499 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004500
cristy3ed852e2009-09-05 21:47:34 +00004501 if (LocaleCompare(image_info->magick,"JNG") != 0)
4502 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004503
glennrp47b9dd52010-11-24 18:12:06 +00004504 /* Verify JNG signature. */
4505
cristy3ed852e2009-09-05 21:47:34 +00004506 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004507
glennrp3b8763e2011-02-21 12:08:18 +00004508 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004509 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004510
glennrp47b9dd52010-11-24 18:12:06 +00004511 /* Allocate a MngInfo structure. */
4512
cristy3ed852e2009-09-05 21:47:34 +00004513 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004514 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004515
cristy3ed852e2009-09-05 21:47:34 +00004516 if (mng_info == (MngInfo *) NULL)
4517 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004518
glennrp47b9dd52010-11-24 18:12:06 +00004519 /* Initialize members of the MngInfo structure. */
4520
cristy3ed852e2009-09-05 21:47:34 +00004521 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4522 have_mng_structure=MagickTrue;
4523
4524 mng_info->image=image;
4525 previous=image;
4526 image=ReadOneJNGImage(mng_info,image_info,exception);
4527 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004528
cristy3ed852e2009-09-05 21:47:34 +00004529 if (image == (Image *) NULL)
4530 {
4531 if (IsImageObject(previous) != MagickFalse)
4532 {
4533 (void) CloseBlob(previous);
4534 (void) DestroyImageList(previous);
4535 }
glennrp0fe50b42010-11-16 03:52:51 +00004536
cristy3ed852e2009-09-05 21:47:34 +00004537 if (logging != MagickFalse)
4538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4539 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004540
cristy3ed852e2009-09-05 21:47:34 +00004541 return((Image *) NULL);
4542 }
4543 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004544
cristy3ed852e2009-09-05 21:47:34 +00004545 if (image->columns == 0 || image->rows == 0)
4546 {
4547 if (logging != MagickFalse)
4548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4549 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004550
cristy3ed852e2009-09-05 21:47:34 +00004551 ThrowReaderException(CorruptImageError,"CorruptImage");
4552 }
glennrp0fe50b42010-11-16 03:52:51 +00004553
cristy3ed852e2009-09-05 21:47:34 +00004554 if (logging != MagickFalse)
4555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004556
cristy3ed852e2009-09-05 21:47:34 +00004557 return(image);
4558}
4559#endif
4560
4561static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4562{
4563 char
4564 page_geometry[MaxTextExtent];
4565
4566 Image
4567 *image,
4568 *previous;
4569
cristy4383ec82011-01-05 15:42:32 +00004570 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004571 logging,
4572 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004573
cristy3ed852e2009-09-05 21:47:34 +00004574 volatile int
4575 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004576 object_id,
4577 term_chunk_found,
4578 skip_to_iend;
4579
cristybb503372010-05-27 20:51:26 +00004580 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004581 image_count=0;
4582
4583 MagickBooleanType
4584 status;
4585
4586 MagickOffsetType
4587 offset;
4588
4589 MngInfo
4590 *mng_info;
4591
4592 MngBox
4593 default_fb,
4594 fb,
4595 previous_fb;
4596
4597#if defined(MNG_INSERT_LAYERS)
4598 PixelPacket
4599 mng_background_color;
4600#endif
4601
4602 register unsigned char
4603 *p;
4604
cristybb503372010-05-27 20:51:26 +00004605 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004606 i;
4607
4608 size_t
4609 count;
4610
cristybb503372010-05-27 20:51:26 +00004611 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004612 loop_level;
4613
4614 volatile short
4615 skipping_loop;
4616
4617#if defined(MNG_INSERT_LAYERS)
4618 unsigned int
4619 mandatory_back=0;
4620#endif
4621
4622 volatile unsigned int
4623#ifdef MNG_OBJECT_BUFFERS
4624 mng_background_object=0,
4625#endif
4626 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4627
cristybb503372010-05-27 20:51:26 +00004628 size_t
cristy3ed852e2009-09-05 21:47:34 +00004629 default_frame_timeout,
4630 frame_timeout,
4631#if defined(MNG_INSERT_LAYERS)
4632 image_height,
4633 image_width,
4634#endif
4635 length;
4636
glennrp38ea0832010-06-02 18:50:28 +00004637 /* These delays are all measured in image ticks_per_second,
4638 * not in MNG ticks_per_second
4639 */
cristybb503372010-05-27 20:51:26 +00004640 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004641 default_frame_delay,
4642 final_delay,
4643 final_image_delay,
4644 frame_delay,
4645#if defined(MNG_INSERT_LAYERS)
4646 insert_layers,
4647#endif
4648 mng_iterations=1,
4649 simplicity=0,
4650 subframe_height=0,
4651 subframe_width=0;
4652
4653 previous_fb.top=0;
4654 previous_fb.bottom=0;
4655 previous_fb.left=0;
4656 previous_fb.right=0;
4657 default_fb.top=0;
4658 default_fb.bottom=0;
4659 default_fb.left=0;
4660 default_fb.right=0;
4661
glennrp47b9dd52010-11-24 18:12:06 +00004662 /* Open image file. */
4663
cristy3ed852e2009-09-05 21:47:34 +00004664 assert(image_info != (const ImageInfo *) NULL);
4665 assert(image_info->signature == MagickSignature);
4666 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4667 assert(exception != (ExceptionInfo *) NULL);
4668 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004669 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004670 image=AcquireImage(image_info);
4671 mng_info=(MngInfo *) NULL;
4672 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004673
cristy3ed852e2009-09-05 21:47:34 +00004674 if (status == MagickFalse)
4675 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004676
cristy3ed852e2009-09-05 21:47:34 +00004677 first_mng_object=MagickFalse;
4678 skipping_loop=(-1);
4679 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004680
4681 /* Allocate a MngInfo structure. */
4682
cristy73bd4a52010-10-05 11:24:23 +00004683 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004684
cristy3ed852e2009-09-05 21:47:34 +00004685 if (mng_info == (MngInfo *) NULL)
4686 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004687
glennrp47b9dd52010-11-24 18:12:06 +00004688 /* Initialize members of the MngInfo structure. */
4689
cristy3ed852e2009-09-05 21:47:34 +00004690 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4691 mng_info->image=image;
4692 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004693
4694 if (LocaleCompare(image_info->magick,"MNG") == 0)
4695 {
4696 char
4697 magic_number[MaxTextExtent];
4698
glennrp47b9dd52010-11-24 18:12:06 +00004699 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004700 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4701 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4702 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004703
4704 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004705 for (i=0; i < MNG_MAX_OBJECTS; i++)
4706 {
cristybb503372010-05-27 20:51:26 +00004707 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4708 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004709 }
4710 mng_info->exists[0]=MagickTrue;
4711 }
glennrp47b9dd52010-11-24 18:12:06 +00004712
cristy3ed852e2009-09-05 21:47:34 +00004713 first_mng_object=MagickTrue;
4714 mng_type=0;
4715#if defined(MNG_INSERT_LAYERS)
4716 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4717#endif
4718 default_frame_delay=0;
4719 default_frame_timeout=0;
4720 frame_delay=0;
4721 final_delay=1;
4722 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4723 object_id=0;
4724 skip_to_iend=MagickFalse;
4725 term_chunk_found=MagickFalse;
4726 mng_info->framing_mode=1;
4727#if defined(MNG_INSERT_LAYERS)
4728 mandatory_back=MagickFalse;
4729#endif
4730#if defined(MNG_INSERT_LAYERS)
4731 mng_background_color=image->background_color;
4732#endif
4733 default_fb=mng_info->frame;
4734 previous_fb=mng_info->frame;
4735 do
4736 {
4737 char
4738 type[MaxTextExtent];
4739
4740 if (LocaleCompare(image_info->magick,"MNG") == 0)
4741 {
4742 unsigned char
4743 *chunk;
4744
4745 /*
4746 Read a new chunk.
4747 */
4748 type[0]='\0';
4749 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4750 length=ReadBlobMSBLong(image);
4751 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4752
4753 if (logging != MagickFalse)
4754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004755 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4756 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004757
4758 if (length > PNG_UINT_31_MAX)
4759 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004760
cristy3ed852e2009-09-05 21:47:34 +00004761 if (count == 0)
4762 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004763
cristy3ed852e2009-09-05 21:47:34 +00004764 p=NULL;
4765 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004766
cristy3ed852e2009-09-05 21:47:34 +00004767 if (length)
4768 {
4769 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004770
cristy3ed852e2009-09-05 21:47:34 +00004771 if (chunk == (unsigned char *) NULL)
4772 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004773
cristybb503372010-05-27 20:51:26 +00004774 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004775 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004776
cristy3ed852e2009-09-05 21:47:34 +00004777 p=chunk;
4778 }
glennrp0fe50b42010-11-16 03:52:51 +00004779
cristy3ed852e2009-09-05 21:47:34 +00004780 (void) ReadBlobMSBLong(image); /* read crc word */
4781
4782#if !defined(JNG_SUPPORTED)
4783 if (memcmp(type,mng_JHDR,4) == 0)
4784 {
4785 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004786
cristy3ed852e2009-09-05 21:47:34 +00004787 if (mng_info->jhdr_warning == 0)
4788 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4789 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004790
cristy3ed852e2009-09-05 21:47:34 +00004791 mng_info->jhdr_warning++;
4792 }
4793#endif
4794 if (memcmp(type,mng_DHDR,4) == 0)
4795 {
4796 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004797
cristy3ed852e2009-09-05 21:47:34 +00004798 if (mng_info->dhdr_warning == 0)
4799 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4800 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004801
cristy3ed852e2009-09-05 21:47:34 +00004802 mng_info->dhdr_warning++;
4803 }
4804 if (memcmp(type,mng_MEND,4) == 0)
4805 break;
glennrp47b9dd52010-11-24 18:12:06 +00004806
cristy3ed852e2009-09-05 21:47:34 +00004807 if (skip_to_iend)
4808 {
4809 if (memcmp(type,mng_IEND,4) == 0)
4810 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004811
cristy3ed852e2009-09-05 21:47:34 +00004812 if (length)
4813 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004814
cristy3ed852e2009-09-05 21:47:34 +00004815 if (logging != MagickFalse)
4816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4817 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004818
cristy3ed852e2009-09-05 21:47:34 +00004819 continue;
4820 }
glennrp0fe50b42010-11-16 03:52:51 +00004821
cristy3ed852e2009-09-05 21:47:34 +00004822 if (memcmp(type,mng_MHDR,4) == 0)
4823 {
cristybb503372010-05-27 20:51:26 +00004824 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004825 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004826
cristybb503372010-05-27 20:51:26 +00004827 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004828 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004829
cristy3ed852e2009-09-05 21:47:34 +00004830 if (logging != MagickFalse)
4831 {
4832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004833 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004835 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004836 }
glennrp0fe50b42010-11-16 03:52:51 +00004837
cristy3ed852e2009-09-05 21:47:34 +00004838 p+=8;
cristy8182b072010-05-30 20:10:53 +00004839 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004840
cristy3ed852e2009-09-05 21:47:34 +00004841 if (mng_info->ticks_per_second == 0)
4842 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004843
cristy3ed852e2009-09-05 21:47:34 +00004844 else
4845 default_frame_delay=1UL*image->ticks_per_second/
4846 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004847
cristy3ed852e2009-09-05 21:47:34 +00004848 frame_delay=default_frame_delay;
4849 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004850
cristy3ed852e2009-09-05 21:47:34 +00004851 if (length > 16)
4852 {
4853 p+=16;
cristy8182b072010-05-30 20:10:53 +00004854 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004855 }
glennrp0fe50b42010-11-16 03:52:51 +00004856
cristy3ed852e2009-09-05 21:47:34 +00004857 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004858
cristy3ed852e2009-09-05 21:47:34 +00004859 if ((simplicity != 0) && ((simplicity | 11) == 11))
4860 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004861
cristy3ed852e2009-09-05 21:47:34 +00004862 if ((simplicity != 0) && ((simplicity | 9) == 9))
4863 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004864
cristy3ed852e2009-09-05 21:47:34 +00004865#if defined(MNG_INSERT_LAYERS)
4866 if (mng_type != 3)
4867 insert_layers=MagickTrue;
4868#endif
cristy4c08aed2011-07-01 19:47:50 +00004869 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004870 {
glennrp47b9dd52010-11-24 18:12:06 +00004871 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004872 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00004873
cristy3ed852e2009-09-05 21:47:34 +00004874 if (GetNextImageInList(image) == (Image *) NULL)
4875 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004876
cristy3ed852e2009-09-05 21:47:34 +00004877 image=SyncNextImageInList(image);
4878 mng_info->image=image;
4879 }
4880
4881 if ((mng_info->mng_width > 65535L) ||
4882 (mng_info->mng_height > 65535L))
4883 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004884
cristy3b6fd2e2011-05-20 12:53:50 +00004885 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004886 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004887 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004888
cristy3ed852e2009-09-05 21:47:34 +00004889 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004890 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004891 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004892 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004893 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004894
cristy3ed852e2009-09-05 21:47:34 +00004895 for (i=0; i < MNG_MAX_OBJECTS; i++)
4896 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004897
cristy3ed852e2009-09-05 21:47:34 +00004898 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4899 continue;
4900 }
4901
4902 if (memcmp(type,mng_TERM,4) == 0)
4903 {
4904 int
4905 repeat=0;
4906
4907
4908 if (length)
4909 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004910
cristy3ed852e2009-09-05 21:47:34 +00004911 if (repeat == 3)
4912 {
cristy8182b072010-05-30 20:10:53 +00004913 final_delay=(png_uint_32) mng_get_long(&p[2]);
4914 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004915
cristy3ed852e2009-09-05 21:47:34 +00004916 if (mng_iterations == PNG_UINT_31_MAX)
4917 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004918
cristy3ed852e2009-09-05 21:47:34 +00004919 image->iterations=mng_iterations;
4920 term_chunk_found=MagickTrue;
4921 }
glennrp0fe50b42010-11-16 03:52:51 +00004922
cristy3ed852e2009-09-05 21:47:34 +00004923 if (logging != MagickFalse)
4924 {
4925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4926 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004927
cristy3ed852e2009-09-05 21:47:34 +00004928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004929 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004930
cristy3ed852e2009-09-05 21:47:34 +00004931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004932 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004933 }
glennrp0fe50b42010-11-16 03:52:51 +00004934
cristy3ed852e2009-09-05 21:47:34 +00004935 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4936 continue;
4937 }
4938 if (memcmp(type,mng_DEFI,4) == 0)
4939 {
4940 if (mng_type == 3)
4941 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4942 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4943 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004944
cristy3ed852e2009-09-05 21:47:34 +00004945 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004946
cristy3ed852e2009-09-05 21:47:34 +00004947 if (mng_type == 2 && object_id != 0)
4948 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4949 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4950 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004951
cristy3ed852e2009-09-05 21:47:34 +00004952 if (object_id > MNG_MAX_OBJECTS)
4953 {
4954 /*
4955 Instead ofsuing a warning we should allocate a larger
4956 MngInfo structure and continue.
4957 */
4958 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4959 CoderError,"object id too large","`%s'",image->filename);
4960 object_id=MNG_MAX_OBJECTS;
4961 }
glennrp0fe50b42010-11-16 03:52:51 +00004962
cristy3ed852e2009-09-05 21:47:34 +00004963 if (mng_info->exists[object_id])
4964 if (mng_info->frozen[object_id])
4965 {
4966 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4967 (void) ThrowMagickException(&image->exception,
4968 GetMagickModule(),CoderError,
4969 "DEFI cannot redefine a frozen MNG object","`%s'",
4970 image->filename);
4971 continue;
4972 }
glennrp0fe50b42010-11-16 03:52:51 +00004973
cristy3ed852e2009-09-05 21:47:34 +00004974 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004975
cristy3ed852e2009-09-05 21:47:34 +00004976 if (length > 2)
4977 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00004978
cristy3ed852e2009-09-05 21:47:34 +00004979 /*
4980 Extract object offset info.
4981 */
4982 if (length > 11)
4983 {
glennrp0fe50b42010-11-16 03:52:51 +00004984 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4985 (p[5] << 16) | (p[6] << 8) | p[7]);
4986
4987 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4988 (p[9] << 16) | (p[10] << 8) | p[11]);
4989
cristy3ed852e2009-09-05 21:47:34 +00004990 if (logging != MagickFalse)
4991 {
4992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004993 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004994 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00004995
cristy3ed852e2009-09-05 21:47:34 +00004996 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004997 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004998 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00004999 }
5000 }
glennrp0fe50b42010-11-16 03:52:51 +00005001
cristy3ed852e2009-09-05 21:47:34 +00005002 /*
5003 Extract object clipping info.
5004 */
5005 if (length > 27)
5006 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5007 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005008
cristy3ed852e2009-09-05 21:47:34 +00005009 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5010 continue;
5011 }
5012 if (memcmp(type,mng_bKGD,4) == 0)
5013 {
5014 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005015
cristy3ed852e2009-09-05 21:47:34 +00005016 if (length > 5)
5017 {
5018 mng_info->mng_global_bkgd.red=
5019 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005020
cristy3ed852e2009-09-05 21:47:34 +00005021 mng_info->mng_global_bkgd.green=
5022 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005023
cristy3ed852e2009-09-05 21:47:34 +00005024 mng_info->mng_global_bkgd.blue=
5025 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005026
cristy3ed852e2009-09-05 21:47:34 +00005027 mng_info->have_global_bkgd=MagickTrue;
5028 }
glennrp0fe50b42010-11-16 03:52:51 +00005029
cristy3ed852e2009-09-05 21:47:34 +00005030 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5031 continue;
5032 }
5033 if (memcmp(type,mng_BACK,4) == 0)
5034 {
5035#if defined(MNG_INSERT_LAYERS)
5036 if (length > 6)
5037 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005038
cristy3ed852e2009-09-05 21:47:34 +00005039 else
5040 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005041
cristy3ed852e2009-09-05 21:47:34 +00005042 if (mandatory_back && length > 5)
5043 {
5044 mng_background_color.red=
5045 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005046
cristy3ed852e2009-09-05 21:47:34 +00005047 mng_background_color.green=
5048 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005049
cristy3ed852e2009-09-05 21:47:34 +00005050 mng_background_color.blue=
5051 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005052
cristy4c08aed2011-07-01 19:47:50 +00005053 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005054 }
glennrp0fe50b42010-11-16 03:52:51 +00005055
cristy3ed852e2009-09-05 21:47:34 +00005056#ifdef MNG_OBJECT_BUFFERS
5057 if (length > 8)
5058 mng_background_object=(p[7] << 8) | p[8];
5059#endif
5060#endif
5061 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5062 continue;
5063 }
glennrp47b9dd52010-11-24 18:12:06 +00005064
cristy3ed852e2009-09-05 21:47:34 +00005065 if (memcmp(type,mng_PLTE,4) == 0)
5066 {
glennrp47b9dd52010-11-24 18:12:06 +00005067 /* Read global PLTE. */
5068
cristy3ed852e2009-09-05 21:47:34 +00005069 if (length && (length < 769))
5070 {
5071 if (mng_info->global_plte == (png_colorp) NULL)
5072 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5073 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005074
cristybb503372010-05-27 20:51:26 +00005075 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005076 {
5077 mng_info->global_plte[i].red=p[3*i];
5078 mng_info->global_plte[i].green=p[3*i+1];
5079 mng_info->global_plte[i].blue=p[3*i+2];
5080 }
glennrp0fe50b42010-11-16 03:52:51 +00005081
cristy35ef8242010-06-03 16:24:13 +00005082 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005083 }
5084#ifdef MNG_LOOSE
5085 for ( ; i < 256; i++)
5086 {
5087 mng_info->global_plte[i].red=i;
5088 mng_info->global_plte[i].green=i;
5089 mng_info->global_plte[i].blue=i;
5090 }
glennrp0fe50b42010-11-16 03:52:51 +00005091
cristy3ed852e2009-09-05 21:47:34 +00005092 if (length)
5093 mng_info->global_plte_length=256;
5094#endif
5095 else
5096 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005097
cristy3ed852e2009-09-05 21:47:34 +00005098 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5099 continue;
5100 }
glennrp47b9dd52010-11-24 18:12:06 +00005101
cristy3ed852e2009-09-05 21:47:34 +00005102 if (memcmp(type,mng_tRNS,4) == 0)
5103 {
5104 /* read global tRNS */
5105
5106 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005107 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005108 mng_info->global_trns[i]=p[i];
5109
5110#ifdef MNG_LOOSE
5111 for ( ; i < 256; i++)
5112 mng_info->global_trns[i]=255;
5113#endif
cristy12560f32010-06-03 16:51:08 +00005114 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005115 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5116 continue;
5117 }
5118 if (memcmp(type,mng_gAMA,4) == 0)
5119 {
5120 if (length == 4)
5121 {
cristybb503372010-05-27 20:51:26 +00005122 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005123 igamma;
5124
cristy8182b072010-05-30 20:10:53 +00005125 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005126 mng_info->global_gamma=((float) igamma)*0.00001;
5127 mng_info->have_global_gama=MagickTrue;
5128 }
glennrp0fe50b42010-11-16 03:52:51 +00005129
cristy3ed852e2009-09-05 21:47:34 +00005130 else
5131 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005132
cristy3ed852e2009-09-05 21:47:34 +00005133 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5134 continue;
5135 }
5136
5137 if (memcmp(type,mng_cHRM,4) == 0)
5138 {
glennrp47b9dd52010-11-24 18:12:06 +00005139 /* Read global cHRM */
5140
cristy3ed852e2009-09-05 21:47:34 +00005141 if (length == 32)
5142 {
cristy8182b072010-05-30 20:10:53 +00005143 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5144 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5145 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005146 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005147 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005148 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005149 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005150 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005151 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005152 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005153 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005154 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005155 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005156 mng_info->have_global_chrm=MagickTrue;
5157 }
5158 else
5159 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005160
cristy3ed852e2009-09-05 21:47:34 +00005161 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5162 continue;
5163 }
glennrp47b9dd52010-11-24 18:12:06 +00005164
cristy3ed852e2009-09-05 21:47:34 +00005165 if (memcmp(type,mng_sRGB,4) == 0)
5166 {
5167 /*
5168 Read global sRGB.
5169 */
5170 if (length)
5171 {
glennrpe610a072010-08-05 17:08:46 +00005172 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005173 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005174 mng_info->have_global_srgb=MagickTrue;
5175 }
5176 else
5177 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005178
cristy3ed852e2009-09-05 21:47:34 +00005179 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5180 continue;
5181 }
glennrp47b9dd52010-11-24 18:12:06 +00005182
cristy3ed852e2009-09-05 21:47:34 +00005183 if (memcmp(type,mng_iCCP,4) == 0)
5184 {
glennrpfd05d622011-02-25 04:10:33 +00005185 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005186
5187 /*
5188 Read global iCCP.
5189 */
5190 if (length)
5191 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005192
cristy3ed852e2009-09-05 21:47:34 +00005193 continue;
5194 }
glennrp47b9dd52010-11-24 18:12:06 +00005195
cristy3ed852e2009-09-05 21:47:34 +00005196 if (memcmp(type,mng_FRAM,4) == 0)
5197 {
5198 if (mng_type == 3)
5199 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5200 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5201 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005202
cristy3ed852e2009-09-05 21:47:34 +00005203 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5204 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005205
cristy3ed852e2009-09-05 21:47:34 +00005206 frame_delay=default_frame_delay;
5207 frame_timeout=default_frame_timeout;
5208 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005209
cristy3ed852e2009-09-05 21:47:34 +00005210 if (length)
5211 if (p[0])
5212 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005213
cristy3ed852e2009-09-05 21:47:34 +00005214 if (logging != MagickFalse)
5215 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5216 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005217
cristy3ed852e2009-09-05 21:47:34 +00005218 if (length > 6)
5219 {
glennrp47b9dd52010-11-24 18:12:06 +00005220 /* Note the delay and frame clipping boundaries. */
5221
cristy3ed852e2009-09-05 21:47:34 +00005222 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005223
cristybb503372010-05-27 20:51:26 +00005224 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005225 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005226
cristy3ed852e2009-09-05 21:47:34 +00005227 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005228
cristybb503372010-05-27 20:51:26 +00005229 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005230 {
5231 int
5232 change_delay,
5233 change_timeout,
5234 change_clipping;
5235
5236 change_delay=(*p++);
5237 change_timeout=(*p++);
5238 change_clipping=(*p++);
5239 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005240
cristy3ed852e2009-09-05 21:47:34 +00005241 if (change_delay)
5242 {
cristy8182b072010-05-30 20:10:53 +00005243 frame_delay=1UL*image->ticks_per_second*
5244 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005245
cristy8182b072010-05-30 20:10:53 +00005246 if (mng_info->ticks_per_second != 0)
5247 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005248
glennrpbb010dd2010-06-01 13:07:15 +00005249 else
5250 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005251
cristy3ed852e2009-09-05 21:47:34 +00005252 if (change_delay == 2)
5253 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005254
cristy3ed852e2009-09-05 21:47:34 +00005255 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005256
cristy3ed852e2009-09-05 21:47:34 +00005257 if (logging != MagickFalse)
5258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005259 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005260 }
glennrp47b9dd52010-11-24 18:12:06 +00005261
cristy3ed852e2009-09-05 21:47:34 +00005262 if (change_timeout)
5263 {
glennrpbb010dd2010-06-01 13:07:15 +00005264 frame_timeout=1UL*image->ticks_per_second*
5265 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005266
glennrpbb010dd2010-06-01 13:07:15 +00005267 if (mng_info->ticks_per_second != 0)
5268 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005269
glennrpbb010dd2010-06-01 13:07:15 +00005270 else
5271 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005272
cristy3ed852e2009-09-05 21:47:34 +00005273 if (change_delay == 2)
5274 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005275
cristy3ed852e2009-09-05 21:47:34 +00005276 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005277
cristy3ed852e2009-09-05 21:47:34 +00005278 if (logging != MagickFalse)
5279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005280 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005281 }
glennrp47b9dd52010-11-24 18:12:06 +00005282
cristy3ed852e2009-09-05 21:47:34 +00005283 if (change_clipping)
5284 {
5285 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5286 p+=17;
5287 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005288
cristy3ed852e2009-09-05 21:47:34 +00005289 if (logging != MagickFalse)
5290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005291 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005292 (double) fb.left,(double) fb.right,(double) fb.top,
5293 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005294
cristy3ed852e2009-09-05 21:47:34 +00005295 if (change_clipping == 2)
5296 default_fb=fb;
5297 }
5298 }
5299 }
5300 mng_info->clip=fb;
5301 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005302
cristybb503372010-05-27 20:51:26 +00005303 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005304 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005305
cristybb503372010-05-27 20:51:26 +00005306 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005307 -mng_info->clip.top);
5308 /*
5309 Insert a background layer behind the frame if framing_mode is 4.
5310 */
5311#if defined(MNG_INSERT_LAYERS)
5312 if (logging != MagickFalse)
5313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005314 " subframe_width=%.20g, subframe_height=%.20g",(double)
5315 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005316
cristy3ed852e2009-09-05 21:47:34 +00005317 if (insert_layers && (mng_info->framing_mode == 4) &&
5318 (subframe_width) && (subframe_height))
5319 {
glennrp47b9dd52010-11-24 18:12:06 +00005320 /* Allocate next image structure. */
cristy4c08aed2011-07-01 19:47:50 +00005321 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005322 {
5323 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005324
cristy3ed852e2009-09-05 21:47:34 +00005325 if (GetNextImageInList(image) == (Image *) NULL)
5326 {
5327 image=DestroyImageList(image);
5328 MngInfoFreeStruct(mng_info,&have_mng_structure);
5329 return((Image *) NULL);
5330 }
glennrp47b9dd52010-11-24 18:12:06 +00005331
cristy3ed852e2009-09-05 21:47:34 +00005332 image=SyncNextImageInList(image);
5333 }
glennrp0fe50b42010-11-16 03:52:51 +00005334
cristy3ed852e2009-09-05 21:47:34 +00005335 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005336
cristy3ed852e2009-09-05 21:47:34 +00005337 if (term_chunk_found)
5338 {
5339 image->start_loop=MagickTrue;
5340 image->iterations=mng_iterations;
5341 term_chunk_found=MagickFalse;
5342 }
glennrp0fe50b42010-11-16 03:52:51 +00005343
cristy3ed852e2009-09-05 21:47:34 +00005344 else
5345 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005346
cristy3ed852e2009-09-05 21:47:34 +00005347 image->columns=subframe_width;
5348 image->rows=subframe_height;
5349 image->page.width=subframe_width;
5350 image->page.height=subframe_height;
5351 image->page.x=mng_info->clip.left;
5352 image->page.y=mng_info->clip.top;
5353 image->background_color=mng_background_color;
5354 image->matte=MagickFalse;
5355 image->delay=0;
5356 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005357
cristy3ed852e2009-09-05 21:47:34 +00005358 if (logging != MagickFalse)
5359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005360 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005361 (double) mng_info->clip.left,(double) mng_info->clip.right,
5362 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005363 }
5364#endif
5365 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5366 continue;
5367 }
5368 if (memcmp(type,mng_CLIP,4) == 0)
5369 {
5370 unsigned int
5371 first_object,
5372 last_object;
5373
5374 /*
5375 Read CLIP.
5376 */
5377 first_object=(p[0] << 8) | p[1];
5378 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005379
cristy3ed852e2009-09-05 21:47:34 +00005380 for (i=(int) first_object; i <= (int) last_object; i++)
5381 {
5382 if (mng_info->exists[i] && !mng_info->frozen[i])
5383 {
5384 MngBox
5385 box;
5386
5387 box=mng_info->object_clip[i];
5388 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5389 }
5390 }
glennrp47b9dd52010-11-24 18:12:06 +00005391
cristy3ed852e2009-09-05 21:47:34 +00005392 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5393 continue;
5394 }
5395 if (memcmp(type,mng_SAVE,4) == 0)
5396 {
5397 for (i=1; i < MNG_MAX_OBJECTS; i++)
5398 if (mng_info->exists[i])
5399 {
5400 mng_info->frozen[i]=MagickTrue;
5401#ifdef MNG_OBJECT_BUFFERS
5402 if (mng_info->ob[i] != (MngBuffer *) NULL)
5403 mng_info->ob[i]->frozen=MagickTrue;
5404#endif
5405 }
glennrp0fe50b42010-11-16 03:52:51 +00005406
cristy3ed852e2009-09-05 21:47:34 +00005407 if (length)
5408 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005409
cristy3ed852e2009-09-05 21:47:34 +00005410 continue;
5411 }
5412
5413 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5414 {
glennrp47b9dd52010-11-24 18:12:06 +00005415 /* Read DISC or SEEK. */
5416
cristy3ed852e2009-09-05 21:47:34 +00005417 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5418 {
5419 for (i=1; i < MNG_MAX_OBJECTS; i++)
5420 MngInfoDiscardObject(mng_info,i);
5421 }
glennrp0fe50b42010-11-16 03:52:51 +00005422
cristy3ed852e2009-09-05 21:47:34 +00005423 else
5424 {
cristybb503372010-05-27 20:51:26 +00005425 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005426 j;
5427
cristybb503372010-05-27 20:51:26 +00005428 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005429 {
5430 i=p[j] << 8 | p[j+1];
5431 MngInfoDiscardObject(mng_info,i);
5432 }
5433 }
glennrp0fe50b42010-11-16 03:52:51 +00005434
cristy3ed852e2009-09-05 21:47:34 +00005435 if (length)
5436 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005437
cristy3ed852e2009-09-05 21:47:34 +00005438 continue;
5439 }
glennrp47b9dd52010-11-24 18:12:06 +00005440
cristy3ed852e2009-09-05 21:47:34 +00005441 if (memcmp(type,mng_MOVE,4) == 0)
5442 {
cristybb503372010-05-27 20:51:26 +00005443 size_t
cristy3ed852e2009-09-05 21:47:34 +00005444 first_object,
5445 last_object;
5446
glennrp47b9dd52010-11-24 18:12:06 +00005447 /* read MOVE */
5448
cristy3ed852e2009-09-05 21:47:34 +00005449 first_object=(p[0] << 8) | p[1];
5450 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005451 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005452 {
5453 if (mng_info->exists[i] && !mng_info->frozen[i])
5454 {
5455 MngPair
5456 new_pair;
5457
5458 MngPair
5459 old_pair;
5460
5461 old_pair.a=mng_info->x_off[i];
5462 old_pair.b=mng_info->y_off[i];
5463 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5464 mng_info->x_off[i]=new_pair.a;
5465 mng_info->y_off[i]=new_pair.b;
5466 }
5467 }
glennrp47b9dd52010-11-24 18:12:06 +00005468
cristy3ed852e2009-09-05 21:47:34 +00005469 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5470 continue;
5471 }
5472
5473 if (memcmp(type,mng_LOOP,4) == 0)
5474 {
cristybb503372010-05-27 20:51:26 +00005475 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005476 loop_level=chunk[0];
5477 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005478
5479 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005480 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005481
cristy3ed852e2009-09-05 21:47:34 +00005482 if (logging != MagickFalse)
5483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005484 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5485 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005486
cristy3ed852e2009-09-05 21:47:34 +00005487 if (loop_iters == 0)
5488 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005489
cristy3ed852e2009-09-05 21:47:34 +00005490 else
5491 {
5492 mng_info->loop_jump[loop_level]=TellBlob(image);
5493 mng_info->loop_count[loop_level]=loop_iters;
5494 }
glennrp0fe50b42010-11-16 03:52:51 +00005495
cristy3ed852e2009-09-05 21:47:34 +00005496 mng_info->loop_iteration[loop_level]=0;
5497 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5498 continue;
5499 }
glennrp47b9dd52010-11-24 18:12:06 +00005500
cristy3ed852e2009-09-05 21:47:34 +00005501 if (memcmp(type,mng_ENDL,4) == 0)
5502 {
5503 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005504
cristy3ed852e2009-09-05 21:47:34 +00005505 if (skipping_loop > 0)
5506 {
5507 if (skipping_loop == loop_level)
5508 {
5509 /*
5510 Found end of zero-iteration loop.
5511 */
5512 skipping_loop=(-1);
5513 mng_info->loop_active[loop_level]=0;
5514 }
5515 }
glennrp47b9dd52010-11-24 18:12:06 +00005516
cristy3ed852e2009-09-05 21:47:34 +00005517 else
5518 {
5519 if (mng_info->loop_active[loop_level] == 1)
5520 {
5521 mng_info->loop_count[loop_level]--;
5522 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005523
cristy3ed852e2009-09-05 21:47:34 +00005524 if (logging != MagickFalse)
5525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005526 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005527 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005528 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005529
cristy3ed852e2009-09-05 21:47:34 +00005530 if (mng_info->loop_count[loop_level] != 0)
5531 {
5532 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5533 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005534
cristy3ed852e2009-09-05 21:47:34 +00005535 if (offset < 0)
5536 ThrowReaderException(CorruptImageError,
5537 "ImproperImageHeader");
5538 }
glennrp47b9dd52010-11-24 18:12:06 +00005539
cristy3ed852e2009-09-05 21:47:34 +00005540 else
5541 {
5542 short
5543 last_level;
5544
5545 /*
5546 Finished loop.
5547 */
5548 mng_info->loop_active[loop_level]=0;
5549 last_level=(-1);
5550 for (i=0; i < loop_level; i++)
5551 if (mng_info->loop_active[i] == 1)
5552 last_level=(short) i;
5553 loop_level=last_level;
5554 }
5555 }
5556 }
glennrp47b9dd52010-11-24 18:12:06 +00005557
cristy3ed852e2009-09-05 21:47:34 +00005558 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5559 continue;
5560 }
glennrp47b9dd52010-11-24 18:12:06 +00005561
cristy3ed852e2009-09-05 21:47:34 +00005562 if (memcmp(type,mng_CLON,4) == 0)
5563 {
5564 if (mng_info->clon_warning == 0)
5565 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5566 CoderError,"CLON is not implemented yet","`%s'",
5567 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005568
cristy3ed852e2009-09-05 21:47:34 +00005569 mng_info->clon_warning++;
5570 }
glennrp47b9dd52010-11-24 18:12:06 +00005571
cristy3ed852e2009-09-05 21:47:34 +00005572 if (memcmp(type,mng_MAGN,4) == 0)
5573 {
5574 png_uint_16
5575 magn_first,
5576 magn_last,
5577 magn_mb,
5578 magn_ml,
5579 magn_mr,
5580 magn_mt,
5581 magn_mx,
5582 magn_my,
5583 magn_methx,
5584 magn_methy;
5585
5586 if (length > 1)
5587 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005588
cristy3ed852e2009-09-05 21:47:34 +00005589 else
5590 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005591
cristy3ed852e2009-09-05 21:47:34 +00005592 if (length > 3)
5593 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005594
cristy3ed852e2009-09-05 21:47:34 +00005595 else
5596 magn_last=magn_first;
5597#ifndef MNG_OBJECT_BUFFERS
5598 if (magn_first || magn_last)
5599 if (mng_info->magn_warning == 0)
5600 {
5601 (void) ThrowMagickException(&image->exception,
5602 GetMagickModule(),CoderError,
5603 "MAGN is not implemented yet for nonzero objects",
5604 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005605
cristy3ed852e2009-09-05 21:47:34 +00005606 mng_info->magn_warning++;
5607 }
5608#endif
5609 if (length > 4)
5610 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005611
cristy3ed852e2009-09-05 21:47:34 +00005612 else
5613 magn_methx=0;
5614
5615 if (length > 6)
5616 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005617
cristy3ed852e2009-09-05 21:47:34 +00005618 else
5619 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005620
cristy3ed852e2009-09-05 21:47:34 +00005621 if (magn_mx == 0)
5622 magn_mx=1;
5623
5624 if (length > 8)
5625 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005626
cristy3ed852e2009-09-05 21:47:34 +00005627 else
5628 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005629
cristy3ed852e2009-09-05 21:47:34 +00005630 if (magn_my == 0)
5631 magn_my=1;
5632
5633 if (length > 10)
5634 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005635
cristy3ed852e2009-09-05 21:47:34 +00005636 else
5637 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005638
cristy3ed852e2009-09-05 21:47:34 +00005639 if (magn_ml == 0)
5640 magn_ml=1;
5641
5642 if (length > 12)
5643 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005644
cristy3ed852e2009-09-05 21:47:34 +00005645 else
5646 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005647
cristy3ed852e2009-09-05 21:47:34 +00005648 if (magn_mr == 0)
5649 magn_mr=1;
5650
5651 if (length > 14)
5652 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005653
cristy3ed852e2009-09-05 21:47:34 +00005654 else
5655 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005656
cristy3ed852e2009-09-05 21:47:34 +00005657 if (magn_mt == 0)
5658 magn_mt=1;
5659
5660 if (length > 16)
5661 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005662
cristy3ed852e2009-09-05 21:47:34 +00005663 else
5664 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005665
cristy3ed852e2009-09-05 21:47:34 +00005666 if (magn_mb == 0)
5667 magn_mb=1;
5668
5669 if (length > 17)
5670 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005671
cristy3ed852e2009-09-05 21:47:34 +00005672 else
5673 magn_methy=magn_methx;
5674
glennrp47b9dd52010-11-24 18:12:06 +00005675
cristy3ed852e2009-09-05 21:47:34 +00005676 if (magn_methx > 5 || magn_methy > 5)
5677 if (mng_info->magn_warning == 0)
5678 {
5679 (void) ThrowMagickException(&image->exception,
5680 GetMagickModule(),CoderError,
5681 "Unknown MAGN method in MNG datastream","`%s'",
5682 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005683
cristy3ed852e2009-09-05 21:47:34 +00005684 mng_info->magn_warning++;
5685 }
5686#ifdef MNG_OBJECT_BUFFERS
5687 /* Magnify existing objects in the range magn_first to magn_last */
5688#endif
5689 if (magn_first == 0 || magn_last == 0)
5690 {
5691 /* Save the magnification factors for object 0 */
5692 mng_info->magn_mb=magn_mb;
5693 mng_info->magn_ml=magn_ml;
5694 mng_info->magn_mr=magn_mr;
5695 mng_info->magn_mt=magn_mt;
5696 mng_info->magn_mx=magn_mx;
5697 mng_info->magn_my=magn_my;
5698 mng_info->magn_methx=magn_methx;
5699 mng_info->magn_methy=magn_methy;
5700 }
5701 }
glennrp47b9dd52010-11-24 18:12:06 +00005702
cristy3ed852e2009-09-05 21:47:34 +00005703 if (memcmp(type,mng_PAST,4) == 0)
5704 {
5705 if (mng_info->past_warning == 0)
5706 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5707 CoderError,"PAST is not implemented yet","`%s'",
5708 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005709
cristy3ed852e2009-09-05 21:47:34 +00005710 mng_info->past_warning++;
5711 }
glennrp47b9dd52010-11-24 18:12:06 +00005712
cristy3ed852e2009-09-05 21:47:34 +00005713 if (memcmp(type,mng_SHOW,4) == 0)
5714 {
5715 if (mng_info->show_warning == 0)
5716 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5717 CoderError,"SHOW is not implemented yet","`%s'",
5718 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005719
cristy3ed852e2009-09-05 21:47:34 +00005720 mng_info->show_warning++;
5721 }
glennrp47b9dd52010-11-24 18:12:06 +00005722
cristy3ed852e2009-09-05 21:47:34 +00005723 if (memcmp(type,mng_sBIT,4) == 0)
5724 {
5725 if (length < 4)
5726 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005727
cristy3ed852e2009-09-05 21:47:34 +00005728 else
5729 {
5730 mng_info->global_sbit.gray=p[0];
5731 mng_info->global_sbit.red=p[0];
5732 mng_info->global_sbit.green=p[1];
5733 mng_info->global_sbit.blue=p[2];
5734 mng_info->global_sbit.alpha=p[3];
5735 mng_info->have_global_sbit=MagickTrue;
5736 }
5737 }
5738 if (memcmp(type,mng_pHYs,4) == 0)
5739 {
5740 if (length > 8)
5741 {
5742 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005743 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005744 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005745 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005746 mng_info->global_phys_unit_type=p[8];
5747 mng_info->have_global_phys=MagickTrue;
5748 }
glennrp47b9dd52010-11-24 18:12:06 +00005749
cristy3ed852e2009-09-05 21:47:34 +00005750 else
5751 mng_info->have_global_phys=MagickFalse;
5752 }
5753 if (memcmp(type,mng_pHYg,4) == 0)
5754 {
5755 if (mng_info->phyg_warning == 0)
5756 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5757 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005758
cristy3ed852e2009-09-05 21:47:34 +00005759 mng_info->phyg_warning++;
5760 }
5761 if (memcmp(type,mng_BASI,4) == 0)
5762 {
5763 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005764
cristy3ed852e2009-09-05 21:47:34 +00005765 if (mng_info->basi_warning == 0)
5766 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5767 CoderError,"BASI is not implemented yet","`%s'",
5768 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005769
cristy3ed852e2009-09-05 21:47:34 +00005770 mng_info->basi_warning++;
5771#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005772 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005773 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005774 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005775 (p[6] << 8) | p[7]);
5776 basi_color_type=p[8];
5777 basi_compression_method=p[9];
5778 basi_filter_type=p[10];
5779 basi_interlace_method=p[11];
5780 if (length > 11)
5781 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005782
cristy3ed852e2009-09-05 21:47:34 +00005783 else
5784 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005785
cristy3ed852e2009-09-05 21:47:34 +00005786 if (length > 13)
5787 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005788
cristy3ed852e2009-09-05 21:47:34 +00005789 else
5790 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005791
cristy3ed852e2009-09-05 21:47:34 +00005792 if (length > 15)
5793 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005794
cristy3ed852e2009-09-05 21:47:34 +00005795 else
5796 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005797
cristy3ed852e2009-09-05 21:47:34 +00005798 if (length > 17)
5799 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005800
cristy3ed852e2009-09-05 21:47:34 +00005801 else
5802 {
5803 if (basi_sample_depth == 16)
5804 basi_alpha=65535L;
5805 else
5806 basi_alpha=255;
5807 }
glennrp47b9dd52010-11-24 18:12:06 +00005808
cristy3ed852e2009-09-05 21:47:34 +00005809 if (length > 19)
5810 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005811
cristy3ed852e2009-09-05 21:47:34 +00005812 else
5813 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005814
cristy3ed852e2009-09-05 21:47:34 +00005815#endif
5816 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5817 continue;
5818 }
glennrp47b9dd52010-11-24 18:12:06 +00005819
cristy3ed852e2009-09-05 21:47:34 +00005820 if (memcmp(type,mng_IHDR,4)
5821#if defined(JNG_SUPPORTED)
5822 && memcmp(type,mng_JHDR,4)
5823#endif
5824 )
5825 {
5826 /* Not an IHDR or JHDR chunk */
5827 if (length)
5828 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005829
cristy3ed852e2009-09-05 21:47:34 +00005830 continue;
5831 }
5832/* Process IHDR */
5833 if (logging != MagickFalse)
5834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5835 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005836
cristy3ed852e2009-09-05 21:47:34 +00005837 mng_info->exists[object_id]=MagickTrue;
5838 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005839
cristy3ed852e2009-09-05 21:47:34 +00005840 if (mng_info->invisible[object_id])
5841 {
5842 if (logging != MagickFalse)
5843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5844 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005845
cristy3ed852e2009-09-05 21:47:34 +00005846 skip_to_iend=MagickTrue;
5847 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5848 continue;
5849 }
5850#if defined(MNG_INSERT_LAYERS)
5851 if (length < 8)
5852 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005853
cristy8182b072010-05-30 20:10:53 +00005854 image_width=(size_t) mng_get_long(p);
5855 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005856#endif
5857 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5858
5859 /*
5860 Insert a transparent background layer behind the entire animation
5861 if it is not full screen.
5862 */
5863#if defined(MNG_INSERT_LAYERS)
5864 if (insert_layers && mng_type && first_mng_object)
5865 {
5866 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5867 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005868 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005869 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005870 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005871 {
cristy4c08aed2011-07-01 19:47:50 +00005872 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005873 {
5874 /*
5875 Allocate next image structure.
5876 */
5877 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005878
cristy3ed852e2009-09-05 21:47:34 +00005879 if (GetNextImageInList(image) == (Image *) NULL)
5880 {
5881 image=DestroyImageList(image);
5882 MngInfoFreeStruct(mng_info,&have_mng_structure);
5883 return((Image *) NULL);
5884 }
glennrp47b9dd52010-11-24 18:12:06 +00005885
cristy3ed852e2009-09-05 21:47:34 +00005886 image=SyncNextImageInList(image);
5887 }
5888 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005889
cristy3ed852e2009-09-05 21:47:34 +00005890 if (term_chunk_found)
5891 {
5892 image->start_loop=MagickTrue;
5893 image->iterations=mng_iterations;
5894 term_chunk_found=MagickFalse;
5895 }
glennrp47b9dd52010-11-24 18:12:06 +00005896
cristy3ed852e2009-09-05 21:47:34 +00005897 else
5898 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005899
5900 /* Make a background rectangle. */
5901
cristy3ed852e2009-09-05 21:47:34 +00005902 image->delay=0;
5903 image->columns=mng_info->mng_width;
5904 image->rows=mng_info->mng_height;
5905 image->page.width=mng_info->mng_width;
5906 image->page.height=mng_info->mng_height;
5907 image->page.x=0;
5908 image->page.y=0;
5909 image->background_color=mng_background_color;
5910 (void) SetImageBackgroundColor(image);
5911 if (logging != MagickFalse)
5912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005913 " Inserted transparent background layer, W=%.20g, H=%.20g",
5914 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005915 }
5916 }
5917 /*
5918 Insert a background layer behind the upcoming image if
5919 framing_mode is 3, and we haven't already inserted one.
5920 */
5921 if (insert_layers && (mng_info->framing_mode == 3) &&
5922 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5923 (simplicity & 0x08)))
5924 {
cristy4c08aed2011-07-01 19:47:50 +00005925 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005926 {
5927 /*
5928 Allocate next image structure.
5929 */
5930 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005931
cristy3ed852e2009-09-05 21:47:34 +00005932 if (GetNextImageInList(image) == (Image *) NULL)
5933 {
5934 image=DestroyImageList(image);
5935 MngInfoFreeStruct(mng_info,&have_mng_structure);
5936 return((Image *) NULL);
5937 }
glennrp47b9dd52010-11-24 18:12:06 +00005938
cristy3ed852e2009-09-05 21:47:34 +00005939 image=SyncNextImageInList(image);
5940 }
glennrp0fe50b42010-11-16 03:52:51 +00005941
cristy3ed852e2009-09-05 21:47:34 +00005942 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005943
cristy3ed852e2009-09-05 21:47:34 +00005944 if (term_chunk_found)
5945 {
5946 image->start_loop=MagickTrue;
5947 image->iterations=mng_iterations;
5948 term_chunk_found=MagickFalse;
5949 }
glennrp0fe50b42010-11-16 03:52:51 +00005950
cristy3ed852e2009-09-05 21:47:34 +00005951 else
5952 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005953
cristy3ed852e2009-09-05 21:47:34 +00005954 image->delay=0;
5955 image->columns=subframe_width;
5956 image->rows=subframe_height;
5957 image->page.width=subframe_width;
5958 image->page.height=subframe_height;
5959 image->page.x=mng_info->clip.left;
5960 image->page.y=mng_info->clip.top;
5961 image->background_color=mng_background_color;
5962 image->matte=MagickFalse;
5963 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005964
cristy3ed852e2009-09-05 21:47:34 +00005965 if (logging != MagickFalse)
5966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005967 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005968 (double) mng_info->clip.left,(double) mng_info->clip.right,
5969 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005970 }
5971#endif /* MNG_INSERT_LAYERS */
5972 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005973
cristy4c08aed2011-07-01 19:47:50 +00005974 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005975 {
5976 /*
5977 Allocate next image structure.
5978 */
5979 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005980
cristy3ed852e2009-09-05 21:47:34 +00005981 if (GetNextImageInList(image) == (Image *) NULL)
5982 {
5983 image=DestroyImageList(image);
5984 MngInfoFreeStruct(mng_info,&have_mng_structure);
5985 return((Image *) NULL);
5986 }
glennrp47b9dd52010-11-24 18:12:06 +00005987
cristy3ed852e2009-09-05 21:47:34 +00005988 image=SyncNextImageInList(image);
5989 }
5990 mng_info->image=image;
5991 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5992 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00005993
cristy3ed852e2009-09-05 21:47:34 +00005994 if (status == MagickFalse)
5995 break;
glennrp0fe50b42010-11-16 03:52:51 +00005996
cristy3ed852e2009-09-05 21:47:34 +00005997 if (term_chunk_found)
5998 {
5999 image->start_loop=MagickTrue;
6000 term_chunk_found=MagickFalse;
6001 }
glennrp0fe50b42010-11-16 03:52:51 +00006002
cristy3ed852e2009-09-05 21:47:34 +00006003 else
6004 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006005
cristy3ed852e2009-09-05 21:47:34 +00006006 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6007 {
6008 image->delay=frame_delay;
6009 frame_delay=default_frame_delay;
6010 }
glennrp0fe50b42010-11-16 03:52:51 +00006011
cristy3ed852e2009-09-05 21:47:34 +00006012 else
6013 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006014
cristy3ed852e2009-09-05 21:47:34 +00006015 image->page.width=mng_info->mng_width;
6016 image->page.height=mng_info->mng_height;
6017 image->page.x=mng_info->x_off[object_id];
6018 image->page.y=mng_info->y_off[object_id];
6019 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006020
cristy3ed852e2009-09-05 21:47:34 +00006021 /*
6022 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6023 */
glennrp47b9dd52010-11-24 18:12:06 +00006024
cristy3ed852e2009-09-05 21:47:34 +00006025 if (logging != MagickFalse)
6026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6027 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6028 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006029
cristybb503372010-05-27 20:51:26 +00006030 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006031
cristy3ed852e2009-09-05 21:47:34 +00006032 if (offset < 0)
6033 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6034 }
6035
6036 previous=image;
6037 mng_info->image=image;
6038 mng_info->mng_type=mng_type;
6039 mng_info->object_id=object_id;
6040
6041 if (memcmp(type,mng_IHDR,4) == 0)
6042 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006043
cristy3ed852e2009-09-05 21:47:34 +00006044#if defined(JNG_SUPPORTED)
6045 else
6046 image=ReadOneJNGImage(mng_info,image_info,exception);
6047#endif
6048
6049 if (image == (Image *) NULL)
6050 {
6051 if (IsImageObject(previous) != MagickFalse)
6052 {
6053 (void) DestroyImageList(previous);
6054 (void) CloseBlob(previous);
6055 }
glennrp47b9dd52010-11-24 18:12:06 +00006056
cristy3ed852e2009-09-05 21:47:34 +00006057 MngInfoFreeStruct(mng_info,&have_mng_structure);
6058 return((Image *) NULL);
6059 }
glennrp0fe50b42010-11-16 03:52:51 +00006060
cristy3ed852e2009-09-05 21:47:34 +00006061 if (image->columns == 0 || image->rows == 0)
6062 {
6063 (void) CloseBlob(image);
6064 image=DestroyImageList(image);
6065 MngInfoFreeStruct(mng_info,&have_mng_structure);
6066 return((Image *) NULL);
6067 }
glennrp0fe50b42010-11-16 03:52:51 +00006068
cristy3ed852e2009-09-05 21:47:34 +00006069 mng_info->image=image;
6070
6071 if (mng_type)
6072 {
6073 MngBox
6074 crop_box;
6075
6076 if (mng_info->magn_methx || mng_info->magn_methy)
6077 {
6078 png_uint_32
6079 magnified_height,
6080 magnified_width;
6081
6082 if (logging != MagickFalse)
6083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6084 " Processing MNG MAGN chunk");
6085
6086 if (mng_info->magn_methx == 1)
6087 {
6088 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006089
cristy3ed852e2009-09-05 21:47:34 +00006090 if (image->columns > 1)
6091 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006092
cristy3ed852e2009-09-05 21:47:34 +00006093 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006094 magnified_width += (png_uint_32)
6095 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006096 }
glennrp47b9dd52010-11-24 18:12:06 +00006097
cristy3ed852e2009-09-05 21:47:34 +00006098 else
6099 {
cristy4e5bc842010-06-09 13:56:01 +00006100 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006101
cristy3ed852e2009-09-05 21:47:34 +00006102 if (image->columns > 1)
6103 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006104
cristy3ed852e2009-09-05 21:47:34 +00006105 if (image->columns > 2)
6106 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006107
cristy3ed852e2009-09-05 21:47:34 +00006108 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006109 magnified_width += (png_uint_32)
6110 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006111 }
glennrp47b9dd52010-11-24 18:12:06 +00006112
cristy3ed852e2009-09-05 21:47:34 +00006113 if (mng_info->magn_methy == 1)
6114 {
6115 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006116
cristy3ed852e2009-09-05 21:47:34 +00006117 if (image->rows > 1)
6118 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006119
cristy3ed852e2009-09-05 21:47:34 +00006120 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006121 magnified_height += (png_uint_32)
6122 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006123 }
glennrp47b9dd52010-11-24 18:12:06 +00006124
cristy3ed852e2009-09-05 21:47:34 +00006125 else
6126 {
cristy4e5bc842010-06-09 13:56:01 +00006127 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006128
cristy3ed852e2009-09-05 21:47:34 +00006129 if (image->rows > 1)
6130 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006131
cristy3ed852e2009-09-05 21:47:34 +00006132 if (image->rows > 2)
6133 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006134
cristy3ed852e2009-09-05 21:47:34 +00006135 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006136 magnified_height += (png_uint_32)
6137 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006138 }
glennrp47b9dd52010-11-24 18:12:06 +00006139
cristy3ed852e2009-09-05 21:47:34 +00006140 if (magnified_height > image->rows ||
6141 magnified_width > image->columns)
6142 {
6143 Image
6144 *large_image;
6145
6146 int
6147 yy;
6148
cristy4c08aed2011-07-01 19:47:50 +00006149 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006150 *next,
6151 *prev;
6152
6153 png_uint_16
6154 magn_methx,
6155 magn_methy;
6156
cristy4c08aed2011-07-01 19:47:50 +00006157 ssize_t
6158 m,
6159 y;
6160
6161 register Quantum
6162 *n,
6163 *q;
6164
6165 register ssize_t
6166 x;
6167
glennrp47b9dd52010-11-24 18:12:06 +00006168 /* Allocate next image structure. */
6169
cristy3ed852e2009-09-05 21:47:34 +00006170 if (logging != MagickFalse)
6171 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6172 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006173
cristy3ed852e2009-09-05 21:47:34 +00006174 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00006175
cristy3ed852e2009-09-05 21:47:34 +00006176 if (GetNextImageInList(image) == (Image *) NULL)
6177 {
6178 image=DestroyImageList(image);
6179 MngInfoFreeStruct(mng_info,&have_mng_structure);
6180 return((Image *) NULL);
6181 }
6182
6183 large_image=SyncNextImageInList(image);
6184
6185 large_image->columns=magnified_width;
6186 large_image->rows=magnified_height;
6187
6188 magn_methx=mng_info->magn_methx;
6189 magn_methy=mng_info->magn_methy;
6190
glennrp3faa9a32011-04-23 14:00:25 +00006191#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006192#define QM unsigned short
6193 if (magn_methx != 1 || magn_methy != 1)
6194 {
6195 /*
6196 Scale pixels to unsigned shorts to prevent
6197 overflow of intermediate values of interpolations
6198 */
cristybb503372010-05-27 20:51:26 +00006199 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006200 {
6201 q=GetAuthenticPixels(image,0,y,image->columns,1,
6202 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006203
cristybb503372010-05-27 20:51:26 +00006204 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006205 {
cristy4c08aed2011-07-01 19:47:50 +00006206 SetPixelRed(image,ScaleQuantumToShort(
6207 GetPixelRed(image,q)),q);
6208 SetPixelGreen(image,ScaleQuantumToShort(
6209 GetPixelGreen(image,q)),q);
6210 SetPixelBlue(image,ScaleQuantumToShort(
6211 GetPixelBlue(image,q)),q);
6212 SetPixelAlpha(image,ScaleQuantumToShort(
6213 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006214 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006215 }
glennrp47b9dd52010-11-24 18:12:06 +00006216
cristy3ed852e2009-09-05 21:47:34 +00006217 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6218 break;
6219 }
6220 }
6221#else
6222#define QM Quantum
6223#endif
6224
6225 if (image->matte != MagickFalse)
6226 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006227
cristy3ed852e2009-09-05 21:47:34 +00006228 else
6229 {
cristy4c08aed2011-07-01 19:47:50 +00006230 large_image->background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00006231 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006232
cristy3ed852e2009-09-05 21:47:34 +00006233 if (magn_methx == 4)
6234 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006235
cristy3ed852e2009-09-05 21:47:34 +00006236 if (magn_methx == 5)
6237 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006238
cristy3ed852e2009-09-05 21:47:34 +00006239 if (magn_methy == 4)
6240 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006241
cristy3ed852e2009-09-05 21:47:34 +00006242 if (magn_methy == 5)
6243 magn_methy=3;
6244 }
6245
6246 /* magnify the rows into the right side of the large image */
6247
6248 if (logging != MagickFalse)
6249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006250 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006251 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006252 yy=0;
6253 length=(size_t) image->columns;
cristy4c08aed2011-07-01 19:47:50 +00006254 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6255 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006256
cristy4c08aed2011-07-01 19:47:50 +00006257 if ((prev == (Quantum *) NULL) ||
6258 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006259 {
6260 image=DestroyImageList(image);
6261 MngInfoFreeStruct(mng_info,&have_mng_structure);
6262 ThrowReaderException(ResourceLimitError,
6263 "MemoryAllocationFailed");
6264 }
glennrp47b9dd52010-11-24 18:12:06 +00006265
cristy3ed852e2009-09-05 21:47:34 +00006266 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6267 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006268
cristybb503372010-05-27 20:51:26 +00006269 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006270 {
6271 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006272 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006273
cristybb503372010-05-27 20:51:26 +00006274 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6275 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006276
cristybb503372010-05-27 20:51:26 +00006277 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6278 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006279
cristybb503372010-05-27 20:51:26 +00006280 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006281 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006282
cristy3ed852e2009-09-05 21:47:34 +00006283 else
cristybb503372010-05-27 20:51:26 +00006284 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006285
cristy3ed852e2009-09-05 21:47:34 +00006286 n=prev;
6287 prev=next;
6288 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006289
cristybb503372010-05-27 20:51:26 +00006290 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006291 {
6292 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6293 exception);
6294 (void) CopyMagickMemory(next,n,length);
6295 }
glennrp47b9dd52010-11-24 18:12:06 +00006296
cristy3ed852e2009-09-05 21:47:34 +00006297 for (i=0; i < m; i++, yy++)
6298 {
cristy4c08aed2011-07-01 19:47:50 +00006299 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006300 *pixels;
6301
cristybb503372010-05-27 20:51:26 +00006302 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006303 pixels=prev;
6304 n=next;
6305 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006306 1,exception);
cristy3ed852e2009-09-05 21:47:34 +00006307 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00006308
cristybb503372010-05-27 20:51:26 +00006309 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006310 {
glennrpfd05d622011-02-25 04:10:33 +00006311 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006312 /*
6313 if (image->storage_class == PseudoClass)
6314 {
6315 }
6316 */
6317
6318 if (magn_methy <= 1)
6319 {
glennrpbb4f99d2011-05-22 11:13:17 +00006320 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006321 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
glennrp847370c2011-07-05 17:37:15 +00006322 SetPixelGreen(large_image,GetPixelGreen(image,
6323 pixels),q);
6324 SetPixelBlue(large_image,GetPixelBlue(image,
6325 pixels),q);
6326 SetPixelAlpha(large_image,GetPixelAlpha(image,
6327 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006328 }
glennrp47b9dd52010-11-24 18:12:06 +00006329
cristy3ed852e2009-09-05 21:47:34 +00006330 else if (magn_methy == 2 || magn_methy == 4)
6331 {
6332 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006333 {
glennrp847370c2011-07-05 17:37:15 +00006334 SetPixelRed(large_image,GetPixelRed(image,
6335 pixels),q);
6336 SetPixelGreen(large_image,GetPixelGreen(image,
6337 pixels),q);
6338 SetPixelBlue(large_image,GetPixelBlue(image,
6339 pixels),q);
6340 SetPixelAlpha(large_image,GetPixelAlpha(image,
6341 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006342 }
glennrp47b9dd52010-11-24 18:12:06 +00006343
cristy3ed852e2009-09-05 21:47:34 +00006344 else
6345 {
6346 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006347 SetPixelRed(large_image,((QM) (((ssize_t)
6348 (2*i*(GetPixelRed(image,n)
6349 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006350 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006351 +GetPixelRed(image,pixels)))),q);
6352 SetPixelGreen(large_image,((QM) (((ssize_t)
6353 (2*i*(GetPixelGreen(image,n)
6354 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006355 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006356 +GetPixelGreen(image,pixels)))),q);
6357 SetPixelBlue(large_image,((QM) (((ssize_t)
6358 (2*i*(GetPixelBlue(image,n)
6359 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006360 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006361 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006362
cristy3ed852e2009-09-05 21:47:34 +00006363 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006364 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6365 (2*i*(GetPixelAlpha(image,n)
6366 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006367 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006368 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006369 }
glennrp47b9dd52010-11-24 18:12:06 +00006370
cristy3ed852e2009-09-05 21:47:34 +00006371 if (magn_methy == 4)
6372 {
6373 /* Replicate nearest */
6374 if (i <= ((m+1) << 1))
glennrp847370c2011-07-05 17:37:15 +00006375 SetPixelAlpha(large_image,GetPixelAlpha(image,
6376 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006377 else
glennrp847370c2011-07-05 17:37:15 +00006378 SetPixelAlpha(large_image,GetPixelAlpha(image,
6379 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006380 }
6381 }
glennrp47b9dd52010-11-24 18:12:06 +00006382
cristy3ed852e2009-09-05 21:47:34 +00006383 else /* if (magn_methy == 3 || magn_methy == 5) */
6384 {
6385 /* Replicate nearest */
6386 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006387 {
glennrp847370c2011-07-05 17:37:15 +00006388 SetPixelRed(large_image,GetPixelRed(image,
6389 pixels),q);
6390 SetPixelGreen(large_image,GetPixelGreen(image,
6391 pixels),q);
6392 SetPixelBlue(large_image,GetPixelBlue(image,
6393 pixels),q);
6394 SetPixelAlpha(large_image,GetPixelAlpha(image,
6395 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006396 }
glennrp47b9dd52010-11-24 18:12:06 +00006397
cristy3ed852e2009-09-05 21:47:34 +00006398 else
glennrpbb4f99d2011-05-22 11:13:17 +00006399 {
cristy4c08aed2011-07-01 19:47:50 +00006400 SetPixelRed(large_image,GetPixelRed(image,n),q);
glennrp847370c2011-07-05 17:37:15 +00006401 SetPixelGreen(large_image,GetPixelGreen(image,n),
6402 q);
6403 SetPixelBlue(large_image,GetPixelBlue(image,n),
6404 q);
6405 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6406 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006407 }
glennrp47b9dd52010-11-24 18:12:06 +00006408
cristy3ed852e2009-09-05 21:47:34 +00006409 if (magn_methy == 5)
6410 {
cristy4c08aed2011-07-01 19:47:50 +00006411 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6412 (GetPixelAlpha(image,n)
6413 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006414 +m))/((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006415 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006416 }
6417 }
cristyed231572011-07-14 02:18:59 +00006418 n+=GetPixelChannels(image);
6419 q+=GetPixelChannels(large_image);
6420 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006421 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006422
cristy3ed852e2009-09-05 21:47:34 +00006423 if (SyncAuthenticPixels(large_image,exception) == 0)
6424 break;
glennrp47b9dd52010-11-24 18:12:06 +00006425
cristy3ed852e2009-09-05 21:47:34 +00006426 } /* i */
6427 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006428
cristy4c08aed2011-07-01 19:47:50 +00006429 prev=(Quantum *) RelinquishMagickMemory(prev);
6430 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006431
6432 length=image->columns;
6433
6434 if (logging != MagickFalse)
6435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6436 " Delete original image");
6437
6438 DeleteImageFromList(&image);
6439
6440 image=large_image;
6441
6442 mng_info->image=image;
6443
6444 /* magnify the columns */
6445 if (logging != MagickFalse)
6446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006447 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006448
cristybb503372010-05-27 20:51:26 +00006449 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006450 {
cristy4c08aed2011-07-01 19:47:50 +00006451 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006452 *pixels;
6453
6454 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristyed231572011-07-14 02:18:59 +00006455 pixels=q+(image->columns-length)*GetPixelChannels(image);
6456 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006457
cristybb503372010-05-27 20:51:26 +00006458 for (x=(ssize_t) (image->columns-length);
6459 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006460 {
cristyed231572011-07-14 02:18:59 +00006461 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006462
cristybb503372010-05-27 20:51:26 +00006463 if (x == (ssize_t) (image->columns-length))
6464 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006465
cristybb503372010-05-27 20:51:26 +00006466 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6467 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006468
cristybb503372010-05-27 20:51:26 +00006469 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6470 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006471
cristybb503372010-05-27 20:51:26 +00006472 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006473 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006474
cristy3ed852e2009-09-05 21:47:34 +00006475 else
cristybb503372010-05-27 20:51:26 +00006476 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006477
cristy3ed852e2009-09-05 21:47:34 +00006478 for (i=0; i < m; i++)
6479 {
6480 if (magn_methx <= 1)
6481 {
6482 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006483 SetPixelRed(image,GetPixelRed(image,pixels),q);
6484 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6485 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6486 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006487 }
glennrp47b9dd52010-11-24 18:12:06 +00006488
cristy3ed852e2009-09-05 21:47:34 +00006489 else if (magn_methx == 2 || magn_methx == 4)
6490 {
6491 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006492 {
cristy4c08aed2011-07-01 19:47:50 +00006493 SetPixelRed(image,GetPixelRed(image,pixels),q);
6494 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6495 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6496 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006497 }
glennrp47b9dd52010-11-24 18:12:06 +00006498
cristyed231572011-07-14 02:18:59 +00006499 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006500 else
6501 {
6502 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006503 SetPixelRed(image,(QM) ((2*i*(
6504 GetPixelRed(image,n)
6505 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006506 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006507 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006508
cristy4c08aed2011-07-01 19:47:50 +00006509 SetPixelGreen(image,(QM) ((2*i*(
6510 GetPixelGreen(image,n)
6511 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006512 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006513 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006514
cristy4c08aed2011-07-01 19:47:50 +00006515 SetPixelBlue(image,(QM) ((2*i*(
6516 GetPixelBlue(image,n)
6517 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006518 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006519 GetPixelBlue(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006520 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006521 SetPixelAlpha(image,(QM) ((2*i*(
6522 GetPixelAlpha(image,n)
6523 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006524 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006525 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006526 }
glennrp47b9dd52010-11-24 18:12:06 +00006527
cristy3ed852e2009-09-05 21:47:34 +00006528 if (magn_methx == 4)
6529 {
6530 /* Replicate nearest */
6531 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006532 {
cristy4c08aed2011-07-01 19:47:50 +00006533 SetPixelAlpha(image,
6534 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006535 }
cristy3ed852e2009-09-05 21:47:34 +00006536 else
glennrpbb4f99d2011-05-22 11:13:17 +00006537 {
cristy4c08aed2011-07-01 19:47:50 +00006538 SetPixelAlpha(image,
6539 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006540 }
cristy3ed852e2009-09-05 21:47:34 +00006541 }
6542 }
glennrp47b9dd52010-11-24 18:12:06 +00006543
cristy3ed852e2009-09-05 21:47:34 +00006544 else /* if (magn_methx == 3 || magn_methx == 5) */
6545 {
6546 /* Replicate nearest */
6547 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006548 {
cristy4c08aed2011-07-01 19:47:50 +00006549 SetPixelRed(image,GetPixelRed(image,pixels),q);
6550 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6551 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6552 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006553 }
glennrp47b9dd52010-11-24 18:12:06 +00006554
cristy3ed852e2009-09-05 21:47:34 +00006555 else
glennrpbb4f99d2011-05-22 11:13:17 +00006556 {
cristy4c08aed2011-07-01 19:47:50 +00006557 SetPixelRed(image,GetPixelRed(image,n),q);
6558 SetPixelGreen(image,GetPixelGreen(image,n),q);
6559 SetPixelBlue(image,GetPixelBlue(image,n),q);
6560 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006561 }
glennrp47b9dd52010-11-24 18:12:06 +00006562
cristy3ed852e2009-09-05 21:47:34 +00006563 if (magn_methx == 5)
6564 {
6565 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006566 SetPixelAlpha(image,
6567 (QM) ((2*i*( GetPixelAlpha(image,n)
6568 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006569 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006570 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006571 }
6572 }
cristyed231572011-07-14 02:18:59 +00006573 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006574 }
cristyed231572011-07-14 02:18:59 +00006575 n+=GetPixelChannels(image);
6576 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006577 }
glennrp47b9dd52010-11-24 18:12:06 +00006578
cristy3ed852e2009-09-05 21:47:34 +00006579 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6580 break;
6581 }
glennrp3faa9a32011-04-23 14:00:25 +00006582#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006583 if (magn_methx != 1 || magn_methy != 1)
6584 {
6585 /*
6586 Rescale pixels to Quantum
6587 */
cristybb503372010-05-27 20:51:26 +00006588 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006589 {
6590 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006591
cristybb503372010-05-27 20:51:26 +00006592 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006593 {
cristy4c08aed2011-07-01 19:47:50 +00006594 SetPixelRed(image,ScaleShortToQuantum(
6595 GetPixelRed(image,q)),q);
6596 SetPixelGreen(image,ScaleShortToQuantum(
6597 GetPixelGreen(image,q)),q);
6598 SetPixelBlue(image,ScaleShortToQuantum(
6599 GetPixelBlue(image,q)),q);
6600 SetPixelAlpha(image,ScaleShortToQuantum(
6601 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006602 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006603 }
glennrp47b9dd52010-11-24 18:12:06 +00006604
cristy3ed852e2009-09-05 21:47:34 +00006605 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6606 break;
6607 }
6608 }
6609#endif
6610 if (logging != MagickFalse)
6611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6612 " Finished MAGN processing");
6613 }
6614 }
6615
6616 /*
6617 Crop_box is with respect to the upper left corner of the MNG.
6618 */
6619 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6620 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6621 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6622 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6623 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6624 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6625 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6626 if ((crop_box.left != (mng_info->image_box.left
6627 +mng_info->x_off[object_id])) ||
6628 (crop_box.right != (mng_info->image_box.right
6629 +mng_info->x_off[object_id])) ||
6630 (crop_box.top != (mng_info->image_box.top
6631 +mng_info->y_off[object_id])) ||
6632 (crop_box.bottom != (mng_info->image_box.bottom
6633 +mng_info->y_off[object_id])))
6634 {
6635 if (logging != MagickFalse)
6636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6637 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006638
cristy3ed852e2009-09-05 21:47:34 +00006639 if ((crop_box.left < crop_box.right) &&
6640 (crop_box.top < crop_box.bottom))
6641 {
6642 Image
6643 *im;
6644
6645 RectangleInfo
6646 crop_info;
6647
6648 /*
6649 Crop_info is with respect to the upper left corner of
6650 the image.
6651 */
6652 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6653 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006654 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6655 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006656 image->page.width=image->columns;
6657 image->page.height=image->rows;
6658 image->page.x=0;
6659 image->page.y=0;
6660 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006661
cristy3ed852e2009-09-05 21:47:34 +00006662 if (im != (Image *) NULL)
6663 {
6664 image->columns=im->columns;
6665 image->rows=im->rows;
6666 im=DestroyImage(im);
6667 image->page.width=image->columns;
6668 image->page.height=image->rows;
6669 image->page.x=crop_box.left;
6670 image->page.y=crop_box.top;
6671 }
6672 }
glennrp47b9dd52010-11-24 18:12:06 +00006673
cristy3ed852e2009-09-05 21:47:34 +00006674 else
6675 {
6676 /*
6677 No pixels in crop area. The MNG spec still requires
6678 a layer, though, so make a single transparent pixel in
6679 the top left corner.
6680 */
6681 image->columns=1;
6682 image->rows=1;
6683 image->colors=2;
6684 (void) SetImageBackgroundColor(image);
6685 image->page.width=1;
6686 image->page.height=1;
6687 image->page.x=0;
6688 image->page.y=0;
6689 }
6690 }
6691#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6692 image=mng_info->image;
6693#endif
6694 }
6695
glennrp2b013e42010-11-24 16:55:50 +00006696#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6697 /* PNG does not handle depths greater than 16 so reduce it even
6698 * if lossy
6699 */
6700 if (image->depth > 16)
6701 image->depth=16;
6702#endif
6703
glennrp3faa9a32011-04-23 14:00:25 +00006704#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006705 if (LosslessReduceDepthOK(image) != MagickFalse)
6706 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006707#endif
glennrpd6afd542010-11-19 01:53:05 +00006708
cristy3ed852e2009-09-05 21:47:34 +00006709 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006710
cristy3ed852e2009-09-05 21:47:34 +00006711 if (image_info->number_scenes != 0)
6712 {
6713 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006714 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006715 break;
6716 }
glennrpd6afd542010-11-19 01:53:05 +00006717
cristy3ed852e2009-09-05 21:47:34 +00006718 if (logging != MagickFalse)
6719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6720 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006721
cristy3ed852e2009-09-05 21:47:34 +00006722 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006723
cristy3ed852e2009-09-05 21:47:34 +00006724 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006725
cristy3ed852e2009-09-05 21:47:34 +00006726 if (logging != MagickFalse)
6727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6728 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006729
cristy3ed852e2009-09-05 21:47:34 +00006730#if defined(MNG_INSERT_LAYERS)
6731 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6732 (mng_info->mng_height))
6733 {
6734 /*
6735 Insert a background layer if nothing else was found.
6736 */
6737 if (logging != MagickFalse)
6738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6739 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006740
cristy4c08aed2011-07-01 19:47:50 +00006741 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006742 {
6743 /*
6744 Allocate next image structure.
6745 */
6746 AcquireNextImage(image_info,image);
6747 if (GetNextImageInList(image) == (Image *) NULL)
6748 {
6749 image=DestroyImageList(image);
6750 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006751
cristy3ed852e2009-09-05 21:47:34 +00006752 if (logging != MagickFalse)
6753 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6754 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006755
cristy3ed852e2009-09-05 21:47:34 +00006756 return((Image *) NULL);
6757 }
6758 image=SyncNextImageInList(image);
6759 }
6760 image->columns=mng_info->mng_width;
6761 image->rows=mng_info->mng_height;
6762 image->page.width=mng_info->mng_width;
6763 image->page.height=mng_info->mng_height;
6764 image->page.x=0;
6765 image->page.y=0;
6766 image->background_color=mng_background_color;
6767 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006768
cristy3ed852e2009-09-05 21:47:34 +00006769 if (image_info->ping == MagickFalse)
6770 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006771
cristy3ed852e2009-09-05 21:47:34 +00006772 mng_info->image_found++;
6773 }
6774#endif
6775 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006776
cristy3ed852e2009-09-05 21:47:34 +00006777 if (mng_iterations == 1)
6778 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006779
cristy3ed852e2009-09-05 21:47:34 +00006780 while (GetPreviousImageInList(image) != (Image *) NULL)
6781 {
6782 image_count++;
6783 if (image_count > 10*mng_info->image_found)
6784 {
6785 if (logging != MagickFalse)
6786 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006787
cristy3ed852e2009-09-05 21:47:34 +00006788 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6789 CoderError,"Linked list is corrupted, beginning of list not found",
6790 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006791
cristy3ed852e2009-09-05 21:47:34 +00006792 return((Image *) NULL);
6793 }
glennrp0fe50b42010-11-16 03:52:51 +00006794
cristy3ed852e2009-09-05 21:47:34 +00006795 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006796
cristy3ed852e2009-09-05 21:47:34 +00006797 if (GetNextImageInList(image) == (Image *) NULL)
6798 {
6799 if (logging != MagickFalse)
6800 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006801
cristy3ed852e2009-09-05 21:47:34 +00006802 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6803 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6804 image_info->filename);
6805 }
6806 }
glennrp47b9dd52010-11-24 18:12:06 +00006807
cristy3ed852e2009-09-05 21:47:34 +00006808 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6809 GetNextImageInList(image) ==
6810 (Image *) NULL)
6811 {
6812 if (logging != MagickFalse)
6813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6814 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006815
cristy3ed852e2009-09-05 21:47:34 +00006816 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6817 CoderError,"image->next for first image is NULL but shouldn't be.",
6818 "`%s'",image_info->filename);
6819 }
glennrp47b9dd52010-11-24 18:12:06 +00006820
cristy3ed852e2009-09-05 21:47:34 +00006821 if (mng_info->image_found == 0)
6822 {
6823 if (logging != MagickFalse)
6824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6825 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006826
cristy3ed852e2009-09-05 21:47:34 +00006827 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6828 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006829
cristy3ed852e2009-09-05 21:47:34 +00006830 if (image != (Image *) NULL)
6831 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006832
cristy3ed852e2009-09-05 21:47:34 +00006833 MngInfoFreeStruct(mng_info,&have_mng_structure);
6834 return((Image *) NULL);
6835 }
6836
6837 if (mng_info->ticks_per_second)
6838 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6839 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006840
cristy3ed852e2009-09-05 21:47:34 +00006841 else
6842 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006843
cristy3ed852e2009-09-05 21:47:34 +00006844 /* Find final nonzero image delay */
6845 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006846
cristy3ed852e2009-09-05 21:47:34 +00006847 while (GetNextImageInList(image) != (Image *) NULL)
6848 {
6849 if (image->delay)
6850 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006851
cristy3ed852e2009-09-05 21:47:34 +00006852 image=GetNextImageInList(image);
6853 }
glennrp0fe50b42010-11-16 03:52:51 +00006854
cristy3ed852e2009-09-05 21:47:34 +00006855 if (final_delay < final_image_delay)
6856 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006857
cristy3ed852e2009-09-05 21:47:34 +00006858 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006859
cristy3ed852e2009-09-05 21:47:34 +00006860 if (logging != MagickFalse)
6861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006862 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6863 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006864
cristy3ed852e2009-09-05 21:47:34 +00006865 if (logging != MagickFalse)
6866 {
6867 int
6868 scene;
6869
6870 scene=0;
6871 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006872
cristy3ed852e2009-09-05 21:47:34 +00006873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6874 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006875
cristy3ed852e2009-09-05 21:47:34 +00006876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006877 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006878
cristy3ed852e2009-09-05 21:47:34 +00006879 while (GetNextImageInList(image) != (Image *) NULL)
6880 {
6881 image=GetNextImageInList(image);
6882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006883 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006884 }
6885 }
6886
6887 image=GetFirstImageInList(image);
6888#ifdef MNG_COALESCE_LAYERS
6889 if (insert_layers)
6890 {
6891 Image
6892 *next_image,
6893 *next;
6894
cristybb503372010-05-27 20:51:26 +00006895 size_t
cristy3ed852e2009-09-05 21:47:34 +00006896 scene;
6897
6898 if (logging != MagickFalse)
6899 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006900
cristy3ed852e2009-09-05 21:47:34 +00006901 scene=image->scene;
6902 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006903
cristy3ed852e2009-09-05 21:47:34 +00006904 if (next_image == (Image *) NULL)
6905 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006906
cristy3ed852e2009-09-05 21:47:34 +00006907 image=DestroyImageList(image);
6908 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006909
cristy3ed852e2009-09-05 21:47:34 +00006910 for (next=image; next != (Image *) NULL; next=next_image)
6911 {
6912 next->page.width=mng_info->mng_width;
6913 next->page.height=mng_info->mng_height;
6914 next->page.x=0;
6915 next->page.y=0;
6916 next->scene=scene++;
6917 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006918
cristy3ed852e2009-09-05 21:47:34 +00006919 if (next_image == (Image *) NULL)
6920 break;
glennrp47b9dd52010-11-24 18:12:06 +00006921
cristy3ed852e2009-09-05 21:47:34 +00006922 if (next->delay == 0)
6923 {
6924 scene--;
6925 next_image->previous=GetPreviousImageInList(next);
6926 if (GetPreviousImageInList(next) == (Image *) NULL)
6927 image=next_image;
6928 else
6929 next->previous->next=next_image;
6930 next=DestroyImage(next);
6931 }
6932 }
6933 }
6934#endif
6935
6936 while (GetNextImageInList(image) != (Image *) NULL)
6937 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006938
cristy3ed852e2009-09-05 21:47:34 +00006939 image->dispose=BackgroundDispose;
6940
6941 if (logging != MagickFalse)
6942 {
6943 int
6944 scene;
6945
6946 scene=0;
6947 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006948
cristy3ed852e2009-09-05 21:47:34 +00006949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6950 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006951
cristy3ed852e2009-09-05 21:47:34 +00006952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006953 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6954 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006955
cristy3ed852e2009-09-05 21:47:34 +00006956 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006957 {
6958 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006959
cristyf2faecf2010-05-28 19:19:36 +00006960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006961 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6962 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006963 }
6964 }
glennrp47b9dd52010-11-24 18:12:06 +00006965
cristy3ed852e2009-09-05 21:47:34 +00006966 image=GetFirstImageInList(image);
6967 MngInfoFreeStruct(mng_info,&have_mng_structure);
6968 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006969
cristy3ed852e2009-09-05 21:47:34 +00006970 if (logging != MagickFalse)
6971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006972
cristy3ed852e2009-09-05 21:47:34 +00006973 return(GetFirstImageInList(image));
6974}
glennrp25c1e2b2010-03-25 01:39:56 +00006975#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006976static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6977{
6978 printf("Your PNG library is too old: You have libpng-%s\n",
6979 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006980
cristy3ed852e2009-09-05 21:47:34 +00006981 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6982 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006983
cristy3ed852e2009-09-05 21:47:34 +00006984 return(Image *) NULL;
6985}
glennrp47b9dd52010-11-24 18:12:06 +00006986
cristy3ed852e2009-09-05 21:47:34 +00006987static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6988{
6989 return(ReadPNGImage(image_info,exception));
6990}
glennrp25c1e2b2010-03-25 01:39:56 +00006991#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006992#endif
6993
6994/*
6995%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6996% %
6997% %
6998% %
6999% R e g i s t e r P N G I m a g e %
7000% %
7001% %
7002% %
7003%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7004%
7005% RegisterPNGImage() adds properties for the PNG image format to
7006% the list of supported formats. The properties include the image format
7007% tag, a method to read and/or write the format, whether the format
7008% supports the saving of more than one frame to the same file or blob,
7009% whether the format supports native in-memory I/O, and a brief
7010% description of the format.
7011%
7012% The format of the RegisterPNGImage method is:
7013%
cristybb503372010-05-27 20:51:26 +00007014% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007015%
7016*/
cristybb503372010-05-27 20:51:26 +00007017ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007018{
7019 char
7020 version[MaxTextExtent];
7021
7022 MagickInfo
7023 *entry;
7024
7025 static const char
7026 *PNGNote=
7027 {
7028 "See http://www.libpng.org/ for details about the PNG format."
7029 },
glennrp47b9dd52010-11-24 18:12:06 +00007030
cristy3ed852e2009-09-05 21:47:34 +00007031 *JNGNote=
7032 {
7033 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7034 "format."
7035 },
glennrp47b9dd52010-11-24 18:12:06 +00007036
cristy3ed852e2009-09-05 21:47:34 +00007037 *MNGNote=
7038 {
7039 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7040 "format."
7041 };
7042
7043 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007044
cristy3ed852e2009-09-05 21:47:34 +00007045#if defined(PNG_LIBPNG_VER_STRING)
7046 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7047 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007048
cristy3ed852e2009-09-05 21:47:34 +00007049 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7050 {
7051 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7052 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7053 MaxTextExtent);
7054 }
7055#endif
glennrp47b9dd52010-11-24 18:12:06 +00007056
cristy3ed852e2009-09-05 21:47:34 +00007057 entry=SetMagickInfo("MNG");
7058 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007059
cristy3ed852e2009-09-05 21:47:34 +00007060#if defined(MAGICKCORE_PNG_DELEGATE)
7061 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7062 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7063#endif
glennrp47b9dd52010-11-24 18:12:06 +00007064
cristy3ed852e2009-09-05 21:47:34 +00007065 entry->magick=(IsImageFormatHandler *) IsMNG;
7066 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007067
cristy3ed852e2009-09-05 21:47:34 +00007068 if (*version != '\0')
7069 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007070
cristy3ed852e2009-09-05 21:47:34 +00007071 entry->module=ConstantString("PNG");
7072 entry->note=ConstantString(MNGNote);
7073 (void) RegisterMagickInfo(entry);
7074
7075 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007076
cristy3ed852e2009-09-05 21:47:34 +00007077#if defined(MAGICKCORE_PNG_DELEGATE)
7078 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7079 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7080#endif
glennrp47b9dd52010-11-24 18:12:06 +00007081
cristy3ed852e2009-09-05 21:47:34 +00007082 entry->magick=(IsImageFormatHandler *) IsPNG;
7083 entry->adjoin=MagickFalse;
7084 entry->description=ConstantString("Portable Network Graphics");
7085 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007086
cristy3ed852e2009-09-05 21:47:34 +00007087 if (*version != '\0')
7088 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007089
cristy3ed852e2009-09-05 21:47:34 +00007090 entry->note=ConstantString(PNGNote);
7091 (void) RegisterMagickInfo(entry);
7092
7093 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007094
cristy3ed852e2009-09-05 21:47:34 +00007095#if defined(MAGICKCORE_PNG_DELEGATE)
7096 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7097 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7098#endif
glennrp47b9dd52010-11-24 18:12:06 +00007099
cristy3ed852e2009-09-05 21:47:34 +00007100 entry->magick=(IsImageFormatHandler *) IsPNG;
7101 entry->adjoin=MagickFalse;
7102 entry->description=ConstantString(
7103 "8-bit indexed with optional binary transparency");
7104 entry->module=ConstantString("PNG");
7105 (void) RegisterMagickInfo(entry);
7106
7107 entry=SetMagickInfo("PNG24");
7108 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007109
cristy3ed852e2009-09-05 21:47:34 +00007110#if defined(ZLIB_VERSION)
7111 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7112 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007113
cristy3ed852e2009-09-05 21:47:34 +00007114 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7115 {
7116 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7117 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7118 }
7119#endif
glennrp47b9dd52010-11-24 18:12:06 +00007120
cristy3ed852e2009-09-05 21:47:34 +00007121 if (*version != '\0')
7122 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007123
cristy3ed852e2009-09-05 21:47:34 +00007124#if defined(MAGICKCORE_PNG_DELEGATE)
7125 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7126 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7127#endif
glennrp47b9dd52010-11-24 18:12:06 +00007128
cristy3ed852e2009-09-05 21:47:34 +00007129 entry->magick=(IsImageFormatHandler *) IsPNG;
7130 entry->adjoin=MagickFalse;
7131 entry->description=ConstantString("opaque 24-bit RGB");
7132 entry->module=ConstantString("PNG");
7133 (void) RegisterMagickInfo(entry);
7134
7135 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007136
cristy3ed852e2009-09-05 21:47:34 +00007137#if defined(MAGICKCORE_PNG_DELEGATE)
7138 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7139 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7140#endif
glennrp47b9dd52010-11-24 18:12:06 +00007141
cristy3ed852e2009-09-05 21:47:34 +00007142 entry->magick=(IsImageFormatHandler *) IsPNG;
7143 entry->adjoin=MagickFalse;
7144 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7145 entry->module=ConstantString("PNG");
7146 (void) RegisterMagickInfo(entry);
7147
7148 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007149
cristy3ed852e2009-09-05 21:47:34 +00007150#if defined(JNG_SUPPORTED)
7151#if defined(MAGICKCORE_PNG_DELEGATE)
7152 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7153 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7154#endif
7155#endif
glennrp47b9dd52010-11-24 18:12:06 +00007156
cristy3ed852e2009-09-05 21:47:34 +00007157 entry->magick=(IsImageFormatHandler *) IsJNG;
7158 entry->adjoin=MagickFalse;
7159 entry->description=ConstantString("JPEG Network Graphics");
7160 entry->module=ConstantString("PNG");
7161 entry->note=ConstantString(JNGNote);
7162 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007163
cristy18b17442009-10-25 18:36:48 +00007164#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007165 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007166#endif
glennrp47b9dd52010-11-24 18:12:06 +00007167
cristy3ed852e2009-09-05 21:47:34 +00007168 return(MagickImageCoderSignature);
7169}
7170
7171/*
7172%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7173% %
7174% %
7175% %
7176% U n r e g i s t e r P N G I m a g e %
7177% %
7178% %
7179% %
7180%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7181%
7182% UnregisterPNGImage() removes format registrations made by the
7183% PNG module from the list of supported formats.
7184%
7185% The format of the UnregisterPNGImage method is:
7186%
7187% UnregisterPNGImage(void)
7188%
7189*/
7190ModuleExport void UnregisterPNGImage(void)
7191{
7192 (void) UnregisterMagickInfo("MNG");
7193 (void) UnregisterMagickInfo("PNG");
7194 (void) UnregisterMagickInfo("PNG8");
7195 (void) UnregisterMagickInfo("PNG24");
7196 (void) UnregisterMagickInfo("PNG32");
7197 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007198
cristy3ed852e2009-09-05 21:47:34 +00007199#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007200 if (ping_semaphore != (SemaphoreInfo *) NULL)
7201 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007202#endif
7203}
7204
7205#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007206#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007207/*
7208%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7209% %
7210% %
7211% %
7212% W r i t e M N G I m a g e %
7213% %
7214% %
7215% %
7216%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7217%
7218% WriteMNGImage() writes an image in the Portable Network Graphics
7219% Group's "Multiple-image Network Graphics" encoded image format.
7220%
7221% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7222%
7223% The format of the WriteMNGImage method is:
7224%
cristy1e178e72011-08-28 19:44:34 +00007225% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7226% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007227%
7228% A description of each parameter follows.
7229%
7230% o image_info: the image info.
7231%
7232% o image: The image.
7233%
cristy1e178e72011-08-28 19:44:34 +00007234% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007235%
7236% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7237% "To do" under ReadPNGImage):
7238%
cristy3ed852e2009-09-05 21:47:34 +00007239% Preserve all unknown and not-yet-handled known chunks found in input
7240% PNG file and copy them into output PNG files according to the PNG
7241% copying rules.
7242%
7243% Write the iCCP chunk at MNG level when (icc profile length > 0)
7244%
7245% Improve selection of color type (use indexed-colour or indexed-colour
7246% with tRNS when 256 or fewer unique RGBA values are present).
7247%
7248% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7249% This will be complicated if we limit ourselves to generating MNG-LC
7250% files. For now we ignore disposal method 3 and simply overlay the next
7251% image on it.
7252%
7253% Check for identical PLTE's or PLTE/tRNS combinations and use a
7254% global MNG PLTE or PLTE/tRNS combination when appropriate.
7255% [mostly done 15 June 1999 but still need to take care of tRNS]
7256%
7257% Check for identical sRGB and replace with a global sRGB (and remove
7258% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7259% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7260% local gAMA/cHRM with local sRGB if appropriate).
7261%
7262% Check for identical sBIT chunks and write global ones.
7263%
7264% Provide option to skip writing the signature tEXt chunks.
7265%
7266% Use signatures to detect identical objects and reuse the first
7267% instance of such objects instead of writing duplicate objects.
7268%
7269% Use a smaller-than-32k value of compression window size when
7270% appropriate.
7271%
7272% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7273% ancillary text chunks and save profiles.
7274%
7275% Provide an option to force LC files (to ensure exact framing rate)
7276% instead of VLC.
7277%
7278% Provide an option to force VLC files instead of LC, even when offsets
7279% are present. This will involve expanding the embedded images with a
7280% transparent region at the top and/or left.
7281*/
7282
cristy3ed852e2009-09-05 21:47:34 +00007283static void
glennrpcf002022011-01-30 02:38:15 +00007284Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007285 png_info *ping_info, unsigned char *profile_type, unsigned char
7286 *profile_description, unsigned char *profile_data, png_uint_32 length)
7287{
cristy3ed852e2009-09-05 21:47:34 +00007288 png_textp
7289 text;
7290
cristybb503372010-05-27 20:51:26 +00007291 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007292 i;
7293
7294 unsigned char
7295 *sp;
7296
7297 png_charp
7298 dp;
7299
7300 png_uint_32
7301 allocated_length,
7302 description_length;
7303
7304 unsigned char
7305 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007306
cristy3ed852e2009-09-05 21:47:34 +00007307 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7308 return;
7309
7310 if (image_info->verbose)
7311 {
glennrp0fe50b42010-11-16 03:52:51 +00007312 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7313 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007314 }
glennrp0fe50b42010-11-16 03:52:51 +00007315
cristy3ed852e2009-09-05 21:47:34 +00007316 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7317 description_length=(png_uint_32) strlen((const char *) profile_description);
7318 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7319 + description_length);
7320 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7321 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7322 text[0].key[0]='\0';
7323 (void) ConcatenateMagickString(text[0].key,
7324 "Raw profile type ",MaxTextExtent);
7325 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7326 sp=profile_data;
7327 dp=text[0].text;
7328 *dp++='\n';
7329 (void) CopyMagickString(dp,(const char *) profile_description,
7330 allocated_length);
7331 dp+=description_length;
7332 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007333 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007334 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007335 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007336
cristybb503372010-05-27 20:51:26 +00007337 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007338 {
7339 if (i%36 == 0)
7340 *dp++='\n';
7341 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7342 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7343 }
glennrp47b9dd52010-11-24 18:12:06 +00007344
cristy3ed852e2009-09-05 21:47:34 +00007345 *dp++='\n';
7346 *dp='\0';
7347 text[0].text_length=(png_size_t) (dp-text[0].text);
7348 text[0].compression=image_info->compression == NoCompression ||
7349 (image_info->compression == UndefinedCompression &&
7350 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007351
cristy3ed852e2009-09-05 21:47:34 +00007352 if (text[0].text_length <= allocated_length)
7353 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007354
cristy3ed852e2009-09-05 21:47:34 +00007355 png_free(ping,text[0].text);
7356 png_free(ping,text[0].key);
7357 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007358}
7359
glennrpcf002022011-01-30 02:38:15 +00007360static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007361 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007362{
7363 char
7364 *name;
7365
7366 const StringInfo
7367 *profile;
7368
7369 unsigned char
7370 *data;
7371
7372 png_uint_32 length;
7373
7374 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007375
7376 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7377 {
cristy3ed852e2009-09-05 21:47:34 +00007378 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007379
cristy3ed852e2009-09-05 21:47:34 +00007380 if (profile != (const StringInfo *) NULL)
7381 {
7382 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007383 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007384
glennrp47b9dd52010-11-24 18:12:06 +00007385 if (LocaleNCompare(name,string,11) == 0)
7386 {
7387 if (logging != MagickFalse)
7388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7389 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007390
glennrpcf002022011-01-30 02:38:15 +00007391 ping_profile=CloneStringInfo(profile);
7392 data=GetStringInfoDatum(ping_profile),
7393 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007394 data[4]=data[3];
7395 data[3]=data[2];
7396 data[2]=data[1];
7397 data[1]=data[0];
7398 (void) WriteBlobMSBULong(image,length-5); /* data length */
7399 (void) WriteBlob(image,length-1,data+1);
7400 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007401 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007402 }
cristy3ed852e2009-09-05 21:47:34 +00007403 }
glennrp47b9dd52010-11-24 18:12:06 +00007404
cristy3ed852e2009-09-05 21:47:34 +00007405 name=GetNextImageProfile(image);
7406 }
glennrp47b9dd52010-11-24 18:12:06 +00007407
cristy3ed852e2009-09-05 21:47:34 +00007408 return(MagickTrue);
7409}
7410
glennrpb9cfe272010-12-21 15:08:06 +00007411
cristy3ed852e2009-09-05 21:47:34 +00007412/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007413static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7414 const ImageInfo *IMimage_info,Image *IMimage)
7415{
7416 Image
7417 *image;
7418
7419 ImageInfo
7420 *image_info;
7421
cristy3ed852e2009-09-05 21:47:34 +00007422 char
7423 s[2];
7424
7425 const char
7426 *name,
7427 *property,
7428 *value;
7429
7430 const StringInfo
7431 *profile;
7432
cristy3ed852e2009-09-05 21:47:34 +00007433 int
cristy3ed852e2009-09-05 21:47:34 +00007434 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007435 pass;
7436
glennrpe9c26dc2010-05-30 01:56:35 +00007437 png_byte
7438 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007439
glennrp39992b42010-11-14 00:03:43 +00007440 png_color
7441 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007442
glennrp5af765f2010-03-30 11:12:18 +00007443 png_color_16
7444 ping_background,
7445 ping_trans_color;
7446
cristy3ed852e2009-09-05 21:47:34 +00007447 png_info
7448 *ping_info;
7449
7450 png_struct
7451 *ping;
7452
glennrp5af765f2010-03-30 11:12:18 +00007453 png_uint_32
7454 ping_height,
7455 ping_width;
7456
cristybb503372010-05-27 20:51:26 +00007457 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007458 y;
7459
7460 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007461 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007462 logging,
glennrp58e01762011-01-07 15:28:54 +00007463 matte,
7464
glennrpda8f3a72011-02-27 23:54:12 +00007465 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007466 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007467 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007468 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007469 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007470 ping_have_bKGD,
7471 ping_have_pHYs,
7472 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007473
7474 ping_exclude_bKGD,
7475 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007476 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007477 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007478 ping_exclude_gAMA,
7479 ping_exclude_iCCP,
7480 /* ping_exclude_iTXt, */
7481 ping_exclude_oFFs,
7482 ping_exclude_pHYs,
7483 ping_exclude_sRGB,
7484 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007485 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007486 ping_exclude_vpAg,
7487 ping_exclude_zCCP, /* hex-encoded iCCP */
7488 ping_exclude_zTXt,
7489
glennrp8d3d6e52011-04-19 04:39:51 +00007490 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007491 ping_need_colortype_warning,
7492
glennrp82b3c532011-03-22 19:20:54 +00007493 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007494 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007495 tried_333,
7496 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007497
7498 QuantumInfo
7499 *quantum_info;
7500
cristybb503372010-05-27 20:51:26 +00007501 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007502 i,
7503 x;
7504
7505 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007506 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007507
glennrp5af765f2010-03-30 11:12:18 +00007508 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007509 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007510 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007511 ping_color_type,
7512 ping_interlace_method,
7513 ping_compression_method,
7514 ping_filter_method,
7515 ping_num_trans;
7516
cristybb503372010-05-27 20:51:26 +00007517 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007518 image_depth,
7519 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007520
cristybb503372010-05-27 20:51:26 +00007521 size_t
cristy3ed852e2009-09-05 21:47:34 +00007522 quality,
7523 rowbytes,
7524 save_image_depth;
7525
glennrpdfd70802010-11-14 01:23:35 +00007526 int
glennrpfd05d622011-02-25 04:10:33 +00007527 j,
glennrpf09bded2011-01-08 01:15:59 +00007528 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007529 number_opaque,
7530 number_semitransparent,
7531 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007532 ping_pHYs_unit_type;
7533
7534 png_uint_32
7535 ping_pHYs_x_resolution,
7536 ping_pHYs_y_resolution;
7537
cristy3ed852e2009-09-05 21:47:34 +00007538 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007539 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007540
glennrpb9cfe272010-12-21 15:08:06 +00007541 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7542 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007543 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007544 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007545
cristy3ed852e2009-09-05 21:47:34 +00007546#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007547 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007548#endif
7549
glennrp5af765f2010-03-30 11:12:18 +00007550 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007551 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007552 ping_color_type=0,
7553 ping_interlace_method=0,
7554 ping_compression_method=0,
7555 ping_filter_method=0,
7556 ping_num_trans = 0;
7557
7558 ping_background.red = 0;
7559 ping_background.green = 0;
7560 ping_background.blue = 0;
7561 ping_background.gray = 0;
7562 ping_background.index = 0;
7563
7564 ping_trans_color.red=0;
7565 ping_trans_color.green=0;
7566 ping_trans_color.blue=0;
7567 ping_trans_color.gray=0;
7568
glennrpdfd70802010-11-14 01:23:35 +00007569 ping_pHYs_unit_type = 0;
7570 ping_pHYs_x_resolution = 0;
7571 ping_pHYs_y_resolution = 0;
7572
glennrpda8f3a72011-02-27 23:54:12 +00007573 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007574 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007575 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007576 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007577 ping_have_bKGD=MagickFalse;
7578 ping_have_pHYs=MagickFalse;
7579 ping_have_tRNS=MagickFalse;
7580
glennrp0e8ea192010-12-24 18:00:33 +00007581 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7582 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007583 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007584 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007585 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007586 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7587 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7588 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7589 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7590 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7591 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007592 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007593 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7594 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7595 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7596
glennrp8d3d6e52011-04-19 04:39:51 +00007597 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007598 ping_need_colortype_warning = MagickFalse;
7599
glennrp56081722011-08-27 21:48:13 +00007600 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7601 * i.e., eliminate the ICC profile and set image->rendering_intent.
7602 * Note that this will not involve any changes to the actual pixels
7603 * but merely passes information to applications that read the resulting
7604 * PNG image.
7605 */
7606 if (ping_exclude_sRGB == MagickFalse)
7607 {
7608 char
7609 *name;
7610
7611 const StringInfo
7612 *profile;
7613
7614 ResetImageProfileIterator(image);
7615 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7616 {
7617 profile=GetImageProfile(image,name);
7618
7619 if (profile != (StringInfo *) NULL)
7620 {
7621 if ((LocaleCompare(name,"ICC") == 0) ||
7622 (LocaleCompare(name,"ICM") == 0))
7623 {
7624 unsigned char
7625 *data;
7626
7627 png_uint_32
7628 length;
7629
7630 {
7631 length=(png_uint_32) GetStringInfoLength(profile);
7632
7633 if (length == 3144)
7634 {
7635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7636 " got a 3144-byte ICC profile (potentially sRGB)");
7637
7638 data=GetStringInfoDatum(profile);
7639
7640 if (data[52]=='s' && data[53]=='R' &&
7641 data[54]=='G' && data[55]=='B')
7642 {
7643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7644 " It is sRGB)");
7645 if (image->rendering_intent==UndefinedIntent);
7646 image->rendering_intent=PerceptualIntent;
7647 }
7648 else
7649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7650 " It is not sRGB (%c%c%c%c)",data[52],
7651 data[53],data[54],data[55]);
7652
7653 }
7654 else
7655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7656 " got a %lu-byte ICC profile",
7657 (unsigned long) length);
7658 }
7659 }
7660 }
7661 name=GetNextImageProfile(image);
7662 }
7663 }
7664
glennrp8bb3a022010-12-13 20:40:04 +00007665 number_opaque = 0;
7666 number_semitransparent = 0;
7667 number_transparent = 0;
7668
glennrpfd05d622011-02-25 04:10:33 +00007669 if (logging != MagickFalse)
7670 {
7671 if (image->storage_class == UndefinedClass)
7672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7673 " storage_class=UndefinedClass");
7674 if (image->storage_class == DirectClass)
7675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7676 " storage_class=DirectClass");
7677 if (image->storage_class == PseudoClass)
7678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7679 " storage_class=PseudoClass");
7680 }
glennrp28af3712011-04-06 18:07:30 +00007681
glennrp7e65e932011-08-19 02:31:16 +00007682 if (image->storage_class == PseudoClass &&
7683 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7684 (mng_info->write_png_colortype != 0 &&
7685 mng_info->write_png_colortype != 4)))
7686 {
7687 (void) SyncImage(image);
7688 image->storage_class = DirectClass;
7689 }
7690
glennrpc6c391a2011-04-27 02:23:56 +00007691 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007692 {
glennrpc6c391a2011-04-27 02:23:56 +00007693 if (image->storage_class != PseudoClass && image->colormap != NULL)
7694 {
7695 /* Free the bogus colormap; it can cause trouble later */
7696 if (logging != MagickFalse)
7697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7698 " Freeing bogus colormap");
7699 (void *) RelinquishMagickMemory(image->colormap);
7700 image->colormap=NULL;
7701 }
glennrp28af3712011-04-06 18:07:30 +00007702 }
glennrpbb4f99d2011-05-22 11:13:17 +00007703
cristy510d06a2011-07-06 23:43:54 +00007704 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00007705 (void) TransformImageColorspace(image,RGBColorspace);
glennrp0fe50b42010-11-16 03:52:51 +00007706
glennrp3241bd02010-12-12 04:36:28 +00007707 /*
7708 Sometimes we get PseudoClass images whose RGB values don't match
7709 the colors in the colormap. This code syncs the RGB values.
7710 */
7711 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7712 (void) SyncImage(image);
7713
glennrpa6a06632011-01-19 15:15:34 +00007714#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7715 if (image->depth > 8)
7716 {
7717 if (logging != MagickFalse)
7718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7719 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7720
7721 image->depth=8;
7722 }
7723#endif
7724
glennrp8e58efd2011-05-20 12:16:29 +00007725 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007726 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7727 {
cristy4c08aed2011-07-01 19:47:50 +00007728 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007729 *r;
7730
7731 ExceptionInfo
7732 *exception;
7733
7734 exception=(&image->exception);
7735
7736 if (image->depth > 8)
7737 {
7738#if MAGICKCORE_QUANTUM_DEPTH > 16
7739 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007740 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007741
7742 for (y=0; y < (ssize_t) image->rows; y++)
7743 {
7744 r=GetAuthenticPixels(image,0,y,image->columns,1,
7745 exception);
7746
cristy4c08aed2011-07-01 19:47:50 +00007747 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007748 break;
7749
7750 for (x=0; x < (ssize_t) image->columns; x++)
7751 {
glennrp54cf7972011-08-06 14:28:09 +00007752 LBR16PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007753 r++;
7754 }
glennrpbb4f99d2011-05-22 11:13:17 +00007755
glennrp8e58efd2011-05-20 12:16:29 +00007756 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7757 break;
7758 }
7759
7760 if (image->storage_class == PseudoClass && image->colormap != NULL)
7761 {
cristy3e08f112011-05-24 13:19:30 +00007762 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007763 {
glennrp91d99252011-06-25 14:30:13 +00007764 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007765 }
7766 }
7767#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7768 }
7769
7770 else if (image->depth > 4)
7771 {
7772#if MAGICKCORE_QUANTUM_DEPTH > 8
7773 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007774 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007775
7776 for (y=0; y < (ssize_t) image->rows; y++)
7777 {
7778 r=GetAuthenticPixels(image,0,y,image->columns,1,
7779 exception);
7780
cristy4c08aed2011-07-01 19:47:50 +00007781 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007782 break;
7783
7784 for (x=0; x < (ssize_t) image->columns; x++)
7785 {
glennrp54cf7972011-08-06 14:28:09 +00007786 LBR08PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007787 r++;
7788 }
glennrpbb4f99d2011-05-22 11:13:17 +00007789
glennrp8e58efd2011-05-20 12:16:29 +00007790 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7791 break;
7792 }
7793
7794 if (image->storage_class == PseudoClass && image->colormap != NULL)
7795 {
cristy3e08f112011-05-24 13:19:30 +00007796 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007797 {
glennrp91d99252011-06-25 14:30:13 +00007798 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007799 }
7800 }
7801#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7802 }
7803 else
7804 if (image->depth > 2)
7805 {
7806 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007807 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007808
7809 for (y=0; y < (ssize_t) image->rows; y++)
7810 {
7811 r=GetAuthenticPixels(image,0,y,image->columns,1,
7812 exception);
7813
cristy4c08aed2011-07-01 19:47:50 +00007814 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007815 break;
7816
7817 for (x=0; x < (ssize_t) image->columns; x++)
7818 {
glennrp54cf7972011-08-06 14:28:09 +00007819 LBR04PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007820 r++;
7821 }
glennrpbb4f99d2011-05-22 11:13:17 +00007822
glennrp8e58efd2011-05-20 12:16:29 +00007823 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7824 break;
7825 }
7826
7827 if (image->storage_class == PseudoClass && image->colormap != NULL)
7828 {
cristy3e08f112011-05-24 13:19:30 +00007829 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007830 {
glennrp91d99252011-06-25 14:30:13 +00007831 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007832 }
7833 }
7834 }
7835
7836 else if (image->depth > 1)
7837 {
7838 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007839 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007840
7841 for (y=0; y < (ssize_t) image->rows; y++)
7842 {
7843 r=GetAuthenticPixels(image,0,y,image->columns,1,
7844 exception);
7845
cristy4c08aed2011-07-01 19:47:50 +00007846 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007847 break;
7848
7849 for (x=0; x < (ssize_t) image->columns; x++)
7850 {
glennrp54cf7972011-08-06 14:28:09 +00007851 LBR02PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007852 r++;
7853 }
glennrpbb4f99d2011-05-22 11:13:17 +00007854
glennrp8e58efd2011-05-20 12:16:29 +00007855 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7856 break;
7857 }
7858
7859 if (image->storage_class == PseudoClass && image->colormap != NULL)
7860 {
cristy3e08f112011-05-24 13:19:30 +00007861 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007862 {
glennrp91d99252011-06-25 14:30:13 +00007863 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007864 }
7865 }
7866 }
7867 else
7868 {
7869 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007870 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007871
7872 for (y=0; y < (ssize_t) image->rows; y++)
7873 {
7874 r=GetAuthenticPixels(image,0,y,image->columns,1,
7875 exception);
7876
cristy4c08aed2011-07-01 19:47:50 +00007877 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007878 break;
7879
7880 for (x=0; x < (ssize_t) image->columns; x++)
7881 {
glennrp54cf7972011-08-06 14:28:09 +00007882 LBR01PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007883 r++;
7884 }
glennrpbb4f99d2011-05-22 11:13:17 +00007885
glennrp8e58efd2011-05-20 12:16:29 +00007886 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7887 break;
7888 }
7889
7890 if (image->storage_class == PseudoClass && image->colormap != NULL)
7891 {
cristy3e08f112011-05-24 13:19:30 +00007892 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007893 {
glennrp91d99252011-06-25 14:30:13 +00007894 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007895 }
7896 }
7897 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007898 }
7899
glennrp67b9c1a2011-04-22 18:47:36 +00007900 /* To do: set to next higher multiple of 8 */
7901 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007902 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007903
glennrp2b013e42010-11-24 16:55:50 +00007904#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7905 /* PNG does not handle depths greater than 16 so reduce it even
7906 * if lossy
7907 */
glennrp8e58efd2011-05-20 12:16:29 +00007908 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007909 image->depth=16;
7910#endif
7911
glennrp3faa9a32011-04-23 14:00:25 +00007912#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007913 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007914 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007915 image->depth = 8;
7916#endif
7917
glennrpc8c2f062011-02-25 19:00:33 +00007918 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007919 * we reduce the transparency to binary and run again, then if there
7920 * 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 +00007921 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7922 * palette. Then (To do) we take care of a final reduction that is only
7923 * needed if there are still 256 colors present and one of them has both
7924 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007925 */
glennrp82b3c532011-03-22 19:20:54 +00007926
glennrp8ca51ad2011-05-12 21:22:32 +00007927 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007928 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007929 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007930
glennrp8ca51ad2011-05-12 21:22:32 +00007931 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007932 {
7933 /* BUILD_PALETTE
7934 *
7935 * Sometimes we get DirectClass images that have 256 colors or fewer.
7936 * This code will build a colormap.
7937 *
7938 * Also, sometimes we get PseudoClass images with an out-of-date
7939 * colormap. This code will replace the colormap with a new one.
7940 * Sometimes we get PseudoClass images that have more than 256 colors.
7941 * This code will delete the colormap and change the image to
7942 * DirectClass.
7943 *
cristy4c08aed2011-07-01 19:47:50 +00007944 * If image->matte is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00007945 * even though it sometimes contains left-over non-opaque values.
7946 *
7947 * Also we gather some information (number of opaque, transparent,
7948 * and semitransparent pixels, and whether the image has any non-gray
7949 * pixels or only black-and-white pixels) that we might need later.
7950 *
7951 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7952 * we need to check for bogus non-opaque values, at least.
7953 */
glennrp3c218112010-11-27 15:31:26 +00007954
glennrpd71e86a2011-02-24 01:28:37 +00007955 ExceptionInfo
7956 *exception;
glennrp3c218112010-11-27 15:31:26 +00007957
glennrpd71e86a2011-02-24 01:28:37 +00007958 int
7959 n;
glennrp3c218112010-11-27 15:31:26 +00007960
glennrpd71e86a2011-02-24 01:28:37 +00007961 PixelPacket
7962 opaque[260],
7963 semitransparent[260],
7964 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007965
cristy4c08aed2011-07-01 19:47:50 +00007966 register const Quantum
7967 *s;
glennrp8bb3a022010-12-13 20:40:04 +00007968
cristy4c08aed2011-07-01 19:47:50 +00007969 register Quantum
7970 *q,
glennrpfd05d622011-02-25 04:10:33 +00007971 *r;
7972
glennrpd71e86a2011-02-24 01:28:37 +00007973 if (logging != MagickFalse)
7974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7975 " Enter BUILD_PALETTE:");
7976
7977 if (logging != MagickFalse)
7978 {
glennrp03812ae2010-12-24 01:31:34 +00007979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007980 " image->columns=%.20g",(double) image->columns);
7981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7982 " image->rows=%.20g",(double) image->rows);
7983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7984 " image->matte=%.20g",(double) image->matte);
7985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7986 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00007987
glennrpfd05d622011-02-25 04:10:33 +00007988 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00007989 {
7990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007991 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00007992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00007993 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00007994
glennrpd71e86a2011-02-24 01:28:37 +00007995 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00007996 {
glennrpd71e86a2011-02-24 01:28:37 +00007997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7998 " %d (%d,%d,%d,%d)",
7999 (int) i,
8000 (int) image->colormap[i].red,
8001 (int) image->colormap[i].green,
8002 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008003 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008004 }
glennrp2cc891a2010-12-24 13:44:32 +00008005
glennrpd71e86a2011-02-24 01:28:37 +00008006 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8007 {
8008 if (i > 255)
8009 {
8010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8011 " %d (%d,%d,%d,%d)",
8012 (int) i,
8013 (int) image->colormap[i].red,
8014 (int) image->colormap[i].green,
8015 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008016 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008017 }
8018 }
glennrp03812ae2010-12-24 01:31:34 +00008019 }
glennrp7ddcc222010-12-11 05:01:05 +00008020
glennrpd71e86a2011-02-24 01:28:37 +00008021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8022 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008023
glennrpd71e86a2011-02-24 01:28:37 +00008024 if (image->colors == 0)
8025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8026 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008027
glennrp8d3d6e52011-04-19 04:39:51 +00008028 if (ping_preserve_colormap == MagickFalse)
8029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8030 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008031 }
8032
8033 exception=(&image->exception);
8034
8035 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008036 number_opaque = 0;
8037 number_semitransparent = 0;
8038 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008039
8040 for (y=0; y < (ssize_t) image->rows; y++)
8041 {
8042 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8043
cristy4c08aed2011-07-01 19:47:50 +00008044 if (q == (const Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008045 break;
8046
8047 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008048 {
glennrp4737d522011-04-29 03:33:42 +00008049 if (image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008050 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008051 {
8052 if (number_opaque < 259)
8053 {
8054 if (number_opaque == 0)
8055 {
cristy4c08aed2011-07-01 19:47:50 +00008056 GetPixelPacket(image, q, opaque);
8057 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008058 number_opaque=1;
8059 }
glennrp2cc891a2010-12-24 13:44:32 +00008060
glennrpd71e86a2011-02-24 01:28:37 +00008061 for (i=0; i< (ssize_t) number_opaque; i++)
8062 {
cristy4c08aed2011-07-01 19:47:50 +00008063 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008064 break;
8065 }
glennrp7ddcc222010-12-11 05:01:05 +00008066
glennrpd71e86a2011-02-24 01:28:37 +00008067 if (i == (ssize_t) number_opaque &&
8068 number_opaque < 259)
8069 {
8070 number_opaque++;
cristy4c08aed2011-07-01 19:47:50 +00008071 GetPixelPacket(image, q, opaque+i);
8072 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008073 }
8074 }
8075 }
cristy4c08aed2011-07-01 19:47:50 +00008076 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008077 {
8078 if (number_transparent < 259)
8079 {
8080 if (number_transparent == 0)
8081 {
cristy4c08aed2011-07-01 19:47:50 +00008082 GetPixelPacket(image, q, transparent);
8083 ping_trans_color.red=(unsigned short)
8084 GetPixelRed(image,q);
8085 ping_trans_color.green=(unsigned short)
8086 GetPixelGreen(image,q);
8087 ping_trans_color.blue=(unsigned short)
8088 GetPixelBlue(image,q);
8089 ping_trans_color.gray=(unsigned short)
8090 GetPixelRed(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008091 number_transparent = 1;
8092 }
8093
8094 for (i=0; i< (ssize_t) number_transparent; i++)
8095 {
cristy4c08aed2011-07-01 19:47:50 +00008096 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008097 break;
8098 }
8099
8100 if (i == (ssize_t) number_transparent &&
8101 number_transparent < 259)
8102 {
8103 number_transparent++;
cristy4c08aed2011-07-01 19:47:50 +00008104 GetPixelPacket(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008105 }
8106 }
8107 }
8108 else
8109 {
8110 if (number_semitransparent < 259)
8111 {
8112 if (number_semitransparent == 0)
8113 {
cristy4c08aed2011-07-01 19:47:50 +00008114 GetPixelPacket(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008115 number_semitransparent = 1;
8116 }
8117
8118 for (i=0; i< (ssize_t) number_semitransparent; i++)
8119 {
cristy4c08aed2011-07-01 19:47:50 +00008120 if (IsPixelEquivalent(image,q, semitransparent+i)
8121 && GetPixelAlpha(image,q) ==
8122 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008123 break;
8124 }
8125
8126 if (i == (ssize_t) number_semitransparent &&
8127 number_semitransparent < 259)
8128 {
8129 number_semitransparent++;
cristy4c08aed2011-07-01 19:47:50 +00008130 GetPixelPacket(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008131 }
8132 }
8133 }
cristyed231572011-07-14 02:18:59 +00008134 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008135 }
8136 }
8137
8138 if (ping_exclude_bKGD == MagickFalse)
8139 {
8140 /* Add the background color to the palette, if it
8141 * isn't already there.
8142 */
glennrpc6c391a2011-04-27 02:23:56 +00008143 if (logging != MagickFalse)
8144 {
8145 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8146 " Check colormap for background (%d,%d,%d)",
8147 (int) image->background_color.red,
8148 (int) image->background_color.green,
8149 (int) image->background_color.blue);
8150 }
glennrpd71e86a2011-02-24 01:28:37 +00008151 for (i=0; i<number_opaque; i++)
8152 {
glennrpca7ad3a2011-04-26 04:44:54 +00008153 if (opaque[i].red == image->background_color.red &&
8154 opaque[i].green == image->background_color.green &&
8155 opaque[i].blue == image->background_color.blue)
8156 break;
glennrpd71e86a2011-02-24 01:28:37 +00008157 }
glennrpd71e86a2011-02-24 01:28:37 +00008158 if (number_opaque < 259 && i == number_opaque)
8159 {
glennrp8e045c82011-04-27 16:40:27 +00008160 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008161 ping_background.index = i;
8162 if (logging != MagickFalse)
8163 {
8164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8165 " background_color index is %d",(int) i);
8166 }
8167
glennrpd71e86a2011-02-24 01:28:37 +00008168 }
glennrpa080bc32011-03-11 18:03:44 +00008169 else if (logging != MagickFalse)
8170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8171 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008172 }
8173
8174 image_colors=number_opaque+number_transparent+number_semitransparent;
8175
glennrpa080bc32011-03-11 18:03:44 +00008176 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8177 {
8178 /* No room for the background color; remove it. */
8179 number_opaque--;
8180 image_colors--;
8181 }
8182
glennrpd71e86a2011-02-24 01:28:37 +00008183 if (logging != MagickFalse)
8184 {
8185 if (image_colors > 256)
8186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8187 " image has more than 256 colors");
8188
8189 else
8190 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8191 " image has %d colors",image_colors);
8192 }
8193
glennrp8d3d6e52011-04-19 04:39:51 +00008194 if (ping_preserve_colormap != MagickFalse)
8195 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008196
glennrpfd05d622011-02-25 04:10:33 +00008197 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008198 {
8199 ping_have_color=MagickFalse;
8200 ping_have_non_bw=MagickFalse;
8201
8202 if(image_colors > 256)
8203 {
8204 for (y=0; y < (ssize_t) image->rows; y++)
8205 {
8206 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8207
cristy4c08aed2011-07-01 19:47:50 +00008208 if (q == (const Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008209 break;
8210
glennrpe5e6b802011-07-20 14:44:40 +00008211 s=q;
8212 for (x=0; x < (ssize_t) image->columns; x++)
8213 {
8214 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8215 GetPixelRed(image,s) != GetPixelBlue(image,s))
8216 {
8217 ping_have_color=MagickTrue;
8218 ping_have_non_bw=MagickTrue;
8219 break;
8220 }
8221 s+=GetPixelChannels(image);
8222 }
8223
8224 if (ping_have_color != MagickFalse)
8225 break;
8226
glennrpd71e86a2011-02-24 01:28:37 +00008227 /* Worst case is black-and-white; we are looking at every
8228 * pixel twice.
8229 */
8230
glennrpd71e86a2011-02-24 01:28:37 +00008231 if (ping_have_non_bw == MagickFalse)
8232 {
8233 s=q;
8234 for (x=0; x < (ssize_t) image->columns; x++)
8235 {
cristy4c08aed2011-07-01 19:47:50 +00008236 if (GetPixelRed(image,s) != 0 &&
8237 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008238 {
8239 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008240 break;
glennrpd71e86a2011-02-24 01:28:37 +00008241 }
cristyed231572011-07-14 02:18:59 +00008242 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008243 }
glennrpe5e6b802011-07-20 14:44:40 +00008244 }
glennrpd71e86a2011-02-24 01:28:37 +00008245 }
glennrpbb4f99d2011-05-22 11:13:17 +00008246 }
8247 }
glennrpd71e86a2011-02-24 01:28:37 +00008248
8249 if (image_colors < 257)
8250 {
8251 PixelPacket
8252 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008253
glennrpd71e86a2011-02-24 01:28:37 +00008254 /*
8255 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008256 */
8257
glennrpd71e86a2011-02-24 01:28:37 +00008258 if (logging != MagickFalse)
8259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8260 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008261
glennrpd71e86a2011-02-24 01:28:37 +00008262 /* Sort palette, transparent first */;
8263
8264 n = 0;
8265
8266 for (i=0; i<number_transparent; i++)
8267 colormap[n++] = transparent[i];
8268
8269 for (i=0; i<number_semitransparent; i++)
8270 colormap[n++] = semitransparent[i];
8271
8272 for (i=0; i<number_opaque; i++)
8273 colormap[n++] = opaque[i];
8274
glennrpc6c391a2011-04-27 02:23:56 +00008275 ping_background.index +=
8276 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008277
glennrpd71e86a2011-02-24 01:28:37 +00008278 /* image_colors < 257; search the colormap instead of the pixels
8279 * to get ping_have_color and ping_have_non_bw
8280 */
8281 for (i=0; i<n; i++)
8282 {
8283 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008284 {
glennrpd71e86a2011-02-24 01:28:37 +00008285 if (colormap[i].red != colormap[i].green ||
8286 colormap[i].red != colormap[i].blue)
8287 {
8288 ping_have_color=MagickTrue;
8289 ping_have_non_bw=MagickTrue;
8290 break;
8291 }
8292 }
8293
8294 if (ping_have_non_bw == MagickFalse)
8295 {
8296 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008297 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008298 }
glennrp8bb3a022010-12-13 20:40:04 +00008299 }
8300
glennrpd71e86a2011-02-24 01:28:37 +00008301 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8302 (number_transparent == 0 && number_semitransparent == 0)) &&
8303 (((mng_info->write_png_colortype-1) ==
8304 PNG_COLOR_TYPE_PALETTE) ||
8305 (mng_info->write_png_colortype == 0)))
8306 {
glennrp6185c532011-01-14 17:58:40 +00008307 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008308 {
glennrpd71e86a2011-02-24 01:28:37 +00008309 if (n != (ssize_t) image_colors)
8310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8311 " image_colors (%d) and n (%d) don't match",
8312 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008313
glennrpd71e86a2011-02-24 01:28:37 +00008314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8315 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008316 }
glennrp03812ae2010-12-24 01:31:34 +00008317
glennrpd71e86a2011-02-24 01:28:37 +00008318 image->colors = image_colors;
8319
8320 if (AcquireImageColormap(image,image_colors) ==
8321 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008322 ThrowWriterException(ResourceLimitError,
8323 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008324
8325 for (i=0; i< (ssize_t) image_colors; i++)
8326 image->colormap[i] = colormap[i];
8327
8328 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008329 {
glennrpd71e86a2011-02-24 01:28:37 +00008330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8331 " image->colors=%d (%d)",
8332 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008333
glennrpd71e86a2011-02-24 01:28:37 +00008334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8335 " Update the pixel indexes");
8336 }
glennrp6185c532011-01-14 17:58:40 +00008337
glennrpfd05d622011-02-25 04:10:33 +00008338 /* Sync the pixel indices with the new colormap */
8339
glennrpd71e86a2011-02-24 01:28:37 +00008340 for (y=0; y < (ssize_t) image->rows; y++)
8341 {
8342 q=GetAuthenticPixels(image,0,y,image->columns,1,
8343 exception);
glennrp6185c532011-01-14 17:58:40 +00008344
cristy4c08aed2011-07-01 19:47:50 +00008345 if (q == (const Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008346 break;
glennrp6185c532011-01-14 17:58:40 +00008347
glennrpbb4f99d2011-05-22 11:13:17 +00008348
glennrpd71e86a2011-02-24 01:28:37 +00008349 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008350 {
glennrpd71e86a2011-02-24 01:28:37 +00008351 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008352 {
glennrpd71e86a2011-02-24 01:28:37 +00008353 if ((image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008354 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8355 image->colormap[i].red == GetPixelRed(image,q) &&
8356 image->colormap[i].green == GetPixelGreen(image,q) &&
8357 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008358 {
cristy4c08aed2011-07-01 19:47:50 +00008359 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008360 break;
glennrp6185c532011-01-14 17:58:40 +00008361 }
glennrp6185c532011-01-14 17:58:40 +00008362 }
cristyed231572011-07-14 02:18:59 +00008363 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008364 }
glennrp6185c532011-01-14 17:58:40 +00008365
glennrpd71e86a2011-02-24 01:28:37 +00008366 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8367 break;
8368 }
8369 }
8370 }
8371
8372 if (logging != MagickFalse)
8373 {
8374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8375 " image->colors=%d", (int) image->colors);
8376
8377 if (image->colormap != NULL)
8378 {
8379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008380 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008381
8382 for (i=0; i < (ssize_t) image->colors; i++)
8383 {
cristy72988482011-03-29 16:34:38 +00008384 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008385 {
8386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8387 " %d (%d,%d,%d,%d)",
8388 (int) i,
8389 (int) image->colormap[i].red,
8390 (int) image->colormap[i].green,
8391 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008392 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008393 }
glennrp6185c532011-01-14 17:58:40 +00008394 }
8395 }
glennrp03812ae2010-12-24 01:31:34 +00008396
glennrpd71e86a2011-02-24 01:28:37 +00008397 if (number_transparent < 257)
8398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8399 " number_transparent = %d",
8400 number_transparent);
8401 else
glennrp03812ae2010-12-24 01:31:34 +00008402
glennrpd71e86a2011-02-24 01:28:37 +00008403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8404 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008405
glennrpd71e86a2011-02-24 01:28:37 +00008406 if (number_opaque < 257)
8407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8408 " number_opaque = %d",
8409 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008410
glennrpd71e86a2011-02-24 01:28:37 +00008411 else
8412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8413 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008414
glennrpd71e86a2011-02-24 01:28:37 +00008415 if (number_semitransparent < 257)
8416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8417 " number_semitransparent = %d",
8418 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008419
glennrpd71e86a2011-02-24 01:28:37 +00008420 else
8421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8422 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008423
glennrpd71e86a2011-02-24 01:28:37 +00008424 if (ping_have_non_bw == MagickFalse)
8425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8426 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008427
glennrpd71e86a2011-02-24 01:28:37 +00008428 else if (ping_have_color == MagickFalse)
8429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8430 " All pixels and the background are gray");
8431
8432 else
8433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8434 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008435
glennrp03812ae2010-12-24 01:31:34 +00008436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8437 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008438 }
glennrpfd05d622011-02-25 04:10:33 +00008439
glennrpc8c2f062011-02-25 19:00:33 +00008440 if (mng_info->write_png8 == MagickFalse)
8441 break;
glennrpfd05d622011-02-25 04:10:33 +00008442
glennrpc8c2f062011-02-25 19:00:33 +00008443 /* Make any reductions necessary for the PNG8 format */
8444 if (image_colors <= 256 &&
8445 image_colors != 0 && image->colormap != NULL &&
8446 number_semitransparent == 0 &&
8447 number_transparent <= 1)
8448 break;
8449
8450 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008451 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8452 * transparent color so if more than one is transparent we merge
8453 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008454 */
glennrp130fc452011-08-20 03:43:18 +00008455 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008456 {
8457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8458 " Thresholding the alpha channel to binary");
8459
8460 for (y=0; y < (ssize_t) image->rows; y++)
8461 {
8462 r=GetAuthenticPixels(image,0,y,image->columns,1,
8463 exception);
8464
cristy4c08aed2011-07-01 19:47:50 +00008465 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008466 break;
8467
8468 for (x=0; x < (ssize_t) image->columns; x++)
8469 {
glennrpf73547f2011-08-20 04:40:26 +00008470 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008471 {
cristy4c08aed2011-07-01 19:47:50 +00008472 SetPixelPacket(image,&image->background_color,r);
8473 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008474 }
8475 else
cristy4c08aed2011-07-01 19:47:50 +00008476 SetPixelAlpha(image,OpaqueAlpha,r);
cristyed231572011-07-14 02:18:59 +00008477 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008478 }
glennrpbb4f99d2011-05-22 11:13:17 +00008479
glennrpc8c2f062011-02-25 19:00:33 +00008480 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8481 break;
8482
8483 if (image_colors != 0 && image_colors <= 256 &&
8484 image->colormap != NULL)
8485 for (i=0; i<image_colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00008486 image->colormap[i].alpha =
8487 (image->colormap[i].alpha > TransparentAlpha/2 ?
8488 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008489 }
8490 continue;
8491 }
8492
8493 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008494 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8495 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8496 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008497 */
glennrpd3371642011-03-22 19:42:23 +00008498 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8499 {
8500 if (logging != MagickFalse)
8501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8502 " Quantizing the background color to 4-4-4");
8503
8504 tried_444 = MagickTrue;
8505
glennrp91d99252011-06-25 14:30:13 +00008506 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008507
8508 if (logging != MagickFalse)
8509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8510 " Quantizing the pixel colors to 4-4-4");
8511
8512 if (image->colormap == NULL)
8513 {
8514 for (y=0; y < (ssize_t) image->rows; y++)
8515 {
8516 r=GetAuthenticPixels(image,0,y,image->columns,1,
8517 exception);
8518
cristy4c08aed2011-07-01 19:47:50 +00008519 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008520 break;
8521
8522 for (x=0; x < (ssize_t) image->columns; x++)
8523 {
cristy4c08aed2011-07-01 19:47:50 +00008524 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008525 LBR04PixelRGB(r);
glennrpd3371642011-03-22 19:42:23 +00008526 r++;
8527 }
glennrpbb4f99d2011-05-22 11:13:17 +00008528
glennrpd3371642011-03-22 19:42:23 +00008529 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8530 break;
8531 }
8532 }
8533
8534 else /* Should not reach this; colormap already exists and
8535 must be <= 256 */
8536 {
8537 if (logging != MagickFalse)
8538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8539 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008540
glennrpd3371642011-03-22 19:42:23 +00008541 for (i=0; i<image_colors; i++)
8542 {
glennrp91d99252011-06-25 14:30:13 +00008543 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008544 }
8545 }
8546 continue;
8547 }
8548
glennrp82b3c532011-03-22 19:20:54 +00008549 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8550 {
8551 if (logging != MagickFalse)
8552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8553 " Quantizing the background color to 3-3-3");
8554
8555 tried_333 = MagickTrue;
8556
glennrp91d99252011-06-25 14:30:13 +00008557 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008558
8559 if (logging != MagickFalse)
8560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008561 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008562
8563 if (image->colormap == NULL)
8564 {
8565 for (y=0; y < (ssize_t) image->rows; y++)
8566 {
8567 r=GetAuthenticPixels(image,0,y,image->columns,1,
8568 exception);
8569
cristy4c08aed2011-07-01 19:47:50 +00008570 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008571 break;
8572
8573 for (x=0; x < (ssize_t) image->columns; x++)
8574 {
cristy4c08aed2011-07-01 19:47:50 +00008575 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8576 LBR03RGB(r);
glennrp82b3c532011-03-22 19:20:54 +00008577 r++;
8578 }
glennrpbb4f99d2011-05-22 11:13:17 +00008579
glennrp82b3c532011-03-22 19:20:54 +00008580 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8581 break;
8582 }
8583 }
8584
8585 else /* Should not reach this; colormap already exists and
8586 must be <= 256 */
8587 {
8588 if (logging != MagickFalse)
8589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008590 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008591 for (i=0; i<image_colors; i++)
8592 {
glennrp91d99252011-06-25 14:30:13 +00008593 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008594 }
glennrpd3371642011-03-22 19:42:23 +00008595 }
8596 continue;
glennrp82b3c532011-03-22 19:20:54 +00008597 }
glennrpc8c2f062011-02-25 19:00:33 +00008598
glennrp8ca51ad2011-05-12 21:22:32 +00008599 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008600 {
8601 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008603 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008604
glennrp8ca51ad2011-05-12 21:22:32 +00008605 tried_332 = MagickTrue;
8606
glennrp3faa9a32011-04-23 14:00:25 +00008607 /* Red and green were already done so we only quantize the blue
8608 * channel
8609 */
8610
glennrp91d99252011-06-25 14:30:13 +00008611 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008612
glennrpc8c2f062011-02-25 19:00:33 +00008613 if (logging != MagickFalse)
8614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008615 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008616
glennrpc8c2f062011-02-25 19:00:33 +00008617 if (image->colormap == NULL)
8618 {
8619 for (y=0; y < (ssize_t) image->rows; y++)
8620 {
8621 r=GetAuthenticPixels(image,0,y,image->columns,1,
8622 exception);
8623
cristy4c08aed2011-07-01 19:47:50 +00008624 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008625 break;
8626
8627 for (x=0; x < (ssize_t) image->columns; x++)
8628 {
cristy4c08aed2011-07-01 19:47:50 +00008629 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008630 LBR02PixelBlue(r);
glennrp52a479c2011-02-26 21:14:38 +00008631 r++;
glennrpc8c2f062011-02-25 19:00:33 +00008632 }
glennrpbb4f99d2011-05-22 11:13:17 +00008633
glennrpc8c2f062011-02-25 19:00:33 +00008634 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8635 break;
8636 }
8637 }
glennrpfd05d622011-02-25 04:10:33 +00008638
glennrpc8c2f062011-02-25 19:00:33 +00008639 else /* Should not reach this; colormap already exists and
8640 must be <= 256 */
8641 {
8642 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008644 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008645 for (i=0; i<image_colors; i++)
8646 {
glennrp91d99252011-06-25 14:30:13 +00008647 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008648 }
8649 }
8650 continue;
8651 }
8652 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008653
8654 if (image_colors == 0 || image_colors > 256)
8655 {
8656 /* Take care of special case with 256 colors + 1 transparent
8657 * color. We don't need to quantize to 2-3-2-1; we only need to
8658 * eliminate one color, so we'll merge the two darkest red
8659 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8660 */
8661 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8662 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8663 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8664 {
8665 image->background_color.red=ScaleCharToQuantum(0x24);
8666 }
glennrpbb4f99d2011-05-22 11:13:17 +00008667
glennrp8ca51ad2011-05-12 21:22:32 +00008668 if (image->colormap == NULL)
8669 {
8670 for (y=0; y < (ssize_t) image->rows; y++)
8671 {
8672 r=GetAuthenticPixels(image,0,y,image->columns,1,
8673 exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008674
cristy4c08aed2011-07-01 19:47:50 +00008675 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008676 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008677
glennrp8ca51ad2011-05-12 21:22:32 +00008678 for (x=0; x < (ssize_t) image->columns; x++)
8679 {
cristy4c08aed2011-07-01 19:47:50 +00008680 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8681 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8682 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8683 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008684 {
cristy4c08aed2011-07-01 19:47:50 +00008685 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008686 }
cristyed231572011-07-14 02:18:59 +00008687 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008688 }
glennrpbb4f99d2011-05-22 11:13:17 +00008689
glennrp8ca51ad2011-05-12 21:22:32 +00008690 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8691 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008692
glennrp8ca51ad2011-05-12 21:22:32 +00008693 }
8694 }
8695
8696 else
8697 {
8698 for (i=0; i<image_colors; i++)
8699 {
8700 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8701 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8702 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8703 {
8704 image->colormap[i].red=ScaleCharToQuantum(0x24);
8705 }
8706 }
8707 }
8708 }
glennrpd71e86a2011-02-24 01:28:37 +00008709 }
glennrpfd05d622011-02-25 04:10:33 +00008710 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008711
glennrpfd05d622011-02-25 04:10:33 +00008712 /* If we are excluding the tRNS chunk and there is transparency,
8713 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8714 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008715 */
glennrp0e8ea192010-12-24 18:00:33 +00008716 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8717 (number_transparent != 0 || number_semitransparent != 0))
8718 {
glennrpd17915c2011-04-29 14:24:22 +00008719 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008720
8721 if (ping_have_color == MagickFalse)
8722 mng_info->write_png_colortype = 5;
8723
8724 else
8725 mng_info->write_png_colortype = 7;
8726
glennrp8d579662011-02-23 02:05:02 +00008727 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008728 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008729 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008730
glennrp0e8ea192010-12-24 18:00:33 +00008731 }
8732
glennrpfd05d622011-02-25 04:10:33 +00008733 /* See if cheap transparency is possible. It is only possible
8734 * when there is a single transparent color, no semitransparent
8735 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008736 * as the transparent color. We only need this information if
8737 * we are writing a PNG with colortype 0 or 2, and we have not
8738 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008739 */
glennrp5a39f372011-02-25 04:52:16 +00008740 if (number_transparent == 1 &&
8741 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008742 {
8743 ping_have_cheap_transparency = MagickTrue;
8744
8745 if (number_semitransparent != 0)
8746 ping_have_cheap_transparency = MagickFalse;
8747
8748 else if (image_colors == 0 || image_colors > 256 ||
8749 image->colormap == NULL)
8750 {
8751 ExceptionInfo
8752 *exception;
8753
cristy4c08aed2011-07-01 19:47:50 +00008754 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008755 *q;
8756
8757 exception=(&image->exception);
8758
8759 for (y=0; y < (ssize_t) image->rows; y++)
8760 {
8761 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8762
cristy4c08aed2011-07-01 19:47:50 +00008763 if (q == (const Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008764 break;
8765
8766 for (x=0; x < (ssize_t) image->columns; x++)
8767 {
cristy4c08aed2011-07-01 19:47:50 +00008768 if (GetPixelAlpha(image,q) != TransparentAlpha &&
glennrp847370c2011-07-05 17:37:15 +00008769 (unsigned short) GetPixelRed(image,q) ==
8770 ping_trans_color.red &&
8771 (unsigned short) GetPixelGreen(image,q) ==
8772 ping_trans_color.green &&
8773 (unsigned short) GetPixelBlue(image,q) ==
8774 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008775 {
8776 ping_have_cheap_transparency = MagickFalse;
8777 break;
8778 }
8779
cristyed231572011-07-14 02:18:59 +00008780 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008781 }
glennrpbb4f99d2011-05-22 11:13:17 +00008782
glennrpfd05d622011-02-25 04:10:33 +00008783 if (ping_have_cheap_transparency == MagickFalse)
8784 break;
8785 }
8786 }
8787 else
8788 {
glennrp67b9c1a2011-04-22 18:47:36 +00008789 /* Assuming that image->colormap[0] is the one transparent color
8790 * and that all others are opaque.
8791 */
glennrpfd05d622011-02-25 04:10:33 +00008792 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008793 for (i=1; i<image_colors; i++)
8794 if (image->colormap[i].red == image->colormap[0].red &&
8795 image->colormap[i].green == image->colormap[0].green &&
8796 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008797 {
glennrp67b9c1a2011-04-22 18:47:36 +00008798 ping_have_cheap_transparency = MagickFalse;
8799 break;
glennrpfd05d622011-02-25 04:10:33 +00008800 }
8801 }
glennrpbb4f99d2011-05-22 11:13:17 +00008802
glennrpfd05d622011-02-25 04:10:33 +00008803 if (logging != MagickFalse)
8804 {
8805 if (ping_have_cheap_transparency == MagickFalse)
8806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8807 " Cheap transparency is not possible.");
8808
8809 else
8810 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8811 " Cheap transparency is possible.");
8812 }
8813 }
8814 else
8815 ping_have_cheap_transparency = MagickFalse;
8816
glennrp8640fb52010-11-23 15:48:26 +00008817 image_depth=image->depth;
8818
glennrp26c990a2010-11-23 02:23:20 +00008819 quantum_info = (QuantumInfo *) NULL;
8820 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008821 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008822 image_matte=image->matte;
8823
glennrp0fe50b42010-11-16 03:52:51 +00008824 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008825 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008826
glennrp52a479c2011-02-26 21:14:38 +00008827 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8828 (image->colors == 0 || image->colormap == NULL))
8829 {
glennrp52a479c2011-02-26 21:14:38 +00008830 image_info=DestroyImageInfo(image_info);
8831 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008832 (void) ThrowMagickException(&IMimage->exception,
8833 GetMagickModule(),CoderError,
8834 "Cannot write PNG8 or color-type 3; colormap is NULL",
8835 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008836#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8837 UnlockSemaphoreInfo(ping_semaphore);
8838#endif
8839 return(MagickFalse);
8840 }
8841
cristy3ed852e2009-09-05 21:47:34 +00008842 /*
8843 Allocate the PNG structures
8844 */
8845#ifdef PNG_USER_MEM_SUPPORTED
8846 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008847 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8848 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008849
cristy3ed852e2009-09-05 21:47:34 +00008850#else
8851 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008852 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008853
cristy3ed852e2009-09-05 21:47:34 +00008854#endif
8855 if (ping == (png_struct *) NULL)
8856 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008857
cristy3ed852e2009-09-05 21:47:34 +00008858 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008859
cristy3ed852e2009-09-05 21:47:34 +00008860 if (ping_info == (png_info *) NULL)
8861 {
8862 png_destroy_write_struct(&ping,(png_info **) NULL);
8863 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8864 }
glennrp0fe50b42010-11-16 03:52:51 +00008865
cristy3ed852e2009-09-05 21:47:34 +00008866 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008867 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008868
glennrp5af765f2010-03-30 11:12:18 +00008869 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008870 {
8871 /*
8872 PNG write failed.
8873 */
8874#ifdef PNG_DEBUG
8875 if (image_info->verbose)
8876 (void) printf("PNG write has failed.\n");
8877#endif
8878 png_destroy_write_struct(&ping,&ping_info);
8879#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008880 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008881#endif
glennrpda8f3a72011-02-27 23:54:12 +00008882 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008883 (void) CloseBlob(image);
8884 image_info=DestroyImageInfo(image_info);
8885 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008886 return(MagickFalse);
8887 }
8888 /*
8889 Prepare PNG for writing.
8890 */
8891#if defined(PNG_MNG_FEATURES_SUPPORTED)
8892 if (mng_info->write_mng)
8893 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008894
cristy3ed852e2009-09-05 21:47:34 +00008895#else
8896# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8897 if (mng_info->write_mng)
8898 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008899
cristy3ed852e2009-09-05 21:47:34 +00008900# endif
8901#endif
glennrp2b013e42010-11-24 16:55:50 +00008902
cristy3ed852e2009-09-05 21:47:34 +00008903 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008904
cristy4e5bc842010-06-09 13:56:01 +00008905 ping_width=(png_uint_32) image->columns;
8906 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008907
cristy3ed852e2009-09-05 21:47:34 +00008908 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8909 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008910
cristy3ed852e2009-09-05 21:47:34 +00008911 if (mng_info->write_png_depth != 0)
8912 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008913
cristy3ed852e2009-09-05 21:47:34 +00008914 /* Adjust requested depth to next higher valid depth if necessary */
8915 if (image_depth > 8)
8916 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008917
cristy3ed852e2009-09-05 21:47:34 +00008918 if ((image_depth > 4) && (image_depth < 8))
8919 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008920
cristy3ed852e2009-09-05 21:47:34 +00008921 if (image_depth == 3)
8922 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008923
cristy3ed852e2009-09-05 21:47:34 +00008924 if (logging != MagickFalse)
8925 {
8926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008927 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008929 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008931 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008933 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008935 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008936 }
glennrp8640fb52010-11-23 15:48:26 +00008937
cristy3ed852e2009-09-05 21:47:34 +00008938 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008939 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008940
glennrp26f37912010-12-23 16:22:42 +00008941
cristy3ed852e2009-09-05 21:47:34 +00008942#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008943 if (ping_exclude_pHYs == MagickFalse)
8944 {
cristy3ed852e2009-09-05 21:47:34 +00008945 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8946 (!mng_info->write_mng || !mng_info->equal_physs))
8947 {
glennrp0fe50b42010-11-16 03:52:51 +00008948 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8950 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008951
8952 if (image->units == PixelsPerInchResolution)
8953 {
glennrpdfd70802010-11-14 01:23:35 +00008954 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008955 ping_pHYs_x_resolution=
8956 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8957 ping_pHYs_y_resolution=
8958 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008959 }
glennrpdfd70802010-11-14 01:23:35 +00008960
cristy3ed852e2009-09-05 21:47:34 +00008961 else if (image->units == PixelsPerCentimeterResolution)
8962 {
glennrpdfd70802010-11-14 01:23:35 +00008963 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008964 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8965 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008966 }
glennrp991d11d2010-11-12 21:55:28 +00008967
cristy3ed852e2009-09-05 21:47:34 +00008968 else
8969 {
glennrpdfd70802010-11-14 01:23:35 +00008970 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8971 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8972 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00008973 }
glennrp991d11d2010-11-12 21:55:28 +00008974
glennrp823b55c2011-03-14 18:46:46 +00008975 if (logging != MagickFalse)
8976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8977 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8978 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8979 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00008980 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008981 }
glennrp26f37912010-12-23 16:22:42 +00008982 }
cristy3ed852e2009-09-05 21:47:34 +00008983#endif
glennrpa521b2f2010-10-29 04:11:03 +00008984
glennrp26f37912010-12-23 16:22:42 +00008985 if (ping_exclude_bKGD == MagickFalse)
8986 {
glennrpa521b2f2010-10-29 04:11:03 +00008987 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00008988 {
glennrpa521b2f2010-10-29 04:11:03 +00008989 unsigned int
8990 mask;
cristy3ed852e2009-09-05 21:47:34 +00008991
glennrpa521b2f2010-10-29 04:11:03 +00008992 mask=0xffff;
8993 if (ping_bit_depth == 8)
8994 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00008995
glennrpa521b2f2010-10-29 04:11:03 +00008996 if (ping_bit_depth == 4)
8997 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00008998
glennrpa521b2f2010-10-29 04:11:03 +00008999 if (ping_bit_depth == 2)
9000 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009001
glennrpa521b2f2010-10-29 04:11:03 +00009002 if (ping_bit_depth == 1)
9003 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009004
glennrpa521b2f2010-10-29 04:11:03 +00009005 ping_background.red=(png_uint_16)
9006 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009007
glennrpa521b2f2010-10-29 04:11:03 +00009008 ping_background.green=(png_uint_16)
9009 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009010
glennrpa521b2f2010-10-29 04:11:03 +00009011 ping_background.blue=(png_uint_16)
9012 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009013
9014 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009015 }
cristy3ed852e2009-09-05 21:47:34 +00009016
glennrp0fe50b42010-11-16 03:52:51 +00009017 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009018 {
9019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9020 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9022 " background_color index is %d",
9023 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009024
9025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9026 " ping_bit_depth=%d",ping_bit_depth);
9027 }
glennrp0fe50b42010-11-16 03:52:51 +00009028
9029 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009030 }
glennrp0fe50b42010-11-16 03:52:51 +00009031
cristy3ed852e2009-09-05 21:47:34 +00009032 /*
9033 Select the color type.
9034 */
9035 matte=image_matte;
9036 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009037
glennrp1273f7b2011-02-24 03:20:30 +00009038 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009039 {
glennrp0fe50b42010-11-16 03:52:51 +00009040
glennrpfd05d622011-02-25 04:10:33 +00009041 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009042 for reducing the sample depth from 8. */
9043
glennrp0fe50b42010-11-16 03:52:51 +00009044 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009045
glennrp8bb3a022010-12-13 20:40:04 +00009046 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009047
9048 /*
9049 Set image palette.
9050 */
9051 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9052
glennrp0fe50b42010-11-16 03:52:51 +00009053 if (logging != MagickFalse)
9054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9055 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009056 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009057
9058 for (i=0; i < (ssize_t) number_colors; i++)
9059 {
9060 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9061 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9062 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9063 if (logging != MagickFalse)
9064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009065#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009066 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009067#else
9068 " %5ld (%5d,%5d,%5d)",
9069#endif
glennrp0fe50b42010-11-16 03:52:51 +00009070 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9071
9072 }
glennrp2b013e42010-11-24 16:55:50 +00009073
glennrp8bb3a022010-12-13 20:40:04 +00009074 ping_have_PLTE=MagickTrue;
9075 image_depth=ping_bit_depth;
9076 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009077
glennrp58e01762011-01-07 15:28:54 +00009078 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009079 {
glennrp0fe50b42010-11-16 03:52:51 +00009080 /*
9081 Identify which colormap entry is transparent.
9082 */
9083 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009084 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009085
glennrp8bb3a022010-12-13 20:40:04 +00009086 for (i=0; i < (ssize_t) number_transparent; i++)
9087 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009088
glennrp0fe50b42010-11-16 03:52:51 +00009089
glennrp2cc891a2010-12-24 13:44:32 +00009090 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009091 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009092
9093 if (ping_num_trans == 0)
9094 ping_have_tRNS=MagickFalse;
9095
glennrp8bb3a022010-12-13 20:40:04 +00009096 else
9097 ping_have_tRNS=MagickTrue;
9098 }
glennrp0fe50b42010-11-16 03:52:51 +00009099
glennrp1273f7b2011-02-24 03:20:30 +00009100 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009101 {
glennrp1273f7b2011-02-24 03:20:30 +00009102 /*
9103 * Identify which colormap entry is the background color.
9104 */
9105
glennrp4f25bd02011-01-01 18:51:28 +00009106 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9107 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9108 break;
glennrp0fe50b42010-11-16 03:52:51 +00009109
glennrp4f25bd02011-01-01 18:51:28 +00009110 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009111
9112 if (logging != MagickFalse)
9113 {
9114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9115 " background_color index is %d",
9116 (int) ping_background.index);
9117 }
glennrp4f25bd02011-01-01 18:51:28 +00009118 }
cristy3ed852e2009-09-05 21:47:34 +00009119 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009120
glennrp7e65e932011-08-19 02:31:16 +00009121 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009122 {
9123 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009124 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009125 }
glennrp0fe50b42010-11-16 03:52:51 +00009126
glennrp7e65e932011-08-19 02:31:16 +00009127 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009128 {
9129 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009130 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009131 }
glennrp0fe50b42010-11-16 03:52:51 +00009132
glennrp8bb3a022010-12-13 20:40:04 +00009133 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009134 {
glennrp5af765f2010-03-30 11:12:18 +00009135 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009136
glennrp8bb3a022010-12-13 20:40:04 +00009137 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009138 {
glennrp5af765f2010-03-30 11:12:18 +00009139 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009140
glennrp5af765f2010-03-30 11:12:18 +00009141 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9142 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009143 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009144
glennrp8bb3a022010-12-13 20:40:04 +00009145 else
9146 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009147
9148 if (logging != MagickFalse)
9149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9150 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009151 }
glennrp0fe50b42010-11-16 03:52:51 +00009152
glennrp7c4c9e62011-03-21 20:23:32 +00009153 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009154 {
9155 if (logging != MagickFalse)
9156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009157 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009158
glennrpd6bf1612010-12-17 17:28:54 +00009159 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009160 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009161
glennrpd6bf1612010-12-17 17:28:54 +00009162 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009163 {
glennrp5af765f2010-03-30 11:12:18 +00009164 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009165 image_matte=MagickFalse;
9166 }
glennrp0fe50b42010-11-16 03:52:51 +00009167
glennrpd6bf1612010-12-17 17:28:54 +00009168 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009169 {
glennrp5af765f2010-03-30 11:12:18 +00009170 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009171 image_matte=MagickTrue;
9172 }
glennrp0fe50b42010-11-16 03:52:51 +00009173
glennrp5aa37f62011-01-02 03:07:57 +00009174 if (image_info->type == PaletteType ||
9175 image_info->type == PaletteMatteType)
9176 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9177
glennrp7c4c9e62011-03-21 20:23:32 +00009178 if (mng_info->write_png_colortype == 0 &&
9179 (image_info->type == UndefinedType ||
9180 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009181 {
glennrp5aa37f62011-01-02 03:07:57 +00009182 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009183 {
glennrp5aa37f62011-01-02 03:07:57 +00009184 if (image_matte == MagickFalse)
9185 {
9186 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9187 image_matte=MagickFalse;
9188 }
glennrp0fe50b42010-11-16 03:52:51 +00009189
glennrp0b206f52011-01-07 04:55:32 +00009190 else
glennrp5aa37f62011-01-02 03:07:57 +00009191 {
9192 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9193 image_matte=MagickTrue;
9194 }
9195 }
9196 else
glennrp8bb3a022010-12-13 20:40:04 +00009197 {
glennrp5aa37f62011-01-02 03:07:57 +00009198 if (image_matte == MagickFalse)
9199 {
9200 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9201 image_matte=MagickFalse;
9202 }
glennrp8bb3a022010-12-13 20:40:04 +00009203
glennrp0b206f52011-01-07 04:55:32 +00009204 else
glennrp5aa37f62011-01-02 03:07:57 +00009205 {
9206 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9207 image_matte=MagickTrue;
9208 }
9209 }
glennrp0fe50b42010-11-16 03:52:51 +00009210 }
glennrp5aa37f62011-01-02 03:07:57 +00009211
cristy3ed852e2009-09-05 21:47:34 +00009212 }
glennrp0fe50b42010-11-16 03:52:51 +00009213
cristy3ed852e2009-09-05 21:47:34 +00009214 if (logging != MagickFalse)
9215 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009216 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009217
glennrp5af765f2010-03-30 11:12:18 +00009218 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009219 {
9220 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9221 ping_color_type == PNG_COLOR_TYPE_RGB ||
9222 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9223 ping_bit_depth=8;
9224 }
cristy3ed852e2009-09-05 21:47:34 +00009225
glennrpd6bf1612010-12-17 17:28:54 +00009226 old_bit_depth=ping_bit_depth;
9227
glennrp5af765f2010-03-30 11:12:18 +00009228 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009229 {
glennrp8d579662011-02-23 02:05:02 +00009230 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9231 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009232 }
glennrp8640fb52010-11-23 15:48:26 +00009233
glennrp5af765f2010-03-30 11:12:18 +00009234 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009235 {
cristy35ef8242010-06-03 16:24:13 +00009236 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009237 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009238
9239 if (image->colors == 0)
9240 {
glennrp0fe50b42010-11-16 03:52:51 +00009241 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00009242 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00009243 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009244 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009245 }
9246
cristy35ef8242010-06-03 16:24:13 +00009247 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009248 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009249 }
glennrp2b013e42010-11-24 16:55:50 +00009250
glennrpd6bf1612010-12-17 17:28:54 +00009251 if (logging != MagickFalse)
9252 {
9253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9254 " Number of colors: %.20g",(double) image_colors);
9255
9256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9257 " Tentative PNG bit depth: %d",ping_bit_depth);
9258 }
9259
9260 if (ping_bit_depth < (int) mng_info->write_png_depth)
9261 ping_bit_depth = mng_info->write_png_depth;
9262 }
glennrp2cc891a2010-12-24 13:44:32 +00009263
glennrp5af765f2010-03-30 11:12:18 +00009264 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009265
cristy3ed852e2009-09-05 21:47:34 +00009266 if (logging != MagickFalse)
9267 {
9268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009269 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009270
cristy3ed852e2009-09-05 21:47:34 +00009271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009272 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009273
cristy3ed852e2009-09-05 21:47:34 +00009274 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009275 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009276
cristy3ed852e2009-09-05 21:47:34 +00009277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009278
glennrp8640fb52010-11-23 15:48:26 +00009279 " image->depth: %.20g",(double) image->depth);
9280
9281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009282 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009283 }
9284
glennrp58e01762011-01-07 15:28:54 +00009285 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009286 {
glennrp4f25bd02011-01-01 18:51:28 +00009287 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009288 {
glennrp7c4c9e62011-03-21 20:23:32 +00009289 if (mng_info->write_png_colortype == 0)
9290 {
9291 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009292
glennrp7c4c9e62011-03-21 20:23:32 +00009293 if (ping_have_color != MagickFalse)
9294 ping_color_type=PNG_COLOR_TYPE_RGBA;
9295 }
glennrp4f25bd02011-01-01 18:51:28 +00009296
9297 /*
9298 * Determine if there is any transparent color.
9299 */
9300 if (number_transparent + number_semitransparent == 0)
9301 {
9302 /*
9303 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9304 */
glennrpa6a06632011-01-19 15:15:34 +00009305
glennrp4f25bd02011-01-01 18:51:28 +00009306 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009307
9308 if (mng_info->write_png_colortype == 0)
9309 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009310 }
9311
9312 else
9313 {
9314 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009315 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009316
9317 mask=0xffff;
9318
9319 if (ping_bit_depth == 8)
9320 mask=0x00ff;
9321
9322 if (ping_bit_depth == 4)
9323 mask=0x000f;
9324
9325 if (ping_bit_depth == 2)
9326 mask=0x0003;
9327
9328 if (ping_bit_depth == 1)
9329 mask=0x0001;
9330
9331 ping_trans_color.red=(png_uint_16)
9332 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9333
9334 ping_trans_color.green=(png_uint_16)
9335 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9336
9337 ping_trans_color.blue=(png_uint_16)
9338 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9339
9340 ping_trans_color.gray=(png_uint_16)
cristy4c08aed2011-07-01 19:47:50 +00009341 (ScaleQuantumToShort(GetPixelPacketIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009342 image->colormap)) & mask);
9343
9344 ping_trans_color.index=(png_byte) 0;
9345
9346 ping_have_tRNS=MagickTrue;
9347 }
9348
9349 if (ping_have_tRNS != MagickFalse)
9350 {
9351 /*
glennrpfd05d622011-02-25 04:10:33 +00009352 * Determine if there is one and only one transparent color
9353 * and if so if it is fully transparent.
9354 */
9355 if (ping_have_cheap_transparency == MagickFalse)
9356 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009357 }
9358
9359 if (ping_have_tRNS != MagickFalse)
9360 {
glennrp7c4c9e62011-03-21 20:23:32 +00009361 if (mng_info->write_png_colortype == 0)
9362 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009363
9364 if (image_depth == 8)
9365 {
9366 ping_trans_color.red&=0xff;
9367 ping_trans_color.green&=0xff;
9368 ping_trans_color.blue&=0xff;
9369 ping_trans_color.gray&=0xff;
9370 }
9371 }
9372 }
cristy3ed852e2009-09-05 21:47:34 +00009373 else
9374 {
cristy3ed852e2009-09-05 21:47:34 +00009375 if (image_depth == 8)
9376 {
glennrp5af765f2010-03-30 11:12:18 +00009377 ping_trans_color.red&=0xff;
9378 ping_trans_color.green&=0xff;
9379 ping_trans_color.blue&=0xff;
9380 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009381 }
9382 }
9383 }
glennrp8640fb52010-11-23 15:48:26 +00009384
cristy3ed852e2009-09-05 21:47:34 +00009385 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009386
glennrp2e09f552010-11-14 00:38:48 +00009387 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009388 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009389
glennrp39992b42010-11-14 00:03:43 +00009390 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009391 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009392 ping_have_color == MagickFalse &&
9393 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009394 {
cristy35ef8242010-06-03 16:24:13 +00009395 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009396
cristy3ed852e2009-09-05 21:47:34 +00009397 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009398 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009399
glennrp7c4c9e62011-03-21 20:23:32 +00009400 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009401 {
glennrp5af765f2010-03-30 11:12:18 +00009402 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009403
cristy3ed852e2009-09-05 21:47:34 +00009404 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009405 {
9406 if (logging != MagickFalse)
9407 {
9408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9409 " Scaling ping_trans_color (0)");
9410 }
9411 ping_trans_color.gray*=0x0101;
9412 }
cristy3ed852e2009-09-05 21:47:34 +00009413 }
glennrp0fe50b42010-11-16 03:52:51 +00009414
cristy3ed852e2009-09-05 21:47:34 +00009415 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9416 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009417
glennrp136ee3a2011-04-27 15:47:45 +00009418 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009419 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009420 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009421
cristy3ed852e2009-09-05 21:47:34 +00009422 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009423 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009424
cristy3ed852e2009-09-05 21:47:34 +00009425 else
9426 {
glennrp5af765f2010-03-30 11:12:18 +00009427 ping_bit_depth=8;
9428 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009429 {
9430 if(!mng_info->write_png_depth)
9431 {
glennrp5af765f2010-03-30 11:12:18 +00009432 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009433
cristy35ef8242010-06-03 16:24:13 +00009434 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009435 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009436 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009437 }
9438 }
glennrp2b013e42010-11-24 16:55:50 +00009439
glennrp0fe50b42010-11-16 03:52:51 +00009440 else if (ping_color_type ==
9441 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009442 mng_info->IsPalette)
9443 {
cristy3ed852e2009-09-05 21:47:34 +00009444 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009445
cristy3ed852e2009-09-05 21:47:34 +00009446 int
9447 depth_4_ok=MagickTrue,
9448 depth_2_ok=MagickTrue,
9449 depth_1_ok=MagickTrue;
9450
cristybb503372010-05-27 20:51:26 +00009451 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009452 {
9453 unsigned char
9454 intensity;
9455
9456 intensity=ScaleQuantumToChar(image->colormap[i].red);
9457
9458 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9459 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9460 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9461 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009462 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009463 depth_1_ok=MagickFalse;
9464 }
glennrp2b013e42010-11-24 16:55:50 +00009465
cristy3ed852e2009-09-05 21:47:34 +00009466 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009467 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009468
cristy3ed852e2009-09-05 21:47:34 +00009469 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009470 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009471
cristy3ed852e2009-09-05 21:47:34 +00009472 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009473 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009474 }
9475 }
glennrp2b013e42010-11-24 16:55:50 +00009476
glennrp5af765f2010-03-30 11:12:18 +00009477 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009478 }
glennrp0fe50b42010-11-16 03:52:51 +00009479
cristy3ed852e2009-09-05 21:47:34 +00009480 else
glennrp0fe50b42010-11-16 03:52:51 +00009481
cristy3ed852e2009-09-05 21:47:34 +00009482 if (mng_info->IsPalette)
9483 {
glennrp17a14852010-05-10 03:01:59 +00009484 number_colors=image_colors;
9485
cristy3ed852e2009-09-05 21:47:34 +00009486 if (image_depth <= 8)
9487 {
cristy3ed852e2009-09-05 21:47:34 +00009488 /*
9489 Set image palette.
9490 */
glennrp5af765f2010-03-30 11:12:18 +00009491 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009492
glennrp58e01762011-01-07 15:28:54 +00009493 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009494 {
glennrp9c1eb072010-06-06 22:19:15 +00009495 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009496
glennrp3b51f0e2010-11-27 18:14:08 +00009497 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009498 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9499 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009500 }
glennrp0fe50b42010-11-16 03:52:51 +00009501
cristy3ed852e2009-09-05 21:47:34 +00009502 else
9503 {
cristybb503372010-05-27 20:51:26 +00009504 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009505 {
9506 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9507 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9508 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9509 }
glennrp0fe50b42010-11-16 03:52:51 +00009510
glennrp3b51f0e2010-11-27 18:14:08 +00009511 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009513 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009514 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009515
glennrp39992b42010-11-14 00:03:43 +00009516 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009517 }
glennrp0fe50b42010-11-16 03:52:51 +00009518
cristy3ed852e2009-09-05 21:47:34 +00009519 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009520 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009521 {
cristybefe4d22010-06-07 01:18:58 +00009522 size_t
9523 one;
9524
glennrp5af765f2010-03-30 11:12:18 +00009525 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009526 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009527
glennrpd17915c2011-04-29 14:24:22 +00009528 while ((one << ping_bit_depth) < (ssize_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009529 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009530 }
glennrp0fe50b42010-11-16 03:52:51 +00009531
glennrp5af765f2010-03-30 11:12:18 +00009532 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009533
glennrp58e01762011-01-07 15:28:54 +00009534 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009535 {
glennrp0fe50b42010-11-16 03:52:51 +00009536 /*
glennrpd6bf1612010-12-17 17:28:54 +00009537 * Set up trans_colors array.
9538 */
glennrp0fe50b42010-11-16 03:52:51 +00009539 assert(number_colors <= 256);
9540
glennrpd6bf1612010-12-17 17:28:54 +00009541 ping_num_trans=(unsigned short) (number_transparent +
9542 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009543
9544 if (ping_num_trans == 0)
9545 ping_have_tRNS=MagickFalse;
9546
glennrpd6bf1612010-12-17 17:28:54 +00009547 else
glennrp0fe50b42010-11-16 03:52:51 +00009548 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009549 if (logging != MagickFalse)
9550 {
9551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9552 " Scaling ping_trans_color (1)");
9553 }
glennrpd6bf1612010-12-17 17:28:54 +00009554 ping_have_tRNS=MagickTrue;
9555
9556 for (i=0; i < ping_num_trans; i++)
9557 {
cristy4c08aed2011-07-01 19:47:50 +00009558 ping_trans_alpha[i]= (png_byte)
9559 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009560 }
glennrp0fe50b42010-11-16 03:52:51 +00009561 }
9562 }
cristy3ed852e2009-09-05 21:47:34 +00009563 }
9564 }
glennrp0fe50b42010-11-16 03:52:51 +00009565
cristy3ed852e2009-09-05 21:47:34 +00009566 else
9567 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009568
cristy3ed852e2009-09-05 21:47:34 +00009569 if (image_depth < 8)
9570 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009571
cristy3ed852e2009-09-05 21:47:34 +00009572 if ((save_image_depth == 16) && (image_depth == 8))
9573 {
glennrp4f25bd02011-01-01 18:51:28 +00009574 if (logging != MagickFalse)
9575 {
9576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9577 " Scaling ping_trans_color from (%d,%d,%d)",
9578 (int) ping_trans_color.red,
9579 (int) ping_trans_color.green,
9580 (int) ping_trans_color.blue);
9581 }
9582
glennrp5af765f2010-03-30 11:12:18 +00009583 ping_trans_color.red*=0x0101;
9584 ping_trans_color.green*=0x0101;
9585 ping_trans_color.blue*=0x0101;
9586 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009587
9588 if (logging != MagickFalse)
9589 {
9590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9591 " to (%d,%d,%d)",
9592 (int) ping_trans_color.red,
9593 (int) ping_trans_color.green,
9594 (int) ping_trans_color.blue);
9595 }
cristy3ed852e2009-09-05 21:47:34 +00009596 }
9597 }
9598
cristy4383ec82011-01-05 15:42:32 +00009599 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9600 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009601
cristy3ed852e2009-09-05 21:47:34 +00009602 /*
9603 Adjust background and transparency samples in sub-8-bit grayscale files.
9604 */
glennrp5af765f2010-03-30 11:12:18 +00009605 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009606 PNG_COLOR_TYPE_GRAY)
9607 {
9608 png_uint_16
9609 maxval;
9610
cristy35ef8242010-06-03 16:24:13 +00009611 size_t
9612 one=1;
9613
cristy22ffd972010-06-03 16:51:47 +00009614 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009615
glennrp4f25bd02011-01-01 18:51:28 +00009616 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009617 {
cristy3ed852e2009-09-05 21:47:34 +00009618
glennrpa521b2f2010-10-29 04:11:03 +00009619 ping_background.gray=(png_uint_16)
glennrp847370c2011-07-05 17:37:15 +00009620 ((maxval/255.)*((GetPixelPacketIntensity(&image->background_color)))
9621 +.5);
cristy3ed852e2009-09-05 21:47:34 +00009622
9623 if (logging != MagickFalse)
9624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009625 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9627 " background_color index is %d",
9628 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009629
glennrp991d11d2010-11-12 21:55:28 +00009630 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009631 }
cristy3ed852e2009-09-05 21:47:34 +00009632
glennrp3e3e20f2011-06-09 04:21:43 +00009633 if (logging != MagickFalse)
9634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9635 " Scaling ping_trans_color.gray from %d",
9636 (int)ping_trans_color.gray);
9637
glennrp9be9b1c2011-06-09 12:21:45 +00009638 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009639 ping_trans_color.gray)+.5);
9640
9641 if (logging != MagickFalse)
9642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9643 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009644 }
glennrp17a14852010-05-10 03:01:59 +00009645
glennrp26f37912010-12-23 16:22:42 +00009646 if (ping_exclude_bKGD == MagickFalse)
9647 {
glennrp1273f7b2011-02-24 03:20:30 +00009648 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009649 {
9650 /*
9651 Identify which colormap entry is the background color.
9652 */
9653
glennrp17a14852010-05-10 03:01:59 +00009654 number_colors=image_colors;
9655
glennrpa521b2f2010-10-29 04:11:03 +00009656 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9657 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009658 break;
9659
9660 ping_background.index=(png_byte) i;
9661
glennrp3b51f0e2010-11-27 18:14:08 +00009662 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009663 {
9664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009665 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009666 }
glennrp0fe50b42010-11-16 03:52:51 +00009667
cristy13d07042010-11-21 20:56:18 +00009668 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009669 {
9670 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009671
9672 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009673 {
9674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9675 " background =(%d,%d,%d)",
9676 (int) ping_background.red,
9677 (int) ping_background.green,
9678 (int) ping_background.blue);
9679 }
9680 }
glennrpa521b2f2010-10-29 04:11:03 +00009681
glennrpd6bf1612010-12-17 17:28:54 +00009682 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009683 {
glennrp3b51f0e2010-11-27 18:14:08 +00009684 if (logging != MagickFalse)
9685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9686 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009687 ping_have_bKGD = MagickFalse;
9688 }
glennrp17a14852010-05-10 03:01:59 +00009689 }
glennrp26f37912010-12-23 16:22:42 +00009690 }
glennrp17a14852010-05-10 03:01:59 +00009691
cristy3ed852e2009-09-05 21:47:34 +00009692 if (logging != MagickFalse)
9693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009694 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009695 /*
9696 Initialize compression level and filtering.
9697 */
9698 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009699 {
9700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9701 " Setting up deflate compression");
9702
9703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9704 " Compression buffer size: 32768");
9705 }
9706
cristy3ed852e2009-09-05 21:47:34 +00009707 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009708
cristy3ed852e2009-09-05 21:47:34 +00009709 if (logging != MagickFalse)
9710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9711 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009712
glennrp10d739e2011-06-29 18:00:52 +00009713 /* Untangle the "-quality" setting:
9714
9715 Undefined is 0; the default is used.
9716 Default is 75
9717
9718 10's digit:
9719
9720 0: Use Z_HUFFMAN_ONLY strategy with the
9721 zlib default compression level
9722
9723 1-9: the zlib compression level
9724
9725 1's digit:
9726
9727 0-4: the PNG filter method
9728
9729 5: libpng adaptive filtering if compression level > 5
9730 libpng filter type "none" if compression level <= 5
9731 or if image is grayscale or palette
9732
9733 6: libpng adaptive filtering
9734
9735 7: "LOCO" filtering (intrapixel differing) if writing
9736 a MNG, othewise "none". Did not work in IM-6.7.0-9
9737 and earlier because of a missing "else".
9738
9739 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009740 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009741
9742 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009743 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009744
9745 Note that using the -quality option, not all combinations of
9746 PNG filter type, zlib compression level, and zlib compression
cristy5e6be1e2011-07-16 01:23:39 +00009747 strategy are possible. This will be addressed soon in a
9748 release that accomodates "-define PNG:compression-strategy",
9749 etc.
glennrp10d739e2011-06-29 18:00:52 +00009750
9751 */
9752
cristy3ed852e2009-09-05 21:47:34 +00009753 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9754 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009755
glennrp18682582011-06-30 18:11:47 +00009756 if (quality <= 9)
9757 {
9758 if (mng_info->write_png_compression_strategy == 0)
9759 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9760 }
9761
9762 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009763 {
9764 int
9765 level;
9766
cristybb503372010-05-27 20:51:26 +00009767 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009768
glennrp18682582011-06-30 18:11:47 +00009769 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009770 }
glennrp0fe50b42010-11-16 03:52:51 +00009771
glennrp18682582011-06-30 18:11:47 +00009772 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009773 {
glennrp18682582011-06-30 18:11:47 +00009774 if ((quality %10) == 8 || (quality %10) == 9)
9775 mng_info->write_png_compression_strategy=Z_RLE;
cristy3ed852e2009-09-05 21:47:34 +00009776 }
glennrp0fe50b42010-11-16 03:52:51 +00009777
glennrp18682582011-06-30 18:11:47 +00009778 if (mng_info->write_png_compression_filter == 0)
9779 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9780
cristy3ed852e2009-09-05 21:47:34 +00009781 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009782 {
glennrp18682582011-06-30 18:11:47 +00009783 if (mng_info->write_png_compression_level)
9784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9785 " Compression level: %d",
9786 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009787
glennrp18682582011-06-30 18:11:47 +00009788 if (mng_info->write_png_compression_strategy)
9789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9790 " Compression strategy: %d",
9791 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009792
glennrp18682582011-06-30 18:11:47 +00009793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9794 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009795
glennrp18682582011-06-30 18:11:47 +00009796 if (mng_info->write_png_compression_filter == PNG_ALL_FILTERS+1)
cristy3ed852e2009-09-05 21:47:34 +00009797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9798 " Base filter method: ADAPTIVE");
glennrp18682582011-06-30 18:11:47 +00009799 else if (mng_info->write_png_compression_filter == PNG_NO_FILTERS+1)
cristy3ed852e2009-09-05 21:47:34 +00009800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9801 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009802 else
9803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9804 " Base filter method: %d",
9805 (int) mng_info->write_png_compression_filter-1);
9806 }
glennrp2b013e42010-11-24 16:55:50 +00009807
glennrp18682582011-06-30 18:11:47 +00009808 if (mng_info->write_png_compression_level != 0)
9809 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9810
9811 if (mng_info->write_png_compression_filter == 6)
9812 {
9813 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9814 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9815 (quality < 50))
9816 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9817 else
9818 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9819 }
9820
9821 if (mng_info->write_png_compression_filter == 7 ||
9822 mng_info->write_png_compression_filter == 10)
9823 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9824
9825 else if (mng_info->write_png_compression_filter == 8)
9826 {
9827#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9828 if (mng_info->write_mng)
9829 {
9830 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9831 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9832 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9833 }
9834#endif
9835 png_set_filter(ping,PNG_FILTER_TYPE_BASE,0);
9836 }
9837
9838 else if (mng_info->write_png_compression_filter == 9)
9839 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9840
9841 else if (mng_info->write_png_compression_filter != 0)
9842 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9843 mng_info->write_png_compression_filter-1);
9844
9845 if (mng_info->write_png_compression_strategy != 0)
9846 png_set_compression_strategy(ping,
9847 mng_info->write_png_compression_strategy-1);
9848
cristy3ed852e2009-09-05 21:47:34 +00009849
glennrp56081722011-08-27 21:48:13 +00009850 if (ping_exclude_sRGB != MagickFalse ||
9851 (image->rendering_intent == UndefinedIntent))
9852 {
9853 if ((ping_exclude_tEXt == MagickFalse ||
9854 ping_exclude_zTXt == MagickFalse) &&
9855 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009856 {
9857 ResetImageProfileIterator(image);
9858 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009859 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009860 profile=GetImageProfile(image,name);
9861
9862 if (profile != (StringInfo *) NULL)
9863 {
glennrp5af765f2010-03-30 11:12:18 +00009864#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009865 if ((LocaleCompare(name,"ICC") == 0) ||
9866 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009867 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009868
9869 if (ping_exclude_iCCP == MagickFalse)
9870 {
9871 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009872#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009873 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009874#else
9875 (png_const_bytep) GetStringInfoDatum(profile),
9876#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009877 (png_uint_32) GetStringInfoLength(profile));
9878 }
glennrp26f37912010-12-23 16:22:42 +00009879 }
glennrp0fe50b42010-11-16 03:52:51 +00009880
glennrpc8cbc5d2011-01-01 00:12:34 +00009881 else
cristy3ed852e2009-09-05 21:47:34 +00009882#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009883 if (ping_exclude_zCCP == MagickFalse)
9884 {
glennrpcf002022011-01-30 02:38:15 +00009885 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009886 (unsigned char *) name,(unsigned char *) name,
9887 GetStringInfoDatum(profile),
9888 (png_uint_32) GetStringInfoLength(profile));
9889 }
9890 }
glennrp0b206f52011-01-07 04:55:32 +00009891
glennrpc8cbc5d2011-01-01 00:12:34 +00009892 if (logging != MagickFalse)
9893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9894 " Setting up text chunk with %s profile",name);
9895
9896 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009897 }
glennrp56081722011-08-27 21:48:13 +00009898 }
cristy3ed852e2009-09-05 21:47:34 +00009899 }
9900
9901#if defined(PNG_WRITE_sRGB_SUPPORTED)
9902 if ((mng_info->have_write_global_srgb == 0) &&
9903 ((image->rendering_intent != UndefinedIntent) ||
9904 (image->colorspace == sRGBColorspace)))
9905 {
glennrp26f37912010-12-23 16:22:42 +00009906 if (ping_exclude_sRGB == MagickFalse)
9907 {
9908 /*
9909 Note image rendering intent.
9910 */
9911 if (logging != MagickFalse)
9912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9913 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009914
glennrp26f37912010-12-23 16:22:42 +00009915 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009916 Magick_RenderingIntent_to_PNG_RenderingIntent(
9917 image->rendering_intent)));
glennrp26f37912010-12-23 16:22:42 +00009918 }
cristy3ed852e2009-09-05 21:47:34 +00009919 }
glennrp26f37912010-12-23 16:22:42 +00009920
glennrp5af765f2010-03-30 11:12:18 +00009921 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009922#endif
9923 {
glennrp2cc891a2010-12-24 13:44:32 +00009924 if (ping_exclude_gAMA == MagickFalse &&
9925 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009926 (image->gamma < .45 || image->gamma > .46)))
9927 {
cristy3ed852e2009-09-05 21:47:34 +00009928 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9929 {
9930 /*
9931 Note image gamma.
9932 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9933 */
9934 if (logging != MagickFalse)
9935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9936 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009937
cristy3ed852e2009-09-05 21:47:34 +00009938 png_set_gAMA(ping,ping_info,image->gamma);
9939 }
glennrp26f37912010-12-23 16:22:42 +00009940 }
glennrp2b013e42010-11-24 16:55:50 +00009941
glennrp26f37912010-12-23 16:22:42 +00009942 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009943 {
glennrp26f37912010-12-23 16:22:42 +00009944 if ((mng_info->have_write_global_chrm == 0) &&
9945 (image->chromaticity.red_primary.x != 0.0))
9946 {
9947 /*
9948 Note image chromaticity.
9949 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9950 */
9951 PrimaryInfo
9952 bp,
9953 gp,
9954 rp,
9955 wp;
cristy3ed852e2009-09-05 21:47:34 +00009956
glennrp26f37912010-12-23 16:22:42 +00009957 wp=image->chromaticity.white_point;
9958 rp=image->chromaticity.red_primary;
9959 gp=image->chromaticity.green_primary;
9960 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009961
glennrp26f37912010-12-23 16:22:42 +00009962 if (logging != MagickFalse)
9963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9964 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009965
glennrp26f37912010-12-23 16:22:42 +00009966 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9967 bp.x,bp.y);
9968 }
9969 }
cristy3ed852e2009-09-05 21:47:34 +00009970 }
glennrpdfd70802010-11-14 01:23:35 +00009971
glennrp5af765f2010-03-30 11:12:18 +00009972 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009973
9974 if (mng_info->write_mng)
9975 png_set_sig_bytes(ping,8);
9976
9977 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9978
glennrpd6bf1612010-12-17 17:28:54 +00009979 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009980 {
9981 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +00009982 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009983 {
glennrp5af765f2010-03-30 11:12:18 +00009984 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +00009985
glennrp5af765f2010-03-30 11:12:18 +00009986 if (ping_bit_depth < 8)
9987 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00009988 }
glennrp0fe50b42010-11-16 03:52:51 +00009989
cristy3ed852e2009-09-05 21:47:34 +00009990 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +00009991 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00009992 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009993 }
9994
glennrp0e8ea192010-12-24 18:00:33 +00009995 if (ping_need_colortype_warning != MagickFalse ||
9996 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00009997 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00009998 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00009999 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +000010000 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +000010001 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +000010002 {
10003 if (logging != MagickFalse)
10004 {
glennrp0e8ea192010-12-24 18:00:33 +000010005 if (ping_need_colortype_warning != MagickFalse)
10006 {
10007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10008 " Image has transparency but tRNS chunk was excluded");
10009 }
10010
cristy3ed852e2009-09-05 21:47:34 +000010011 if (mng_info->write_png_depth)
10012 {
10013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10014 " Defined PNG:bit-depth=%u, Computed depth=%u",
10015 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +000010016 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +000010017 }
glennrp0e8ea192010-12-24 18:00:33 +000010018
cristy3ed852e2009-09-05 21:47:34 +000010019 if (mng_info->write_png_colortype)
10020 {
10021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10022 " Defined PNG:color-type=%u, Computed color type=%u",
10023 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +000010024 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010025 }
10026 }
glennrp0e8ea192010-12-24 18:00:33 +000010027
glennrp3bd2e412010-08-10 13:34:52 +000010028 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +000010029 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
10030 }
10031
glennrp58e01762011-01-07 15:28:54 +000010032 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010033 {
10034 /* Add an opaque matte channel */
10035 image->matte = MagickTrue;
10036 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +000010037
glennrpb4a13412010-05-05 12:47:19 +000010038 if (logging != MagickFalse)
10039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10040 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +000010041 }
10042
glennrp0e319732011-01-25 21:53:13 +000010043 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +000010044 {
glennrp991d11d2010-11-12 21:55:28 +000010045 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +000010046 {
glennrp991d11d2010-11-12 21:55:28 +000010047 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010048 if (logging != MagickFalse)
10049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10050 " Setting ping_have_tRNS=MagickTrue.");
10051 }
glennrpe9c26dc2010-05-30 01:56:35 +000010052 }
10053
cristy3ed852e2009-09-05 21:47:34 +000010054 if (logging != MagickFalse)
10055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10056 " Writing PNG header chunks");
10057
glennrp5af765f2010-03-30 11:12:18 +000010058 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10059 ping_bit_depth,ping_color_type,
10060 ping_interlace_method,ping_compression_method,
10061 ping_filter_method);
10062
glennrp39992b42010-11-14 00:03:43 +000010063 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10064 {
glennrpf09bded2011-01-08 01:15:59 +000010065 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010066
glennrp3b51f0e2010-11-27 18:14:08 +000010067 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +000010068 {
glennrp8640fb52010-11-23 15:48:26 +000010069 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +000010070 {
glennrpd6bf1612010-12-17 17:28:54 +000010071 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +000010072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010073 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10074 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010075 (int) palette[i].red,
10076 (int) palette[i].green,
10077 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +000010078 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010079 (int) ping_trans_alpha[i]);
10080 else
10081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010082 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010083 (int) i,
10084 (int) palette[i].red,
10085 (int) palette[i].green,
10086 (int) palette[i].blue);
10087 }
glennrp39992b42010-11-14 00:03:43 +000010088 }
glennrp39992b42010-11-14 00:03:43 +000010089 }
10090
glennrp26f37912010-12-23 16:22:42 +000010091 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010092 {
glennrp26f37912010-12-23 16:22:42 +000010093 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010094 {
glennrp26f37912010-12-23 16:22:42 +000010095 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010096 if (logging)
10097 {
10098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10099 " Setting up bKGD chunk");
10100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10101 " background color = (%d,%d,%d)",
10102 (int) ping_background.red,
10103 (int) ping_background.green,
10104 (int) ping_background.blue);
10105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10106 " index = %d, gray=%d",
10107 (int) ping_background.index,
10108 (int) ping_background.gray);
10109 }
10110 }
glennrp26f37912010-12-23 16:22:42 +000010111 }
10112
10113 if (ping_exclude_pHYs == MagickFalse)
10114 {
10115 if (ping_have_pHYs != MagickFalse)
10116 {
10117 png_set_pHYs(ping,ping_info,
10118 ping_pHYs_x_resolution,
10119 ping_pHYs_y_resolution,
10120 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010121
10122 if (logging)
10123 {
10124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10125 " Setting up pHYs chunk");
10126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10127 " x_resolution=%lu",
10128 (unsigned long) ping_pHYs_x_resolution);
10129 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10130 " y_resolution=%lu",
10131 (unsigned long) ping_pHYs_y_resolution);
10132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10133 " unit_type=%lu",
10134 (unsigned long) ping_pHYs_unit_type);
10135 }
glennrp26f37912010-12-23 16:22:42 +000010136 }
glennrpdfd70802010-11-14 01:23:35 +000010137 }
10138
10139#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010140 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010141 {
glennrp26f37912010-12-23 16:22:42 +000010142 if (image->page.x || image->page.y)
10143 {
10144 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10145 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010146
glennrp26f37912010-12-23 16:22:42 +000010147 if (logging != MagickFalse)
10148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10149 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10150 (int) image->page.x, (int) image->page.y);
10151 }
glennrpdfd70802010-11-14 01:23:35 +000010152 }
10153#endif
10154
glennrpda8f3a72011-02-27 23:54:12 +000010155 if (mng_info->need_blob != MagickFalse)
10156 {
10157 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
10158 MagickFalse)
10159 png_error(ping,"WriteBlob Failed");
10160
10161 ping_have_blob=MagickTrue;
10162 }
10163
cristy3ed852e2009-09-05 21:47:34 +000010164 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010165
glennrp39992b42010-11-14 00:03:43 +000010166 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010167 {
glennrp3b51f0e2010-11-27 18:14:08 +000010168 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010169 {
10170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10171 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10172 }
10173
10174 if (ping_color_type == 3)
10175 (void) png_set_tRNS(ping, ping_info,
10176 ping_trans_alpha,
10177 ping_num_trans,
10178 NULL);
10179
10180 else
10181 {
10182 (void) png_set_tRNS(ping, ping_info,
10183 NULL,
10184 0,
10185 &ping_trans_color);
10186
glennrp3b51f0e2010-11-27 18:14:08 +000010187 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010188 {
10189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010190 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010191 (int) ping_trans_color.red,
10192 (int) ping_trans_color.green,
10193 (int) ping_trans_color.blue);
10194 }
10195 }
glennrp991d11d2010-11-12 21:55:28 +000010196 }
10197
cristy3ed852e2009-09-05 21:47:34 +000010198 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010199 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010200
cristy3ed852e2009-09-05 21:47:34 +000010201 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010202
cristy3ed852e2009-09-05 21:47:34 +000010203 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010204 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010205
glennrp26f37912010-12-23 16:22:42 +000010206 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010207 {
glennrp4f25bd02011-01-01 18:51:28 +000010208 if ((image->page.width != 0 && image->page.width != image->columns) ||
10209 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010210 {
10211 unsigned char
10212 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010213
glennrp26f37912010-12-23 16:22:42 +000010214 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10215 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010216 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010217 PNGLong(chunk+4,(png_uint_32) image->page.width);
10218 PNGLong(chunk+8,(png_uint_32) image->page.height);
10219 chunk[12]=0; /* unit = pixels */
10220 (void) WriteBlob(image,13,chunk);
10221 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10222 }
cristy3ed852e2009-09-05 21:47:34 +000010223 }
10224
10225#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010226 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010227#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010228 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010229#undef PNG_HAVE_IDAT
10230#endif
10231
10232 png_set_packing(ping);
10233 /*
10234 Allocate memory.
10235 */
10236 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010237 if (image_depth > 8)
10238 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010239 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010240 {
glennrpb4a13412010-05-05 12:47:19 +000010241 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010242 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010243 break;
glennrp0fe50b42010-11-16 03:52:51 +000010244
glennrpb4a13412010-05-05 12:47:19 +000010245 case PNG_COLOR_TYPE_GRAY_ALPHA:
10246 rowbytes*=2;
10247 break;
glennrp0fe50b42010-11-16 03:52:51 +000010248
glennrpb4a13412010-05-05 12:47:19 +000010249 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010250 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010251 break;
glennrp0fe50b42010-11-16 03:52:51 +000010252
glennrpb4a13412010-05-05 12:47:19 +000010253 default:
10254 break;
cristy3ed852e2009-09-05 21:47:34 +000010255 }
glennrp3b51f0e2010-11-27 18:14:08 +000010256
10257 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010258 {
10259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10260 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010261
glennrpb4a13412010-05-05 12:47:19 +000010262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010263 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010264 }
glennrpcf002022011-01-30 02:38:15 +000010265 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10266 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010267
glennrpcf002022011-01-30 02:38:15 +000010268 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010269 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010270
cristy3ed852e2009-09-05 21:47:34 +000010271 /*
10272 Initialize image scanlines.
10273 */
glennrp5af765f2010-03-30 11:12:18 +000010274 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010275 {
10276 /*
10277 PNG write failed.
10278 */
10279#ifdef PNG_DEBUG
10280 if (image_info->verbose)
10281 (void) printf("PNG write has failed.\n");
10282#endif
10283 png_destroy_write_struct(&ping,&ping_info);
10284 if (quantum_info != (QuantumInfo *) NULL)
10285 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010286 if (ping_pixels != (unsigned char *) NULL)
10287 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010288#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010289 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010290#endif
glennrpda8f3a72011-02-27 23:54:12 +000010291 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010292 (void) CloseBlob(image);
10293 image_info=DestroyImageInfo(image_info);
10294 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010295 return(MagickFalse);
10296 }
cristyed552522009-10-16 14:04:35 +000010297 quantum_info=AcquireQuantumInfo(image_info,image);
10298 if (quantum_info == (QuantumInfo *) NULL)
10299 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010300 quantum_info->format=UndefinedQuantumFormat;
10301 quantum_info->depth=image_depth;
10302 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010303
cristy3ed852e2009-09-05 21:47:34 +000010304 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010305 !mng_info->write_png32) &&
10306 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010307 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010308 image_matte == MagickFalse &&
10309 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010310 {
glennrp8bb3a022010-12-13 20:40:04 +000010311 /* Palette, Bilevel, or Opaque Monochrome */
cristy4c08aed2011-07-01 19:47:50 +000010312 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010313 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010314
cristy3ed852e2009-09-05 21:47:34 +000010315 quantum_info->depth=8;
10316 for (pass=0; pass < num_passes; pass++)
10317 {
10318 /*
10319 Convert PseudoClass image to a PNG monochrome image.
10320 */
cristybb503372010-05-27 20:51:26 +000010321 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010322 {
glennrpd71e86a2011-02-24 01:28:37 +000010323 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10325 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010326
cristy3ed852e2009-09-05 21:47:34 +000010327 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010328
cristy4c08aed2011-07-01 19:47:50 +000010329 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010330 break;
glennrp0fe50b42010-11-16 03:52:51 +000010331
cristy3ed852e2009-09-05 21:47:34 +000010332 if (mng_info->IsPalette)
10333 {
cristy4c08aed2011-07-01 19:47:50 +000010334 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010335 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010336 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10337 mng_info->write_png_depth &&
10338 mng_info->write_png_depth != old_bit_depth)
10339 {
10340 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010341 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010342 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010343 >> (8-old_bit_depth));
10344 }
10345 }
glennrp0fe50b42010-11-16 03:52:51 +000010346
cristy3ed852e2009-09-05 21:47:34 +000010347 else
10348 {
cristy4c08aed2011-07-01 19:47:50 +000010349 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010350 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010351 }
glennrp0fe50b42010-11-16 03:52:51 +000010352
cristy3ed852e2009-09-05 21:47:34 +000010353 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010354 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010355 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010356 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010357
glennrp3b51f0e2010-11-27 18:14:08 +000010358 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10360 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010361
glennrpcf002022011-01-30 02:38:15 +000010362 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010363 }
10364 if (image->previous == (Image *) NULL)
10365 {
10366 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10367 if (status == MagickFalse)
10368 break;
10369 }
10370 }
10371 }
glennrp0fe50b42010-11-16 03:52:51 +000010372
glennrp8bb3a022010-12-13 20:40:04 +000010373 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010374 {
glennrp0fe50b42010-11-16 03:52:51 +000010375 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010376 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010377 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010378 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010379 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010380 {
cristy4c08aed2011-07-01 19:47:50 +000010381 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010382 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010383
glennrp8bb3a022010-12-13 20:40:04 +000010384 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010385 {
glennrp8bb3a022010-12-13 20:40:04 +000010386
cristybb503372010-05-27 20:51:26 +000010387 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010388 {
10389 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010390
cristy4c08aed2011-07-01 19:47:50 +000010391 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010392 break;
glennrp2cc891a2010-12-24 13:44:32 +000010393
glennrp5af765f2010-03-30 11:12:18 +000010394 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010395 {
glennrp8bb3a022010-12-13 20:40:04 +000010396 if (mng_info->IsPalette)
cristy4c08aed2011-07-01 19:47:50 +000010397 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010398 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010399
glennrp8bb3a022010-12-13 20:40:04 +000010400 else
cristy4c08aed2011-07-01 19:47:50 +000010401 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010402 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010403
glennrp3b51f0e2010-11-27 18:14:08 +000010404 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010406 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010407 }
glennrp2cc891a2010-12-24 13:44:32 +000010408
glennrp8bb3a022010-12-13 20:40:04 +000010409 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10410 {
10411 if (logging != MagickFalse && y == 0)
10412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10413 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010414
cristy4c08aed2011-07-01 19:47:50 +000010415 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010416 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010417 }
glennrp2cc891a2010-12-24 13:44:32 +000010418
glennrp3b51f0e2010-11-27 18:14:08 +000010419 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010421 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010422
glennrpcf002022011-01-30 02:38:15 +000010423 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010424 }
glennrp2cc891a2010-12-24 13:44:32 +000010425
glennrp8bb3a022010-12-13 20:40:04 +000010426 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010427 {
glennrp8bb3a022010-12-13 20:40:04 +000010428 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10429 if (status == MagickFalse)
10430 break;
cristy3ed852e2009-09-05 21:47:34 +000010431 }
cristy3ed852e2009-09-05 21:47:34 +000010432 }
10433 }
glennrp8bb3a022010-12-13 20:40:04 +000010434
10435 else
10436 {
cristy4c08aed2011-07-01 19:47:50 +000010437 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010438 *p;
10439
10440 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010441 {
glennrp8bb3a022010-12-13 20:40:04 +000010442 if ((image_depth > 8) || (mng_info->write_png24 ||
10443 mng_info->write_png32 ||
10444 (!mng_info->write_png8 && !mng_info->IsPalette)))
10445 {
10446 for (y=0; y < (ssize_t) image->rows; y++)
10447 {
10448 p=GetVirtualPixels(image,0,y,image->columns,1,
10449 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010450
cristy4c08aed2011-07-01 19:47:50 +000010451 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010452 break;
glennrp2cc891a2010-12-24 13:44:32 +000010453
glennrp8bb3a022010-12-13 20:40:04 +000010454 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10455 {
10456 if (image->storage_class == DirectClass)
cristy4c08aed2011-07-01 19:47:50 +000010457 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010458 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010459
glennrp8bb3a022010-12-13 20:40:04 +000010460 else
cristy4c08aed2011-07-01 19:47:50 +000010461 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010462 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010463 }
glennrp2cc891a2010-12-24 13:44:32 +000010464
glennrp8bb3a022010-12-13 20:40:04 +000010465 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10466 {
cristy4c08aed2011-07-01 19:47:50 +000010467 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010468 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +000010469 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010470
glennrp8bb3a022010-12-13 20:40:04 +000010471 if (logging != MagickFalse && y == 0)
10472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10473 " Writing GRAY_ALPHA PNG pixels (3)");
10474 }
glennrp2cc891a2010-12-24 13:44:32 +000010475
glennrp8bb3a022010-12-13 20:40:04 +000010476 else if (image_matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +000010477 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010478 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010479
glennrp8bb3a022010-12-13 20:40:04 +000010480 else
cristy4c08aed2011-07-01 19:47:50 +000010481 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010482 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010483
glennrp8bb3a022010-12-13 20:40:04 +000010484 if (logging != MagickFalse && y == 0)
10485 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10486 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010487
glennrpcf002022011-01-30 02:38:15 +000010488 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010489 }
10490 }
glennrp2cc891a2010-12-24 13:44:32 +000010491
glennrp8bb3a022010-12-13 20:40:04 +000010492 else
10493 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10494 mng_info->write_png32 ||
10495 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10496 {
10497 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10498 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10499 {
10500 if (logging != MagickFalse)
10501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10502 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010503
glennrp8bb3a022010-12-13 20:40:04 +000010504 quantum_info->depth=8;
10505 image_depth=8;
10506 }
glennrp2cc891a2010-12-24 13:44:32 +000010507
glennrp8bb3a022010-12-13 20:40:04 +000010508 for (y=0; y < (ssize_t) image->rows; y++)
10509 {
10510 if (logging != MagickFalse && y == 0)
10511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10512 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010513
glennrp770d1932011-03-06 22:11:17 +000010514 p=GetVirtualPixels(image,0,y,image->columns,1,
10515 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010516
cristy4c08aed2011-07-01 19:47:50 +000010517 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010518 break;
glennrp2cc891a2010-12-24 13:44:32 +000010519
glennrp8bb3a022010-12-13 20:40:04 +000010520 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010521 {
glennrp4bf89732011-03-21 13:48:28 +000010522 quantum_info->depth=image->depth;
10523
cristy4c08aed2011-07-01 19:47:50 +000010524 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010525 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +000010526 }
glennrp2cc891a2010-12-24 13:44:32 +000010527
glennrp8bb3a022010-12-13 20:40:04 +000010528 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10529 {
10530 if (logging != MagickFalse && y == 0)
10531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10532 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010533
cristy4c08aed2011-07-01 19:47:50 +000010534 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010535 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +000010536 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010537 }
glennrp2cc891a2010-12-24 13:44:32 +000010538
glennrp8bb3a022010-12-13 20:40:04 +000010539 else
glennrp8bb3a022010-12-13 20:40:04 +000010540 {
cristy4c08aed2011-07-01 19:47:50 +000010541 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +000010542 quantum_info,IndexQuantum,ping_pixels,&image->exception);
10543
10544 if (logging != MagickFalse && y <= 2)
10545 {
10546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010547 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010548
10549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10550 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10551 (int)ping_pixels[0],(int)ping_pixels[1]);
10552 }
glennrp8bb3a022010-12-13 20:40:04 +000010553 }
glennrpcf002022011-01-30 02:38:15 +000010554 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010555 }
10556 }
glennrp2cc891a2010-12-24 13:44:32 +000010557
glennrp8bb3a022010-12-13 20:40:04 +000010558 if (image->previous == (Image *) NULL)
10559 {
10560 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10561 if (status == MagickFalse)
10562 break;
10563 }
cristy3ed852e2009-09-05 21:47:34 +000010564 }
glennrp8bb3a022010-12-13 20:40:04 +000010565 }
10566 }
10567
cristyb32b90a2009-09-07 21:45:48 +000010568 if (quantum_info != (QuantumInfo *) NULL)
10569 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010570
10571 if (logging != MagickFalse)
10572 {
10573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010574 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010575
cristy3ed852e2009-09-05 21:47:34 +000010576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010577 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010578
cristy3ed852e2009-09-05 21:47:34 +000010579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010580 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010581
cristy3ed852e2009-09-05 21:47:34 +000010582 if (mng_info->write_png_depth)
10583 {
10584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10585 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
10586 }
glennrp0fe50b42010-11-16 03:52:51 +000010587
cristy3ed852e2009-09-05 21:47:34 +000010588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010589 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010590
cristy3ed852e2009-09-05 21:47:34 +000010591 if (mng_info->write_png_colortype)
10592 {
10593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10594 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
10595 }
glennrp0fe50b42010-11-16 03:52:51 +000010596
cristy3ed852e2009-09-05 21:47:34 +000010597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010598 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010599
cristy3ed852e2009-09-05 21:47:34 +000010600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010601 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010602 }
10603 /*
glennrpa0ed0092011-04-18 16:36:29 +000010604 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010605 */
glennrp823b55c2011-03-14 18:46:46 +000010606 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010607 {
glennrp26f37912010-12-23 16:22:42 +000010608 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010609 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010610 while (property != (const char *) NULL)
10611 {
10612 png_textp
10613 text;
glennrp2cc891a2010-12-24 13:44:32 +000010614
glennrp26f37912010-12-23 16:22:42 +000010615 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +000010616
10617 /* Don't write any "png:" properties; those are just for "identify" */
10618 if (LocaleNCompare(property,"png:",4) != 0 &&
10619
10620 /* Suppress density and units if we wrote a pHYs chunk */
10621 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010622 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010623 LocaleCompare(property,"units") != 0) &&
10624
10625 /* Suppress the IM-generated Date:create and Date:modify */
10626 (ping_exclude_date == MagickFalse ||
10627 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010628 {
glennrpc70af4a2011-03-07 00:08:23 +000010629 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010630 {
glennrpc70af4a2011-03-07 00:08:23 +000010631 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10632 text[0].key=(char *) property;
10633 text[0].text=(char *) value;
10634 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010635
glennrpc70af4a2011-03-07 00:08:23 +000010636 if (ping_exclude_tEXt != MagickFalse)
10637 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10638
10639 else if (ping_exclude_zTXt != MagickFalse)
10640 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10641
10642 else
glennrp26f37912010-12-23 16:22:42 +000010643 {
glennrpc70af4a2011-03-07 00:08:23 +000010644 text[0].compression=image_info->compression == NoCompression ||
10645 (image_info->compression == UndefinedCompression &&
10646 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10647 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010648 }
glennrp2cc891a2010-12-24 13:44:32 +000010649
glennrpc70af4a2011-03-07 00:08:23 +000010650 if (logging != MagickFalse)
10651 {
10652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10653 " Setting up text chunk");
10654
10655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10656 " keyword: %s",text[0].key);
10657 }
10658
10659 png_set_text(ping,ping_info,text,1);
10660 png_free(ping,text);
10661 }
glennrp26f37912010-12-23 16:22:42 +000010662 }
10663 property=GetNextImageProperty(image);
10664 }
cristy3ed852e2009-09-05 21:47:34 +000010665 }
10666
10667 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010668 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010669
10670 if (logging != MagickFalse)
10671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10672 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010673
cristy3ed852e2009-09-05 21:47:34 +000010674 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010675
cristy3ed852e2009-09-05 21:47:34 +000010676 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10677 {
10678 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010679 (ping_width != mng_info->page.width) ||
10680 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010681 {
10682 unsigned char
10683 chunk[32];
10684
10685 /*
10686 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10687 */
10688 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10689 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010690 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010691 chunk[4]=4;
10692 chunk[5]=0; /* frame name separator (no name) */
10693 chunk[6]=1; /* flag for changing delay, for next frame only */
10694 chunk[7]=0; /* flag for changing frame timeout */
10695 chunk[8]=1; /* flag for changing frame clipping for next frame */
10696 chunk[9]=0; /* flag for changing frame sync_id */
10697 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10698 chunk[14]=0; /* clipping boundaries delta type */
10699 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10700 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010701 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010702 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10703 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010704 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010705 (void) WriteBlob(image,31,chunk);
10706 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10707 mng_info->old_framing_mode=4;
10708 mng_info->framing_mode=1;
10709 }
glennrp0fe50b42010-11-16 03:52:51 +000010710
cristy3ed852e2009-09-05 21:47:34 +000010711 else
10712 mng_info->framing_mode=3;
10713 }
10714 if (mng_info->write_mng && !mng_info->need_fram &&
10715 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +000010716 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010717 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010718 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010719
cristy3ed852e2009-09-05 21:47:34 +000010720 /*
10721 Free PNG resources.
10722 */
glennrp5af765f2010-03-30 11:12:18 +000010723
cristy3ed852e2009-09-05 21:47:34 +000010724 png_destroy_write_struct(&ping,&ping_info);
10725
glennrpcf002022011-01-30 02:38:15 +000010726 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010727
10728#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010729 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010730#endif
10731
glennrpda8f3a72011-02-27 23:54:12 +000010732 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010733 (void) CloseBlob(image);
10734
10735 image_info=DestroyImageInfo(image_info);
10736 image=DestroyImage(image);
10737
10738 /* Store bit depth actually written */
10739 s[0]=(char) ping_bit_depth;
10740 s[1]='\0';
10741
10742 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
10743
cristy3ed852e2009-09-05 21:47:34 +000010744 if (logging != MagickFalse)
10745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10746 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010747
cristy3ed852e2009-09-05 21:47:34 +000010748 return(MagickTrue);
10749/* End write one PNG image */
10750}
10751
10752/*
10753%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10754% %
10755% %
10756% %
10757% W r i t e P N G I m a g e %
10758% %
10759% %
10760% %
10761%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10762%
10763% WritePNGImage() writes a Portable Network Graphics (PNG) or
10764% Multiple-image Network Graphics (MNG) image file.
10765%
10766% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10767%
10768% The format of the WritePNGImage method is:
10769%
cristy1e178e72011-08-28 19:44:34 +000010770% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10771% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010772%
10773% A description of each parameter follows:
10774%
10775% o image_info: the image info.
10776%
10777% o image: The image.
10778%
cristy1e178e72011-08-28 19:44:34 +000010779% o exception: return any errors or warnings in this structure.
10780%
cristy3ed852e2009-09-05 21:47:34 +000010781% Returns MagickTrue on success, MagickFalse on failure.
10782%
10783% Communicating with the PNG encoder:
10784%
10785% While the datastream written is always in PNG format and normally would
10786% be given the "png" file extension, this method also writes the following
10787% pseudo-formats which are subsets of PNG:
10788%
glennrp5a39f372011-02-25 04:52:16 +000010789% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10790% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010791% is present, the tRNS chunk must only have values 0 and 255
10792% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010793% transparent). If other values are present they will be
10794% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010795% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000010796% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10797% of any resulting fully-transparent pixels is changed to
10798% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000010799%
10800% If you want better quantization or dithering of the colors
10801% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010802% PNG encoder. The pixels contain 8-bit indices even if
10803% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010804% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010805% PNG grayscale type might be slightly more efficient. Please
10806% note that writing to the PNG8 format may result in loss
10807% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010808%
10809% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10810% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010811% one of the colors as transparent. The only loss incurred
10812% is reduction of sample depth to 8. If the image has more
10813% than one transparent color, has semitransparent pixels, or
10814% has an opaque pixel with the same RGB components as the
10815% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010816%
10817% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10818% transparency is permitted, i.e., the alpha sample for
10819% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010820% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010821% The only loss in data is the reduction of the sample depth
10822% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010823%
10824% o -define: For more precise control of the PNG output, you can use the
10825% Image options "png:bit-depth" and "png:color-type". These
10826% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010827% from the application programming interfaces. The options
10828% are case-independent and are converted to lowercase before
10829% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010830%
10831% png:color-type can be 0, 2, 3, 4, or 6.
10832%
10833% When png:color-type is 0 (Grayscale), png:bit-depth can
10834% be 1, 2, 4, 8, or 16.
10835%
10836% When png:color-type is 2 (RGB), png:bit-depth can
10837% be 8 or 16.
10838%
10839% When png:color-type is 3 (Indexed), png:bit-depth can
10840% be 1, 2, 4, or 8. This refers to the number of bits
10841% used to store the index. The color samples always have
10842% bit-depth 8 in indexed PNG files.
10843%
10844% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10845% png:bit-depth can be 8 or 16.
10846%
glennrp5a39f372011-02-25 04:52:16 +000010847% If the image cannot be written without loss with the requested bit-depth
10848% and color-type, a PNG file will not be written, and the encoder will
10849% return MagickFalse.
10850%
cristy3ed852e2009-09-05 21:47:34 +000010851% Since image encoders should not be responsible for the "heavy lifting",
10852% the user should make sure that ImageMagick has already reduced the
10853% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010854% transparency prior to attempting to write the image with depth, color,
glennrp54dc0692011-07-01 19:02:13 +000010855% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010856%
cristy3ed852e2009-09-05 21:47:34 +000010857% Note that another definition, "png:bit-depth-written" exists, but it
10858% is not intended for external use. It is only used internally by the
10859% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10860%
10861% It is possible to request that the PNG encoder write previously-formatted
10862% ancillary chunks in the output PNG file, using the "-profile" commandline
10863% option as shown below or by setting the profile via a programming
10864% interface:
10865%
10866% -profile PNG-chunk-x:<file>
10867%
10868% where x is a location flag and <file> is a file containing the chunk
10869% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010870% This encoder will compute the chunk length and CRC, so those must not
10871% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010872%
10873% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10874% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10875% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010876% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010877%
glennrpbb8a7332010-11-13 15:17:35 +000010878% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010879%
glennrp3241bd02010-12-12 04:36:28 +000010880% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010881%
glennrpd6afd542010-11-19 01:53:05 +000010882% o 32-bit depth is reduced to 16.
10883% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10884% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010885% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010886% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010887% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010888% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10889% this can be done without loss and a larger bit depth N was not
10890% requested via the "-define PNG:bit-depth=N" option.
10891% o If matte channel is present but only one transparent color is
10892% present, RGB+tRNS is written instead of RGBA
10893% o Opaque matte channel is removed (or added, if color-type 4 or 6
10894% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010895%
cristy3ed852e2009-09-05 21:47:34 +000010896%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10897*/
10898static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +000010899 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010900{
10901 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010902 excluding,
10903 logging,
10904 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010905 status;
10906
10907 MngInfo
10908 *mng_info;
10909
10910 const char
10911 *value;
10912
10913 int
glennrp21f0e622011-01-07 16:20:57 +000010914 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010915 source;
10916
cristy3ed852e2009-09-05 21:47:34 +000010917 /*
10918 Open image file.
10919 */
10920 assert(image_info != (const ImageInfo *) NULL);
10921 assert(image_info->signature == MagickSignature);
10922 assert(image != (Image *) NULL);
10923 assert(image->signature == MagickSignature);
10924 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010925 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010926 /*
10927 Allocate a MngInfo structure.
10928 */
10929 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010930 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010931
cristy3ed852e2009-09-05 21:47:34 +000010932 if (mng_info == (MngInfo *) NULL)
10933 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010934
cristy3ed852e2009-09-05 21:47:34 +000010935 /*
10936 Initialize members of the MngInfo structure.
10937 */
10938 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10939 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010940 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010941 have_mng_structure=MagickTrue;
10942
10943 /* See if user has requested a specific PNG subformat */
10944
10945 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10946 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10947 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10948
10949 if (mng_info->write_png8)
10950 {
glennrp9c1eb072010-06-06 22:19:15 +000010951 mng_info->write_png_colortype = /* 3 */ 4;
10952 mng_info->write_png_depth = 8;
10953 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010954 }
10955
10956 if (mng_info->write_png24)
10957 {
glennrp9c1eb072010-06-06 22:19:15 +000010958 mng_info->write_png_colortype = /* 2 */ 3;
10959 mng_info->write_png_depth = 8;
10960 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010961
glennrp9c1eb072010-06-06 22:19:15 +000010962 if (image->matte == MagickTrue)
10963 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010964
glennrp9c1eb072010-06-06 22:19:15 +000010965 else
10966 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010967
glennrp9c1eb072010-06-06 22:19:15 +000010968 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010969 }
10970
10971 if (mng_info->write_png32)
10972 {
glennrp9c1eb072010-06-06 22:19:15 +000010973 mng_info->write_png_colortype = /* 6 */ 7;
10974 mng_info->write_png_depth = 8;
10975 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010976
glennrp9c1eb072010-06-06 22:19:15 +000010977 if (image->matte == MagickTrue)
10978 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010979
glennrp9c1eb072010-06-06 22:19:15 +000010980 else
10981 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010982
glennrp9c1eb072010-06-06 22:19:15 +000010983 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010984 }
10985
10986 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000010987
cristy3ed852e2009-09-05 21:47:34 +000010988 if (value != (char *) NULL)
10989 {
10990 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010991 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010992
cristy3ed852e2009-09-05 21:47:34 +000010993 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010994 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000010995
cristy3ed852e2009-09-05 21:47:34 +000010996 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010997 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010998
cristy3ed852e2009-09-05 21:47:34 +000010999 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011000 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011001
cristy3ed852e2009-09-05 21:47:34 +000011002 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011003 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011004
glennrpbb8a7332010-11-13 15:17:35 +000011005 else
11006 (void) ThrowMagickException(&image->exception,
11007 GetMagickModule(),CoderWarning,
11008 "ignoring invalid defined png:bit-depth",
11009 "=%s",value);
11010
cristy3ed852e2009-09-05 21:47:34 +000011011 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011013 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011014 }
glennrp0fe50b42010-11-16 03:52:51 +000011015
cristy3ed852e2009-09-05 21:47:34 +000011016 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011017
cristy3ed852e2009-09-05 21:47:34 +000011018 if (value != (char *) NULL)
11019 {
11020 /* We must store colortype+1 because 0 is a valid colortype */
11021 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011022 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011023
cristy3ed852e2009-09-05 21:47:34 +000011024 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011025 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011026
cristy3ed852e2009-09-05 21:47:34 +000011027 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011028 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011029
cristy3ed852e2009-09-05 21:47:34 +000011030 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011031 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011032
cristy3ed852e2009-09-05 21:47:34 +000011033 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011034 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011035
glennrpbb8a7332010-11-13 15:17:35 +000011036 else
11037 (void) ThrowMagickException(&image->exception,
11038 GetMagickModule(),CoderWarning,
11039 "ignoring invalid defined png:color-type",
11040 "=%s",value);
11041
cristy3ed852e2009-09-05 21:47:34 +000011042 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011044 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011045 }
11046
glennrp0e8ea192010-12-24 18:00:33 +000011047 /* Check for chunks to be excluded:
11048 *
glennrp0dff56c2011-01-29 19:10:02 +000011049 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011050 * listed in the "unused_chunks" array, above.
11051 *
11052 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
11053 * define (in the image properties or in the image artifacts)
11054 * or via a mng_info member. For convenience, in addition
11055 * to or instead of a comma-separated list of chunks, the
11056 * "exclude-chunk" string can be simply "all" or "none".
11057 *
11058 * The exclude-chunk define takes priority over the mng_info.
11059 *
11060 * A "PNG:include-chunk" define takes priority over both the
11061 * mng_info and the "PNG:exclude-chunk" define. Like the
11062 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011063 * well as a comma-separated list. Chunks that are unknown to
11064 * ImageMagick are always excluded, regardless of their "copy-safe"
11065 * status according to the PNG specification, and even if they
11066 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000011067 *
11068 * Finally, all chunks listed in the "unused_chunks" array are
11069 * automatically excluded, regardless of the other instructions
11070 * or lack thereof.
11071 *
11072 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11073 * will not be written and the gAMA chunk will only be written if it
11074 * is not between .45 and .46, or approximately (1.0/2.2).
11075 *
11076 * If you exclude tRNS and the image has transparency, the colortype
11077 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11078 *
11079 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011080 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011081 */
11082
glennrp26f37912010-12-23 16:22:42 +000011083 mng_info->ping_exclude_bKGD=MagickFalse;
11084 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011085 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011086 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11087 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011088 mng_info->ping_exclude_iCCP=MagickFalse;
11089 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11090 mng_info->ping_exclude_oFFs=MagickFalse;
11091 mng_info->ping_exclude_pHYs=MagickFalse;
11092 mng_info->ping_exclude_sRGB=MagickFalse;
11093 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011094 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011095 mng_info->ping_exclude_vpAg=MagickFalse;
11096 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11097 mng_info->ping_exclude_zTXt=MagickFalse;
11098
glennrp8d3d6e52011-04-19 04:39:51 +000011099 mng_info->ping_preserve_colormap=MagickFalse;
11100
11101 value=GetImageArtifact(image,"png:preserve-colormap");
11102 if (value == NULL)
11103 value=GetImageOption(image_info,"png:preserve-colormap");
11104 if (value != NULL)
11105 mng_info->ping_preserve_colormap=MagickTrue;
11106
glennrp18682582011-06-30 18:11:47 +000011107 /* Thes compression-level, compression-strategy, and compression-filter
11108 * defines take precedence over values from the -quality option.
11109 */
11110 value=GetImageArtifact(image,"png:compression-level");
11111 if (value == NULL)
11112 value=GetImageOption(image_info,"png:compression-level");
11113 if (value != NULL)
11114 {
glennrp18682582011-06-30 18:11:47 +000011115 /* We have to add 1 to everything because 0 is a valid input,
11116 * and we want to use 0 (the default) to mean undefined.
11117 */
11118 if (LocaleCompare(value,"0") == 0)
11119 mng_info->write_png_compression_level = 1;
11120
11121 if (LocaleCompare(value,"1") == 0)
11122 mng_info->write_png_compression_level = 2;
11123
11124 else if (LocaleCompare(value,"2") == 0)
11125 mng_info->write_png_compression_level = 3;
11126
11127 else if (LocaleCompare(value,"3") == 0)
11128 mng_info->write_png_compression_level = 4;
11129
11130 else if (LocaleCompare(value,"4") == 0)
11131 mng_info->write_png_compression_level = 5;
11132
11133 else if (LocaleCompare(value,"5") == 0)
11134 mng_info->write_png_compression_level = 6;
11135
11136 else if (LocaleCompare(value,"6") == 0)
11137 mng_info->write_png_compression_level = 7;
11138
11139 else if (LocaleCompare(value,"7") == 0)
11140 mng_info->write_png_compression_level = 8;
11141
11142 else if (LocaleCompare(value,"8") == 0)
11143 mng_info->write_png_compression_level = 9;
11144
11145 else if (LocaleCompare(value,"9") == 0)
11146 mng_info->write_png_compression_level = 10;
11147
11148 else
11149 (void) ThrowMagickException(&image->exception,
11150 GetMagickModule(),CoderWarning,
11151 "ignoring invalid defined png:compression-level",
11152 "=%s",value);
11153 }
11154
11155 value=GetImageArtifact(image,"png:compression-strategy");
11156 if (value == NULL)
11157 value=GetImageOption(image_info,"png:compression-strategy");
11158 if (value != NULL)
11159 {
11160
11161 if (LocaleCompare(value,"0") == 0)
11162 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11163
11164 else if (LocaleCompare(value,"1") == 0)
11165 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11166
11167 else if (LocaleCompare(value,"2") == 0)
11168 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11169
11170 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011171#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011172 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011173#else
11174 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11175#endif
glennrp18682582011-06-30 18:11:47 +000011176
11177 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011178#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011179 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011180#else
11181 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11182#endif
glennrp18682582011-06-30 18:11:47 +000011183
11184 else
11185 (void) ThrowMagickException(&image->exception,
11186 GetMagickModule(),CoderWarning,
11187 "ignoring invalid defined png:compression-strategy",
11188 "=%s",value);
11189 }
11190
11191 value=GetImageArtifact(image,"png:compression-filter");
11192 if (value == NULL)
11193 value=GetImageOption(image_info,"png:compression-filter");
11194 if (value != NULL)
11195 {
11196
11197 /* To do: combinations of filters allowed by libpng
11198 * masks 0x08 through 0xf8
11199 *
11200 * Implement this as a comma-separated list of 0,1,2,3,4,5
11201 * where 5 is a special case meaning PNG_ALL_FILTERS.
11202 */
11203
11204 if (LocaleCompare(value,"0") == 0)
11205 mng_info->write_png_compression_filter = 1;
11206
11207 if (LocaleCompare(value,"1") == 0)
11208 mng_info->write_png_compression_filter = 2;
11209
11210 else if (LocaleCompare(value,"2") == 0)
11211 mng_info->write_png_compression_filter = 3;
11212
11213 else if (LocaleCompare(value,"3") == 0)
11214 mng_info->write_png_compression_filter = 4;
11215
11216 else if (LocaleCompare(value,"4") == 0)
11217 mng_info->write_png_compression_filter = 5;
11218
11219 else if (LocaleCompare(value,"5") == 0)
11220 mng_info->write_png_compression_filter = 6;
11221
glennrp54dc0692011-07-01 19:02:13 +000011222 else if (LocaleCompare(value,"6") == 0)
11223 mng_info->write_png_compression_filter = 7;
11224
11225 else if (LocaleCompare(value,"7") == 0)
11226 mng_info->write_png_compression_filter = 8;
11227
11228 else if (LocaleCompare(value,"8") == 0)
11229 mng_info->write_png_compression_filter = 9;
11230
11231 else if (LocaleCompare(value,"9") == 0)
11232 mng_info->write_png_compression_filter = 10;
glennrp18682582011-06-30 18:11:47 +000011233
11234 else
11235 (void) ThrowMagickException(&image->exception,
11236 GetMagickModule(),CoderWarning,
11237 "ignoring invalid defined png:compression-filter",
11238 "=%s",value);
11239 }
11240
glennrp03812ae2010-12-24 01:31:34 +000011241 excluding=MagickFalse;
11242
glennrp5c7cf4e2010-12-24 00:30:00 +000011243 for (source=0; source<1; source++)
11244 {
11245 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011246 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011247 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011248
11249 if (value == NULL)
11250 value=GetImageArtifact(image,"png:exclude-chunks");
11251 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011252 else
glennrpacba0042010-12-24 14:27:26 +000011253 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011254 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011255
glennrpacba0042010-12-24 14:27:26 +000011256 if (value == NULL)
11257 value=GetImageOption(image_info,"png:exclude-chunks");
11258 }
11259
glennrp03812ae2010-12-24 01:31:34 +000011260 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011261 {
glennrp03812ae2010-12-24 01:31:34 +000011262
11263 size_t
11264 last;
11265
11266 excluding=MagickTrue;
11267
11268 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011269 {
11270 if (source == 0)
11271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11272 " png:exclude-chunk=%s found in image artifacts.\n", value);
11273 else
11274 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11275 " png:exclude-chunk=%s found in image properties.\n", value);
11276 }
glennrp03812ae2010-12-24 01:31:34 +000011277
11278 last=strlen(value);
11279
11280 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011281 {
glennrp03812ae2010-12-24 01:31:34 +000011282
11283 if (LocaleNCompare(value+i,"all",3) == 0)
11284 {
11285 mng_info->ping_exclude_bKGD=MagickTrue;
11286 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011287 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011288 mng_info->ping_exclude_EXIF=MagickTrue;
11289 mng_info->ping_exclude_gAMA=MagickTrue;
11290 mng_info->ping_exclude_iCCP=MagickTrue;
11291 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11292 mng_info->ping_exclude_oFFs=MagickTrue;
11293 mng_info->ping_exclude_pHYs=MagickTrue;
11294 mng_info->ping_exclude_sRGB=MagickTrue;
11295 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011296 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011297 mng_info->ping_exclude_vpAg=MagickTrue;
11298 mng_info->ping_exclude_zCCP=MagickTrue;
11299 mng_info->ping_exclude_zTXt=MagickTrue;
11300 i--;
11301 }
glennrp2cc891a2010-12-24 13:44:32 +000011302
glennrp03812ae2010-12-24 01:31:34 +000011303 if (LocaleNCompare(value+i,"none",4) == 0)
11304 {
11305 mng_info->ping_exclude_bKGD=MagickFalse;
11306 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011307 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011308 mng_info->ping_exclude_EXIF=MagickFalse;
11309 mng_info->ping_exclude_gAMA=MagickFalse;
11310 mng_info->ping_exclude_iCCP=MagickFalse;
11311 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11312 mng_info->ping_exclude_oFFs=MagickFalse;
11313 mng_info->ping_exclude_pHYs=MagickFalse;
11314 mng_info->ping_exclude_sRGB=MagickFalse;
11315 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011316 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011317 mng_info->ping_exclude_vpAg=MagickFalse;
11318 mng_info->ping_exclude_zCCP=MagickFalse;
11319 mng_info->ping_exclude_zTXt=MagickFalse;
11320 }
glennrp2cc891a2010-12-24 13:44:32 +000011321
glennrp03812ae2010-12-24 01:31:34 +000011322 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11323 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011324
glennrp03812ae2010-12-24 01:31:34 +000011325 if (LocaleNCompare(value+i,"chrm",4) == 0)
11326 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011327
glennrpa0ed0092011-04-18 16:36:29 +000011328 if (LocaleNCompare(value+i,"date",4) == 0)
11329 mng_info->ping_exclude_date=MagickTrue;
11330
glennrp03812ae2010-12-24 01:31:34 +000011331 if (LocaleNCompare(value+i,"exif",4) == 0)
11332 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011333
glennrp03812ae2010-12-24 01:31:34 +000011334 if (LocaleNCompare(value+i,"gama",4) == 0)
11335 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011336
glennrp03812ae2010-12-24 01:31:34 +000011337 if (LocaleNCompare(value+i,"iccp",4) == 0)
11338 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011339
glennrp03812ae2010-12-24 01:31:34 +000011340 /*
11341 if (LocaleNCompare(value+i,"itxt",4) == 0)
11342 mng_info->ping_exclude_iTXt=MagickTrue;
11343 */
glennrp2cc891a2010-12-24 13:44:32 +000011344
glennrp03812ae2010-12-24 01:31:34 +000011345 if (LocaleNCompare(value+i,"gama",4) == 0)
11346 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011347
glennrp03812ae2010-12-24 01:31:34 +000011348 if (LocaleNCompare(value+i,"offs",4) == 0)
11349 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011350
glennrp03812ae2010-12-24 01:31:34 +000011351 if (LocaleNCompare(value+i,"phys",4) == 0)
11352 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011353
glennrpa1e3b7b2010-12-24 16:37:33 +000011354 if (LocaleNCompare(value+i,"srgb",4) == 0)
11355 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011356
glennrp03812ae2010-12-24 01:31:34 +000011357 if (LocaleNCompare(value+i,"text",4) == 0)
11358 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011359
glennrpa1e3b7b2010-12-24 16:37:33 +000011360 if (LocaleNCompare(value+i,"trns",4) == 0)
11361 mng_info->ping_exclude_tRNS=MagickTrue;
11362
glennrp03812ae2010-12-24 01:31:34 +000011363 if (LocaleNCompare(value+i,"vpag",4) == 0)
11364 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011365
glennrp03812ae2010-12-24 01:31:34 +000011366 if (LocaleNCompare(value+i,"zccp",4) == 0)
11367 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011368
glennrp03812ae2010-12-24 01:31:34 +000011369 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11370 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011371
glennrp03812ae2010-12-24 01:31:34 +000011372 }
glennrpce91ed52010-12-23 22:37:49 +000011373 }
glennrp26f37912010-12-23 16:22:42 +000011374 }
11375
glennrp5c7cf4e2010-12-24 00:30:00 +000011376 for (source=0; source<1; source++)
11377 {
11378 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011379 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011380 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011381
11382 if (value == NULL)
11383 value=GetImageArtifact(image,"png:include-chunks");
11384 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011385 else
glennrpacba0042010-12-24 14:27:26 +000011386 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011387 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011388
glennrpacba0042010-12-24 14:27:26 +000011389 if (value == NULL)
11390 value=GetImageOption(image_info,"png:include-chunks");
11391 }
11392
glennrp03812ae2010-12-24 01:31:34 +000011393 if (value != NULL)
11394 {
11395 size_t
11396 last;
glennrp26f37912010-12-23 16:22:42 +000011397
glennrp03812ae2010-12-24 01:31:34 +000011398 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011399
glennrp03812ae2010-12-24 01:31:34 +000011400 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011401 {
11402 if (source == 0)
11403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11404 " png:include-chunk=%s found in image artifacts.\n", value);
11405 else
11406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11407 " png:include-chunk=%s found in image properties.\n", value);
11408 }
glennrp03812ae2010-12-24 01:31:34 +000011409
11410 last=strlen(value);
11411
11412 for (i=0; i<(int) last; i+=5)
11413 {
11414 if (LocaleNCompare(value+i,"all",3) == 0)
11415 {
11416 mng_info->ping_exclude_bKGD=MagickFalse;
11417 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011418 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011419 mng_info->ping_exclude_EXIF=MagickFalse;
11420 mng_info->ping_exclude_gAMA=MagickFalse;
11421 mng_info->ping_exclude_iCCP=MagickFalse;
11422 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11423 mng_info->ping_exclude_oFFs=MagickFalse;
11424 mng_info->ping_exclude_pHYs=MagickFalse;
11425 mng_info->ping_exclude_sRGB=MagickFalse;
11426 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011427 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011428 mng_info->ping_exclude_vpAg=MagickFalse;
11429 mng_info->ping_exclude_zCCP=MagickFalse;
11430 mng_info->ping_exclude_zTXt=MagickFalse;
11431 i--;
11432 }
glennrp2cc891a2010-12-24 13:44:32 +000011433
glennrp03812ae2010-12-24 01:31:34 +000011434 if (LocaleNCompare(value+i,"none",4) == 0)
11435 {
11436 mng_info->ping_exclude_bKGD=MagickTrue;
11437 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011438 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011439 mng_info->ping_exclude_EXIF=MagickTrue;
11440 mng_info->ping_exclude_gAMA=MagickTrue;
11441 mng_info->ping_exclude_iCCP=MagickTrue;
11442 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11443 mng_info->ping_exclude_oFFs=MagickTrue;
11444 mng_info->ping_exclude_pHYs=MagickTrue;
11445 mng_info->ping_exclude_sRGB=MagickTrue;
11446 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011447 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011448 mng_info->ping_exclude_vpAg=MagickTrue;
11449 mng_info->ping_exclude_zCCP=MagickTrue;
11450 mng_info->ping_exclude_zTXt=MagickTrue;
11451 }
glennrp2cc891a2010-12-24 13:44:32 +000011452
glennrp03812ae2010-12-24 01:31:34 +000011453 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11454 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011455
glennrp03812ae2010-12-24 01:31:34 +000011456 if (LocaleNCompare(value+i,"chrm",4) == 0)
11457 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011458
glennrpa0ed0092011-04-18 16:36:29 +000011459 if (LocaleNCompare(value+i,"date",4) == 0)
11460 mng_info->ping_exclude_date=MagickFalse;
11461
glennrp03812ae2010-12-24 01:31:34 +000011462 if (LocaleNCompare(value+i,"exif",4) == 0)
11463 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011464
glennrp03812ae2010-12-24 01:31:34 +000011465 if (LocaleNCompare(value+i,"gama",4) == 0)
11466 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011467
glennrp03812ae2010-12-24 01:31:34 +000011468 if (LocaleNCompare(value+i,"iccp",4) == 0)
11469 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011470
glennrp03812ae2010-12-24 01:31:34 +000011471 /*
11472 if (LocaleNCompare(value+i,"itxt",4) == 0)
11473 mng_info->ping_exclude_iTXt=MagickFalse;
11474 */
glennrp2cc891a2010-12-24 13:44:32 +000011475
glennrp03812ae2010-12-24 01:31:34 +000011476 if (LocaleNCompare(value+i,"gama",4) == 0)
11477 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011478
glennrp03812ae2010-12-24 01:31:34 +000011479 if (LocaleNCompare(value+i,"offs",4) == 0)
11480 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011481
glennrp03812ae2010-12-24 01:31:34 +000011482 if (LocaleNCompare(value+i,"phys",4) == 0)
11483 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011484
glennrpa1e3b7b2010-12-24 16:37:33 +000011485 if (LocaleNCompare(value+i,"srgb",4) == 0)
11486 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011487
glennrp03812ae2010-12-24 01:31:34 +000011488 if (LocaleNCompare(value+i,"text",4) == 0)
11489 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011490
glennrpa1e3b7b2010-12-24 16:37:33 +000011491 if (LocaleNCompare(value+i,"trns",4) == 0)
11492 mng_info->ping_exclude_tRNS=MagickFalse;
11493
glennrp03812ae2010-12-24 01:31:34 +000011494 if (LocaleNCompare(value+i,"vpag",4) == 0)
11495 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011496
glennrp03812ae2010-12-24 01:31:34 +000011497 if (LocaleNCompare(value+i,"zccp",4) == 0)
11498 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011499
glennrp03812ae2010-12-24 01:31:34 +000011500 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11501 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011502
glennrp03812ae2010-12-24 01:31:34 +000011503 }
glennrpce91ed52010-12-23 22:37:49 +000011504 }
glennrp26f37912010-12-23 16:22:42 +000011505 }
11506
glennrp03812ae2010-12-24 01:31:34 +000011507 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011508 {
11509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11510 " Chunks to be excluded from the output PNG:");
11511 if (mng_info->ping_exclude_bKGD != MagickFalse)
11512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11513 " bKGD");
11514 if (mng_info->ping_exclude_cHRM != MagickFalse)
11515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11516 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011517 if (mng_info->ping_exclude_date != MagickFalse)
11518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11519 " date");
glennrp26f37912010-12-23 16:22:42 +000011520 if (mng_info->ping_exclude_EXIF != MagickFalse)
11521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11522 " EXIF");
11523 if (mng_info->ping_exclude_gAMA != MagickFalse)
11524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11525 " gAMA");
11526 if (mng_info->ping_exclude_iCCP != MagickFalse)
11527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11528 " iCCP");
11529/*
11530 if (mng_info->ping_exclude_iTXt != MagickFalse)
11531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11532 " iTXt");
11533*/
11534 if (mng_info->ping_exclude_oFFs != MagickFalse)
11535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11536 " oFFs");
11537 if (mng_info->ping_exclude_pHYs != MagickFalse)
11538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11539 " pHYs");
11540 if (mng_info->ping_exclude_sRGB != MagickFalse)
11541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11542 " sRGB");
11543 if (mng_info->ping_exclude_tEXt != MagickFalse)
11544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11545 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011546 if (mng_info->ping_exclude_tRNS != MagickFalse)
11547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11548 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011549 if (mng_info->ping_exclude_vpAg != MagickFalse)
11550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11551 " vpAg");
11552 if (mng_info->ping_exclude_zCCP != MagickFalse)
11553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11554 " zCCP");
11555 if (mng_info->ping_exclude_zTXt != MagickFalse)
11556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11557 " zTXt");
11558 }
11559
glennrpb9cfe272010-12-21 15:08:06 +000011560 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011561
glennrpb9cfe272010-12-21 15:08:06 +000011562 status=WriteOnePNGImage(mng_info,image_info,image);
cristy3ed852e2009-09-05 21:47:34 +000011563
11564 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011565
cristy3ed852e2009-09-05 21:47:34 +000011566 if (logging != MagickFalse)
11567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011568
cristy3ed852e2009-09-05 21:47:34 +000011569 return(status);
11570}
11571
11572#if defined(JNG_SUPPORTED)
11573
11574/* Write one JNG image */
11575static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11576 const ImageInfo *image_info,Image *image)
11577{
11578 Image
11579 *jpeg_image;
11580
11581 ImageInfo
11582 *jpeg_image_info;
11583
11584 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011585 logging,
cristy3ed852e2009-09-05 21:47:34 +000011586 status;
11587
11588 size_t
11589 length;
11590
11591 unsigned char
11592 *blob,
11593 chunk[80],
11594 *p;
11595
11596 unsigned int
11597 jng_alpha_compression_method,
11598 jng_alpha_sample_depth,
11599 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011600 transparent;
11601
cristybb503372010-05-27 20:51:26 +000011602 size_t
cristy3ed852e2009-09-05 21:47:34 +000011603 jng_quality;
11604
11605 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011606 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011607
11608 blob=(unsigned char *) NULL;
11609 jpeg_image=(Image *) NULL;
11610 jpeg_image_info=(ImageInfo *) NULL;
11611
11612 status=MagickTrue;
11613 transparent=image_info->type==GrayscaleMatteType ||
11614 image_info->type==TrueColorMatteType;
11615 jng_color_type=10;
11616 jng_alpha_sample_depth=0;
11617 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11618 jng_alpha_compression_method=0;
11619
11620 if (image->matte != MagickFalse)
11621 {
11622 /* if any pixels are transparent */
11623 transparent=MagickTrue;
11624 if (image_info->compression==JPEGCompression)
11625 jng_alpha_compression_method=8;
11626 }
11627
11628 if (transparent)
11629 {
cristybd5a96c2011-08-21 00:04:26 +000011630 ChannelType
11631 channel_mask;
11632
cristy3ed852e2009-09-05 21:47:34 +000011633 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011634
cristy3ed852e2009-09-05 21:47:34 +000011635 /* Create JPEG blob, image, and image_info */
11636 if (logging != MagickFalse)
11637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +000011638 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011639
cristy3ed852e2009-09-05 21:47:34 +000011640 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011641
cristy3ed852e2009-09-05 21:47:34 +000011642 if (jpeg_image_info == (ImageInfo *) NULL)
11643 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011644
cristy3ed852e2009-09-05 21:47:34 +000011645 if (logging != MagickFalse)
11646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11647 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011648
cristy3ed852e2009-09-05 21:47:34 +000011649 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011650
cristy3ed852e2009-09-05 21:47:34 +000011651 if (jpeg_image == (Image *) NULL)
11652 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011653
cristy3ed852e2009-09-05 21:47:34 +000011654 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristybd5a96c2011-08-21 00:04:26 +000011655 channel_mask=SetPixelChannelMask(jpeg_image,AlphaChannel);
cristy3139dc22011-07-08 00:11:42 +000011656 status=SeparateImage(jpeg_image);
cristybd5a96c2011-08-21 00:04:26 +000011657 (void) SetPixelChannelMap(jpeg_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +000011658 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011659
cristy3ed852e2009-09-05 21:47:34 +000011660 if (jng_quality >= 1000)
11661 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000011662
cristy3ed852e2009-09-05 21:47:34 +000011663 else
11664 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000011665
cristy3ed852e2009-09-05 21:47:34 +000011666 jpeg_image_info->type=GrayscaleType;
11667 (void) SetImageType(jpeg_image,GrayscaleType);
11668 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011669 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011670 "%s",jpeg_image->filename);
11671 }
11672
11673 /* To do: check bit depth of PNG alpha channel */
11674
11675 /* Check if image is grayscale. */
11676 if (image_info->type != TrueColorMatteType && image_info->type !=
11677 TrueColorType && ImageIsGray(image))
11678 jng_color_type-=2;
11679
11680 if (transparent)
11681 {
11682 if (jng_alpha_compression_method==0)
11683 {
11684 const char
11685 *value;
11686
cristy4c08aed2011-07-01 19:47:50 +000011687 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011688 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11689 &image->exception);
11690 if (logging != MagickFalse)
11691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11692 " Creating PNG blob.");
11693 length=0;
11694
11695 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11696 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11697 jpeg_image_info->interlace=NoInterlace;
11698
11699 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11700 &image->exception);
11701
11702 /* Retrieve sample depth used */
11703 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
11704 if (value != (char *) NULL)
11705 jng_alpha_sample_depth= (unsigned int) value[0];
11706 }
11707 else
11708 {
cristy4c08aed2011-07-01 19:47:50 +000011709 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011710
11711 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11712 &image->exception);
11713
11714 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11715 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11716 jpeg_image_info->interlace=NoInterlace;
11717 if (logging != MagickFalse)
11718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11719 " Creating blob.");
11720 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000011721 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000011722 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011723
cristy3ed852e2009-09-05 21:47:34 +000011724 if (logging != MagickFalse)
11725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011726 " Successfully read jpeg_image into a blob, length=%.20g.",
11727 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011728
11729 }
11730 /* Destroy JPEG image and image_info */
11731 jpeg_image=DestroyImage(jpeg_image);
11732 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11733 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11734 }
11735
11736 /* Write JHDR chunk */
11737 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11738 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011739 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011740 PNGLong(chunk+4,(png_uint_32) image->columns);
11741 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011742 chunk[12]=jng_color_type;
11743 chunk[13]=8; /* sample depth */
11744 chunk[14]=8; /*jng_image_compression_method */
11745 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11746 chunk[16]=jng_alpha_sample_depth;
11747 chunk[17]=jng_alpha_compression_method;
11748 chunk[18]=0; /*jng_alpha_filter_method */
11749 chunk[19]=0; /*jng_alpha_interlace_method */
11750 (void) WriteBlob(image,20,chunk);
11751 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11752 if (logging != MagickFalse)
11753 {
11754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011755 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011756
cristy3ed852e2009-09-05 21:47:34 +000011757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011758 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011759
cristy3ed852e2009-09-05 21:47:34 +000011760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11761 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011762
cristy3ed852e2009-09-05 21:47:34 +000011763 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11764 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011765
cristy3ed852e2009-09-05 21:47:34 +000011766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11767 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011768
cristy3ed852e2009-09-05 21:47:34 +000011769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11770 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011771
cristy3ed852e2009-09-05 21:47:34 +000011772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11773 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011774
cristy3ed852e2009-09-05 21:47:34 +000011775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11776 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011777
cristy3ed852e2009-09-05 21:47:34 +000011778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11779 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011780
cristy3ed852e2009-09-05 21:47:34 +000011781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11782 " JNG alpha interlace:%5d",0);
11783 }
11784
glennrp0fe50b42010-11-16 03:52:51 +000011785 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011786 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011787
11788 /*
11789 Write leading ancillary chunks
11790 */
11791
11792 if (transparent)
11793 {
11794 /*
11795 Write JNG bKGD chunk
11796 */
11797
11798 unsigned char
11799 blue,
11800 green,
11801 red;
11802
cristybb503372010-05-27 20:51:26 +000011803 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011804 num_bytes;
11805
11806 if (jng_color_type == 8 || jng_color_type == 12)
11807 num_bytes=6L;
11808 else
11809 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011810 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011811 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011812 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011813 red=ScaleQuantumToChar(image->background_color.red);
11814 green=ScaleQuantumToChar(image->background_color.green);
11815 blue=ScaleQuantumToChar(image->background_color.blue);
11816 *(chunk+4)=0;
11817 *(chunk+5)=red;
11818 *(chunk+6)=0;
11819 *(chunk+7)=green;
11820 *(chunk+8)=0;
11821 *(chunk+9)=blue;
11822 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11823 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11824 }
11825
11826 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11827 {
11828 /*
11829 Write JNG sRGB chunk
11830 */
11831 (void) WriteBlobMSBULong(image,1L);
11832 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011833 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011834
cristy3ed852e2009-09-05 21:47:34 +000011835 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011836 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011837 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011838 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011839
cristy3ed852e2009-09-05 21:47:34 +000011840 else
glennrpe610a072010-08-05 17:08:46 +000011841 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011842 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011843 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011844
cristy3ed852e2009-09-05 21:47:34 +000011845 (void) WriteBlob(image,5,chunk);
11846 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11847 }
11848 else
11849 {
11850 if (image->gamma != 0.0)
11851 {
11852 /*
11853 Write JNG gAMA chunk
11854 */
11855 (void) WriteBlobMSBULong(image,4L);
11856 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011857 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011858 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011859 (void) WriteBlob(image,8,chunk);
11860 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11861 }
glennrp0fe50b42010-11-16 03:52:51 +000011862
cristy3ed852e2009-09-05 21:47:34 +000011863 if ((mng_info->equal_chrms == MagickFalse) &&
11864 (image->chromaticity.red_primary.x != 0.0))
11865 {
11866 PrimaryInfo
11867 primary;
11868
11869 /*
11870 Write JNG cHRM chunk
11871 */
11872 (void) WriteBlobMSBULong(image,32L);
11873 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011874 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011875 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011876 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11877 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011878 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011879 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11880 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011881 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011882 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11883 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011884 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011885 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11886 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011887 (void) WriteBlob(image,36,chunk);
11888 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11889 }
11890 }
glennrp0fe50b42010-11-16 03:52:51 +000011891
cristy3ed852e2009-09-05 21:47:34 +000011892 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11893 {
11894 /*
11895 Write JNG pHYs chunk
11896 */
11897 (void) WriteBlobMSBULong(image,9L);
11898 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011899 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011900 if (image->units == PixelsPerInchResolution)
11901 {
cristy35ef8242010-06-03 16:24:13 +000011902 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011903 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011904
cristy35ef8242010-06-03 16:24:13 +000011905 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011906 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011907
cristy3ed852e2009-09-05 21:47:34 +000011908 chunk[12]=1;
11909 }
glennrp0fe50b42010-11-16 03:52:51 +000011910
cristy3ed852e2009-09-05 21:47:34 +000011911 else
11912 {
11913 if (image->units == PixelsPerCentimeterResolution)
11914 {
cristy35ef8242010-06-03 16:24:13 +000011915 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011916 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011917
cristy35ef8242010-06-03 16:24:13 +000011918 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011919 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011920
cristy3ed852e2009-09-05 21:47:34 +000011921 chunk[12]=1;
11922 }
glennrp0fe50b42010-11-16 03:52:51 +000011923
cristy3ed852e2009-09-05 21:47:34 +000011924 else
11925 {
cristy35ef8242010-06-03 16:24:13 +000011926 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11927 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011928 chunk[12]=0;
11929 }
11930 }
11931 (void) WriteBlob(image,13,chunk);
11932 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11933 }
11934
11935 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11936 {
11937 /*
11938 Write JNG oFFs chunk
11939 */
11940 (void) WriteBlobMSBULong(image,9L);
11941 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011942 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011943 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11944 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011945 chunk[12]=0;
11946 (void) WriteBlob(image,13,chunk);
11947 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11948 }
11949 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11950 {
11951 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11952 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011953 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011954 PNGLong(chunk+4,(png_uint_32) image->page.width);
11955 PNGLong(chunk+8,(png_uint_32) image->page.height);
11956 chunk[12]=0; /* unit = pixels */
11957 (void) WriteBlob(image,13,chunk);
11958 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11959 }
11960
11961
11962 if (transparent)
11963 {
11964 if (jng_alpha_compression_method==0)
11965 {
cristybb503372010-05-27 20:51:26 +000011966 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011967 i;
11968
cristybb503372010-05-27 20:51:26 +000011969 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011970 len;
11971
11972 /* Write IDAT chunk header */
11973 if (logging != MagickFalse)
11974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011975 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000011976 length);
cristy3ed852e2009-09-05 21:47:34 +000011977
11978 /* Copy IDAT chunks */
11979 len=0;
11980 p=blob+8;
cristybb503372010-05-27 20:51:26 +000011981 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000011982 {
11983 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11984 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000011985
cristy3ed852e2009-09-05 21:47:34 +000011986 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11987 {
11988 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000011989 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000011990 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000011991 (void) WriteBlob(image,(size_t) len+4,p);
11992 (void) WriteBlobMSBULong(image,
11993 crc32(0,p,(uInt) len+4));
11994 }
glennrp0fe50b42010-11-16 03:52:51 +000011995
cristy3ed852e2009-09-05 21:47:34 +000011996 else
11997 {
11998 if (logging != MagickFalse)
11999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012000 " Skipping %c%c%c%c chunk, length=%.20g.",
12001 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012002 }
12003 p+=(8+len);
12004 }
12005 }
12006 else
12007 {
12008 /* Write JDAA chunk header */
12009 if (logging != MagickFalse)
12010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012011 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012012 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012013 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012014 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012015 /* Write JDAT chunk(s) data */
12016 (void) WriteBlob(image,4,chunk);
12017 (void) WriteBlob(image,length,blob);
12018 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12019 (uInt) length));
12020 }
12021 blob=(unsigned char *) RelinquishMagickMemory(blob);
12022 }
12023
12024 /* Encode image as a JPEG blob */
12025 if (logging != MagickFalse)
12026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12027 " Creating jpeg_image_info.");
12028 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12029 if (jpeg_image_info == (ImageInfo *) NULL)
12030 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12031
12032 if (logging != MagickFalse)
12033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12034 " Creating jpeg_image.");
12035
12036 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12037 if (jpeg_image == (Image *) NULL)
12038 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12039 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12040
12041 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012042 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012043 jpeg_image->filename);
12044
12045 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12046 &image->exception);
12047
12048 if (logging != MagickFalse)
12049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012050 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12051 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012052
12053 if (jng_color_type == 8 || jng_color_type == 12)
12054 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012055
cristy3ed852e2009-09-05 21:47:34 +000012056 jpeg_image_info->quality=jng_quality % 1000;
12057 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12058 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012059
cristy3ed852e2009-09-05 21:47:34 +000012060 if (logging != MagickFalse)
12061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12062 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012063
cristy3ed852e2009-09-05 21:47:34 +000012064 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000012065
cristy3ed852e2009-09-05 21:47:34 +000012066 if (logging != MagickFalse)
12067 {
12068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012069 " Successfully read jpeg_image into a blob, length=%.20g.",
12070 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012071
12072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012073 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012074 }
glennrp0fe50b42010-11-16 03:52:51 +000012075
cristy3ed852e2009-09-05 21:47:34 +000012076 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012077 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012078 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012079 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012080 (void) WriteBlob(image,4,chunk);
12081 (void) WriteBlob(image,length,blob);
12082 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12083
12084 jpeg_image=DestroyImage(jpeg_image);
12085 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12086 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12087 blob=(unsigned char *) RelinquishMagickMemory(blob);
12088
12089 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012090 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012091
12092 /* Write IEND chunk */
12093 (void) WriteBlobMSBULong(image,0L);
12094 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012095 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012096 (void) WriteBlob(image,4,chunk);
12097 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12098
12099 if (logging != MagickFalse)
12100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12101 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012102
cristy3ed852e2009-09-05 21:47:34 +000012103 return(status);
12104}
12105
12106
12107/*
12108%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12109% %
12110% %
12111% %
12112% W r i t e J N G I m a g e %
12113% %
12114% %
12115% %
12116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12117%
12118% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12119%
12120% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12121%
12122% The format of the WriteJNGImage method is:
12123%
cristy1e178e72011-08-28 19:44:34 +000012124% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12125% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012126%
12127% A description of each parameter follows:
12128%
12129% o image_info: the image info.
12130%
12131% o image: The image.
12132%
cristy1e178e72011-08-28 19:44:34 +000012133% o exception: return any errors or warnings in this structure.
12134%
cristy3ed852e2009-09-05 21:47:34 +000012135%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12136*/
cristy1e178e72011-08-28 19:44:34 +000012137static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12138 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012139{
12140 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012141 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012142 logging,
cristy3ed852e2009-09-05 21:47:34 +000012143 status;
12144
12145 MngInfo
12146 *mng_info;
12147
cristy3ed852e2009-09-05 21:47:34 +000012148 /*
12149 Open image file.
12150 */
12151 assert(image_info != (const ImageInfo *) NULL);
12152 assert(image_info->signature == MagickSignature);
12153 assert(image != (Image *) NULL);
12154 assert(image->signature == MagickSignature);
12155 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012156 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012157 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12158 if (status == MagickFalse)
12159 return(status);
12160
12161 /*
12162 Allocate a MngInfo structure.
12163 */
12164 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012165 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012166 if (mng_info == (MngInfo *) NULL)
12167 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12168 /*
12169 Initialize members of the MngInfo structure.
12170 */
12171 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12172 mng_info->image=image;
12173 have_mng_structure=MagickTrue;
12174
12175 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12176
12177 status=WriteOneJNGImage(mng_info,image_info,image);
12178 (void) CloseBlob(image);
12179
12180 (void) CatchImageException(image);
12181 MngInfoFreeStruct(mng_info,&have_mng_structure);
12182 if (logging != MagickFalse)
12183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12184 return(status);
12185}
12186#endif
12187
cristy1e178e72011-08-28 19:44:34 +000012188static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12189 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012190{
12191 const char
12192 *option;
12193
12194 Image
12195 *next_image;
12196
12197 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012198 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012199 status;
12200
glennrp03812ae2010-12-24 01:31:34 +000012201 volatile MagickBooleanType
12202 logging;
12203
cristy3ed852e2009-09-05 21:47:34 +000012204 MngInfo
12205 *mng_info;
12206
12207 int
cristy3ed852e2009-09-05 21:47:34 +000012208 image_count,
12209 need_iterations,
12210 need_matte;
12211
12212 volatile int
12213#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12214 defined(PNG_MNG_FEATURES_SUPPORTED)
12215 need_local_plte,
12216#endif
12217 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012218 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012219 use_global_plte;
12220
cristybb503372010-05-27 20:51:26 +000012221 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012222 i;
12223
12224 unsigned char
12225 chunk[800];
12226
12227 volatile unsigned int
12228 write_jng,
12229 write_mng;
12230
cristybb503372010-05-27 20:51:26 +000012231 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012232 scene;
12233
cristybb503372010-05-27 20:51:26 +000012234 size_t
cristy3ed852e2009-09-05 21:47:34 +000012235 final_delay=0,
12236 initial_delay;
12237
glennrpd5045b42010-03-24 12:40:35 +000012238#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012239 if (image_info->verbose)
12240 printf("Your PNG library (libpng-%s) is rather old.\n",
12241 PNG_LIBPNG_VER_STRING);
12242#endif
12243
12244 /*
12245 Open image file.
12246 */
12247 assert(image_info != (const ImageInfo *) NULL);
12248 assert(image_info->signature == MagickSignature);
12249 assert(image != (Image *) NULL);
12250 assert(image->signature == MagickSignature);
12251 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012252 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012253 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12254 if (status == MagickFalse)
12255 return(status);
12256
12257 /*
12258 Allocate a MngInfo structure.
12259 */
12260 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012261 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012262 if (mng_info == (MngInfo *) NULL)
12263 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12264 /*
12265 Initialize members of the MngInfo structure.
12266 */
12267 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12268 mng_info->image=image;
12269 have_mng_structure=MagickTrue;
12270 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12271
12272 /*
12273 * See if user has requested a specific PNG subformat to be used
12274 * for all of the PNGs in the MNG being written, e.g.,
12275 *
12276 * convert *.png png8:animation.mng
12277 *
12278 * To do: check -define png:bit_depth and png:color_type as well,
12279 * or perhaps use mng:bit_depth and mng:color_type instead for
12280 * global settings.
12281 */
12282
12283 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12284 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12285 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12286
12287 write_jng=MagickFalse;
12288 if (image_info->compression == JPEGCompression)
12289 write_jng=MagickTrue;
12290
12291 mng_info->adjoin=image_info->adjoin &&
12292 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12293
cristy3ed852e2009-09-05 21:47:34 +000012294 if (logging != MagickFalse)
12295 {
12296 /* Log some info about the input */
12297 Image
12298 *p;
12299
12300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12301 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012302
cristy3ed852e2009-09-05 21:47:34 +000012303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012304 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012305
cristy3ed852e2009-09-05 21:47:34 +000012306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12307 " Type: %d",image_info->type);
12308
12309 scene=0;
12310 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12311 {
12312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012313 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012314
cristy3ed852e2009-09-05 21:47:34 +000012315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012316 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012317
cristy3ed852e2009-09-05 21:47:34 +000012318 if (p->matte)
12319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12320 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012321
cristy3ed852e2009-09-05 21:47:34 +000012322 else
12323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12324 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012325
cristy3ed852e2009-09-05 21:47:34 +000012326 if (p->storage_class == PseudoClass)
12327 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12328 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012329
cristy3ed852e2009-09-05 21:47:34 +000012330 else
12331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12332 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012333
cristy3ed852e2009-09-05 21:47:34 +000012334 if (p->colors)
12335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012336 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012337
cristy3ed852e2009-09-05 21:47:34 +000012338 else
12339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12340 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012341
cristy3ed852e2009-09-05 21:47:34 +000012342 if (mng_info->adjoin == MagickFalse)
12343 break;
12344 }
12345 }
12346
cristy3ed852e2009-09-05 21:47:34 +000012347 use_global_plte=MagickFalse;
12348 all_images_are_gray=MagickFalse;
12349#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12350 need_local_plte=MagickTrue;
12351#endif
12352 need_defi=MagickFalse;
12353 need_matte=MagickFalse;
12354 mng_info->framing_mode=1;
12355 mng_info->old_framing_mode=1;
12356
12357 if (write_mng)
12358 if (image_info->page != (char *) NULL)
12359 {
12360 /*
12361 Determine image bounding box.
12362 */
12363 SetGeometry(image,&mng_info->page);
12364 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12365 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12366 }
12367 if (write_mng)
12368 {
12369 unsigned int
12370 need_geom;
12371
12372 unsigned short
12373 red,
12374 green,
12375 blue;
12376
12377 mng_info->page=image->page;
12378 need_geom=MagickTrue;
12379 if (mng_info->page.width || mng_info->page.height)
12380 need_geom=MagickFalse;
12381 /*
12382 Check all the scenes.
12383 */
12384 initial_delay=image->delay;
12385 need_iterations=MagickFalse;
12386 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12387 mng_info->equal_physs=MagickTrue,
12388 mng_info->equal_gammas=MagickTrue;
12389 mng_info->equal_srgbs=MagickTrue;
12390 mng_info->equal_backgrounds=MagickTrue;
12391 image_count=0;
12392#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12393 defined(PNG_MNG_FEATURES_SUPPORTED)
12394 all_images_are_gray=MagickTrue;
12395 mng_info->equal_palettes=MagickFalse;
12396 need_local_plte=MagickFalse;
12397#endif
12398 for (next_image=image; next_image != (Image *) NULL; )
12399 {
12400 if (need_geom)
12401 {
12402 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12403 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012404
cristy3ed852e2009-09-05 21:47:34 +000012405 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12406 mng_info->page.height=next_image->rows+next_image->page.y;
12407 }
glennrp0fe50b42010-11-16 03:52:51 +000012408
cristy3ed852e2009-09-05 21:47:34 +000012409 if (next_image->page.x || next_image->page.y)
12410 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012411
cristy3ed852e2009-09-05 21:47:34 +000012412 if (next_image->matte)
12413 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012414
cristy3ed852e2009-09-05 21:47:34 +000012415 if ((int) next_image->dispose >= BackgroundDispose)
12416 if (next_image->matte || next_image->page.x || next_image->page.y ||
12417 ((next_image->columns < mng_info->page.width) &&
12418 (next_image->rows < mng_info->page.height)))
12419 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012420
cristy3ed852e2009-09-05 21:47:34 +000012421 if (next_image->iterations)
12422 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012423
cristy3ed852e2009-09-05 21:47:34 +000012424 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012425
cristy3ed852e2009-09-05 21:47:34 +000012426 if (final_delay != initial_delay || final_delay > 1UL*
12427 next_image->ticks_per_second)
12428 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012429
cristy3ed852e2009-09-05 21:47:34 +000012430#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12431 defined(PNG_MNG_FEATURES_SUPPORTED)
12432 /*
12433 check for global palette possibility.
12434 */
12435 if (image->matte != MagickFalse)
12436 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012437
cristy3ed852e2009-09-05 21:47:34 +000012438 if (need_local_plte == 0)
12439 {
12440 if (ImageIsGray(image) == MagickFalse)
12441 all_images_are_gray=MagickFalse;
12442 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12443 if (use_global_plte == 0)
12444 use_global_plte=mng_info->equal_palettes;
12445 need_local_plte=!mng_info->equal_palettes;
12446 }
12447#endif
12448 if (GetNextImageInList(next_image) != (Image *) NULL)
12449 {
12450 if (next_image->background_color.red !=
12451 next_image->next->background_color.red ||
12452 next_image->background_color.green !=
12453 next_image->next->background_color.green ||
12454 next_image->background_color.blue !=
12455 next_image->next->background_color.blue)
12456 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012457
cristy3ed852e2009-09-05 21:47:34 +000012458 if (next_image->gamma != next_image->next->gamma)
12459 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012460
cristy3ed852e2009-09-05 21:47:34 +000012461 if (next_image->rendering_intent !=
12462 next_image->next->rendering_intent)
12463 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012464
cristy3ed852e2009-09-05 21:47:34 +000012465 if ((next_image->units != next_image->next->units) ||
12466 (next_image->x_resolution != next_image->next->x_resolution) ||
12467 (next_image->y_resolution != next_image->next->y_resolution))
12468 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012469
cristy3ed852e2009-09-05 21:47:34 +000012470 if (mng_info->equal_chrms)
12471 {
12472 if (next_image->chromaticity.red_primary.x !=
12473 next_image->next->chromaticity.red_primary.x ||
12474 next_image->chromaticity.red_primary.y !=
12475 next_image->next->chromaticity.red_primary.y ||
12476 next_image->chromaticity.green_primary.x !=
12477 next_image->next->chromaticity.green_primary.x ||
12478 next_image->chromaticity.green_primary.y !=
12479 next_image->next->chromaticity.green_primary.y ||
12480 next_image->chromaticity.blue_primary.x !=
12481 next_image->next->chromaticity.blue_primary.x ||
12482 next_image->chromaticity.blue_primary.y !=
12483 next_image->next->chromaticity.blue_primary.y ||
12484 next_image->chromaticity.white_point.x !=
12485 next_image->next->chromaticity.white_point.x ||
12486 next_image->chromaticity.white_point.y !=
12487 next_image->next->chromaticity.white_point.y)
12488 mng_info->equal_chrms=MagickFalse;
12489 }
12490 }
12491 image_count++;
12492 next_image=GetNextImageInList(next_image);
12493 }
12494 if (image_count < 2)
12495 {
12496 mng_info->equal_backgrounds=MagickFalse;
12497 mng_info->equal_chrms=MagickFalse;
12498 mng_info->equal_gammas=MagickFalse;
12499 mng_info->equal_srgbs=MagickFalse;
12500 mng_info->equal_physs=MagickFalse;
12501 use_global_plte=MagickFalse;
12502#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12503 need_local_plte=MagickTrue;
12504#endif
12505 need_iterations=MagickFalse;
12506 }
glennrp0fe50b42010-11-16 03:52:51 +000012507
cristy3ed852e2009-09-05 21:47:34 +000012508 if (mng_info->need_fram == MagickFalse)
12509 {
12510 /*
12511 Only certain framing rates 100/n are exactly representable without
12512 the FRAM chunk but we'll allow some slop in VLC files
12513 */
12514 if (final_delay == 0)
12515 {
12516 if (need_iterations != MagickFalse)
12517 {
12518 /*
12519 It's probably a GIF with loop; don't run it *too* fast.
12520 */
glennrp02617122010-07-28 13:07:35 +000012521 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012522 {
12523 final_delay=10;
12524 (void) ThrowMagickException(&image->exception,
12525 GetMagickModule(),CoderWarning,
12526 "input has zero delay between all frames; assuming",
12527 " 10 cs `%s'","");
12528 }
cristy3ed852e2009-09-05 21:47:34 +000012529 }
12530 else
12531 mng_info->ticks_per_second=0;
12532 }
12533 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012534 mng_info->ticks_per_second=(png_uint_32)
12535 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012536 if (final_delay > 50)
12537 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012538
cristy3ed852e2009-09-05 21:47:34 +000012539 if (final_delay > 75)
12540 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012541
cristy3ed852e2009-09-05 21:47:34 +000012542 if (final_delay > 125)
12543 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012544
cristy3ed852e2009-09-05 21:47:34 +000012545 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12546 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12547 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12548 1UL*image->ticks_per_second))
12549 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12550 }
glennrp0fe50b42010-11-16 03:52:51 +000012551
cristy3ed852e2009-09-05 21:47:34 +000012552 if (mng_info->need_fram != MagickFalse)
12553 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12554 /*
12555 If pseudocolor, we should also check to see if all the
12556 palettes are identical and write a global PLTE if they are.
12557 ../glennrp Feb 99.
12558 */
12559 /*
12560 Write the MNG version 1.0 signature and MHDR chunk.
12561 */
12562 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12563 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12564 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012565 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012566 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12567 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012568 PNGLong(chunk+12,mng_info->ticks_per_second);
12569 PNGLong(chunk+16,0L); /* layer count=unknown */
12570 PNGLong(chunk+20,0L); /* frame count=unknown */
12571 PNGLong(chunk+24,0L); /* play time=unknown */
12572 if (write_jng)
12573 {
12574 if (need_matte)
12575 {
12576 if (need_defi || mng_info->need_fram || use_global_plte)
12577 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012578
cristy3ed852e2009-09-05 21:47:34 +000012579 else
12580 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12581 }
glennrp0fe50b42010-11-16 03:52:51 +000012582
cristy3ed852e2009-09-05 21:47:34 +000012583 else
12584 {
12585 if (need_defi || mng_info->need_fram || use_global_plte)
12586 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012587
cristy3ed852e2009-09-05 21:47:34 +000012588 else
12589 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12590 }
12591 }
glennrp0fe50b42010-11-16 03:52:51 +000012592
cristy3ed852e2009-09-05 21:47:34 +000012593 else
12594 {
12595 if (need_matte)
12596 {
12597 if (need_defi || mng_info->need_fram || use_global_plte)
12598 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012599
cristy3ed852e2009-09-05 21:47:34 +000012600 else
12601 PNGLong(chunk+28,9L); /* simplicity=VLC */
12602 }
glennrp0fe50b42010-11-16 03:52:51 +000012603
cristy3ed852e2009-09-05 21:47:34 +000012604 else
12605 {
12606 if (need_defi || mng_info->need_fram || use_global_plte)
12607 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012608
cristy3ed852e2009-09-05 21:47:34 +000012609 else
12610 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12611 }
12612 }
12613 (void) WriteBlob(image,32,chunk);
12614 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12615 option=GetImageOption(image_info,"mng:need-cacheoff");
12616 if (option != (const char *) NULL)
12617 {
12618 size_t
12619 length;
12620
12621 /*
12622 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12623 */
12624 PNGType(chunk,mng_nEED);
12625 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012626 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012627 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012628 length+=4;
12629 (void) WriteBlob(image,length,chunk);
12630 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12631 }
12632 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12633 (GetNextImageInList(image) != (Image *) NULL) &&
12634 (image->iterations != 1))
12635 {
12636 /*
12637 Write MNG TERM chunk
12638 */
12639 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12640 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012641 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012642 chunk[4]=3; /* repeat animation */
12643 chunk[5]=0; /* show last frame when done */
12644 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12645 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012646
cristy3ed852e2009-09-05 21:47:34 +000012647 if (image->iterations == 0)
12648 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012649
cristy3ed852e2009-09-05 21:47:34 +000012650 else
12651 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012652
cristy3ed852e2009-09-05 21:47:34 +000012653 if (logging != MagickFalse)
12654 {
12655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012656 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12657 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012658
cristy3ed852e2009-09-05 21:47:34 +000012659 if (image->iterations == 0)
12660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012661 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012662
cristy3ed852e2009-09-05 21:47:34 +000012663 else
12664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012665 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012666 }
12667 (void) WriteBlob(image,14,chunk);
12668 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12669 }
12670 /*
12671 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12672 */
12673 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12674 mng_info->equal_srgbs)
12675 {
12676 /*
12677 Write MNG sRGB chunk
12678 */
12679 (void) WriteBlobMSBULong(image,1L);
12680 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012681 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012682
cristy3ed852e2009-09-05 21:47:34 +000012683 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012684 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012685 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012686 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012687
cristy3ed852e2009-09-05 21:47:34 +000012688 else
glennrpe610a072010-08-05 17:08:46 +000012689 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012690 Magick_RenderingIntent_to_PNG_RenderingIntent(
12691 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012692
cristy3ed852e2009-09-05 21:47:34 +000012693 (void) WriteBlob(image,5,chunk);
12694 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12695 mng_info->have_write_global_srgb=MagickTrue;
12696 }
glennrp0fe50b42010-11-16 03:52:51 +000012697
cristy3ed852e2009-09-05 21:47:34 +000012698 else
12699 {
12700 if (image->gamma && mng_info->equal_gammas)
12701 {
12702 /*
12703 Write MNG gAMA chunk
12704 */
12705 (void) WriteBlobMSBULong(image,4L);
12706 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012707 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012708 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012709 (void) WriteBlob(image,8,chunk);
12710 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12711 mng_info->have_write_global_gama=MagickTrue;
12712 }
12713 if (mng_info->equal_chrms)
12714 {
12715 PrimaryInfo
12716 primary;
12717
12718 /*
12719 Write MNG cHRM chunk
12720 */
12721 (void) WriteBlobMSBULong(image,32L);
12722 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012723 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012724 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012725 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12726 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012727 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012728 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12729 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012730 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012731 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12732 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012733 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012734 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12735 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012736 (void) WriteBlob(image,36,chunk);
12737 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12738 mng_info->have_write_global_chrm=MagickTrue;
12739 }
12740 }
12741 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
12742 {
12743 /*
12744 Write MNG pHYs chunk
12745 */
12746 (void) WriteBlobMSBULong(image,9L);
12747 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012748 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012749
cristy3ed852e2009-09-05 21:47:34 +000012750 if (image->units == PixelsPerInchResolution)
12751 {
cristy35ef8242010-06-03 16:24:13 +000012752 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012753 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012754
cristy35ef8242010-06-03 16:24:13 +000012755 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012756 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012757
cristy3ed852e2009-09-05 21:47:34 +000012758 chunk[12]=1;
12759 }
glennrp0fe50b42010-11-16 03:52:51 +000012760
cristy3ed852e2009-09-05 21:47:34 +000012761 else
12762 {
12763 if (image->units == PixelsPerCentimeterResolution)
12764 {
cristy35ef8242010-06-03 16:24:13 +000012765 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012766 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012767
cristy35ef8242010-06-03 16:24:13 +000012768 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012769 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012770
cristy3ed852e2009-09-05 21:47:34 +000012771 chunk[12]=1;
12772 }
glennrp0fe50b42010-11-16 03:52:51 +000012773
cristy3ed852e2009-09-05 21:47:34 +000012774 else
12775 {
cristy35ef8242010-06-03 16:24:13 +000012776 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
12777 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012778 chunk[12]=0;
12779 }
12780 }
12781 (void) WriteBlob(image,13,chunk);
12782 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12783 }
12784 /*
12785 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12786 or does not cover the entire frame.
12787 */
12788 if (write_mng && (image->matte || image->page.x > 0 ||
12789 image->page.y > 0 || (image->page.width &&
12790 (image->page.width+image->page.x < mng_info->page.width))
12791 || (image->page.height && (image->page.height+image->page.y
12792 < mng_info->page.height))))
12793 {
12794 (void) WriteBlobMSBULong(image,6L);
12795 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012796 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012797 red=ScaleQuantumToShort(image->background_color.red);
12798 green=ScaleQuantumToShort(image->background_color.green);
12799 blue=ScaleQuantumToShort(image->background_color.blue);
12800 PNGShort(chunk+4,red);
12801 PNGShort(chunk+6,green);
12802 PNGShort(chunk+8,blue);
12803 (void) WriteBlob(image,10,chunk);
12804 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12805 if (mng_info->equal_backgrounds)
12806 {
12807 (void) WriteBlobMSBULong(image,6L);
12808 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012809 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012810 (void) WriteBlob(image,10,chunk);
12811 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12812 }
12813 }
12814
12815#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12816 if ((need_local_plte == MagickFalse) &&
12817 (image->storage_class == PseudoClass) &&
12818 (all_images_are_gray == MagickFalse))
12819 {
cristybb503372010-05-27 20:51:26 +000012820 size_t
cristy3ed852e2009-09-05 21:47:34 +000012821 data_length;
12822
12823 /*
12824 Write MNG PLTE chunk
12825 */
12826 data_length=3*image->colors;
12827 (void) WriteBlobMSBULong(image,data_length);
12828 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012829 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012830
cristybb503372010-05-27 20:51:26 +000012831 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012832 {
12833 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
12834 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
12835 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
12836 }
glennrp0fe50b42010-11-16 03:52:51 +000012837
cristy3ed852e2009-09-05 21:47:34 +000012838 (void) WriteBlob(image,data_length+4,chunk);
12839 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12840 mng_info->have_write_global_plte=MagickTrue;
12841 }
12842#endif
12843 }
12844 scene=0;
12845 mng_info->delay=0;
12846#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12847 defined(PNG_MNG_FEATURES_SUPPORTED)
12848 mng_info->equal_palettes=MagickFalse;
12849#endif
12850 do
12851 {
12852 if (mng_info->adjoin)
12853 {
12854#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12855 defined(PNG_MNG_FEATURES_SUPPORTED)
12856 /*
12857 If we aren't using a global palette for the entire MNG, check to
12858 see if we can use one for two or more consecutive images.
12859 */
12860 if (need_local_plte && use_global_plte && !all_images_are_gray)
12861 {
12862 if (mng_info->IsPalette)
12863 {
12864 /*
12865 When equal_palettes is true, this image has the same palette
12866 as the previous PseudoClass image
12867 */
12868 mng_info->have_write_global_plte=mng_info->equal_palettes;
12869 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12870 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12871 {
12872 /*
12873 Write MNG PLTE chunk
12874 */
cristybb503372010-05-27 20:51:26 +000012875 size_t
cristy3ed852e2009-09-05 21:47:34 +000012876 data_length;
12877
12878 data_length=3*image->colors;
12879 (void) WriteBlobMSBULong(image,data_length);
12880 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012881 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012882
cristybb503372010-05-27 20:51:26 +000012883 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012884 {
12885 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12886 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12887 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12888 }
glennrp0fe50b42010-11-16 03:52:51 +000012889
cristy3ed852e2009-09-05 21:47:34 +000012890 (void) WriteBlob(image,data_length+4,chunk);
12891 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12892 (uInt) (data_length+4)));
12893 mng_info->have_write_global_plte=MagickTrue;
12894 }
12895 }
12896 else
12897 mng_info->have_write_global_plte=MagickFalse;
12898 }
12899#endif
12900 if (need_defi)
12901 {
cristybb503372010-05-27 20:51:26 +000012902 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012903 previous_x,
12904 previous_y;
12905
12906 if (scene)
12907 {
12908 previous_x=mng_info->page.x;
12909 previous_y=mng_info->page.y;
12910 }
12911 else
12912 {
12913 previous_x=0;
12914 previous_y=0;
12915 }
12916 mng_info->page=image->page;
12917 if ((mng_info->page.x != previous_x) ||
12918 (mng_info->page.y != previous_y))
12919 {
12920 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12921 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012922 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012923 chunk[4]=0; /* object 0 MSB */
12924 chunk[5]=0; /* object 0 LSB */
12925 chunk[6]=0; /* visible */
12926 chunk[7]=0; /* abstract */
12927 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12928 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12929 (void) WriteBlob(image,16,chunk);
12930 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12931 }
12932 }
12933 }
12934
12935 mng_info->write_mng=write_mng;
12936
12937 if ((int) image->dispose >= 3)
12938 mng_info->framing_mode=3;
12939
12940 if (mng_info->need_fram && mng_info->adjoin &&
12941 ((image->delay != mng_info->delay) ||
12942 (mng_info->framing_mode != mng_info->old_framing_mode)))
12943 {
12944 if (image->delay == mng_info->delay)
12945 {
12946 /*
12947 Write a MNG FRAM chunk with the new framing mode.
12948 */
12949 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12950 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012951 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012952 chunk[4]=(unsigned char) mng_info->framing_mode;
12953 (void) WriteBlob(image,5,chunk);
12954 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12955 }
12956 else
12957 {
12958 /*
12959 Write a MNG FRAM chunk with the delay.
12960 */
12961 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12962 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012963 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012964 chunk[4]=(unsigned char) mng_info->framing_mode;
12965 chunk[5]=0; /* frame name separator (no name) */
12966 chunk[6]=2; /* flag for changing default delay */
12967 chunk[7]=0; /* flag for changing frame timeout */
12968 chunk[8]=0; /* flag for changing frame clipping */
12969 chunk[9]=0; /* flag for changing frame sync_id */
12970 PNGLong(chunk+10,(png_uint_32)
12971 ((mng_info->ticks_per_second*
12972 image->delay)/MagickMax(image->ticks_per_second,1)));
12973 (void) WriteBlob(image,14,chunk);
12974 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000012975 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000012976 }
12977 mng_info->old_framing_mode=mng_info->framing_mode;
12978 }
12979
12980#if defined(JNG_SUPPORTED)
12981 if (image_info->compression == JPEGCompression)
12982 {
12983 ImageInfo
12984 *write_info;
12985
12986 if (logging != MagickFalse)
12987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12988 " Writing JNG object.");
12989 /* To do: specify the desired alpha compression method. */
12990 write_info=CloneImageInfo(image_info);
12991 write_info->compression=UndefinedCompression;
12992 status=WriteOneJNGImage(mng_info,write_info,image);
12993 write_info=DestroyImageInfo(write_info);
12994 }
12995 else
12996#endif
12997 {
12998 if (logging != MagickFalse)
12999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13000 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013001
glennrpb9cfe272010-12-21 15:08:06 +000013002 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013003 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013004
13005 /* We don't want any ancillary chunks written */
13006 mng_info->ping_exclude_bKGD=MagickTrue;
13007 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013008 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013009 mng_info->ping_exclude_EXIF=MagickTrue;
13010 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013011 mng_info->ping_exclude_iCCP=MagickTrue;
13012 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13013 mng_info->ping_exclude_oFFs=MagickTrue;
13014 mng_info->ping_exclude_pHYs=MagickTrue;
13015 mng_info->ping_exclude_sRGB=MagickTrue;
13016 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013017 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013018 mng_info->ping_exclude_vpAg=MagickTrue;
13019 mng_info->ping_exclude_zCCP=MagickTrue;
13020 mng_info->ping_exclude_zTXt=MagickTrue;
13021
cristy3ed852e2009-09-05 21:47:34 +000013022 status=WriteOnePNGImage(mng_info,image_info,image);
13023 }
13024
13025 if (status == MagickFalse)
13026 {
13027 MngInfoFreeStruct(mng_info,&have_mng_structure);
13028 (void) CloseBlob(image);
13029 return(MagickFalse);
13030 }
13031 (void) CatchImageException(image);
13032 if (GetNextImageInList(image) == (Image *) NULL)
13033 break;
13034 image=SyncNextImageInList(image);
13035 status=SetImageProgress(image,SaveImagesTag,scene++,
13036 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013037
cristy3ed852e2009-09-05 21:47:34 +000013038 if (status == MagickFalse)
13039 break;
glennrp0fe50b42010-11-16 03:52:51 +000013040
cristy3ed852e2009-09-05 21:47:34 +000013041 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013042
cristy3ed852e2009-09-05 21:47:34 +000013043 if (write_mng)
13044 {
13045 while (GetPreviousImageInList(image) != (Image *) NULL)
13046 image=GetPreviousImageInList(image);
13047 /*
13048 Write the MEND chunk.
13049 */
13050 (void) WriteBlobMSBULong(image,0x00000000L);
13051 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013052 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013053 (void) WriteBlob(image,4,chunk);
13054 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13055 }
13056 /*
13057 Relinquish resources.
13058 */
13059 (void) CloseBlob(image);
13060 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013061
cristy3ed852e2009-09-05 21:47:34 +000013062 if (logging != MagickFalse)
13063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013064
cristy3ed852e2009-09-05 21:47:34 +000013065 return(MagickTrue);
13066}
glennrpd5045b42010-03-24 12:40:35 +000013067#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013068
cristy3ed852e2009-09-05 21:47:34 +000013069static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13070{
13071 image=image;
13072 printf("Your PNG library is too old: You have libpng-%s\n",
13073 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013074
cristy3ed852e2009-09-05 21:47:34 +000013075 ThrowBinaryException(CoderError,"PNG library is too old",
13076 image_info->filename);
13077}
glennrp39992b42010-11-14 00:03:43 +000013078
cristy3ed852e2009-09-05 21:47:34 +000013079static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13080{
13081 return(WritePNGImage(image_info,image));
13082}
glennrpd5045b42010-03-24 12:40:35 +000013083#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013084#endif