blob: 650acb345e6fd5cf4540935a370bc2d0a271b806 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP N N GGGG %
7% P P NN N G %
8% PPPP N N N G GG %
9% P N NN G G %
10% P N N GGG %
11% %
12% %
13% Read/Write Portable Network Graphics Image Format %
14% %
15% Software Design %
16% John Cristy %
17% Glenn Randers-Pehrson %
18% November 1997 %
19% %
20% %
cristy1454be72011-12-19 01:52:48 +000021% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000022% dedicated to making software imaging solutions freely available. %
23% %
24% You may not use this file except in compliance with the License. You may %
25% obtain a copy of the License at %
26% %
27% http://www.imagemagick.org/script/license.php %
28% %
29% Unless required by applicable law or agreed to in writing, software %
30% distributed under the License is distributed on an "AS IS" BASIS, %
31% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32% See the License for the specific language governing permissions and %
33% limitations under the License. %
34% %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38*/
39
40
41/*
42 Include declarations.
43*/
cristy4c08aed2011-07-01 19:47:50 +000044#include "MagickCore/studio.h"
45#include "MagickCore/artifact.h"
46#include "MagickCore/attribute.h"
47#include "MagickCore/blob.h"
48#include "MagickCore/blob-private.h"
49#include "MagickCore/cache.h"
50#include "MagickCore/color.h"
51#include "MagickCore/color-private.h"
52#include "MagickCore/colormap.h"
53#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000054#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000055#include "MagickCore/constitute.h"
56#include "MagickCore/enhance.h"
57#include "MagickCore/exception.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/geometry.h"
60#include "MagickCore/histogram.h"
61#include "MagickCore/image.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/layer.h"
64#include "MagickCore/list.h"
65#include "MagickCore/log.h"
66#include "MagickCore/MagickCore.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/module.h"
69#include "MagickCore/monitor.h"
70#include "MagickCore/monitor-private.h"
71#include "MagickCore/option.h"
72#include "MagickCore/pixel.h"
73#include "MagickCore/pixel-accessor.h"
74#include "MagickCore/profile.h"
75#include "MagickCore/property.h"
76#include "MagickCore/quantum-private.h"
77#include "MagickCore/resource_.h"
78#include "MagickCore/semaphore.h"
79#include "MagickCore/quantum-private.h"
80#include "MagickCore/static.h"
81#include "MagickCore/statistic.h"
82#include "MagickCore/string_.h"
83#include "MagickCore/string-private.h"
84#include "MagickCore/transform.h"
85#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000086#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp286a6352009-11-09 02:58:50 +000087
glennrp7ef138c2009-11-10 13:50:20 +000088/* Suppress libpng pedantic warnings that were added in
89 * libpng-1.2.41 and libpng-1.4.0. If you are working on
glennrpfaa852b2010-03-30 12:17:00 +000090 * migration to libpng-1.5, remove these defines and then
glennrp7ef138c2009-11-10 13:50:20 +000091 * fix any code that generates warnings.
92 */
glennrp991e92a2010-01-28 03:09:00 +000093/* #define PNG_DEPRECATED Use of this function is deprecated */
glennrpfaa852b2010-03-30 12:17:00 +000094/* #define PNG_USE_RESULT The result of this function must be checked */
95/* #define PNG_NORETURN This function does not return */
96/* #define PNG_ALLOCATED The result of the function is new memory */
glennrp8371ecc2011-02-19 19:15:38 +000097/* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
glennrp5b927342011-04-14 14:31:48 +000098
99/* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
cristy75cfe702011-04-14 14:27:17 +0000100#define PNG_PTR_NORETURN
glennrp286a6352009-11-09 02:58:50 +0000101
cristy3ed852e2009-09-05 21:47:34 +0000102#include "png.h"
103#include "zlib.h"
104
105/* ImageMagick differences */
106#define first_scene scene
107
glennrpd5045b42010-03-24 12:40:35 +0000108#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000109/*
110 Optional declarations. Define or undefine them as you like.
111*/
112/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
113
114/*
115 Features under construction. Define these to work on them.
116*/
117#undef MNG_OBJECT_BUFFERS
118#undef MNG_BASI_SUPPORTED
119#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
120#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
cristy3ed852e2009-09-05 21:47:34 +0000121#if defined(MAGICKCORE_JPEG_DELEGATE)
122# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
123#endif
124#if !defined(RGBColorMatchExact)
125#define IsPNGColorEqual(color,target) \
glennrp8e045c82011-04-27 16:40:27 +0000126 (((color).red == (target).red) && \
127 ((color).green == (target).green) && \
128 ((color).blue == (target).blue))
cristy3ed852e2009-09-05 21:47:34 +0000129#endif
130
glennrp8e58efd2011-05-20 12:16:29 +0000131/* Macros for left-bit-replication to ensure that pixels
glennrp1a2061f2011-10-18 12:30:45 +0000132 * and PixelInfos all have the same image->depth, and for use
glennrp8e58efd2011-05-20 12:16:29 +0000133 * in PNG8 quantization.
134 */
135
136
137/* LBR01: Replicate top bit */
138
glennrp05001c32011-08-06 13:04:16 +0000139#define LBR01PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000140 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
141 0 : QuantumRange);
142
glennrp91d99252011-06-25 14:30:13 +0000143#define LBR01PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000144 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
145 0 : QuantumRange);
146
glennrp91d99252011-06-25 14:30:13 +0000147#define LBR01PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000148 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
149 0 : QuantumRange);
150
cristy4c08aed2011-07-01 19:47:50 +0000151#define LBR01PacketAlpha(pixelpacket) \
152 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000153 0 : QuantumRange);
154
glennrp91d99252011-06-25 14:30:13 +0000155#define LBR01PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000156 { \
glennrp05001c32011-08-06 13:04:16 +0000157 LBR01PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000158 LBR01PacketGreen((pixelpacket)); \
159 LBR01PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000160 }
glennrp8e58efd2011-05-20 12:16:29 +0000161
glennrp91d99252011-06-25 14:30:13 +0000162#define LBR01PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000163 { \
glennrp91d99252011-06-25 14:30:13 +0000164 LBR01PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000165 LBR01PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000166 }
glennrp8e58efd2011-05-20 12:16:29 +0000167
cristyef618312011-06-25 12:26:44 +0000168#define LBR01PixelRed(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000169 (ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000170 0 : QuantumRange);
171
glennrp54cf7972011-08-06 14:28:09 +0000172#define LBR01PixelGreen(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000173 (ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000174 0 : QuantumRange);
175
glennrp54cf7972011-08-06 14:28:09 +0000176#define LBR01PixelBlue(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000177 (ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000178 0 : QuantumRange);
179
glennrp54cf7972011-08-06 14:28:09 +0000180#define LBR01PixelAlpha(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000181 (ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000182 0 : QuantumRange);
183
glennrp54cf7972011-08-06 14:28:09 +0000184#define LBR01PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000185 { \
cristyef618312011-06-25 12:26:44 +0000186 LBR01PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000187 LBR01PixelGreen((pixel)); \
188 LBR01PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000189 }
glennrp8e58efd2011-05-20 12:16:29 +0000190
glennrp54cf7972011-08-06 14:28:09 +0000191#define LBR01PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000192 { \
glennrp54cf7972011-08-06 14:28:09 +0000193 LBR01PixelRGB((pixel)); \
194 LBR01PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000195 }
glennrp8e58efd2011-05-20 12:16:29 +0000196
197/* LBR02: Replicate top 2 bits */
198
glennrp05001c32011-08-06 13:04:16 +0000199#define LBR02PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000200 { \
201 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
202 (pixelpacket).red=ScaleCharToQuantum( \
203 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
204 }
glennrp91d99252011-06-25 14:30:13 +0000205#define LBR02PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000206 { \
207 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
208 (pixelpacket).green=ScaleCharToQuantum( \
209 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
210 }
glennrp91d99252011-06-25 14:30:13 +0000211#define LBR02PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000212 { \
213 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
214 (pixelpacket).blue=ScaleCharToQuantum( \
215 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
216 }
cristy4c08aed2011-07-01 19:47:50 +0000217#define LBR02PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000218 { \
cristy4c08aed2011-07-01 19:47:50 +0000219 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
220 (pixelpacket).alpha=ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000221 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
222 }
223
glennrp91d99252011-06-25 14:30:13 +0000224#define LBR02PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000225 { \
glennrp05001c32011-08-06 13:04:16 +0000226 LBR02PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000227 LBR02PacketGreen((pixelpacket)); \
228 LBR02PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000229 }
glennrp8e58efd2011-05-20 12:16:29 +0000230
glennrp91d99252011-06-25 14:30:13 +0000231#define LBR02PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000232 { \
glennrp91d99252011-06-25 14:30:13 +0000233 LBR02PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000234 LBR02PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000235 }
glennrp8e58efd2011-05-20 12:16:29 +0000236
cristyef618312011-06-25 12:26:44 +0000237#define LBR02PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000238 { \
cristy4c08aed2011-07-01 19:47:50 +0000239 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000240 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000241 SetPixelRed(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000242 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
243 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000244 }
glennrp54cf7972011-08-06 14:28:09 +0000245#define LBR02PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000246 { \
cristy4c08aed2011-07-01 19:47:50 +0000247 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000248 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000249 SetPixelGreen(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000250 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
251 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000252 }
glennrp54cf7972011-08-06 14:28:09 +0000253#define LBR02PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000254 { \
255 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000256 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
257 SetPixelBlue(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000258 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
259 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000260 }
glennrp54cf7972011-08-06 14:28:09 +0000261#define LBR02PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000262 { \
263 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000264 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
265 SetPixelAlpha(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000266 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
267 (pixel) ); \
glennrp8e58efd2011-05-20 12:16:29 +0000268 }
269
glennrp54cf7972011-08-06 14:28:09 +0000270#define LBR02PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000271 { \
cristyef618312011-06-25 12:26:44 +0000272 LBR02PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000273 LBR02PixelGreen((pixel)); \
274 LBR02PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000275 }
glennrp8e58efd2011-05-20 12:16:29 +0000276
glennrp54cf7972011-08-06 14:28:09 +0000277#define LBR02PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000278 { \
glennrp54cf7972011-08-06 14:28:09 +0000279 LBR02PixelRGB((pixel)); \
280 LBR02PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000281 }
glennrp8e58efd2011-05-20 12:16:29 +0000282
283/* LBR03: Replicate top 3 bits (only used with opaque pixels during
284 PNG8 quantization) */
285
glennrp05001c32011-08-06 13:04:16 +0000286#define LBR03PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000287 { \
288 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
289 (pixelpacket).red=ScaleCharToQuantum( \
290 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
291 }
glennrp91d99252011-06-25 14:30:13 +0000292#define LBR03PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000293 { \
294 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
295 (pixelpacket).green=ScaleCharToQuantum( \
296 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
297 }
glennrp91d99252011-06-25 14:30:13 +0000298#define LBR03PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000299 { \
300 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
301 (pixelpacket).blue=ScaleCharToQuantum( \
302 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
303 }
304
glennrp91d99252011-06-25 14:30:13 +0000305#define LBR03PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000306 { \
glennrp05001c32011-08-06 13:04:16 +0000307 LBR03PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000308 LBR03PacketGreen((pixelpacket)); \
309 LBR03PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000310 }
glennrp8e58efd2011-05-20 12:16:29 +0000311
cristyef618312011-06-25 12:26:44 +0000312#define LBR03PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000313 { \
cristy4c08aed2011-07-01 19:47:50 +0000314 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000315 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000316 SetPixelRed(image, ScaleCharToQuantum( \
317 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000318 }
cristy4c08aed2011-07-01 19:47:50 +0000319#define LBR03Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000320 { \
cristy4c08aed2011-07-01 19:47:50 +0000321 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000322 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000323 SetPixelGreen(image, ScaleCharToQuantum( \
324 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000325 }
cristy4c08aed2011-07-01 19:47:50 +0000326#define LBR03Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000327 { \
cristy4c08aed2011-07-01 19:47:50 +0000328 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000329 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000330 SetPixelBlue(image, ScaleCharToQuantum( \
331 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000332 }
333
cristy4c08aed2011-07-01 19:47:50 +0000334#define LBR03RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000335 { \
cristyef618312011-06-25 12:26:44 +0000336 LBR03PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000337 LBR03Green((pixel)); \
338 LBR03Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000339 }
glennrp8e58efd2011-05-20 12:16:29 +0000340
341/* LBR04: Replicate top 4 bits */
342
glennrp05001c32011-08-06 13:04:16 +0000343#define LBR04PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000344 { \
345 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
346 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
347 }
glennrp91d99252011-06-25 14:30:13 +0000348#define LBR04PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000349 { \
350 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
351 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
352 }
glennrp91d99252011-06-25 14:30:13 +0000353#define LBR04PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000354 { \
355 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
356 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
357 }
cristy4c08aed2011-07-01 19:47:50 +0000358#define LBR04PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000359 { \
cristy4c08aed2011-07-01 19:47:50 +0000360 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
361 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
glennrp8e58efd2011-05-20 12:16:29 +0000362 }
363
glennrp91d99252011-06-25 14:30:13 +0000364#define LBR04PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000365 { \
glennrp05001c32011-08-06 13:04:16 +0000366 LBR04PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000367 LBR04PacketGreen((pixelpacket)); \
368 LBR04PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000369 }
glennrp8e58efd2011-05-20 12:16:29 +0000370
glennrp91d99252011-06-25 14:30:13 +0000371#define LBR04PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000372 { \
glennrp91d99252011-06-25 14:30:13 +0000373 LBR04PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000374 LBR04PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000375 }
glennrp8e58efd2011-05-20 12:16:29 +0000376
cristyef618312011-06-25 12:26:44 +0000377#define LBR04PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000378 { \
cristy4c08aed2011-07-01 19:47:50 +0000379 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000380 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000381 SetPixelRed(image,\
382 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000383 }
glennrp54cf7972011-08-06 14:28:09 +0000384#define LBR04PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000385 { \
cristy4c08aed2011-07-01 19:47:50 +0000386 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000387 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000388 SetPixelGreen(image,\
389 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000390 }
glennrp54cf7972011-08-06 14:28:09 +0000391#define LBR04PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000392 { \
393 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000394 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
395 SetPixelBlue(image,\
396 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000397 }
glennrp54cf7972011-08-06 14:28:09 +0000398#define LBR04PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000399 { \
400 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000401 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
402 SetPixelAlpha(image,\
403 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000404 }
405
glennrp54cf7972011-08-06 14:28:09 +0000406#define LBR04PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000407 { \
cristyef618312011-06-25 12:26:44 +0000408 LBR04PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000409 LBR04PixelGreen((pixel)); \
410 LBR04PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000411 }
glennrp8e58efd2011-05-20 12:16:29 +0000412
glennrp54cf7972011-08-06 14:28:09 +0000413#define LBR04PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000414 { \
glennrp54cf7972011-08-06 14:28:09 +0000415 LBR04PixelRGB((pixel)); \
416 LBR04PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000417 }
glennrp8e58efd2011-05-20 12:16:29 +0000418
419
420/* LBR08: Replicate top 8 bits */
421
glennrp05001c32011-08-06 13:04:16 +0000422#define LBR08PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000423 { \
424 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
425 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
426 }
glennrp91d99252011-06-25 14:30:13 +0000427#define LBR08PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000428 { \
429 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
430 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
431 }
glennrp91d99252011-06-25 14:30:13 +0000432#define LBR08PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000433 { \
434 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
435 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
436 }
cristy4c08aed2011-07-01 19:47:50 +0000437#define LBR08PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000438 { \
cristy4c08aed2011-07-01 19:47:50 +0000439 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
440 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000441 }
442
glennrp91d99252011-06-25 14:30:13 +0000443#define LBR08PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000444 { \
glennrp05001c32011-08-06 13:04:16 +0000445 LBR08PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000446 LBR08PacketGreen((pixelpacket)); \
447 LBR08PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000448 }
glennrp8e58efd2011-05-20 12:16:29 +0000449
glennrp91d99252011-06-25 14:30:13 +0000450#define LBR08PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000451 { \
glennrp91d99252011-06-25 14:30:13 +0000452 LBR08PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000453 LBR08PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000454 }
glennrp8e58efd2011-05-20 12:16:29 +0000455
cristyef618312011-06-25 12:26:44 +0000456#define LBR08PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000457 { \
458 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000459 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
460 SetPixelRed(image,\
461 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000462 }
glennrp54cf7972011-08-06 14:28:09 +0000463#define LBR08PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000464 { \
465 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000466 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
467 SetPixelGreen(image,\
468 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000469 }
glennrp54cf7972011-08-06 14:28:09 +0000470#define LBR08PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000471 { \
472 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000473 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
474 SetPixelBlue(image,\
475 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000476 }
glennrp54cf7972011-08-06 14:28:09 +0000477#define LBR08PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000478 { \
479 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000480 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
481 SetPixelAlpha(image,\
482 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000483 }
484
glennrp54cf7972011-08-06 14:28:09 +0000485#define LBR08PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000486 { \
cristyef618312011-06-25 12:26:44 +0000487 LBR08PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000488 LBR08PixelGreen((pixel)); \
489 LBR08PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000490 }
glennrp8e58efd2011-05-20 12:16:29 +0000491
glennrp54cf7972011-08-06 14:28:09 +0000492#define LBR08PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000493 { \
glennrp54cf7972011-08-06 14:28:09 +0000494 LBR08PixelRGB((pixel)); \
495 LBR08PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000496 }
glennrp8e58efd2011-05-20 12:16:29 +0000497
498
499/* LBR16: Replicate top 16 bits */
500
glennrp05001c32011-08-06 13:04:16 +0000501#define LBR16PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000502 { \
503 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
504 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
505 }
glennrp91d99252011-06-25 14:30:13 +0000506#define LBR16PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000507 { \
508 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
509 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
510 }
glennrp91d99252011-06-25 14:30:13 +0000511#define LBR16PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000512 { \
513 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
514 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
515 }
cristy4c08aed2011-07-01 19:47:50 +0000516#define LBR16PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000517 { \
cristy4c08aed2011-07-01 19:47:50 +0000518 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
519 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000520 }
521
glennrp91d99252011-06-25 14:30:13 +0000522#define LBR16PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000523 { \
glennrp05001c32011-08-06 13:04:16 +0000524 LBR16PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000525 LBR16PacketGreen((pixelpacket)); \
526 LBR16PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000527 }
glennrp8e58efd2011-05-20 12:16:29 +0000528
glennrp91d99252011-06-25 14:30:13 +0000529#define LBR16PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000530 { \
glennrp91d99252011-06-25 14:30:13 +0000531 LBR16PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000532 LBR16PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000533 }
glennrp8e58efd2011-05-20 12:16:29 +0000534
cristyef618312011-06-25 12:26:44 +0000535#define LBR16PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000536 { \
537 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000538 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
539 SetPixelRed(image,\
540 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000541 }
glennrp54cf7972011-08-06 14:28:09 +0000542#define LBR16PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000543 { \
544 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000545 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
546 SetPixelGreen(image,\
547 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000548 }
glennrp54cf7972011-08-06 14:28:09 +0000549#define LBR16PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000550 { \
551 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000552 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
553 SetPixelBlue(image,\
554 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000555 }
glennrp54cf7972011-08-06 14:28:09 +0000556#define LBR16PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000557 { \
558 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000559 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
560 SetPixelAlpha(image,\
561 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000562 }
563
glennrp54cf7972011-08-06 14:28:09 +0000564#define LBR16PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000565 { \
cristyef618312011-06-25 12:26:44 +0000566 LBR16PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000567 LBR16PixelGreen((pixel)); \
568 LBR16PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000569 }
glennrp8e58efd2011-05-20 12:16:29 +0000570
glennrp54cf7972011-08-06 14:28:09 +0000571#define LBR16PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000572 { \
glennrp54cf7972011-08-06 14:28:09 +0000573 LBR16PixelRGB((pixel)); \
574 LBR16PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000575 }
glennrp8e58efd2011-05-20 12:16:29 +0000576
cristy3ed852e2009-09-05 21:47:34 +0000577/*
578 Establish thread safety.
579 setjmp/longjmp is claimed to be safe on these platforms:
580 setjmp/longjmp is alleged to be unsafe on these platforms:
581*/
582#ifndef SETJMP_IS_THREAD_SAFE
583#define PNG_SETJMP_NOT_THREAD_SAFE
584#endif
585
586#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
587static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000588 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000589#endif
590
591/*
592 This temporary until I set up malloc'ed object attributes array.
593 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
594 waste more memory.
595*/
596#define MNG_MAX_OBJECTS 256
597
598/*
599 If this not defined, spec is interpreted strictly. If it is
600 defined, an attempt will be made to recover from some errors,
601 including
602 o global PLTE too short
603*/
604#undef MNG_LOOSE
605
606/*
607 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
608 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
609 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
610 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
611 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
612 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
613 will be enabled by default in libpng-1.2.0.
614*/
cristy3ed852e2009-09-05 21:47:34 +0000615#ifdef PNG_MNG_FEATURES_SUPPORTED
616# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
617# define PNG_READ_EMPTY_PLTE_SUPPORTED
618# endif
619# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
620# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
621# endif
622#endif
623
624/*
cristybb503372010-05-27 20:51:26 +0000625 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000626 This macro is only defined in libpng-1.0.3 and later.
627 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
628*/
629#ifndef PNG_UINT_31_MAX
630#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
631#endif
632
633/*
634 Constant strings for known chunk types. If you need to add a chunk,
635 add a string holding the name here. To make the code more
636 portable, we use ASCII numbers like this, not characters.
637*/
638
glennrp85dcf872011-12-07 02:51:47 +0000639static png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
640static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
641static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
642static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
643static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
644static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
645static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
646static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
647static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
648static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
649static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
650static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
651static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
652static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
653static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
654static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
655static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
656static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
657static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
658static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
659static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
660static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
661static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
662static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
663static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
664static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
665static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
666static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
667static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
668static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
669static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
670static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
671static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
672static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000673
674#if defined(JNG_SUPPORTED)
glennrp85dcf872011-12-07 02:51:47 +0000675static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
676static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
677static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
678static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
679static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
680static png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000681#endif
682
683/*
684Other known chunks that are not yet supported by ImageMagick:
glennrp85dcf872011-12-07 02:51:47 +0000685static png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
686static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
687static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
688static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
689static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
690static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
691static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
692static png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000693*/
694
695typedef struct _MngBox
696{
cristy8182b072010-05-30 20:10:53 +0000697 long
cristy3ed852e2009-09-05 21:47:34 +0000698 left,
699 right,
700 top,
701 bottom;
702} MngBox;
703
704typedef struct _MngPair
705{
cristy8182b072010-05-30 20:10:53 +0000706 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000707 a,
708 b;
709} MngPair;
710
711#ifdef MNG_OBJECT_BUFFERS
712typedef struct _MngBuffer
713{
714
cristybb503372010-05-27 20:51:26 +0000715 size_t
cristy3ed852e2009-09-05 21:47:34 +0000716 height,
717 width;
718
719 Image
720 *image;
721
722 png_color
723 plte[256];
724
725 int
726 reference_count;
727
728 unsigned char
729 alpha_sample_depth,
730 compression_method,
731 color_type,
732 concrete,
733 filter_method,
734 frozen,
735 image_type,
736 interlace_method,
737 pixel_sample_depth,
738 plte_length,
739 sample_depth,
740 viewable;
741} MngBuffer;
742#endif
743
744typedef struct _MngInfo
745{
746
747#ifdef MNG_OBJECT_BUFFERS
748 MngBuffer
749 *ob[MNG_MAX_OBJECTS];
750#endif
751
752 Image *
753 image;
754
755 RectangleInfo
756 page;
757
758 int
759 adjoin,
760#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
761 bytes_in_read_buffer,
762 found_empty_plte,
763#endif
764 equal_backgrounds,
765 equal_chrms,
766 equal_gammas,
767#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
768 defined(PNG_MNG_FEATURES_SUPPORTED)
769 equal_palettes,
770#endif
771 equal_physs,
772 equal_srgbs,
773 framing_mode,
774 have_global_bkgd,
775 have_global_chrm,
776 have_global_gama,
777 have_global_phys,
778 have_global_sbit,
779 have_global_srgb,
780 have_saved_bkgd_index,
781 have_write_global_chrm,
782 have_write_global_gama,
783 have_write_global_plte,
784 have_write_global_srgb,
785 need_fram,
786 object_id,
787 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000788 saved_bkgd_index;
789
790 int
791 new_number_colors;
792
cristybb503372010-05-27 20:51:26 +0000793 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000794 image_found,
795 loop_count[256],
796 loop_iteration[256],
797 scenes_found,
798 x_off[MNG_MAX_OBJECTS],
799 y_off[MNG_MAX_OBJECTS];
800
801 MngBox
802 clip,
803 frame,
804 image_box,
805 object_clip[MNG_MAX_OBJECTS];
806
807 unsigned char
808 /* These flags could be combined into one byte */
809 exists[MNG_MAX_OBJECTS],
810 frozen[MNG_MAX_OBJECTS],
811 loop_active[256],
812 invisible[MNG_MAX_OBJECTS],
813 viewable[MNG_MAX_OBJECTS];
814
815 MagickOffsetType
816 loop_jump[256];
817
818 png_colorp
819 global_plte;
820
821 png_color_8
822 global_sbit;
823
824 png_byte
825#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
826 read_buffer[8],
827#endif
828 global_trns[256];
829
830 float
831 global_gamma;
832
833 ChromaticityInfo
834 global_chrm;
835
836 RenderingIntent
837 global_srgb_intent;
838
cristy35ef8242010-06-03 16:24:13 +0000839 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000840 delay,
841 global_plte_length,
842 global_trns_length,
843 global_x_pixels_per_unit,
844 global_y_pixels_per_unit,
845 mng_width,
846 mng_height,
847 ticks_per_second;
848
glennrpb9cfe272010-12-21 15:08:06 +0000849 MagickBooleanType
850 need_blob;
851
cristy3ed852e2009-09-05 21:47:34 +0000852 unsigned int
853 IsPalette,
854 global_phys_unit_type,
855 basi_warning,
856 clon_warning,
857 dhdr_warning,
858 jhdr_warning,
859 magn_warning,
860 past_warning,
861 phyg_warning,
862 phys_warning,
863 sbit_warning,
864 show_warning,
865 mng_type,
866 write_mng,
867 write_png_colortype,
868 write_png_depth,
glennrp18682582011-06-30 18:11:47 +0000869 write_png_compression_level,
870 write_png_compression_strategy,
871 write_png_compression_filter,
cristy3ed852e2009-09-05 21:47:34 +0000872 write_png8,
873 write_png24,
874 write_png32;
875
876#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000877 size_t
cristy3ed852e2009-09-05 21:47:34 +0000878 basi_width,
879 basi_height;
880
881 unsigned int
882 basi_depth,
883 basi_color_type,
884 basi_compression_method,
885 basi_filter_type,
886 basi_interlace_method,
887 basi_red,
888 basi_green,
889 basi_blue,
890 basi_alpha,
891 basi_viewable;
892#endif
893
894 png_uint_16
895 magn_first,
896 magn_last,
897 magn_mb,
898 magn_ml,
899 magn_mr,
900 magn_mt,
901 magn_mx,
902 magn_my,
903 magn_methx,
904 magn_methy;
905
cristy101ab702011-10-13 13:06:32 +0000906 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000907 mng_global_bkgd;
908
glennrp26f37912010-12-23 16:22:42 +0000909 /* Added at version 6.6.6-7 */
910 MagickBooleanType
911 ping_exclude_bKGD,
912 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000913 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000914 ping_exclude_EXIF,
915 ping_exclude_gAMA,
916 ping_exclude_iCCP,
917 /* ping_exclude_iTXt, */
918 ping_exclude_oFFs,
919 ping_exclude_pHYs,
920 ping_exclude_sRGB,
921 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000922 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000923 ping_exclude_vpAg,
924 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000925 ping_exclude_zTXt,
926 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000927
cristy3ed852e2009-09-05 21:47:34 +0000928} MngInfo;
929#endif /* VER */
930
931/*
932 Forward declarations.
933*/
934static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000935 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000936
cristy3ed852e2009-09-05 21:47:34 +0000937static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000938 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000939
cristy3ed852e2009-09-05 21:47:34 +0000940#if defined(JNG_SUPPORTED)
941static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000942 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000943#endif
944
glennrp0c3e06b2010-11-19 13:45:02 +0000945#if PNG_LIBPNG_VER > 10011
946
glennrpfd05d622011-02-25 04:10:33 +0000947
glennrp0c3e06b2010-11-19 13:45:02 +0000948#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
949static MagickBooleanType
cristyc82a27b2011-10-21 01:07:16 +0000950LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
glennrp0c3e06b2010-11-19 13:45:02 +0000951{
glennrp67b9c1a2011-04-22 18:47:36 +0000952 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
953 *
954 * This is true if the high byte and the next highest byte of
955 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000956 * are equal to each other. We check this by seeing if the samples
957 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000958 *
959 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000960 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000961 */
962
glennrp3faa9a32011-04-23 14:00:25 +0000963#define QuantumToCharToQuantumEqQuantum(quantum) \
964 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
965
glennrp0c3e06b2010-11-19 13:45:02 +0000966 MagickBooleanType
967 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000968
glennrp03e11f62011-04-22 13:30:16 +0000969 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000970 {
971
cristy4c08aed2011-07-01 19:47:50 +0000972 const Quantum
glennrp0c3e06b2010-11-19 13:45:02 +0000973 *p;
974
975 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000976 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
977 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
978 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
979 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000980
981 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
982 {
983 int indx;
984
985 for (indx=0; indx < (ssize_t) image->colors; indx++)
986 {
glennrp3faa9a32011-04-23 14:00:25 +0000987 ok_to_reduce=(
988 QuantumToCharToQuantumEqQuantum(
989 image->colormap[indx].red) &&
990 QuantumToCharToQuantumEqQuantum(
991 image->colormap[indx].green) &&
992 QuantumToCharToQuantumEqQuantum(
993 image->colormap[indx].blue)) ?
994 MagickTrue : MagickFalse;
995
glennrp0c3e06b2010-11-19 13:45:02 +0000996 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000997 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000998 }
999 }
1000
1001 if ((ok_to_reduce != MagickFalse) &&
1002 (image->storage_class != PseudoClass))
1003 {
1004 ssize_t
1005 y;
1006
1007 register ssize_t
1008 x;
1009
1010 for (y=0; y < (ssize_t) image->rows; y++)
1011 {
cristyc82a27b2011-10-21 01:07:16 +00001012 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0c3e06b2010-11-19 13:45:02 +00001013
cristy4c08aed2011-07-01 19:47:50 +00001014 if (p == (const Quantum *) NULL)
glennrp0c3e06b2010-11-19 13:45:02 +00001015 {
1016 ok_to_reduce = MagickFalse;
1017 break;
1018 }
1019
1020 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1021 {
glennrp3faa9a32011-04-23 14:00:25 +00001022 ok_to_reduce=
cristy4c08aed2011-07-01 19:47:50 +00001023 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1024 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1025 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
glennrp3faa9a32011-04-23 14:00:25 +00001026 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001027
1028 if (ok_to_reduce == MagickFalse)
1029 break;
1030
cristyed231572011-07-14 02:18:59 +00001031 p+=GetPixelChannels(image);
glennrp0c3e06b2010-11-19 13:45:02 +00001032 }
glennrp8640fb52010-11-23 15:48:26 +00001033 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +00001034 break;
1035 }
1036 }
1037
1038 if (ok_to_reduce != MagickFalse)
1039 {
glennrp0c3e06b2010-11-19 13:45:02 +00001040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001041 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +00001042 }
glennrpa6a06632011-01-19 15:15:34 +00001043 else
1044 {
1045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001046 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +00001047 }
glennrp0c3e06b2010-11-19 13:45:02 +00001048 }
1049
1050 return ok_to_reduce;
1051}
1052#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1053
glennrpe610a072010-08-05 17:08:46 +00001054static int
glennrpcf002022011-01-30 02:38:15 +00001055Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +00001056{
glennrpe610a072010-08-05 17:08:46 +00001057 switch (intent)
1058 {
1059 case PerceptualIntent:
1060 return 0;
glennrp0fe50b42010-11-16 03:52:51 +00001061
glennrpe610a072010-08-05 17:08:46 +00001062 case RelativeIntent:
1063 return 1;
glennrp0fe50b42010-11-16 03:52:51 +00001064
glennrpe610a072010-08-05 17:08:46 +00001065 case SaturationIntent:
1066 return 2;
glennrp0fe50b42010-11-16 03:52:51 +00001067
glennrpe610a072010-08-05 17:08:46 +00001068 case AbsoluteIntent:
1069 return 3;
glennrp0fe50b42010-11-16 03:52:51 +00001070
glennrpe610a072010-08-05 17:08:46 +00001071 default:
1072 return -1;
1073 }
1074}
1075
1076static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +00001077Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +00001078{
glennrpcf002022011-01-30 02:38:15 +00001079 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001080 {
1081 case 0:
1082 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001083
glennrpe610a072010-08-05 17:08:46 +00001084 case 1:
1085 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001086
glennrpe610a072010-08-05 17:08:46 +00001087 case 2:
1088 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001089
glennrpe610a072010-08-05 17:08:46 +00001090 case 3:
1091 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001092
glennrpe610a072010-08-05 17:08:46 +00001093 default:
1094 return UndefinedIntent;
1095 }
1096}
1097
cristybb503372010-05-27 20:51:26 +00001098static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001099{
1100 if (x > y)
1101 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001102
cristy3ed852e2009-09-05 21:47:34 +00001103 return(y);
1104}
glennrp0c3e06b2010-11-19 13:45:02 +00001105
cristybb503372010-05-27 20:51:26 +00001106static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001107{
1108 if (x < y)
1109 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001110
cristy3ed852e2009-09-05 21:47:34 +00001111 return(y);
1112}
glennrp0c3e06b2010-11-19 13:45:02 +00001113
cristy3ed852e2009-09-05 21:47:34 +00001114
1115/*
1116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1117% %
1118% %
1119% %
1120% I m a g e I s G r a y %
1121% %
1122% %
1123% %
1124%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1125% %
cristy4c08aed2011-07-01 19:47:50 +00001126% Like IsImageGray except does not change DirectClass to PseudoClass %
cristy3ed852e2009-09-05 21:47:34 +00001127% %
1128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1129*/
cristyc82a27b2011-10-21 01:07:16 +00001130static MagickBooleanType ImageIsGray(Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001131{
cristy4c08aed2011-07-01 19:47:50 +00001132 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001133 *p;
1134
cristybb503372010-05-27 20:51:26 +00001135 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001136 i,
1137 x,
1138 y;
1139
1140 assert(image != (Image *) NULL);
1141 assert(image->signature == MagickSignature);
1142 if (image->debug != MagickFalse)
1143 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1144
1145 if (image->storage_class == PseudoClass)
1146 {
cristybb503372010-05-27 20:51:26 +00001147 for (i=0; i < (ssize_t) image->colors; i++)
cristy101ab702011-10-13 13:06:32 +00001148 if (IsPixelInfoGray(image->colormap+i) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001149 return(MagickFalse);
1150 return(MagickTrue);
1151 }
cristybb503372010-05-27 20:51:26 +00001152 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001153 {
cristyc82a27b2011-10-21 01:07:16 +00001154 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001155 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001156 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +00001157 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001158 {
cristy4c08aed2011-07-01 19:47:50 +00001159 if (IsPixelGray(image,p) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001160 return(MagickFalse);
cristyed231572011-07-14 02:18:59 +00001161 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001162 }
1163 }
1164 return(MagickTrue);
1165}
glennrpd5045b42010-03-24 12:40:35 +00001166#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001167#endif /* MAGICKCORE_PNG_DELEGATE */
1168
1169/*
1170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1171% %
1172% %
1173% %
1174% I s M N G %
1175% %
1176% %
1177% %
1178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1179%
1180% IsMNG() returns MagickTrue if the image format type, identified by the
1181% magick string, is MNG.
1182%
1183% The format of the IsMNG method is:
1184%
1185% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1186%
1187% A description of each parameter follows:
1188%
1189% o magick: compare image format pattern against these bytes.
1190%
1191% o length: Specifies the length of the magick string.
1192%
1193%
1194*/
1195static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1196{
1197 if (length < 8)
1198 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001199
cristy3ed852e2009-09-05 21:47:34 +00001200 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1201 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001202
cristy3ed852e2009-09-05 21:47:34 +00001203 return(MagickFalse);
1204}
1205
1206/*
1207%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1208% %
1209% %
1210% %
1211% I s J N G %
1212% %
1213% %
1214% %
1215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1216%
1217% IsJNG() returns MagickTrue if the image format type, identified by the
1218% magick string, is JNG.
1219%
1220% The format of the IsJNG method is:
1221%
1222% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1223%
1224% A description of each parameter follows:
1225%
1226% o magick: compare image format pattern against these bytes.
1227%
1228% o length: Specifies the length of the magick string.
1229%
1230%
1231*/
1232static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1233{
1234 if (length < 8)
1235 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001236
cristy3ed852e2009-09-05 21:47:34 +00001237 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1238 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001239
cristy3ed852e2009-09-05 21:47:34 +00001240 return(MagickFalse);
1241}
1242
1243/*
1244%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245% %
1246% %
1247% %
1248% I s P N G %
1249% %
1250% %
1251% %
1252%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1253%
1254% IsPNG() returns MagickTrue if the image format type, identified by the
1255% magick string, is PNG.
1256%
1257% The format of the IsPNG method is:
1258%
1259% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1260%
1261% A description of each parameter follows:
1262%
1263% o magick: compare image format pattern against these bytes.
1264%
1265% o length: Specifies the length of the magick string.
1266%
1267*/
1268static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1269{
1270 if (length < 8)
1271 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001272
cristy3ed852e2009-09-05 21:47:34 +00001273 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1274 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001275
cristy3ed852e2009-09-05 21:47:34 +00001276 return(MagickFalse);
1277}
1278
1279#if defined(MAGICKCORE_PNG_DELEGATE)
1280#if defined(__cplusplus) || defined(c_plusplus)
1281extern "C" {
1282#endif
1283
glennrpd5045b42010-03-24 12:40:35 +00001284#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001285static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001286{
1287 unsigned char
1288 buffer[4];
1289
1290 assert(image != (Image *) NULL);
1291 assert(image->signature == MagickSignature);
1292 buffer[0]=(unsigned char) (value >> 24);
1293 buffer[1]=(unsigned char) (value >> 16);
1294 buffer[2]=(unsigned char) (value >> 8);
1295 buffer[3]=(unsigned char) value;
1296 return((size_t) WriteBlob(image,4,buffer));
1297}
1298
1299static void PNGLong(png_bytep p,png_uint_32 value)
1300{
1301 *p++=(png_byte) ((value >> 24) & 0xff);
1302 *p++=(png_byte) ((value >> 16) & 0xff);
1303 *p++=(png_byte) ((value >> 8) & 0xff);
1304 *p++=(png_byte) (value & 0xff);
1305}
1306
glennrpa521b2f2010-10-29 04:11:03 +00001307#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001308static void PNGsLong(png_bytep p,png_int_32 value)
1309{
1310 *p++=(png_byte) ((value >> 24) & 0xff);
1311 *p++=(png_byte) ((value >> 16) & 0xff);
1312 *p++=(png_byte) ((value >> 8) & 0xff);
1313 *p++=(png_byte) (value & 0xff);
1314}
glennrpa521b2f2010-10-29 04:11:03 +00001315#endif
cristy3ed852e2009-09-05 21:47:34 +00001316
1317static void PNGShort(png_bytep p,png_uint_16 value)
1318{
1319 *p++=(png_byte) ((value >> 8) & 0xff);
1320 *p++=(png_byte) (value & 0xff);
1321}
1322
1323static void PNGType(png_bytep p,png_bytep type)
1324{
1325 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1326}
1327
glennrp03812ae2010-12-24 01:31:34 +00001328static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1329 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001330{
1331 if (logging != MagickFalse)
1332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001333 " Writing %c%c%c%c chunk, length: %.20g",
1334 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001335}
glennrpd5045b42010-03-24 12:40:35 +00001336#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001337
1338#if defined(__cplusplus) || defined(c_plusplus)
1339}
1340#endif
1341
glennrpd5045b42010-03-24 12:40:35 +00001342#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001343/*
1344%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1345% %
1346% %
1347% %
1348% R e a d P N G I m a g e %
1349% %
1350% %
1351% %
1352%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1353%
1354% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1355% Multiple-image Network Graphics (MNG) image file and returns it. It
1356% allocates the memory necessary for the new Image structure and returns a
1357% pointer to the new image or set of images.
1358%
1359% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1360%
1361% The format of the ReadPNGImage method is:
1362%
1363% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1364%
1365% A description of each parameter follows:
1366%
1367% o image_info: the image info.
1368%
1369% o exception: return any errors or warnings in this structure.
1370%
1371% To do, more or less in chronological order (as of version 5.5.2,
1372% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1373%
1374% Get 16-bit cheap transparency working.
1375%
1376% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1377%
1378% Preserve all unknown and not-yet-handled known chunks found in input
1379% PNG file and copy them into output PNG files according to the PNG
1380% copying rules.
1381%
1382% (At this point, PNG encoding should be in full MNG compliance)
1383%
1384% Provide options for choice of background to use when the MNG BACK
1385% chunk is not present or is not mandatory (i.e., leave transparent,
1386% user specified, MNG BACK, PNG bKGD)
1387%
1388% Implement LOOP/ENDL [done, but could do discretionary loops more
1389% efficiently by linking in the duplicate frames.].
1390%
1391% Decode and act on the MHDR simplicity profile (offer option to reject
1392% files or attempt to process them anyway when the profile isn't LC or VLC).
1393%
1394% Upgrade to full MNG without Delta-PNG.
1395%
1396% o BACK [done a while ago except for background image ID]
1397% o MOVE [done 15 May 1999]
1398% o CLIP [done 15 May 1999]
1399% o DISC [done 19 May 1999]
1400% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1401% o SEEK [partially done 19 May 1999 (discard function only)]
1402% o SHOW
1403% o PAST
1404% o BASI
1405% o MNG-level tEXt/iTXt/zTXt
1406% o pHYg
1407% o pHYs
1408% o sBIT
1409% o bKGD
1410% o iTXt (wait for libpng implementation).
1411%
1412% Use the scene signature to discover when an identical scene is
1413% being reused, and just point to the original image->exception instead
1414% of storing another set of pixels. This not specific to MNG
1415% but could be applied generally.
1416%
1417% Upgrade to full MNG with Delta-PNG.
1418%
1419% JNG tEXt/iTXt/zTXt
1420%
1421% We will not attempt to read files containing the CgBI chunk.
1422% They are really Xcode files meant for display on the iPhone.
1423% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001424% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001425% since irretrievable loss of color data has occurred due to the
1426% use of premultiplied alpha.
1427*/
1428
1429#if defined(__cplusplus) || defined(c_plusplus)
1430extern "C" {
1431#endif
1432
1433/*
1434 This the function that does the actual reading of data. It is
1435 the same as the one supplied in libpng, except that it receives the
1436 datastream from the ReadBlob() function instead of standard input.
1437*/
1438static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1439{
1440 Image
1441 *image;
1442
1443 image=(Image *) png_get_io_ptr(png_ptr);
1444 if (length)
1445 {
1446 png_size_t
1447 check;
1448
1449 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1450 if (check != length)
1451 {
1452 char
1453 msg[MaxTextExtent];
1454
cristy3b6fd2e2011-05-20 12:53:50 +00001455 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001456 "Expected %.20g bytes; found %.20g bytes",(double) length,
1457 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001458 png_warning(png_ptr,msg);
1459 png_error(png_ptr,"Read Exception");
1460 }
1461 }
1462}
1463
1464#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1465 !defined(PNG_MNG_FEATURES_SUPPORTED)
1466/* We use mng_get_data() instead of png_get_data() if we have a libpng
1467 * older than libpng-1.0.3a, which was the first to allow the empty
1468 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1469 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1470 * encountered after an empty PLTE, so we have to look ahead for bKGD
1471 * chunks and remove them from the datastream that is passed to libpng,
1472 * and store their contents for later use.
1473 */
1474static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1475{
1476 MngInfo
1477 *mng_info;
1478
1479 Image
1480 *image;
1481
1482 png_size_t
1483 check;
1484
cristybb503372010-05-27 20:51:26 +00001485 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001486 i;
1487
1488 i=0;
1489 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1490 image=(Image *) mng_info->image;
1491 while (mng_info->bytes_in_read_buffer && length)
1492 {
1493 data[i]=mng_info->read_buffer[i];
1494 mng_info->bytes_in_read_buffer--;
1495 length--;
1496 i++;
1497 }
1498 if (length)
1499 {
1500 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001501
cristy3ed852e2009-09-05 21:47:34 +00001502 if (check != length)
1503 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001504
cristy3ed852e2009-09-05 21:47:34 +00001505 if (length == 4)
1506 {
1507 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1508 (data[3] == 0))
1509 {
1510 check=(png_size_t) ReadBlob(image,(size_t) length,
1511 (char *) mng_info->read_buffer);
1512 mng_info->read_buffer[4]=0;
1513 mng_info->bytes_in_read_buffer=4;
1514 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1515 mng_info->found_empty_plte=MagickTrue;
1516 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1517 {
1518 mng_info->found_empty_plte=MagickFalse;
1519 mng_info->have_saved_bkgd_index=MagickFalse;
1520 }
1521 }
glennrp0fe50b42010-11-16 03:52:51 +00001522
cristy3ed852e2009-09-05 21:47:34 +00001523 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1524 (data[3] == 1))
1525 {
1526 check=(png_size_t) ReadBlob(image,(size_t) length,
1527 (char *) mng_info->read_buffer);
1528 mng_info->read_buffer[4]=0;
1529 mng_info->bytes_in_read_buffer=4;
1530 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1531 if (mng_info->found_empty_plte)
1532 {
1533 /*
1534 Skip the bKGD data byte and CRC.
1535 */
1536 check=(png_size_t)
1537 ReadBlob(image,5,(char *) mng_info->read_buffer);
1538 check=(png_size_t) ReadBlob(image,(size_t) length,
1539 (char *) mng_info->read_buffer);
1540 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1541 mng_info->have_saved_bkgd_index=MagickTrue;
1542 mng_info->bytes_in_read_buffer=0;
1543 }
1544 }
1545 }
1546 }
1547}
1548#endif
1549
1550static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1551{
1552 Image
1553 *image;
1554
1555 image=(Image *) png_get_io_ptr(png_ptr);
1556 if (length)
1557 {
1558 png_size_t
1559 check;
1560
cristybb503372010-05-27 20:51:26 +00001561 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001562
cristy3ed852e2009-09-05 21:47:34 +00001563 if (check != length)
1564 png_error(png_ptr,"WriteBlob Failed");
1565 }
1566}
1567
1568static void png_flush_data(png_structp png_ptr)
1569{
1570 (void) png_ptr;
1571}
1572
1573#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1574static int PalettesAreEqual(Image *a,Image *b)
1575{
cristybb503372010-05-27 20:51:26 +00001576 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001577 i;
1578
1579 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1580 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001581
cristy3ed852e2009-09-05 21:47:34 +00001582 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1583 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001584
cristy3ed852e2009-09-05 21:47:34 +00001585 if (a->colors != b->colors)
1586 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001587
cristybb503372010-05-27 20:51:26 +00001588 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001589 {
1590 if ((a->colormap[i].red != b->colormap[i].red) ||
1591 (a->colormap[i].green != b->colormap[i].green) ||
1592 (a->colormap[i].blue != b->colormap[i].blue))
1593 return((int) MagickFalse);
1594 }
glennrp0fe50b42010-11-16 03:52:51 +00001595
cristy3ed852e2009-09-05 21:47:34 +00001596 return((int) MagickTrue);
1597}
1598#endif
1599
1600static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1601{
1602 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1603 mng_info->exists[i] && !mng_info->frozen[i])
1604 {
1605#ifdef MNG_OBJECT_BUFFERS
1606 if (mng_info->ob[i] != (MngBuffer *) NULL)
1607 {
1608 if (mng_info->ob[i]->reference_count > 0)
1609 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001610
cristy3ed852e2009-09-05 21:47:34 +00001611 if (mng_info->ob[i]->reference_count == 0)
1612 {
1613 if (mng_info->ob[i]->image != (Image *) NULL)
1614 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001615
cristy3ed852e2009-09-05 21:47:34 +00001616 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1617 }
1618 }
1619 mng_info->ob[i]=(MngBuffer *) NULL;
1620#endif
1621 mng_info->exists[i]=MagickFalse;
1622 mng_info->invisible[i]=MagickFalse;
1623 mng_info->viewable[i]=MagickFalse;
1624 mng_info->frozen[i]=MagickFalse;
1625 mng_info->x_off[i]=0;
1626 mng_info->y_off[i]=0;
1627 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001628 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001629 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001630 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001631 }
1632}
1633
glennrp21f0e622011-01-07 16:20:57 +00001634static void MngInfoFreeStruct(MngInfo *mng_info,
1635 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001636{
glennrp21f0e622011-01-07 16:20:57 +00001637 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001638 {
cristybb503372010-05-27 20:51:26 +00001639 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001640 i;
1641
1642 for (i=1; i < MNG_MAX_OBJECTS; i++)
1643 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001644
cristy3ed852e2009-09-05 21:47:34 +00001645 if (mng_info->global_plte != (png_colorp) NULL)
1646 mng_info->global_plte=(png_colorp)
1647 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001648
cristy3ed852e2009-09-05 21:47:34 +00001649 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1650 *have_mng_structure=MagickFalse;
1651 }
1652}
1653
1654static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1655{
1656 MngBox
1657 box;
1658
1659 box=box1;
1660 if (box.left < box2.left)
1661 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001662
cristy3ed852e2009-09-05 21:47:34 +00001663 if (box.top < box2.top)
1664 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001665
cristy3ed852e2009-09-05 21:47:34 +00001666 if (box.right > box2.right)
1667 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001668
cristy3ed852e2009-09-05 21:47:34 +00001669 if (box.bottom > box2.bottom)
1670 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001671
cristy3ed852e2009-09-05 21:47:34 +00001672 return box;
1673}
1674
1675static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1676{
1677 MngBox
1678 box;
1679
1680 /*
1681 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1682 */
cristybb503372010-05-27 20:51:26 +00001683 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1684 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1685 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1686 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001687 if (delta_type != 0)
1688 {
1689 box.left+=previous_box.left;
1690 box.right+=previous_box.right;
1691 box.top+=previous_box.top;
1692 box.bottom+=previous_box.bottom;
1693 }
glennrp0fe50b42010-11-16 03:52:51 +00001694
cristy3ed852e2009-09-05 21:47:34 +00001695 return(box);
1696}
1697
1698static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1699 unsigned char *p)
1700{
1701 MngPair
1702 pair;
1703 /*
cristybb503372010-05-27 20:51:26 +00001704 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001705 */
cristy8182b072010-05-30 20:10:53 +00001706 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1707 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001708
cristy3ed852e2009-09-05 21:47:34 +00001709 if (delta_type != 0)
1710 {
1711 pair.a+=previous_pair.a;
1712 pair.b+=previous_pair.b;
1713 }
glennrp0fe50b42010-11-16 03:52:51 +00001714
cristy3ed852e2009-09-05 21:47:34 +00001715 return(pair);
1716}
1717
cristy8182b072010-05-30 20:10:53 +00001718static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001719{
cristy8182b072010-05-30 20:10:53 +00001720 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001721}
1722
cristyc82a27b2011-10-21 01:07:16 +00001723typedef struct _PNGErrorInfo
cristy3ed852e2009-09-05 21:47:34 +00001724{
1725 Image
1726 *image;
1727
cristyc82a27b2011-10-21 01:07:16 +00001728 ExceptionInfo
1729 *exception;
1730} PNGErrorInfo;
glennrp0fe50b42010-11-16 03:52:51 +00001731
cristyc82a27b2011-10-21 01:07:16 +00001732static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1733{
1734 ExceptionInfo
1735 *exception;
glennrp0fe50b42010-11-16 03:52:51 +00001736
cristyc82a27b2011-10-21 01:07:16 +00001737 Image
1738 *image;
1739
1740 PNGErrorInfo
1741 *error_info;
1742
1743 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1744 image=error_info->image;
1745 exception=error_info->exception;
1746
1747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1748 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1749
1750 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1751 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001752
glennrpe4017e32011-01-08 17:16:09 +00001753#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001754 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1755 * are building with libpng-1.4.x and can be ignored.
1756 */
cristy3ed852e2009-09-05 21:47:34 +00001757 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001758#else
1759 png_longjmp(ping,1);
1760#endif
cristy3ed852e2009-09-05 21:47:34 +00001761}
1762
glennrpcf002022011-01-30 02:38:15 +00001763static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001764{
cristyc82a27b2011-10-21 01:07:16 +00001765 ExceptionInfo
1766 *exception;
1767
cristy3ed852e2009-09-05 21:47:34 +00001768 Image
1769 *image;
1770
cristyc82a27b2011-10-21 01:07:16 +00001771 PNGErrorInfo
1772 *error_info;
1773
cristy3ed852e2009-09-05 21:47:34 +00001774 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1775 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001776
cristyc82a27b2011-10-21 01:07:16 +00001777 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1778 image=error_info->image;
1779 exception=error_info->exception;
1780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1781 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001782
cristyc82a27b2011-10-21 01:07:16 +00001783 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
cristy3ed852e2009-09-05 21:47:34 +00001784 message,"`%s'",image->filename);
1785}
1786
1787#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001788static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001789{
1790#if (PNG_LIBPNG_VER < 10011)
1791 png_voidp
1792 ret;
1793
cristydf0d90e2011-12-12 01:03:55 +00001794 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001795 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001796
cristy3ed852e2009-09-05 21:47:34 +00001797 if (ret == NULL)
1798 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001799
cristy3ed852e2009-09-05 21:47:34 +00001800 return(ret);
1801#else
cristydf0d90e2011-12-12 01:03:55 +00001802 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001803 return((png_voidp) AcquireMagickMemory((size_t) size));
1804#endif
1805}
1806
1807/*
1808 Free a pointer. It is removed from the list at the same time.
1809*/
glennrpcf002022011-01-30 02:38:15 +00001810static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001811{
glennrp3bd393f2011-12-21 18:54:53 +00001812 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001813 ptr=RelinquishMagickMemory(ptr);
1814 return((png_free_ptr) NULL);
1815}
1816#endif
1817
1818#if defined(__cplusplus) || defined(c_plusplus)
1819}
1820#endif
1821
1822static int
glennrpcf002022011-01-30 02:38:15 +00001823Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristyd15e6592011-10-15 00:13:06 +00001824 png_textp text,int ii,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001825{
cristybb503372010-05-27 20:51:26 +00001826 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001827 i;
1828
1829 register unsigned char
1830 *dp;
1831
1832 register png_charp
1833 sp;
1834
1835 png_uint_32
1836 length,
1837 nibbles;
1838
1839 StringInfo
1840 *profile;
1841
glennrp0c3e06b2010-11-19 13:45:02 +00001842 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001843 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1844 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1845 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1846 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1847 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1848 13,14,15};
1849
1850 sp=text[ii].text+1;
1851 /* look for newline */
1852 while (*sp != '\n')
1853 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001854
cristy3ed852e2009-09-05 21:47:34 +00001855 /* look for length */
1856 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1857 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001858
cristyf2f27272009-12-17 14:48:46 +00001859 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001860
glennrp97f90e22011-02-22 05:47:58 +00001861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1862 " length: %lu",(unsigned long) length);
1863
cristy3ed852e2009-09-05 21:47:34 +00001864 while (*sp != ' ' && *sp != '\n')
1865 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001866
cristy3ed852e2009-09-05 21:47:34 +00001867 /* allocate space */
1868 if (length == 0)
1869 {
cristyc82a27b2011-10-21 01:07:16 +00001870 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001871 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1872 return(MagickFalse);
1873 }
glennrp0fe50b42010-11-16 03:52:51 +00001874
cristy8723e4b2011-09-01 13:11:19 +00001875 profile=BlobToStringInfo((const void *) NULL,length);
glennrp0fe50b42010-11-16 03:52:51 +00001876
cristy3ed852e2009-09-05 21:47:34 +00001877 if (profile == (StringInfo *) NULL)
1878 {
cristyc82a27b2011-10-21 01:07:16 +00001879 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001880 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1881 "unable to copy profile");
1882 return(MagickFalse);
1883 }
glennrp0fe50b42010-11-16 03:52:51 +00001884
cristy3ed852e2009-09-05 21:47:34 +00001885 /* copy profile, skipping white space and column 1 "=" signs */
1886 dp=GetStringInfoDatum(profile);
1887 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001888
cristybb503372010-05-27 20:51:26 +00001889 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001890 {
1891 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1892 {
1893 if (*sp == '\0')
1894 {
cristyc82a27b2011-10-21 01:07:16 +00001895 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001896 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1897 profile=DestroyStringInfo(profile);
1898 return(MagickFalse);
1899 }
1900 sp++;
1901 }
glennrp0fe50b42010-11-16 03:52:51 +00001902
cristy3ed852e2009-09-05 21:47:34 +00001903 if (i%2 == 0)
1904 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001905
cristy3ed852e2009-09-05 21:47:34 +00001906 else
1907 (*dp++)+=unhex[(int) *sp++];
1908 }
1909 /*
1910 We have already read "Raw profile type.
1911 */
cristyd15e6592011-10-15 00:13:06 +00001912 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001913 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001914
cristy3ed852e2009-09-05 21:47:34 +00001915 if (image_info->verbose)
1916 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001917
cristy3ed852e2009-09-05 21:47:34 +00001918 return MagickTrue;
1919}
1920
1921#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1922static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1923{
1924 Image
1925 *image;
1926
1927
1928 /* The unknown chunk structure contains the chunk data:
1929 png_byte name[5];
1930 png_byte *data;
1931 png_size_t size;
1932
1933 Note that libpng has already taken care of the CRC handling.
1934 */
1935
1936
1937 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1938 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1939 return(0); /* Did not recognize */
1940
1941 /* recognized vpAg */
1942
1943 if (chunk->size != 9)
1944 return(-1); /* Error return */
1945
1946 if (chunk->data[8] != 0)
1947 return(0); /* ImageMagick requires pixel units */
1948
1949 image=(Image *) png_get_user_chunk_ptr(ping);
1950
cristybb503372010-05-27 20:51:26 +00001951 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001952 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001953
cristybb503372010-05-27 20:51:26 +00001954 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001955 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1956
1957 /* Return one of the following: */
1958 /* return(-n); chunk had an error */
1959 /* return(0); did not recognize */
1960 /* return(n); success */
1961
1962 return(1);
1963
1964}
1965#endif
1966
1967/*
1968%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1969% %
1970% %
1971% %
1972% R e a d O n e P N G I m a g e %
1973% %
1974% %
1975% %
1976%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1977%
1978% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1979% (minus the 8-byte signature) and returns it. It allocates the memory
1980% necessary for the new Image structure and returns a pointer to the new
1981% image.
1982%
1983% The format of the ReadOnePNGImage method is:
1984%
1985% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1986% ExceptionInfo *exception)
1987%
1988% A description of each parameter follows:
1989%
1990% o mng_info: Specifies a pointer to a MngInfo structure.
1991%
1992% o image_info: the image info.
1993%
1994% o exception: return any errors or warnings in this structure.
1995%
1996*/
1997static Image *ReadOnePNGImage(MngInfo *mng_info,
1998 const ImageInfo *image_info, ExceptionInfo *exception)
1999{
2000 /* Read one PNG image */
2001
glennrpcc95c3f2011-04-18 16:46:48 +00002002 /* To do: Read the tIME chunk into the date:modify property */
2003 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2004
cristy3ed852e2009-09-05 21:47:34 +00002005 Image
2006 *image;
2007
2008 int
glennrp4eb39312011-03-30 21:34:55 +00002009 intent,
glennrpcb395ac2011-03-30 19:50:23 +00002010 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00002011 num_text,
glennrp4eb39312011-03-30 21:34:55 +00002012 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00002013 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00002014 pass,
2015 ping_bit_depth,
2016 ping_color_type,
2017 ping_interlace_method,
2018 ping_compression_method,
2019 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00002020 ping_num_trans,
2021 unit_type;
2022
2023 double
2024 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002025
2026 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002027 logging,
cristy3ed852e2009-09-05 21:47:34 +00002028 status;
2029
cristyc82a27b2011-10-21 01:07:16 +00002030 PixelInfo
2031 transparent_color;
2032
2033 PNGErrorInfo
2034 error_info;
2035
glennrpfaa852b2010-03-30 12:17:00 +00002036 png_bytep
2037 ping_trans_alpha;
2038
2039 png_color_16p
2040 ping_background,
2041 ping_trans_color;
2042
cristy3ed852e2009-09-05 21:47:34 +00002043 png_info
2044 *end_info,
2045 *ping_info;
2046
2047 png_struct
2048 *ping;
2049
2050 png_textp
2051 text;
2052
glennrpfaa852b2010-03-30 12:17:00 +00002053 png_uint_32
2054 ping_height,
2055 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002056 x_resolution,
2057 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002058
cristy3ed852e2009-09-05 21:47:34 +00002059 QuantumInfo
2060 *quantum_info;
2061
2062 unsigned char
glennrpcf002022011-01-30 02:38:15 +00002063 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002064
cristybb503372010-05-27 20:51:26 +00002065 ssize_t
cristy756ae432011-11-19 02:18:25 +00002066 ping_rowbytes,
cristy3ed852e2009-09-05 21:47:34 +00002067 y;
2068
2069 register unsigned char
2070 *p;
2071
cristybb503372010-05-27 20:51:26 +00002072 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002073 i,
2074 x;
2075
cristy4c08aed2011-07-01 19:47:50 +00002076 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002077 *q;
2078
2079 size_t
glennrp39992b42010-11-14 00:03:43 +00002080 length,
cristy3ed852e2009-09-05 21:47:34 +00002081 row_offset;
2082
cristyeb3b22a2011-03-31 20:16:11 +00002083 ssize_t
2084 j;
2085
cristy3ed852e2009-09-05 21:47:34 +00002086#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2087 png_byte unused_chunks[]=
2088 {
2089 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2090 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2091 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2092 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2093 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2094 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2095 };
2096#endif
2097
2098 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002099 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002100
2101#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002102 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002103#endif
2104
glennrp25c1e2b2010-03-25 01:39:56 +00002105#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002106 if (image_info->verbose)
2107 printf("Your PNG library (libpng-%s) is rather old.\n",
2108 PNG_LIBPNG_VER_STRING);
2109#endif
2110
glennrp61b4c952009-11-10 20:40:41 +00002111#if (PNG_LIBPNG_VER >= 10400)
2112# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2113 if (image_info->verbose)
2114 {
2115 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2116 PNG_LIBPNG_VER_STRING);
2117 printf("Please update it.\n");
2118 }
2119# endif
2120#endif
2121
2122
cristyed552522009-10-16 14:04:35 +00002123 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002124 image=mng_info->image;
2125
glennrpa6a06632011-01-19 15:15:34 +00002126 if (logging != MagickFalse)
2127 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2128 " image->matte=%d",(int) image->matte);
2129
glennrp0e319732011-01-25 21:53:13 +00002130 /* Set to an out-of-range color unless tRNS chunk is present */
2131 transparent_color.red=65537;
2132 transparent_color.green=65537;
2133 transparent_color.blue=65537;
cristy4c08aed2011-07-01 19:47:50 +00002134 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002135
glennrpcb395ac2011-03-30 19:50:23 +00002136 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002137 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002138 num_raw_profiles = 0;
2139
cristy3ed852e2009-09-05 21:47:34 +00002140 /*
2141 Allocate the PNG structures
2142 */
2143#ifdef PNG_USER_MEM_SUPPORTED
cristyc82a27b2011-10-21 01:07:16 +00002144 error_info.image=image;
2145 error_info.exception=exception;
2146 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002147 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2148 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002149#else
cristyc82a27b2011-10-21 01:07:16 +00002150 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002151 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002152#endif
2153 if (ping == (png_struct *) NULL)
2154 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002155
cristy3ed852e2009-09-05 21:47:34 +00002156 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002157
cristy3ed852e2009-09-05 21:47:34 +00002158 if (ping_info == (png_info *) NULL)
2159 {
2160 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2161 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2162 }
glennrp0fe50b42010-11-16 03:52:51 +00002163
cristy3ed852e2009-09-05 21:47:34 +00002164 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002165
cristy3ed852e2009-09-05 21:47:34 +00002166 if (end_info == (png_info *) NULL)
2167 {
2168 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2169 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2170 }
glennrp0fe50b42010-11-16 03:52:51 +00002171
glennrpcf002022011-01-30 02:38:15 +00002172 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002173
glennrpfaa852b2010-03-30 12:17:00 +00002174 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002175 {
2176 /*
2177 PNG image is corrupt.
2178 */
2179 png_destroy_read_struct(&ping,&ping_info,&end_info);
2180#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002181 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002182#endif
2183 if (logging != MagickFalse)
2184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2185 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002186
cristy3ed852e2009-09-05 21:47:34 +00002187 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002188 {
cristyc82a27b2011-10-21 01:07:16 +00002189 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002190 image->columns=0;
2191 }
glennrp0fe50b42010-11-16 03:52:51 +00002192
cristy3ed852e2009-09-05 21:47:34 +00002193 return(GetFirstImageInList(image));
2194 }
2195 /*
2196 Prepare PNG for reading.
2197 */
glennrpfaa852b2010-03-30 12:17:00 +00002198
cristy3ed852e2009-09-05 21:47:34 +00002199 mng_info->image_found++;
2200 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002201
cristy3ed852e2009-09-05 21:47:34 +00002202 if (LocaleCompare(image_info->magick,"MNG") == 0)
2203 {
2204#if defined(PNG_MNG_FEATURES_SUPPORTED)
2205 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2206 png_set_read_fn(ping,image,png_get_data);
2207#else
2208#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2209 png_permit_empty_plte(ping,MagickTrue);
2210 png_set_read_fn(ping,image,png_get_data);
2211#else
2212 mng_info->image=image;
2213 mng_info->bytes_in_read_buffer=0;
2214 mng_info->found_empty_plte=MagickFalse;
2215 mng_info->have_saved_bkgd_index=MagickFalse;
2216 png_set_read_fn(ping,mng_info,mng_get_data);
2217#endif
2218#endif
2219 }
glennrp0fe50b42010-11-16 03:52:51 +00002220
cristy3ed852e2009-09-05 21:47:34 +00002221 else
2222 png_set_read_fn(ping,image,png_get_data);
2223
2224#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2225 /* Ignore unused chunks and all unknown chunks except for vpAg */
2226 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2227 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2228 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2229 (int)sizeof(unused_chunks)/5);
2230 /* Callback for other unknown chunks */
2231 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2232#endif
2233
glennrp991e92a2010-01-28 03:09:00 +00002234#if (PNG_LIBPNG_VER < 10400)
2235# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2236 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002237 /* Disable thread-unsafe features of pnggccrd */
2238 if (png_access_version_number() >= 10200)
2239 {
2240 png_uint_32 mmx_disable_mask=0;
2241 png_uint_32 asm_flags;
2242
2243 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2244 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2245 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2246 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2247 asm_flags=png_get_asm_flags(ping);
2248 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2249 }
glennrp991e92a2010-01-28 03:09:00 +00002250# endif
cristy3ed852e2009-09-05 21:47:34 +00002251#endif
2252
2253 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002254
2255 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2256 &ping_bit_depth,&ping_color_type,
2257 &ping_interlace_method,&ping_compression_method,
2258 &ping_filter_method);
2259
2260 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2261 &ping_trans_color);
2262
2263 (void) png_get_bKGD(ping, ping_info, &ping_background);
2264
2265 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002266 {
glennrpfaa852b2010-03-30 12:17:00 +00002267 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2268 {
2269 png_set_packing(ping);
2270 ping_bit_depth = 8;
2271 }
cristy3ed852e2009-09-05 21:47:34 +00002272 }
glennrpfaa852b2010-03-30 12:17:00 +00002273
2274 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002275 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002276 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00002277 if (logging != MagickFalse)
2278 {
2279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002280 " PNG width: %.20g, height: %.20g",
2281 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002282
cristy3ed852e2009-09-05 21:47:34 +00002283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2284 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002285 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002286
cristy3ed852e2009-09-05 21:47:34 +00002287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2288 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002289 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002290
cristy3ed852e2009-09-05 21:47:34 +00002291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2292 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002293 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002294 }
2295
glennrpfaa852b2010-03-30 12:17:00 +00002296#ifdef PNG_READ_iCCP_SUPPORTED
2297 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002298 {
2299 int
2300 compression;
2301
glennrpe4017e32011-01-08 17:16:09 +00002302#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002303 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002304 info;
2305#else
2306 png_bytep
2307 info;
2308#endif
2309
2310 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002311 name;
2312
2313 png_uint_32
2314 profile_length;
2315
2316 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2317 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002318
cristy3ed852e2009-09-05 21:47:34 +00002319 if (profile_length != 0)
2320 {
2321 StringInfo
2322 *profile;
2323
2324 if (logging != MagickFalse)
2325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2326 " Reading PNG iCCP chunk.");
cristye8f8f382011-09-01 13:32:37 +00002327 profile=BlobToStringInfo(info,profile_length);
2328 if (profile == (StringInfo *) NULL)
2329 {
cristyc82a27b2011-10-21 01:07:16 +00002330 (void) ThrowMagickException(exception,GetMagickModule(),
cristye8f8f382011-09-01 13:32:37 +00002331 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2332 "unable to copy profile");
cristyad5b0852011-09-08 02:01:21 +00002333 return((Image *) NULL);
cristye8f8f382011-09-01 13:32:37 +00002334 }
cristy3ed852e2009-09-05 21:47:34 +00002335 SetStringInfoDatum(profile,(const unsigned char *) info);
cristyd15e6592011-10-15 00:13:06 +00002336 (void) SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00002337 profile=DestroyStringInfo(profile);
2338 }
2339 }
2340#endif
2341#if defined(PNG_READ_sRGB_SUPPORTED)
2342 {
cristy3ed852e2009-09-05 21:47:34 +00002343 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00002344 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2345 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00002346
cristy3ed852e2009-09-05 21:47:34 +00002347 if (png_get_sRGB(ping,ping_info,&intent))
2348 {
glennrpcf002022011-01-30 02:38:15 +00002349 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2350 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002351
cristy3ed852e2009-09-05 21:47:34 +00002352 if (logging != MagickFalse)
2353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002354 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002355 }
2356 }
2357#endif
2358 {
glennrpfaa852b2010-03-30 12:17:00 +00002359 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2360 if (mng_info->have_global_gama)
2361 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002362
cristy3ed852e2009-09-05 21:47:34 +00002363 if (png_get_gAMA(ping,ping_info,&file_gamma))
2364 {
2365 image->gamma=(float) file_gamma;
2366 if (logging != MagickFalse)
2367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2368 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2369 }
2370 }
glennrpfaa852b2010-03-30 12:17:00 +00002371 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2372 {
2373 if (mng_info->have_global_chrm != MagickFalse)
2374 {
2375 (void) png_set_cHRM(ping,ping_info,
2376 mng_info->global_chrm.white_point.x,
2377 mng_info->global_chrm.white_point.y,
2378 mng_info->global_chrm.red_primary.x,
2379 mng_info->global_chrm.red_primary.y,
2380 mng_info->global_chrm.green_primary.x,
2381 mng_info->global_chrm.green_primary.y,
2382 mng_info->global_chrm.blue_primary.x,
2383 mng_info->global_chrm.blue_primary.y);
2384 }
2385 }
glennrp0fe50b42010-11-16 03:52:51 +00002386
glennrpfaa852b2010-03-30 12:17:00 +00002387 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002388 {
2389 (void) png_get_cHRM(ping,ping_info,
2390 &image->chromaticity.white_point.x,
2391 &image->chromaticity.white_point.y,
2392 &image->chromaticity.red_primary.x,
2393 &image->chromaticity.red_primary.y,
2394 &image->chromaticity.green_primary.x,
2395 &image->chromaticity.green_primary.y,
2396 &image->chromaticity.blue_primary.x,
2397 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002398
cristy3ed852e2009-09-05 21:47:34 +00002399 if (logging != MagickFalse)
2400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2401 " Reading PNG cHRM chunk.");
2402 }
glennrp0fe50b42010-11-16 03:52:51 +00002403
glennrpe610a072010-08-05 17:08:46 +00002404 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002405 {
glennrpe610a072010-08-05 17:08:46 +00002406 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002407 Magick_RenderingIntent_to_PNG_RenderingIntent
2408 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00002409 png_set_gAMA(ping,ping_info,0.45455f);
2410 png_set_cHRM(ping,ping_info,
2411 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2412 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002413 }
cristy3ed852e2009-09-05 21:47:34 +00002414#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002415 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002416 {
cristy905ef802011-02-23 00:29:18 +00002417 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2418 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002419
cristy3ed852e2009-09-05 21:47:34 +00002420 if (logging != MagickFalse)
2421 if (image->page.x || image->page.y)
2422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002423 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2424 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002425 }
2426#endif
2427#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002428 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2429 {
2430 if (mng_info->have_global_phys)
2431 {
2432 png_set_pHYs(ping,ping_info,
2433 mng_info->global_x_pixels_per_unit,
2434 mng_info->global_y_pixels_per_unit,
2435 mng_info->global_phys_unit_type);
2436 }
2437 }
2438
2439 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002440 {
cristy3ed852e2009-09-05 21:47:34 +00002441 /*
2442 Set image resolution.
2443 */
2444 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002445 &unit_type);
cristy2a11bef2011-10-28 18:33:11 +00002446 image->resolution.x=(double) x_resolution;
2447 image->resolution.y=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002448
cristy3ed852e2009-09-05 21:47:34 +00002449 if (unit_type == PNG_RESOLUTION_METER)
2450 {
2451 image->units=PixelsPerCentimeterResolution;
cristy2a11bef2011-10-28 18:33:11 +00002452 image->resolution.x=(double) x_resolution/100.0;
2453 image->resolution.y=(double) y_resolution/100.0;
cristy3ed852e2009-09-05 21:47:34 +00002454 }
glennrp0fe50b42010-11-16 03:52:51 +00002455
cristy3ed852e2009-09-05 21:47:34 +00002456 if (logging != MagickFalse)
2457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002458 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2459 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002460 }
cristy3ed852e2009-09-05 21:47:34 +00002461#endif
glennrp823b55c2011-03-14 18:46:46 +00002462
glennrpfaa852b2010-03-30 12:17:00 +00002463 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002464 {
2465 int
2466 number_colors;
2467
2468 png_colorp
2469 palette;
2470
2471 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002472
cristy3ed852e2009-09-05 21:47:34 +00002473 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002474 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002475 {
2476 if (mng_info->global_plte_length)
2477 {
2478 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2479 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002480
glennrpfaa852b2010-03-30 12:17:00 +00002481 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002482 if (mng_info->global_trns_length)
2483 {
2484 if (mng_info->global_trns_length >
2485 mng_info->global_plte_length)
cristyc82a27b2011-10-21 01:07:16 +00002486 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00002487 GetMagickModule(),CoderError,
2488 "global tRNS has more entries than global PLTE",
2489 "`%s'",image_info->filename);
2490 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2491 (int) mng_info->global_trns_length,NULL);
2492 }
glennrpbfd9e612011-04-22 14:02:20 +00002493#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002494 if (
2495#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2496 mng_info->have_saved_bkgd_index ||
2497#endif
glennrpfaa852b2010-03-30 12:17:00 +00002498 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002499 {
2500 png_color_16
2501 background;
2502
2503#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2504 if (mng_info->have_saved_bkgd_index)
2505 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002506#endif
glennrpfaa852b2010-03-30 12:17:00 +00002507 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2508 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002509
cristy3ed852e2009-09-05 21:47:34 +00002510 background.red=(png_uint_16)
2511 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002512
cristy3ed852e2009-09-05 21:47:34 +00002513 background.green=(png_uint_16)
2514 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002515
cristy3ed852e2009-09-05 21:47:34 +00002516 background.blue=(png_uint_16)
2517 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002518
glennrpc6c391a2011-04-27 02:23:56 +00002519 background.gray=(png_uint_16)
2520 mng_info->global_plte[background.index].green;
2521
cristy3ed852e2009-09-05 21:47:34 +00002522 png_set_bKGD(ping,ping_info,&background);
2523 }
2524#endif
2525 }
2526 else
cristyc82a27b2011-10-21 01:07:16 +00002527 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00002528 CoderError,"No global PLTE in file","`%s'",
2529 image_info->filename);
2530 }
2531 }
2532
glennrpbfd9e612011-04-22 14:02:20 +00002533#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002534 if (mng_info->have_global_bkgd &&
2535 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002536 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002537
glennrpfaa852b2010-03-30 12:17:00 +00002538 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002539 {
glennrpbfd9e612011-04-22 14:02:20 +00002540 unsigned int
2541 bkgd_scale;
2542
cristy3ed852e2009-09-05 21:47:34 +00002543 /*
2544 Set image background color.
2545 */
2546 if (logging != MagickFalse)
2547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2548 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002549
glennrpbfd9e612011-04-22 14:02:20 +00002550 /* Scale background components to 16-bit, then scale
2551 * to quantum depth
2552 */
2553 if (logging != MagickFalse)
2554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2555 " raw ping_background=(%d,%d,%d).",ping_background->red,
2556 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002557
glennrpbfd9e612011-04-22 14:02:20 +00002558 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002559
glennrpbfd9e612011-04-22 14:02:20 +00002560 if (ping_bit_depth == 1)
2561 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002562
glennrpbfd9e612011-04-22 14:02:20 +00002563 else if (ping_bit_depth == 2)
2564 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002565
glennrpbfd9e612011-04-22 14:02:20 +00002566 else if (ping_bit_depth == 4)
2567 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002568
glennrpbfd9e612011-04-22 14:02:20 +00002569 if (ping_bit_depth <= 8)
2570 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002571
glennrpbfd9e612011-04-22 14:02:20 +00002572 ping_background->red *= bkgd_scale;
2573 ping_background->green *= bkgd_scale;
2574 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002575
glennrpbfd9e612011-04-22 14:02:20 +00002576 if (logging != MagickFalse)
2577 {
glennrp2cbb4482010-06-02 04:37:24 +00002578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2579 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002580
glennrp2cbb4482010-06-02 04:37:24 +00002581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2582 " ping_background=(%d,%d,%d).",ping_background->red,
2583 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002584 }
glennrp2cbb4482010-06-02 04:37:24 +00002585
glennrpbfd9e612011-04-22 14:02:20 +00002586 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002587 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002588
glennrpbfd9e612011-04-22 14:02:20 +00002589 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002590 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002591
glennrpbfd9e612011-04-22 14:02:20 +00002592 image->background_color.blue=
2593 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002594
cristy4c08aed2011-07-01 19:47:50 +00002595 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002596
glennrpbfd9e612011-04-22 14:02:20 +00002597 if (logging != MagickFalse)
2598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2599 " image->background_color=(%.20g,%.20g,%.20g).",
2600 (double) image->background_color.red,
2601 (double) image->background_color.green,
2602 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002603 }
glennrpbfd9e612011-04-22 14:02:20 +00002604#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002605
glennrpfaa852b2010-03-30 12:17:00 +00002606 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002607 {
2608 /*
glennrpa6a06632011-01-19 15:15:34 +00002609 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002610 */
2611 int
2612 max_sample;
2613
cristy35ef8242010-06-03 16:24:13 +00002614 size_t
2615 one=1;
2616
cristy3ed852e2009-09-05 21:47:34 +00002617 if (logging != MagickFalse)
2618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2619 " Reading PNG tRNS chunk.");
2620
cristyf9cca6a2010-06-04 23:49:28 +00002621 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002622
glennrpfaa852b2010-03-30 12:17:00 +00002623 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2624 (int)ping_trans_color->gray > max_sample) ||
2625 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2626 ((int)ping_trans_color->red > max_sample ||
2627 (int)ping_trans_color->green > max_sample ||
2628 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002629 {
2630 if (logging != MagickFalse)
2631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2632 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002633 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002634 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002635 image->matte=MagickFalse;
2636 }
2637 else
2638 {
glennrpa6a06632011-01-19 15:15:34 +00002639 int
2640 scale_to_short;
2641
2642 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2643
2644 /* Scale transparent_color to short */
2645 transparent_color.red= scale_to_short*ping_trans_color->red;
2646 transparent_color.green= scale_to_short*ping_trans_color->green;
2647 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy4c08aed2011-07-01 19:47:50 +00002648 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002649
glennrpfaa852b2010-03-30 12:17:00 +00002650 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002651 {
glennrp0f111982010-07-07 20:18:33 +00002652 if (logging != MagickFalse)
2653 {
2654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2655 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002656
glennrp0f111982010-07-07 20:18:33 +00002657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy101ab702011-10-13 13:06:32 +00002658 " scaled graylevel is %.20g.",transparent_color.alpha);
glennrp0f111982010-07-07 20:18:33 +00002659 }
cristy4c08aed2011-07-01 19:47:50 +00002660 transparent_color.red=transparent_color.alpha;
2661 transparent_color.green=transparent_color.alpha;
2662 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002663 }
2664 }
2665 }
2666#if defined(PNG_READ_sBIT_SUPPORTED)
2667 if (mng_info->have_global_sbit)
2668 {
glennrpfaa852b2010-03-30 12:17:00 +00002669 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002670 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2671 }
2672#endif
2673 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002674
cristy3ed852e2009-09-05 21:47:34 +00002675 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002676
2677 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2678
cristy3ed852e2009-09-05 21:47:34 +00002679 /*
2680 Initialize image structure.
2681 */
2682 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002683 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002684 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002685 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002686 if (mng_info->mng_type == 0)
2687 {
glennrpfaa852b2010-03-30 12:17:00 +00002688 mng_info->mng_width=ping_width;
2689 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002690 mng_info->frame=mng_info->image_box;
2691 mng_info->clip=mng_info->image_box;
2692 }
glennrp0fe50b42010-11-16 03:52:51 +00002693
cristy3ed852e2009-09-05 21:47:34 +00002694 else
2695 {
2696 image->page.y=mng_info->y_off[mng_info->object_id];
2697 }
glennrp0fe50b42010-11-16 03:52:51 +00002698
cristy3ed852e2009-09-05 21:47:34 +00002699 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002700 image->columns=ping_width;
2701 image->rows=ping_height;
2702 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002703 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002704 {
cristybefe4d22010-06-07 01:18:58 +00002705 size_t
2706 one;
2707
cristy3ed852e2009-09-05 21:47:34 +00002708 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002709 one=1;
2710 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002711#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2712 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002713 image->colors=256;
2714#else
2715 if (image->colors > 65536L)
2716 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002717#endif
glennrpfaa852b2010-03-30 12:17:00 +00002718 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002719 {
2720 int
2721 number_colors;
2722
2723 png_colorp
2724 palette;
2725
2726 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002727 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002728
cristy3ed852e2009-09-05 21:47:34 +00002729 if (logging != MagickFalse)
2730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2731 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2732 }
2733 }
2734
2735 if (image->storage_class == PseudoClass)
2736 {
2737 /*
2738 Initialize image colormap.
2739 */
cristy018f07f2011-09-04 21:15:19 +00002740 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002741 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002742
glennrpfaa852b2010-03-30 12:17:00 +00002743 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002744 {
2745 int
2746 number_colors;
2747
2748 png_colorp
2749 palette;
2750
2751 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002752
glennrp6af6cf12011-04-22 13:05:16 +00002753 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002754 {
2755 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2756 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2757 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2758 }
glennrp6af6cf12011-04-22 13:05:16 +00002759
glennrp67b9c1a2011-04-22 18:47:36 +00002760 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002761 {
2762 image->colormap[i].red=0;
2763 image->colormap[i].green=0;
2764 image->colormap[i].blue=0;
2765 }
cristy3ed852e2009-09-05 21:47:34 +00002766 }
glennrp0fe50b42010-11-16 03:52:51 +00002767
cristy3ed852e2009-09-05 21:47:34 +00002768 else
2769 {
cristybb503372010-05-27 20:51:26 +00002770 size_t
cristy3ed852e2009-09-05 21:47:34 +00002771 scale;
2772
glennrpfaa852b2010-03-30 12:17:00 +00002773 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002774
cristy3ed852e2009-09-05 21:47:34 +00002775 if (scale < 1)
2776 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002777
cristybb503372010-05-27 20:51:26 +00002778 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002779 {
2780 image->colormap[i].red=(Quantum) (i*scale);
2781 image->colormap[i].green=(Quantum) (i*scale);
2782 image->colormap[i].blue=(Quantum) (i*scale);
2783 }
2784 }
2785 }
glennrp147bc912011-03-30 18:47:21 +00002786
glennrpcb395ac2011-03-30 19:50:23 +00002787 /* Set some properties for reporting by "identify" */
2788 {
glennrp147bc912011-03-30 18:47:21 +00002789 char
2790 msg[MaxTextExtent];
2791
2792 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2793 ping_interlace_method in value */
2794
cristy3b6fd2e2011-05-20 12:53:50 +00002795 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002796 "%d, %d",(int) ping_width, (int) ping_height);
cristy5d6fc9c2011-12-27 03:10:42 +00002797 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002798
cristy3b6fd2e2011-05-20 12:53:50 +00002799 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
cristy5d6fc9c2011-12-27 03:10:42 +00002800 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002801
cristy3b6fd2e2011-05-20 12:53:50 +00002802 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
cristy5d6fc9c2011-12-27 03:10:42 +00002803 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002804
cristy3b6fd2e2011-05-20 12:53:50 +00002805 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002806 (int) ping_interlace_method);
cristy5d6fc9c2011-12-27 03:10:42 +00002807 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
glennrpcb395ac2011-03-30 19:50:23 +00002808 }
glennrp147bc912011-03-30 18:47:21 +00002809
cristy3ed852e2009-09-05 21:47:34 +00002810 /*
2811 Read image scanlines.
2812 */
2813 if (image->delay != 0)
2814 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002815
glennrp0ca69b12010-07-26 01:57:52 +00002816 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002817 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2818 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002819 {
2820 if (logging != MagickFalse)
2821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002822 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002823 mng_info->scenes_found-1);
2824 png_destroy_read_struct(&ping,&ping_info,&end_info);
2825#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002826 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002827#endif
2828 if (logging != MagickFalse)
2829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2830 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002831
cristy3ed852e2009-09-05 21:47:34 +00002832 return(image);
2833 }
glennrp0fe50b42010-11-16 03:52:51 +00002834
cristy3ed852e2009-09-05 21:47:34 +00002835 if (logging != MagickFalse)
2836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2837 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002838
cristy3ed852e2009-09-05 21:47:34 +00002839 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002840 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2841 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002842
cristy3ed852e2009-09-05 21:47:34 +00002843 else
glennrpcf002022011-01-30 02:38:15 +00002844 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2845 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002846
glennrpcf002022011-01-30 02:38:15 +00002847 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002848 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002849
cristy3ed852e2009-09-05 21:47:34 +00002850 if (logging != MagickFalse)
2851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2852 " Converting PNG pixels to pixel packets");
2853 /*
2854 Convert PNG pixels to pixel packets.
2855 */
glennrpfaa852b2010-03-30 12:17:00 +00002856 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002857 {
2858 /*
2859 PNG image is corrupt.
2860 */
2861 png_destroy_read_struct(&ping,&ping_info,&end_info);
2862#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002863 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002864#endif
2865 if (quantum_info != (QuantumInfo *) NULL)
2866 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002867
glennrpcf002022011-01-30 02:38:15 +00002868 if (ping_pixels != (unsigned char *) NULL)
2869 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002870
cristy3ed852e2009-09-05 21:47:34 +00002871 if (logging != MagickFalse)
2872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2873 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002874
cristy3ed852e2009-09-05 21:47:34 +00002875 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002876 {
cristyc82a27b2011-10-21 01:07:16 +00002877 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002878 image->columns=0;
2879 }
glennrp0fe50b42010-11-16 03:52:51 +00002880
cristy3ed852e2009-09-05 21:47:34 +00002881 return(GetFirstImageInList(image));
2882 }
glennrp0fe50b42010-11-16 03:52:51 +00002883
cristyed552522009-10-16 14:04:35 +00002884 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002885
cristyed552522009-10-16 14:04:35 +00002886 if (quantum_info == (QuantumInfo *) NULL)
2887 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002888
glennrpc8cbc5d2011-01-01 00:12:34 +00002889 {
2890
2891 MagickBooleanType
2892 found_transparent_pixel;
2893
2894 found_transparent_pixel=MagickFalse;
2895
cristy3ed852e2009-09-05 21:47:34 +00002896 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002897 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002898 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002899 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002900 /*
2901 Convert image to DirectClass pixel packets.
2902 */
glennrp67b9c1a2011-04-22 18:47:36 +00002903#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2904 int
2905 depth;
2906
2907 depth=(ssize_t) ping_bit_depth;
2908#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002909 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2910 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2911 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2912 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002913
glennrpc8cbc5d2011-01-01 00:12:34 +00002914 for (y=0; y < (ssize_t) image->rows; y++)
2915 {
2916 if (num_passes > 1)
2917 row_offset=ping_rowbytes*y;
2918
2919 else
2920 row_offset=0;
2921
glennrpcf002022011-01-30 02:38:15 +00002922 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002923 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2924
cristyacd2ed22011-08-30 01:44:23 +00002925 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00002926 break;
2927
glennrpc8cbc5d2011-01-01 00:12:34 +00002928 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2929 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002930 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002931
2932 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2933 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002934 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002935
2936 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2937 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002938 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002939
2940 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2941 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002942 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002943
2944 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2945 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002946 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002947
glennrpc8cbc5d2011-01-01 00:12:34 +00002948 if (found_transparent_pixel == MagickFalse)
2949 {
2950 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002951 if (y== 0 && logging != MagickFalse)
2952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2953 " Looking for cheap transparent pixel");
2954
glennrpc8cbc5d2011-01-01 00:12:34 +00002955 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2956 {
glennrp5aa37f62011-01-02 03:07:57 +00002957 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2958 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy4c08aed2011-07-01 19:47:50 +00002959 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00002960 {
glennrpa6a06632011-01-19 15:15:34 +00002961 if (logging != MagickFalse)
2962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2963 " ...got one.");
2964
glennrpc8cbc5d2011-01-01 00:12:34 +00002965 found_transparent_pixel = MagickTrue;
2966 break;
2967 }
glennrp4f25bd02011-01-01 18:51:28 +00002968 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2969 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp847370c2011-07-05 17:37:15 +00002970 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2971 transparent_color.red &&
2972 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2973 transparent_color.green &&
2974 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2975 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002976 {
glennrpa6a06632011-01-19 15:15:34 +00002977 if (logging != MagickFalse)
2978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2979 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002980 found_transparent_pixel = MagickTrue;
2981 break;
2982 }
cristyed231572011-07-14 02:18:59 +00002983 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00002984 }
2985 }
2986
2987 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2988 {
2989 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2990 image->rows);
2991
2992 if (status == MagickFalse)
2993 break;
2994 }
2995 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2996 break;
2997 }
2998
2999 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3000 {
3001 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00003002 if (status == MagickFalse)
3003 break;
3004 }
cristy3ed852e2009-09-05 21:47:34 +00003005 }
cristy3ed852e2009-09-05 21:47:34 +00003006 }
glennrp0fe50b42010-11-16 03:52:51 +00003007
cristy3ed852e2009-09-05 21:47:34 +00003008 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00003009
cristy3ed852e2009-09-05 21:47:34 +00003010 for (pass=0; pass < num_passes; pass++)
3011 {
3012 Quantum
3013 *quantum_scanline;
3014
3015 register Quantum
3016 *r;
3017
3018 /*
3019 Convert grayscale image to PseudoClass pixel packets.
3020 */
glennrpc17d96f2011-06-27 01:20:11 +00003021 if (logging != MagickFalse)
3022 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3023 " Converting grayscale pixels to pixel packets");
cristy4c08aed2011-07-01 19:47:50 +00003024
glennrpfaa852b2010-03-30 12:17:00 +00003025 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00003026 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00003027
cristy3ed852e2009-09-05 21:47:34 +00003028 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3029 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003030
cristy3ed852e2009-09-05 21:47:34 +00003031 if (quantum_scanline == (Quantum *) NULL)
3032 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003033
cristybb503372010-05-27 20:51:26 +00003034 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003035 {
3036 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003037 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003038
cristy3ed852e2009-09-05 21:47:34 +00003039 else
3040 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003041
glennrpcf002022011-01-30 02:38:15 +00003042 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00003043 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003044
cristyacd2ed22011-08-30 01:44:23 +00003045 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003046 break;
glennrp0fe50b42010-11-16 03:52:51 +00003047
glennrpcf002022011-01-30 02:38:15 +00003048 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003049 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003050
glennrpfaa852b2010-03-30 12:17:00 +00003051 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003052 {
3053 case 1:
3054 {
cristybb503372010-05-27 20:51:26 +00003055 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003056 bit;
3057
cristybb503372010-05-27 20:51:26 +00003058 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003059 {
3060 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003061 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003062 p++;
3063 }
glennrp0fe50b42010-11-16 03:52:51 +00003064
cristy3ed852e2009-09-05 21:47:34 +00003065 if ((image->columns % 8) != 0)
3066 {
cristybb503372010-05-27 20:51:26 +00003067 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003068 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003069 }
glennrp0fe50b42010-11-16 03:52:51 +00003070
cristy3ed852e2009-09-05 21:47:34 +00003071 break;
3072 }
glennrp47b9dd52010-11-24 18:12:06 +00003073
cristy3ed852e2009-09-05 21:47:34 +00003074 case 2:
3075 {
cristybb503372010-05-27 20:51:26 +00003076 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003077 {
glennrpa18d5bc2011-04-23 14:51:34 +00003078 *r++=(*p >> 6) & 0x03;
3079 *r++=(*p >> 4) & 0x03;
3080 *r++=(*p >> 2) & 0x03;
3081 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003082 }
glennrp0fe50b42010-11-16 03:52:51 +00003083
cristy3ed852e2009-09-05 21:47:34 +00003084 if ((image->columns % 4) != 0)
3085 {
cristybb503372010-05-27 20:51:26 +00003086 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003087 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003088 }
glennrp0fe50b42010-11-16 03:52:51 +00003089
cristy3ed852e2009-09-05 21:47:34 +00003090 break;
3091 }
glennrp47b9dd52010-11-24 18:12:06 +00003092
cristy3ed852e2009-09-05 21:47:34 +00003093 case 4:
3094 {
cristybb503372010-05-27 20:51:26 +00003095 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003096 {
glennrpa18d5bc2011-04-23 14:51:34 +00003097 *r++=(*p >> 4) & 0x0f;
3098 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003099 }
glennrp0fe50b42010-11-16 03:52:51 +00003100
cristy3ed852e2009-09-05 21:47:34 +00003101 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003102 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003103
cristy3ed852e2009-09-05 21:47:34 +00003104 break;
3105 }
glennrp47b9dd52010-11-24 18:12:06 +00003106
cristy3ed852e2009-09-05 21:47:34 +00003107 case 8:
3108 {
glennrpfaa852b2010-03-30 12:17:00 +00003109 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003110 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003111 {
glennrpa18d5bc2011-04-23 14:51:34 +00003112 *r++=*p++;
cristy4c08aed2011-07-01 19:47:50 +00003113 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3114 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003115 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003116 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003117 }
glennrp0fe50b42010-11-16 03:52:51 +00003118
cristy3ed852e2009-09-05 21:47:34 +00003119 else
cristybb503372010-05-27 20:51:26 +00003120 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003121 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003122
cristy3ed852e2009-09-05 21:47:34 +00003123 break;
3124 }
glennrp47b9dd52010-11-24 18:12:06 +00003125
cristy3ed852e2009-09-05 21:47:34 +00003126 case 16:
3127 {
cristybb503372010-05-27 20:51:26 +00003128 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003129 {
glennrpc17d96f2011-06-27 01:20:11 +00003130#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003131 size_t
3132 quantum;
3133
3134 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003135 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003136
3137 else
glennrpc17d96f2011-06-27 01:20:11 +00003138 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003139
glennrp58f77c72011-04-23 14:09:09 +00003140 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003141 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003142 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003143
3144 if (ping_color_type == 4)
3145 {
glennrpc17d96f2011-06-27 01:20:11 +00003146 if (image->colors > 256)
3147 quantum=((*p++) << 8);
3148 else
3149 quantum=0;
3150
glennrp58f77c72011-04-23 14:09:09 +00003151 quantum|=(*p++);
cristy4c08aed2011-07-01 19:47:50 +00003152 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3153 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003154 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003155 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003156 }
glennrp58f77c72011-04-23 14:09:09 +00003157
3158#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3159 *r++=(*p++);
3160 p++; /* strip low byte */
3161
3162 if (ping_color_type == 4)
3163 {
cristy4c08aed2011-07-01 19:47:50 +00003164 SetPixelAlpha(image,*p++,q);
3165 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003166 found_transparent_pixel = MagickTrue;
3167 p++;
cristyed231572011-07-14 02:18:59 +00003168 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003169 }
cristy3ed852e2009-09-05 21:47:34 +00003170#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003171 }
glennrp47b9dd52010-11-24 18:12:06 +00003172
cristy3ed852e2009-09-05 21:47:34 +00003173 break;
3174 }
glennrp47b9dd52010-11-24 18:12:06 +00003175
cristy3ed852e2009-09-05 21:47:34 +00003176 default:
3177 break;
3178 }
glennrp3faa9a32011-04-23 14:00:25 +00003179
cristy3ed852e2009-09-05 21:47:34 +00003180 /*
3181 Transfer image scanline.
3182 */
3183 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003184
cristy4c08aed2011-07-01 19:47:50 +00003185 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3186
cristyacd2ed22011-08-30 01:44:23 +00003187 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00003188 break;
cristybb503372010-05-27 20:51:26 +00003189 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00003190 {
3191 SetPixelIndex(image,*r++,q);
cristyed231572011-07-14 02:18:59 +00003192 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00003193 }
glennrp0fe50b42010-11-16 03:52:51 +00003194
cristy3ed852e2009-09-05 21:47:34 +00003195 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3196 break;
glennrp0fe50b42010-11-16 03:52:51 +00003197
cristy7a287bf2010-02-14 02:18:09 +00003198 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3199 {
cristycee97112010-05-28 00:44:52 +00003200 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003201 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003202
cristy7a287bf2010-02-14 02:18:09 +00003203 if (status == MagickFalse)
3204 break;
3205 }
cristy3ed852e2009-09-05 21:47:34 +00003206 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003207
cristy7a287bf2010-02-14 02:18:09 +00003208 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003209 {
3210 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003211
cristy3ed852e2009-09-05 21:47:34 +00003212 if (status == MagickFalse)
3213 break;
3214 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003215
cristy3ed852e2009-09-05 21:47:34 +00003216 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3217 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003218
3219 image->matte=found_transparent_pixel;
3220
3221 if (logging != MagickFalse)
3222 {
3223 if (found_transparent_pixel != MagickFalse)
3224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3225 " Found transparent pixel");
3226 else
glennrp5aa37f62011-01-02 03:07:57 +00003227 {
3228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3229 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003230
glennrp5aa37f62011-01-02 03:07:57 +00003231 ping_color_type&=0x03;
3232 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003233 }
3234 }
3235
cristyb32b90a2009-09-07 21:45:48 +00003236 if (quantum_info != (QuantumInfo *) NULL)
3237 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00003238
cristy5c6f7892010-05-05 22:53:29 +00003239 if (image->storage_class == PseudoClass)
3240 {
cristyaeb2cbc2010-05-07 13:28:58 +00003241 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003242 matte;
3243
3244 matte=image->matte;
3245 image->matte=MagickFalse;
cristyea1a8aa2011-10-20 13:24:06 +00003246 (void) SyncImage(image,exception);
cristyaeb2cbc2010-05-07 13:28:58 +00003247 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003248 }
glennrp47b9dd52010-11-24 18:12:06 +00003249
glennrp4eb39312011-03-30 21:34:55 +00003250 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003251
3252 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003253 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003254 {
3255 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003256 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003257 image->colors=2;
cristyea1a8aa2011-10-20 13:24:06 +00003258 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003259#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003260 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003261#endif
3262 if (logging != MagickFalse)
3263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3264 " exit ReadOnePNGImage() early.");
3265 return(image);
3266 }
glennrp47b9dd52010-11-24 18:12:06 +00003267
glennrpfaa852b2010-03-30 12:17:00 +00003268 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003269 {
3270 ClassType
3271 storage_class;
3272
3273 /*
3274 Image has a transparent background.
3275 */
3276 storage_class=image->storage_class;
3277 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003278
glennrp3c218112010-11-27 15:31:26 +00003279/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003280
glennrp0fe50b42010-11-16 03:52:51 +00003281 if (storage_class == PseudoClass)
3282 {
3283 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003284 {
glennrp0fe50b42010-11-16 03:52:51 +00003285 for (x=0; x < ping_num_trans; x++)
3286 {
cristy4c08aed2011-07-01 19:47:50 +00003287 image->colormap[x].alpha =
3288 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003289 }
glennrpc11cf6a2010-03-20 16:46:19 +00003290 }
glennrp47b9dd52010-11-24 18:12:06 +00003291
glennrp0fe50b42010-11-16 03:52:51 +00003292 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3293 {
3294 for (x=0; x < (int) image->colors; x++)
3295 {
3296 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy4c08aed2011-07-01 19:47:50 +00003297 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003298 {
cristy4c08aed2011-07-01 19:47:50 +00003299 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003300 }
3301 }
3302 }
cristyea1a8aa2011-10-20 13:24:06 +00003303 (void) SyncImage(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003304 }
glennrp47b9dd52010-11-24 18:12:06 +00003305
glennrpa6a06632011-01-19 15:15:34 +00003306#if 1 /* Should have already been done above, but glennrp problem P10
3307 * needs this.
3308 */
glennrp0fe50b42010-11-16 03:52:51 +00003309 else
3310 {
3311 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003312 {
glennrp0fe50b42010-11-16 03:52:51 +00003313 image->storage_class=storage_class;
3314 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3315
cristyacd2ed22011-08-30 01:44:23 +00003316 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003317 break;
3318
glennrp0fe50b42010-11-16 03:52:51 +00003319
glennrpa6a06632011-01-19 15:15:34 +00003320 /* Caution: on a Q8 build, this does not distinguish between
3321 * 16-bit colors that differ only in the low byte
3322 */
glennrp0fe50b42010-11-16 03:52:51 +00003323 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3324 {
glennrp847370c2011-07-05 17:37:15 +00003325 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3326 transparent_color.red &&
3327 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3328 transparent_color.green &&
3329 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3330 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003331 {
cristy4c08aed2011-07-01 19:47:50 +00003332 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003333 }
glennrp0fe50b42010-11-16 03:52:51 +00003334
glennrp67b9c1a2011-04-22 18:47:36 +00003335#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003336 else
glennrp4f25bd02011-01-01 18:51:28 +00003337 {
cristy4c08aed2011-07-01 19:47:50 +00003338 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003339 }
glennrpa6a06632011-01-19 15:15:34 +00003340#endif
glennrp0fe50b42010-11-16 03:52:51 +00003341
cristyed231572011-07-14 02:18:59 +00003342 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003343 }
3344
3345 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3346 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003347 }
glennrp0fe50b42010-11-16 03:52:51 +00003348 }
glennrpa6a06632011-01-19 15:15:34 +00003349#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003350
cristy3ed852e2009-09-05 21:47:34 +00003351 image->storage_class=DirectClass;
3352 }
glennrp3c218112010-11-27 15:31:26 +00003353
cristyb40fc462010-08-08 00:49:49 +00003354 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3355 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3356 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00003357
cristyeb3b22a2011-03-31 20:16:11 +00003358 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003359 {
3360 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003361 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3362 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003363 else
glennrpa0ed0092011-04-18 16:36:29 +00003364 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3365 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003366
glennrp4eb39312011-03-30 21:34:55 +00003367 if (status != MagickFalse)
3368 for (i=0; i < (ssize_t) num_text; i++)
3369 {
3370 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003371
glennrp4eb39312011-03-30 21:34:55 +00003372 if (logging != MagickFalse)
3373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3374 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003375
glennrp4eb39312011-03-30 21:34:55 +00003376 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003377 {
cristyd15e6592011-10-15 00:13:06 +00003378 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i,
3379 exception);
glennrp4eb39312011-03-30 21:34:55 +00003380 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003381 }
glennrp0fe50b42010-11-16 03:52:51 +00003382
glennrp4eb39312011-03-30 21:34:55 +00003383 else
3384 {
3385 char
3386 *value;
3387
3388 length=text[i].text_length;
3389 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3390 sizeof(*value));
3391 if (value == (char *) NULL)
3392 {
cristyc82a27b2011-10-21 01:07:16 +00003393 (void) ThrowMagickException(exception,GetMagickModule(),
glennrp4eb39312011-03-30 21:34:55 +00003394 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3395 image->filename);
3396 break;
3397 }
3398 *value='\0';
3399 (void) ConcatenateMagickString(value,text[i].text,length+2);
3400
3401 /* Don't save "density" or "units" property if we have a pHYs
3402 * chunk
3403 */
3404 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3405 (LocaleCompare(text[i].key,"density") != 0 &&
3406 LocaleCompare(text[i].key,"units") != 0))
cristyd15e6592011-10-15 00:13:06 +00003407 (void) SetImageProperty(image,text[i].key,value,exception);
glennrp4eb39312011-03-30 21:34:55 +00003408
3409 if (logging != MagickFalse)
3410 {
3411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3412 " length: %lu",(unsigned long) length);
3413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3414 " Keyword: %s",text[i].key);
3415 }
3416
3417 value=DestroyString(value);
3418 }
3419 }
3420 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003421 }
glennrp3c218112010-11-27 15:31:26 +00003422
cristy3ed852e2009-09-05 21:47:34 +00003423#ifdef MNG_OBJECT_BUFFERS
3424 /*
3425 Store the object if necessary.
3426 */
3427 if (object_id && !mng_info->frozen[object_id])
3428 {
3429 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3430 {
3431 /*
3432 create a new object buffer.
3433 */
3434 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003435 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003436
cristy3ed852e2009-09-05 21:47:34 +00003437 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3438 {
3439 mng_info->ob[object_id]->image=(Image *) NULL;
3440 mng_info->ob[object_id]->reference_count=1;
3441 }
3442 }
glennrp47b9dd52010-11-24 18:12:06 +00003443
cristy3ed852e2009-09-05 21:47:34 +00003444 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3445 mng_info->ob[object_id]->frozen)
3446 {
3447 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
cristyc82a27b2011-10-21 01:07:16 +00003448 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003449 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3450 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003451
cristy3ed852e2009-09-05 21:47:34 +00003452 if (mng_info->ob[object_id]->frozen)
cristyc82a27b2011-10-21 01:07:16 +00003453 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003454 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3455 "`%s'",image->filename);
3456 }
glennrp0fe50b42010-11-16 03:52:51 +00003457
cristy3ed852e2009-09-05 21:47:34 +00003458 else
3459 {
cristy3ed852e2009-09-05 21:47:34 +00003460
3461 if (mng_info->ob[object_id]->image != (Image *) NULL)
3462 mng_info->ob[object_id]->image=DestroyImage
3463 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003464
cristy3ed852e2009-09-05 21:47:34 +00003465 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
cristyc82a27b2011-10-21 01:07:16 +00003466 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003467
cristy3ed852e2009-09-05 21:47:34 +00003468 if (mng_info->ob[object_id]->image != (Image *) NULL)
3469 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003470
cristy3ed852e2009-09-05 21:47:34 +00003471 else
cristyc82a27b2011-10-21 01:07:16 +00003472 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003473 ResourceLimitError,"Cloning image for object buffer failed",
3474 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003475
glennrpfaa852b2010-03-30 12:17:00 +00003476 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003477 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003478
glennrpfaa852b2010-03-30 12:17:00 +00003479 mng_info->ob[object_id]->width=ping_width;
3480 mng_info->ob[object_id]->height=ping_height;
3481 mng_info->ob[object_id]->color_type=ping_color_type;
3482 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3483 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3484 mng_info->ob[object_id]->compression_method=
3485 ping_compression_method;
3486 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003487
glennrpfaa852b2010-03-30 12:17:00 +00003488 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003489 {
3490 int
3491 number_colors;
3492
3493 png_colorp
3494 plte;
3495
3496 /*
3497 Copy the PLTE to the object buffer.
3498 */
3499 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3500 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003501
cristy3ed852e2009-09-05 21:47:34 +00003502 for (i=0; i < number_colors; i++)
3503 {
3504 mng_info->ob[object_id]->plte[i]=plte[i];
3505 }
3506 }
glennrp47b9dd52010-11-24 18:12:06 +00003507
cristy3ed852e2009-09-05 21:47:34 +00003508 else
3509 mng_info->ob[object_id]->plte_length=0;
3510 }
3511 }
3512#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003513
3514 /* Set image->matte to MagickTrue if the input colortype supports
3515 * alpha or if a valid tRNS chunk is present, no matter whether there
3516 * is actual transparency present.
3517 */
3518 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3519 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3520 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3521 MagickTrue : MagickFalse;
3522
glennrpcb395ac2011-03-30 19:50:23 +00003523 /* Set more properties for identify to retrieve */
3524 {
3525 char
3526 msg[MaxTextExtent];
3527
glennrp4eb39312011-03-30 21:34:55 +00003528 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003529 {
3530 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003531 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003532 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
cristy5d6fc9c2011-12-27 03:10:42 +00003533 (void) SetImageProperty(image,"png:text ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003534 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003535 }
3536
3537 if (num_raw_profiles != 0)
3538 {
cristy3b6fd2e2011-05-20 12:53:50 +00003539 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003540 "%d were found", num_raw_profiles);
cristy5d6fc9c2011-12-27 03:10:42 +00003541 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003542 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003543 }
3544
glennrpcb395ac2011-03-30 19:50:23 +00003545 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003546 {
cristy3b6fd2e2011-05-20 12:53:50 +00003547 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003548 "chunk was found (see Chromaticity, above)");
cristy5d6fc9c2011-12-27 03:10:42 +00003549 (void) SetImageProperty(image,"png:cHRM ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003550 exception);
glennrp59612252011-03-30 21:45:21 +00003551 }
glennrpcb395ac2011-03-30 19:50:23 +00003552
3553 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003554 {
cristy3b6fd2e2011-05-20 12:53:50 +00003555 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003556 "chunk was found (see Background color, above)");
cristy5d6fc9c2011-12-27 03:10:42 +00003557 (void) SetImageProperty(image,"png:bKGD ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003558 exception);
glennrp59612252011-03-30 21:45:21 +00003559 }
3560
cristy3b6fd2e2011-05-20 12:53:50 +00003561 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003562 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003563
3564 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy5d6fc9c2011-12-27 03:10:42 +00003565 (void) SetImageProperty(image,"png:iCCP ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003566 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003567
glennrpcb395ac2011-03-30 19:50:23 +00003568 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy5d6fc9c2011-12-27 03:10:42 +00003569 (void) SetImageProperty(image,"png:tRNS ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003570 exception);
glennrp4eb39312011-03-30 21:34:55 +00003571
3572#if defined(PNG_sRGB_SUPPORTED)
3573 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3574 {
cristy3b6fd2e2011-05-20 12:53:50 +00003575 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003576 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003577 (int) intent);
cristy5d6fc9c2011-12-27 03:10:42 +00003578 (void) SetImageProperty(image,"png:sRGB ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003579 exception);
glennrp4eb39312011-03-30 21:34:55 +00003580 }
3581#endif
3582
3583 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3584 {
cristy3b6fd2e2011-05-20 12:53:50 +00003585 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003586 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003587 file_gamma);
cristy5d6fc9c2011-12-27 03:10:42 +00003588 (void) SetImageProperty(image,"png:gAMA ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003589 exception);
glennrp4eb39312011-03-30 21:34:55 +00003590 }
3591
3592#if defined(PNG_pHYs_SUPPORTED)
3593 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3594 {
cristy3b6fd2e2011-05-20 12:53:50 +00003595 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003596 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003597 (double) x_resolution,(double) y_resolution, unit_type);
cristy5d6fc9c2011-12-27 03:10:42 +00003598 (void) SetImageProperty(image,"png:pHYs ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003599 exception);
glennrp4eb39312011-03-30 21:34:55 +00003600 }
3601#endif
3602
3603#if defined(PNG_oFFs_SUPPORTED)
3604 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3605 {
cristy3b6fd2e2011-05-20 12:53:50 +00003606 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003607 (double) image->page.x,(double) image->page.y);
cristy5d6fc9c2011-12-27 03:10:42 +00003608 (void) SetImageProperty(image,"png:oFFs ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003609 exception);
glennrp4eb39312011-03-30 21:34:55 +00003610 }
3611#endif
3612
glennrp07523c72011-03-31 18:12:10 +00003613 if ((image->page.width != 0 && image->page.width != image->columns) ||
3614 (image->page.height != 0 && image->page.height != image->rows))
3615 {
cristy3b6fd2e2011-05-20 12:53:50 +00003616 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003617 "width=%.20g, height=%.20g",
3618 (double) image->page.width,(double) image->page.height);
cristy5d6fc9c2011-12-27 03:10:42 +00003619 (void) SetImageProperty(image,"png:vpAg ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003620 exception);
glennrp07523c72011-03-31 18:12:10 +00003621 }
glennrpcb395ac2011-03-30 19:50:23 +00003622 }
3623
cristy3ed852e2009-09-05 21:47:34 +00003624 /*
3625 Relinquish resources.
3626 */
3627 png_destroy_read_struct(&ping,&ping_info,&end_info);
3628
glennrpcf002022011-01-30 02:38:15 +00003629 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003630#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003631 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003632#endif
3633
3634 if (logging != MagickFalse)
3635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3636 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003637
cristy3ed852e2009-09-05 21:47:34 +00003638 return(image);
3639
3640/* end of reading one PNG image */
3641}
3642
3643static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3644{
3645 Image
3646 *image,
3647 *previous;
3648
3649 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003650 have_mng_structure,
3651 logging,
cristy3ed852e2009-09-05 21:47:34 +00003652 status;
3653
3654 MngInfo
3655 *mng_info;
3656
3657 char
3658 magic_number[MaxTextExtent];
3659
cristy3ed852e2009-09-05 21:47:34 +00003660 ssize_t
3661 count;
3662
3663 /*
3664 Open image file.
3665 */
3666 assert(image_info != (const ImageInfo *) NULL);
3667 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003668
cristy3ed852e2009-09-05 21:47:34 +00003669 if (image_info->debug != MagickFalse)
3670 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3671 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003672
cristy3ed852e2009-09-05 21:47:34 +00003673 assert(exception != (ExceptionInfo *) NULL);
3674 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003675 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy9950d572011-10-01 18:22:35 +00003676 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003677 mng_info=(MngInfo *) NULL;
3678 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003679
cristy3ed852e2009-09-05 21:47:34 +00003680 if (status == MagickFalse)
3681 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003682
cristy3ed852e2009-09-05 21:47:34 +00003683 /*
3684 Verify PNG signature.
3685 */
3686 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003687
glennrpdde35db2011-02-21 12:06:32 +00003688 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003689 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003690
cristy3ed852e2009-09-05 21:47:34 +00003691 /*
3692 Allocate a MngInfo structure.
3693 */
3694 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003695 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003696
cristy3ed852e2009-09-05 21:47:34 +00003697 if (mng_info == (MngInfo *) NULL)
3698 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003699
cristy3ed852e2009-09-05 21:47:34 +00003700 /*
3701 Initialize members of the MngInfo structure.
3702 */
3703 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3704 mng_info->image=image;
3705 have_mng_structure=MagickTrue;
3706
3707 previous=image;
3708 image=ReadOnePNGImage(mng_info,image_info,exception);
3709 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003710
cristy3ed852e2009-09-05 21:47:34 +00003711 if (image == (Image *) NULL)
3712 {
3713 if (previous != (Image *) NULL)
3714 {
3715 if (previous->signature != MagickSignature)
3716 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003717
cristy3ed852e2009-09-05 21:47:34 +00003718 (void) CloseBlob(previous);
3719 (void) DestroyImageList(previous);
3720 }
glennrp0fe50b42010-11-16 03:52:51 +00003721
cristy3ed852e2009-09-05 21:47:34 +00003722 if (logging != MagickFalse)
3723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3724 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003725
cristy3ed852e2009-09-05 21:47:34 +00003726 return((Image *) NULL);
3727 }
glennrp47b9dd52010-11-24 18:12:06 +00003728
cristy3ed852e2009-09-05 21:47:34 +00003729 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003730
cristy3ed852e2009-09-05 21:47:34 +00003731 if ((image->columns == 0) || (image->rows == 0))
3732 {
3733 if (logging != MagickFalse)
3734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3735 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003736
cristy3ed852e2009-09-05 21:47:34 +00003737 ThrowReaderException(CorruptImageError,"CorruptImage");
3738 }
glennrp47b9dd52010-11-24 18:12:06 +00003739
cristy3ed852e2009-09-05 21:47:34 +00003740 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3741 {
cristy018f07f2011-09-04 21:15:19 +00003742 (void) SetImageType(image,TrueColorType,exception);
cristy3ed852e2009-09-05 21:47:34 +00003743 image->matte=MagickFalse;
3744 }
glennrp0fe50b42010-11-16 03:52:51 +00003745
cristy3ed852e2009-09-05 21:47:34 +00003746 if (LocaleCompare(image_info->magick,"PNG32") == 0)
cristy018f07f2011-09-04 21:15:19 +00003747 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003748
cristy3ed852e2009-09-05 21:47:34 +00003749 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3751 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3752 (double) image->page.width,(double) image->page.height,
3753 (double) image->page.x,(double) image->page.y);
3754
3755 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003757
cristy3ed852e2009-09-05 21:47:34 +00003758 return(image);
3759}
3760
3761
3762
3763#if defined(JNG_SUPPORTED)
3764/*
3765%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3766% %
3767% %
3768% %
3769% R e a d O n e J N G I m a g e %
3770% %
3771% %
3772% %
3773%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3774%
3775% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3776% (minus the 8-byte signature) and returns it. It allocates the memory
3777% necessary for the new Image structure and returns a pointer to the new
3778% image.
3779%
3780% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3781%
3782% The format of the ReadOneJNGImage method is:
3783%
3784% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3785% ExceptionInfo *exception)
3786%
3787% A description of each parameter follows:
3788%
3789% o mng_info: Specifies a pointer to a MngInfo structure.
3790%
3791% o image_info: the image info.
3792%
3793% o exception: return any errors or warnings in this structure.
3794%
3795*/
3796static Image *ReadOneJNGImage(MngInfo *mng_info,
3797 const ImageInfo *image_info, ExceptionInfo *exception)
3798{
3799 Image
3800 *alpha_image,
3801 *color_image,
3802 *image,
3803 *jng_image;
3804
3805 ImageInfo
3806 *alpha_image_info,
3807 *color_image_info;
3808
cristy4383ec82011-01-05 15:42:32 +00003809 MagickBooleanType
3810 logging;
3811
cristybb503372010-05-27 20:51:26 +00003812 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003813 y;
3814
3815 MagickBooleanType
3816 status;
3817
3818 png_uint_32
3819 jng_height,
3820 jng_width;
3821
3822 png_byte
3823 jng_color_type,
3824 jng_image_sample_depth,
3825 jng_image_compression_method,
3826 jng_image_interlace_method,
3827 jng_alpha_sample_depth,
3828 jng_alpha_compression_method,
3829 jng_alpha_filter_method,
3830 jng_alpha_interlace_method;
3831
cristy4c08aed2011-07-01 19:47:50 +00003832 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003833 *s;
3834
cristybb503372010-05-27 20:51:26 +00003835 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003836 i,
3837 x;
3838
cristy4c08aed2011-07-01 19:47:50 +00003839 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003840 *q;
3841
3842 register unsigned char
3843 *p;
3844
3845 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003846 read_JSEP,
3847 reading_idat,
3848 skip_to_iend;
3849
cristybb503372010-05-27 20:51:26 +00003850 size_t
cristy3ed852e2009-09-05 21:47:34 +00003851 length;
3852
3853 jng_alpha_compression_method=0;
3854 jng_alpha_sample_depth=8;
3855 jng_color_type=0;
3856 jng_height=0;
3857 jng_width=0;
3858 alpha_image=(Image *) NULL;
3859 color_image=(Image *) NULL;
3860 alpha_image_info=(ImageInfo *) NULL;
3861 color_image_info=(ImageInfo *) NULL;
3862
3863 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003864 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003865
3866 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003867
cristy4c08aed2011-07-01 19:47:50 +00003868 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003869 {
3870 /*
3871 Allocate next image structure.
3872 */
3873 if (logging != MagickFalse)
3874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3875 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003876
cristy9950d572011-10-01 18:22:35 +00003877 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003878
cristy3ed852e2009-09-05 21:47:34 +00003879 if (GetNextImageInList(image) == (Image *) NULL)
3880 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003881
cristy3ed852e2009-09-05 21:47:34 +00003882 image=SyncNextImageInList(image);
3883 }
3884 mng_info->image=image;
3885
3886 /*
3887 Signature bytes have already been read.
3888 */
3889
3890 read_JSEP=MagickFalse;
3891 reading_idat=MagickFalse;
3892 skip_to_iend=MagickFalse;
3893 for (;;)
3894 {
3895 char
3896 type[MaxTextExtent];
3897
3898 unsigned char
3899 *chunk;
3900
3901 unsigned int
3902 count;
3903
3904 /*
3905 Read a new JNG chunk.
3906 */
3907 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3908 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003909
cristy3ed852e2009-09-05 21:47:34 +00003910 if (status == MagickFalse)
3911 break;
glennrp0fe50b42010-11-16 03:52:51 +00003912
cristy3ed852e2009-09-05 21:47:34 +00003913 type[0]='\0';
3914 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3915 length=ReadBlobMSBLong(image);
3916 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3917
3918 if (logging != MagickFalse)
3919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003920 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3921 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003922
3923 if (length > PNG_UINT_31_MAX || count == 0)
3924 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003925
cristy3ed852e2009-09-05 21:47:34 +00003926 p=NULL;
3927 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003928
cristy3ed852e2009-09-05 21:47:34 +00003929 if (length)
3930 {
3931 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003932
cristy3ed852e2009-09-05 21:47:34 +00003933 if (chunk == (unsigned char *) NULL)
3934 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003935
cristybb503372010-05-27 20:51:26 +00003936 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003937 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003938
cristy3ed852e2009-09-05 21:47:34 +00003939 p=chunk;
3940 }
glennrp47b9dd52010-11-24 18:12:06 +00003941
cristy3ed852e2009-09-05 21:47:34 +00003942 (void) ReadBlobMSBLong(image); /* read crc word */
3943
3944 if (skip_to_iend)
3945 {
3946 if (length)
3947 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003948
cristy3ed852e2009-09-05 21:47:34 +00003949 continue;
3950 }
3951
3952 if (memcmp(type,mng_JHDR,4) == 0)
3953 {
3954 if (length == 16)
3955 {
cristybb503372010-05-27 20:51:26 +00003956 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003957 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003958 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003959 (p[6] << 8) | p[7]);
3960 jng_color_type=p[8];
3961 jng_image_sample_depth=p[9];
3962 jng_image_compression_method=p[10];
3963 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003964
cristy3ed852e2009-09-05 21:47:34 +00003965 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3966 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003967
cristy3ed852e2009-09-05 21:47:34 +00003968 jng_alpha_sample_depth=p[12];
3969 jng_alpha_compression_method=p[13];
3970 jng_alpha_filter_method=p[14];
3971 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003972
cristy3ed852e2009-09-05 21:47:34 +00003973 if (logging != MagickFalse)
3974 {
3975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003976 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003977
cristy3ed852e2009-09-05 21:47:34 +00003978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003979 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003980
cristy3ed852e2009-09-05 21:47:34 +00003981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3982 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003983
cristy3ed852e2009-09-05 21:47:34 +00003984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3985 " jng_image_sample_depth: %3d",
3986 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003987
cristy3ed852e2009-09-05 21:47:34 +00003988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3989 " jng_image_compression_method:%3d",
3990 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003991
cristy3ed852e2009-09-05 21:47:34 +00003992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3993 " jng_image_interlace_method: %3d",
3994 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003995
cristy3ed852e2009-09-05 21:47:34 +00003996 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3997 " jng_alpha_sample_depth: %3d",
3998 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003999
cristy3ed852e2009-09-05 21:47:34 +00004000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4001 " jng_alpha_compression_method:%3d",
4002 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004003
cristy3ed852e2009-09-05 21:47:34 +00004004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4005 " jng_alpha_filter_method: %3d",
4006 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00004007
cristy3ed852e2009-09-05 21:47:34 +00004008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4009 " jng_alpha_interlace_method: %3d",
4010 jng_alpha_interlace_method);
4011 }
4012 }
glennrp47b9dd52010-11-24 18:12:06 +00004013
cristy3ed852e2009-09-05 21:47:34 +00004014 if (length)
4015 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004016
cristy3ed852e2009-09-05 21:47:34 +00004017 continue;
4018 }
4019
4020
4021 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4022 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4023 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4024 {
4025 /*
4026 o create color_image
4027 o open color_blob, attached to color_image
4028 o if (color type has alpha)
4029 open alpha_blob, attached to alpha_image
4030 */
4031
cristy73bd4a52010-10-05 11:24:23 +00004032 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004033
cristy3ed852e2009-09-05 21:47:34 +00004034 if (color_image_info == (ImageInfo *) NULL)
4035 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004036
cristy3ed852e2009-09-05 21:47:34 +00004037 GetImageInfo(color_image_info);
cristy9950d572011-10-01 18:22:35 +00004038 color_image=AcquireImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004039
cristy3ed852e2009-09-05 21:47:34 +00004040 if (color_image == (Image *) NULL)
4041 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4042
4043 if (logging != MagickFalse)
4044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4045 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004046
cristy3ed852e2009-09-05 21:47:34 +00004047 (void) AcquireUniqueFilename(color_image->filename);
4048 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4049 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004050
cristy3ed852e2009-09-05 21:47:34 +00004051 if (status == MagickFalse)
4052 return((Image *) NULL);
4053
4054 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4055 {
4056 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004057 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004058
cristy3ed852e2009-09-05 21:47:34 +00004059 if (alpha_image_info == (ImageInfo *) NULL)
4060 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004061
cristy3ed852e2009-09-05 21:47:34 +00004062 GetImageInfo(alpha_image_info);
cristy9950d572011-10-01 18:22:35 +00004063 alpha_image=AcquireImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004064
cristy3ed852e2009-09-05 21:47:34 +00004065 if (alpha_image == (Image *) NULL)
4066 {
4067 alpha_image=DestroyImage(alpha_image);
4068 ThrowReaderException(ResourceLimitError,
4069 "MemoryAllocationFailed");
4070 }
glennrp0fe50b42010-11-16 03:52:51 +00004071
cristy3ed852e2009-09-05 21:47:34 +00004072 if (logging != MagickFalse)
4073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4074 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004075
cristy3ed852e2009-09-05 21:47:34 +00004076 (void) AcquireUniqueFilename(alpha_image->filename);
4077 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4078 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004079
cristy3ed852e2009-09-05 21:47:34 +00004080 if (status == MagickFalse)
4081 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004082
cristy3ed852e2009-09-05 21:47:34 +00004083 if (jng_alpha_compression_method == 0)
4084 {
4085 unsigned char
4086 data[18];
4087
4088 if (logging != MagickFalse)
4089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4090 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004091
cristy3ed852e2009-09-05 21:47:34 +00004092 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4093 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004094
cristy3ed852e2009-09-05 21:47:34 +00004095 (void) WriteBlobMSBULong(alpha_image,13L);
4096 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004097 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004098 PNGLong(data+4,jng_width);
4099 PNGLong(data+8,jng_height);
4100 data[12]=jng_alpha_sample_depth;
4101 data[13]=0; /* color_type gray */
4102 data[14]=0; /* compression method 0 */
4103 data[15]=0; /* filter_method 0 */
4104 data[16]=0; /* interlace_method 0 */
4105 (void) WriteBlob(alpha_image,17,data);
4106 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4107 }
4108 }
4109 reading_idat=MagickTrue;
4110 }
4111
4112 if (memcmp(type,mng_JDAT,4) == 0)
4113 {
glennrp47b9dd52010-11-24 18:12:06 +00004114 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004115
4116 if (logging != MagickFalse)
4117 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4118 " Copying JDAT chunk data to color_blob.");
4119
4120 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004121
cristy3ed852e2009-09-05 21:47:34 +00004122 if (length)
4123 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004124
cristy3ed852e2009-09-05 21:47:34 +00004125 continue;
4126 }
4127
4128 if (memcmp(type,mng_IDAT,4) == 0)
4129 {
4130 png_byte
4131 data[5];
4132
glennrp47b9dd52010-11-24 18:12:06 +00004133 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004134
4135 if (image_info->ping == MagickFalse)
4136 {
4137 if (logging != MagickFalse)
4138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4139 " Copying IDAT chunk data to alpha_blob.");
4140
cristybb503372010-05-27 20:51:26 +00004141 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004142 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004143 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004144 (void) WriteBlob(alpha_image,4,data);
4145 (void) WriteBlob(alpha_image,length,chunk);
4146 (void) WriteBlobMSBULong(alpha_image,
4147 crc32(crc32(0,data,4),chunk,(uInt) length));
4148 }
glennrp0fe50b42010-11-16 03:52:51 +00004149
cristy3ed852e2009-09-05 21:47:34 +00004150 if (length)
4151 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004152
cristy3ed852e2009-09-05 21:47:34 +00004153 continue;
4154 }
4155
4156 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4157 {
glennrp47b9dd52010-11-24 18:12:06 +00004158 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004159
4160 if (image_info->ping == MagickFalse)
4161 {
4162 if (logging != MagickFalse)
4163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4164 " Copying JDAA chunk data to alpha_blob.");
4165
4166 (void) WriteBlob(alpha_image,length,chunk);
4167 }
glennrp0fe50b42010-11-16 03:52:51 +00004168
cristy3ed852e2009-09-05 21:47:34 +00004169 if (length)
4170 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004171
cristy3ed852e2009-09-05 21:47:34 +00004172 continue;
4173 }
4174
4175 if (memcmp(type,mng_JSEP,4) == 0)
4176 {
4177 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004178
cristy3ed852e2009-09-05 21:47:34 +00004179 if (length)
4180 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004181
cristy3ed852e2009-09-05 21:47:34 +00004182 continue;
4183 }
4184
4185 if (memcmp(type,mng_bKGD,4) == 0)
4186 {
4187 if (length == 2)
4188 {
4189 image->background_color.red=ScaleCharToQuantum(p[1]);
4190 image->background_color.green=image->background_color.red;
4191 image->background_color.blue=image->background_color.red;
4192 }
glennrp0fe50b42010-11-16 03:52:51 +00004193
cristy3ed852e2009-09-05 21:47:34 +00004194 if (length == 6)
4195 {
4196 image->background_color.red=ScaleCharToQuantum(p[1]);
4197 image->background_color.green=ScaleCharToQuantum(p[3]);
4198 image->background_color.blue=ScaleCharToQuantum(p[5]);
4199 }
glennrp0fe50b42010-11-16 03:52:51 +00004200
cristy3ed852e2009-09-05 21:47:34 +00004201 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4202 continue;
4203 }
4204
4205 if (memcmp(type,mng_gAMA,4) == 0)
4206 {
4207 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004208 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004209
cristy3ed852e2009-09-05 21:47:34 +00004210 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4211 continue;
4212 }
4213
4214 if (memcmp(type,mng_cHRM,4) == 0)
4215 {
4216 if (length == 32)
4217 {
cristy8182b072010-05-30 20:10:53 +00004218 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4219 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4220 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4221 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4222 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4223 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4224 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4225 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004226 }
glennrp47b9dd52010-11-24 18:12:06 +00004227
cristy3ed852e2009-09-05 21:47:34 +00004228 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4229 continue;
4230 }
4231
4232 if (memcmp(type,mng_sRGB,4) == 0)
4233 {
4234 if (length == 1)
4235 {
glennrpe610a072010-08-05 17:08:46 +00004236 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004237 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004238 image->gamma=0.45455f;
4239 image->chromaticity.red_primary.x=0.6400f;
4240 image->chromaticity.red_primary.y=0.3300f;
4241 image->chromaticity.green_primary.x=0.3000f;
4242 image->chromaticity.green_primary.y=0.6000f;
4243 image->chromaticity.blue_primary.x=0.1500f;
4244 image->chromaticity.blue_primary.y=0.0600f;
4245 image->chromaticity.white_point.x=0.3127f;
4246 image->chromaticity.white_point.y=0.3290f;
4247 }
glennrp47b9dd52010-11-24 18:12:06 +00004248
cristy3ed852e2009-09-05 21:47:34 +00004249 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4250 continue;
4251 }
4252
4253 if (memcmp(type,mng_oFFs,4) == 0)
4254 {
4255 if (length > 8)
4256 {
glennrp5eae7602011-02-22 15:21:32 +00004257 image->page.x=(ssize_t) mng_get_long(p);
4258 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004259
cristy3ed852e2009-09-05 21:47:34 +00004260 if ((int) p[8] != 0)
4261 {
4262 image->page.x/=10000;
4263 image->page.y/=10000;
4264 }
4265 }
glennrp47b9dd52010-11-24 18:12:06 +00004266
cristy3ed852e2009-09-05 21:47:34 +00004267 if (length)
4268 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004269
cristy3ed852e2009-09-05 21:47:34 +00004270 continue;
4271 }
4272
4273 if (memcmp(type,mng_pHYs,4) == 0)
4274 {
4275 if (length > 8)
4276 {
cristy2a11bef2011-10-28 18:33:11 +00004277 image->resolution.x=(double) mng_get_long(p);
4278 image->resolution.y=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004279 if ((int) p[8] == PNG_RESOLUTION_METER)
4280 {
4281 image->units=PixelsPerCentimeterResolution;
cristy2a11bef2011-10-28 18:33:11 +00004282 image->resolution.x=image->resolution.x/100.0f;
4283 image->resolution.y=image->resolution.y/100.0f;
cristy3ed852e2009-09-05 21:47:34 +00004284 }
4285 }
glennrp0fe50b42010-11-16 03:52:51 +00004286
cristy3ed852e2009-09-05 21:47:34 +00004287 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4288 continue;
4289 }
4290
4291#if 0
4292 if (memcmp(type,mng_iCCP,4) == 0)
4293 {
glennrpfd05d622011-02-25 04:10:33 +00004294 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004295 if (length)
4296 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004297
cristy3ed852e2009-09-05 21:47:34 +00004298 continue;
4299 }
4300#endif
4301
4302 if (length)
4303 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4304
4305 if (memcmp(type,mng_IEND,4))
4306 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004307
cristy3ed852e2009-09-05 21:47:34 +00004308 break;
4309 }
4310
4311
4312 /* IEND found */
4313
4314 /*
4315 Finish up reading image data:
4316
4317 o read main image from color_blob.
4318
4319 o close color_blob.
4320
4321 o if (color_type has alpha)
4322 if alpha_encoding is PNG
4323 read secondary image from alpha_blob via ReadPNG
4324 if alpha_encoding is JPEG
4325 read secondary image from alpha_blob via ReadJPEG
4326
4327 o close alpha_blob.
4328
4329 o copy intensity of secondary image into
cristy4c08aed2011-07-01 19:47:50 +00004330 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004331
4332 o destroy the secondary image.
4333 */
4334
4335 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004336
cristy3ed852e2009-09-05 21:47:34 +00004337 if (logging != MagickFalse)
4338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4339 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004340
cristy3b6fd2e2011-05-20 12:53:50 +00004341 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004342 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004343
cristy3ed852e2009-09-05 21:47:34 +00004344 color_image_info->ping=MagickFalse; /* To do: avoid this */
4345 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004346
cristy3ed852e2009-09-05 21:47:34 +00004347 if (jng_image == (Image *) NULL)
4348 return((Image *) NULL);
4349
4350 (void) RelinquishUniqueFileResource(color_image->filename);
4351 color_image=DestroyImage(color_image);
4352 color_image_info=DestroyImageInfo(color_image_info);
4353
4354 if (jng_image == (Image *) NULL)
4355 return((Image *) NULL);
4356
4357 if (logging != MagickFalse)
4358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4359 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004360
cristy3ed852e2009-09-05 21:47:34 +00004361 image->rows=jng_height;
4362 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004363
cristybb503372010-05-27 20:51:26 +00004364 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004365 {
cristyc82a27b2011-10-21 01:07:16 +00004366 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00004367 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004368 for (x=(ssize_t) image->columns; x != 0; x--)
4369 {
4370 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4371 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4372 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004373 q+=GetPixelChannels(image);
4374 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004375 }
glennrp47b9dd52010-11-24 18:12:06 +00004376
cristy3ed852e2009-09-05 21:47:34 +00004377 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4378 break;
4379 }
glennrp0fe50b42010-11-16 03:52:51 +00004380
cristy3ed852e2009-09-05 21:47:34 +00004381 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004382
cristy3ed852e2009-09-05 21:47:34 +00004383 if (image_info->ping == MagickFalse)
4384 {
4385 if (jng_color_type >= 12)
4386 {
4387 if (jng_alpha_compression_method == 0)
4388 {
4389 png_byte
4390 data[5];
4391 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4392 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004393 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004394 (void) WriteBlob(alpha_image,4,data);
4395 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4396 }
glennrp0fe50b42010-11-16 03:52:51 +00004397
cristy3ed852e2009-09-05 21:47:34 +00004398 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004399
cristy3ed852e2009-09-05 21:47:34 +00004400 if (logging != MagickFalse)
4401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00004402 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004403
cristy3b6fd2e2011-05-20 12:53:50 +00004404 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004405 "%s",alpha_image->filename);
4406
4407 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004408
cristy3ed852e2009-09-05 21:47:34 +00004409 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004410 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004411 {
4412 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristyc82a27b2011-10-21 01:07:16 +00004413 exception);
cristy3ed852e2009-09-05 21:47:34 +00004414 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004415
cristy3ed852e2009-09-05 21:47:34 +00004416 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00004417 for (x=(ssize_t) image->columns; x != 0; x--)
4418 {
4419 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004420 q+=GetPixelChannels(image);
4421 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004422 }
glennrp0fe50b42010-11-16 03:52:51 +00004423
cristy3ed852e2009-09-05 21:47:34 +00004424 else
cristy4c08aed2011-07-01 19:47:50 +00004425 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004426 {
cristy4c08aed2011-07-01 19:47:50 +00004427 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4428 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004429 image->matte=MagickTrue;
cristyed231572011-07-14 02:18:59 +00004430 q+=GetPixelChannels(image);
4431 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004432 }
glennrp0fe50b42010-11-16 03:52:51 +00004433
cristy3ed852e2009-09-05 21:47:34 +00004434 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4435 break;
4436 }
4437 (void) RelinquishUniqueFileResource(alpha_image->filename);
4438 alpha_image=DestroyImage(alpha_image);
4439 alpha_image_info=DestroyImageInfo(alpha_image_info);
4440 if (jng_image != (Image *) NULL)
4441 jng_image=DestroyImage(jng_image);
4442 }
4443 }
4444
glennrp47b9dd52010-11-24 18:12:06 +00004445 /* Read the JNG image. */
4446
cristy3ed852e2009-09-05 21:47:34 +00004447 if (mng_info->mng_type == 0)
4448 {
4449 mng_info->mng_width=jng_width;
4450 mng_info->mng_height=jng_height;
4451 }
glennrp0fe50b42010-11-16 03:52:51 +00004452
cristy3ed852e2009-09-05 21:47:34 +00004453 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004454 {
4455 image->page.width=jng_width;
4456 image->page.height=jng_height;
4457 }
4458
cristy3ed852e2009-09-05 21:47:34 +00004459 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004460 {
4461 image->page.x=mng_info->x_off[mng_info->object_id];
4462 image->page.y=mng_info->y_off[mng_info->object_id];
4463 }
4464
cristy3ed852e2009-09-05 21:47:34 +00004465 else
glennrp0fe50b42010-11-16 03:52:51 +00004466 {
4467 image->page.y=mng_info->y_off[mng_info->object_id];
4468 }
4469
cristy3ed852e2009-09-05 21:47:34 +00004470 mng_info->image_found++;
4471 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4472 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004473
cristy3ed852e2009-09-05 21:47:34 +00004474 if (logging != MagickFalse)
4475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4476 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004477
cristy3ed852e2009-09-05 21:47:34 +00004478 return(image);
4479}
4480
4481/*
4482%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4483% %
4484% %
4485% %
4486% R e a d J N G I m a g e %
4487% %
4488% %
4489% %
4490%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4491%
4492% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4493% (including the 8-byte signature) and returns it. It allocates the memory
4494% necessary for the new Image structure and returns a pointer to the new
4495% image.
4496%
4497% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4498%
4499% The format of the ReadJNGImage method is:
4500%
4501% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4502% *exception)
4503%
4504% A description of each parameter follows:
4505%
4506% o image_info: the image info.
4507%
4508% o exception: return any errors or warnings in this structure.
4509%
4510*/
4511
4512static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4513{
4514 Image
4515 *image,
4516 *previous;
4517
4518 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004519 have_mng_structure,
4520 logging,
cristy3ed852e2009-09-05 21:47:34 +00004521 status;
4522
4523 MngInfo
4524 *mng_info;
4525
4526 char
4527 magic_number[MaxTextExtent];
4528
cristy3ed852e2009-09-05 21:47:34 +00004529 size_t
4530 count;
4531
4532 /*
4533 Open image file.
4534 */
4535 assert(image_info != (const ImageInfo *) NULL);
4536 assert(image_info->signature == MagickSignature);
4537 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4538 assert(exception != (ExceptionInfo *) NULL);
4539 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004540 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy9950d572011-10-01 18:22:35 +00004541 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004542 mng_info=(MngInfo *) NULL;
4543 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004544
cristy3ed852e2009-09-05 21:47:34 +00004545 if (status == MagickFalse)
4546 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004547
cristy3ed852e2009-09-05 21:47:34 +00004548 if (LocaleCompare(image_info->magick,"JNG") != 0)
4549 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004550
glennrp47b9dd52010-11-24 18:12:06 +00004551 /* Verify JNG signature. */
4552
cristy3ed852e2009-09-05 21:47:34 +00004553 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004554
glennrp3b8763e2011-02-21 12:08:18 +00004555 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004556 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004557
glennrp47b9dd52010-11-24 18:12:06 +00004558 /* Allocate a MngInfo structure. */
4559
cristy3ed852e2009-09-05 21:47:34 +00004560 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004561 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004562
cristy3ed852e2009-09-05 21:47:34 +00004563 if (mng_info == (MngInfo *) NULL)
4564 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004565
glennrp47b9dd52010-11-24 18:12:06 +00004566 /* Initialize members of the MngInfo structure. */
4567
cristy3ed852e2009-09-05 21:47:34 +00004568 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4569 have_mng_structure=MagickTrue;
4570
4571 mng_info->image=image;
4572 previous=image;
4573 image=ReadOneJNGImage(mng_info,image_info,exception);
4574 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004575
cristy3ed852e2009-09-05 21:47:34 +00004576 if (image == (Image *) NULL)
4577 {
4578 if (IsImageObject(previous) != MagickFalse)
4579 {
4580 (void) CloseBlob(previous);
4581 (void) DestroyImageList(previous);
4582 }
glennrp0fe50b42010-11-16 03:52:51 +00004583
cristy3ed852e2009-09-05 21:47:34 +00004584 if (logging != MagickFalse)
4585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4586 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004587
cristy3ed852e2009-09-05 21:47:34 +00004588 return((Image *) NULL);
4589 }
4590 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004591
cristy3ed852e2009-09-05 21:47:34 +00004592 if (image->columns == 0 || image->rows == 0)
4593 {
4594 if (logging != MagickFalse)
4595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4596 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004597
cristy3ed852e2009-09-05 21:47:34 +00004598 ThrowReaderException(CorruptImageError,"CorruptImage");
4599 }
glennrp0fe50b42010-11-16 03:52:51 +00004600
cristy3ed852e2009-09-05 21:47:34 +00004601 if (logging != MagickFalse)
4602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004603
cristy3ed852e2009-09-05 21:47:34 +00004604 return(image);
4605}
4606#endif
4607
4608static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4609{
4610 char
4611 page_geometry[MaxTextExtent];
4612
4613 Image
4614 *image,
4615 *previous;
4616
cristy4383ec82011-01-05 15:42:32 +00004617 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004618 logging,
4619 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004620
cristy3ed852e2009-09-05 21:47:34 +00004621 volatile int
4622 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004623 object_id,
4624 term_chunk_found,
4625 skip_to_iend;
4626
cristybb503372010-05-27 20:51:26 +00004627 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004628 image_count=0;
4629
4630 MagickBooleanType
4631 status;
4632
4633 MagickOffsetType
4634 offset;
4635
4636 MngInfo
4637 *mng_info;
4638
4639 MngBox
4640 default_fb,
4641 fb,
4642 previous_fb;
4643
4644#if defined(MNG_INSERT_LAYERS)
cristy101ab702011-10-13 13:06:32 +00004645 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004646 mng_background_color;
4647#endif
4648
4649 register unsigned char
4650 *p;
4651
cristybb503372010-05-27 20:51:26 +00004652 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004653 i;
4654
4655 size_t
4656 count;
4657
cristybb503372010-05-27 20:51:26 +00004658 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004659 loop_level;
4660
4661 volatile short
4662 skipping_loop;
4663
4664#if defined(MNG_INSERT_LAYERS)
4665 unsigned int
4666 mandatory_back=0;
4667#endif
4668
4669 volatile unsigned int
4670#ifdef MNG_OBJECT_BUFFERS
4671 mng_background_object=0,
4672#endif
4673 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4674
cristybb503372010-05-27 20:51:26 +00004675 size_t
cristy3ed852e2009-09-05 21:47:34 +00004676 default_frame_timeout,
4677 frame_timeout,
4678#if defined(MNG_INSERT_LAYERS)
4679 image_height,
4680 image_width,
4681#endif
4682 length;
4683
glennrp38ea0832010-06-02 18:50:28 +00004684 /* These delays are all measured in image ticks_per_second,
4685 * not in MNG ticks_per_second
4686 */
cristybb503372010-05-27 20:51:26 +00004687 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004688 default_frame_delay,
4689 final_delay,
4690 final_image_delay,
4691 frame_delay,
4692#if defined(MNG_INSERT_LAYERS)
4693 insert_layers,
4694#endif
4695 mng_iterations=1,
4696 simplicity=0,
4697 subframe_height=0,
4698 subframe_width=0;
4699
4700 previous_fb.top=0;
4701 previous_fb.bottom=0;
4702 previous_fb.left=0;
4703 previous_fb.right=0;
4704 default_fb.top=0;
4705 default_fb.bottom=0;
4706 default_fb.left=0;
4707 default_fb.right=0;
4708
glennrp47b9dd52010-11-24 18:12:06 +00004709 /* Open image file. */
4710
cristy3ed852e2009-09-05 21:47:34 +00004711 assert(image_info != (const ImageInfo *) NULL);
4712 assert(image_info->signature == MagickSignature);
4713 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4714 assert(exception != (ExceptionInfo *) NULL);
4715 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004716 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy9950d572011-10-01 18:22:35 +00004717 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004718 mng_info=(MngInfo *) NULL;
4719 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004720
cristy3ed852e2009-09-05 21:47:34 +00004721 if (status == MagickFalse)
4722 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004723
cristy3ed852e2009-09-05 21:47:34 +00004724 first_mng_object=MagickFalse;
4725 skipping_loop=(-1);
4726 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004727
4728 /* Allocate a MngInfo structure. */
4729
cristy73bd4a52010-10-05 11:24:23 +00004730 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004731
cristy3ed852e2009-09-05 21:47:34 +00004732 if (mng_info == (MngInfo *) NULL)
4733 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004734
glennrp47b9dd52010-11-24 18:12:06 +00004735 /* Initialize members of the MngInfo structure. */
4736
cristy3ed852e2009-09-05 21:47:34 +00004737 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4738 mng_info->image=image;
4739 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004740
4741 if (LocaleCompare(image_info->magick,"MNG") == 0)
4742 {
4743 char
4744 magic_number[MaxTextExtent];
4745
glennrp47b9dd52010-11-24 18:12:06 +00004746 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004747 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4748 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4749 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004750
4751 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004752 for (i=0; i < MNG_MAX_OBJECTS; i++)
4753 {
cristybb503372010-05-27 20:51:26 +00004754 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4755 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004756 }
4757 mng_info->exists[0]=MagickTrue;
4758 }
glennrp47b9dd52010-11-24 18:12:06 +00004759
cristy3ed852e2009-09-05 21:47:34 +00004760 first_mng_object=MagickTrue;
4761 mng_type=0;
4762#if defined(MNG_INSERT_LAYERS)
4763 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4764#endif
4765 default_frame_delay=0;
4766 default_frame_timeout=0;
4767 frame_delay=0;
4768 final_delay=1;
4769 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4770 object_id=0;
4771 skip_to_iend=MagickFalse;
4772 term_chunk_found=MagickFalse;
4773 mng_info->framing_mode=1;
4774#if defined(MNG_INSERT_LAYERS)
4775 mandatory_back=MagickFalse;
4776#endif
4777#if defined(MNG_INSERT_LAYERS)
4778 mng_background_color=image->background_color;
4779#endif
4780 default_fb=mng_info->frame;
4781 previous_fb=mng_info->frame;
4782 do
4783 {
4784 char
4785 type[MaxTextExtent];
4786
4787 if (LocaleCompare(image_info->magick,"MNG") == 0)
4788 {
4789 unsigned char
4790 *chunk;
4791
4792 /*
4793 Read a new chunk.
4794 */
4795 type[0]='\0';
4796 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4797 length=ReadBlobMSBLong(image);
4798 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4799
4800 if (logging != MagickFalse)
4801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004802 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4803 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004804
4805 if (length > PNG_UINT_31_MAX)
4806 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004807
cristy3ed852e2009-09-05 21:47:34 +00004808 if (count == 0)
4809 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004810
cristy3ed852e2009-09-05 21:47:34 +00004811 p=NULL;
4812 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004813
cristy3ed852e2009-09-05 21:47:34 +00004814 if (length)
4815 {
4816 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004817
cristy3ed852e2009-09-05 21:47:34 +00004818 if (chunk == (unsigned char *) NULL)
4819 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004820
cristybb503372010-05-27 20:51:26 +00004821 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004822 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004823
cristy3ed852e2009-09-05 21:47:34 +00004824 p=chunk;
4825 }
glennrp0fe50b42010-11-16 03:52:51 +00004826
cristy3ed852e2009-09-05 21:47:34 +00004827 (void) ReadBlobMSBLong(image); /* read crc word */
4828
4829#if !defined(JNG_SUPPORTED)
4830 if (memcmp(type,mng_JHDR,4) == 0)
4831 {
4832 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004833
cristy3ed852e2009-09-05 21:47:34 +00004834 if (mng_info->jhdr_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00004835 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004836 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004837
cristy3ed852e2009-09-05 21:47:34 +00004838 mng_info->jhdr_warning++;
4839 }
4840#endif
4841 if (memcmp(type,mng_DHDR,4) == 0)
4842 {
4843 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004844
cristy3ed852e2009-09-05 21:47:34 +00004845 if (mng_info->dhdr_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00004846 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004847 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004848
cristy3ed852e2009-09-05 21:47:34 +00004849 mng_info->dhdr_warning++;
4850 }
4851 if (memcmp(type,mng_MEND,4) == 0)
4852 break;
glennrp47b9dd52010-11-24 18:12:06 +00004853
cristy3ed852e2009-09-05 21:47:34 +00004854 if (skip_to_iend)
4855 {
4856 if (memcmp(type,mng_IEND,4) == 0)
4857 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004858
cristy3ed852e2009-09-05 21:47:34 +00004859 if (length)
4860 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004861
cristy3ed852e2009-09-05 21:47:34 +00004862 if (logging != MagickFalse)
4863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4864 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004865
cristy3ed852e2009-09-05 21:47:34 +00004866 continue;
4867 }
glennrp0fe50b42010-11-16 03:52:51 +00004868
cristy3ed852e2009-09-05 21:47:34 +00004869 if (memcmp(type,mng_MHDR,4) == 0)
4870 {
cristybb503372010-05-27 20:51:26 +00004871 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004872 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004873
cristybb503372010-05-27 20:51:26 +00004874 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004875 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004876
cristy3ed852e2009-09-05 21:47:34 +00004877 if (logging != MagickFalse)
4878 {
4879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004880 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004882 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004883 }
glennrp0fe50b42010-11-16 03:52:51 +00004884
cristy3ed852e2009-09-05 21:47:34 +00004885 p+=8;
cristy8182b072010-05-30 20:10:53 +00004886 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004887
cristy3ed852e2009-09-05 21:47:34 +00004888 if (mng_info->ticks_per_second == 0)
4889 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004890
cristy3ed852e2009-09-05 21:47:34 +00004891 else
4892 default_frame_delay=1UL*image->ticks_per_second/
4893 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004894
cristy3ed852e2009-09-05 21:47:34 +00004895 frame_delay=default_frame_delay;
4896 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004897
cristy3ed852e2009-09-05 21:47:34 +00004898 if (length > 16)
4899 {
4900 p+=16;
cristy8182b072010-05-30 20:10:53 +00004901 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004902 }
glennrp0fe50b42010-11-16 03:52:51 +00004903
cristy3ed852e2009-09-05 21:47:34 +00004904 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004905
cristy3ed852e2009-09-05 21:47:34 +00004906 if ((simplicity != 0) && ((simplicity | 11) == 11))
4907 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004908
cristy3ed852e2009-09-05 21:47:34 +00004909 if ((simplicity != 0) && ((simplicity | 9) == 9))
4910 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004911
cristy3ed852e2009-09-05 21:47:34 +00004912#if defined(MNG_INSERT_LAYERS)
4913 if (mng_type != 3)
4914 insert_layers=MagickTrue;
4915#endif
cristy4c08aed2011-07-01 19:47:50 +00004916 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004917 {
glennrp47b9dd52010-11-24 18:12:06 +00004918 /* Allocate next image structure. */
cristy9950d572011-10-01 18:22:35 +00004919 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004920
cristy3ed852e2009-09-05 21:47:34 +00004921 if (GetNextImageInList(image) == (Image *) NULL)
4922 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004923
cristy3ed852e2009-09-05 21:47:34 +00004924 image=SyncNextImageInList(image);
4925 mng_info->image=image;
4926 }
4927
4928 if ((mng_info->mng_width > 65535L) ||
4929 (mng_info->mng_height > 65535L))
4930 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004931
cristy3b6fd2e2011-05-20 12:53:50 +00004932 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004933 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004934 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004935
cristy3ed852e2009-09-05 21:47:34 +00004936 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004937 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004938 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004939 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004940 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004941
cristy3ed852e2009-09-05 21:47:34 +00004942 for (i=0; i < MNG_MAX_OBJECTS; i++)
4943 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004944
cristy3ed852e2009-09-05 21:47:34 +00004945 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4946 continue;
4947 }
4948
4949 if (memcmp(type,mng_TERM,4) == 0)
4950 {
4951 int
4952 repeat=0;
4953
4954
4955 if (length)
4956 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004957
cristy3ed852e2009-09-05 21:47:34 +00004958 if (repeat == 3)
4959 {
cristy8182b072010-05-30 20:10:53 +00004960 final_delay=(png_uint_32) mng_get_long(&p[2]);
4961 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004962
cristy3ed852e2009-09-05 21:47:34 +00004963 if (mng_iterations == PNG_UINT_31_MAX)
4964 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004965
cristy3ed852e2009-09-05 21:47:34 +00004966 image->iterations=mng_iterations;
4967 term_chunk_found=MagickTrue;
4968 }
glennrp0fe50b42010-11-16 03:52:51 +00004969
cristy3ed852e2009-09-05 21:47:34 +00004970 if (logging != MagickFalse)
4971 {
4972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4973 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004974
cristy3ed852e2009-09-05 21:47:34 +00004975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004976 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004977
cristy3ed852e2009-09-05 21:47:34 +00004978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004979 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004980 }
glennrp0fe50b42010-11-16 03:52:51 +00004981
cristy3ed852e2009-09-05 21:47:34 +00004982 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4983 continue;
4984 }
4985 if (memcmp(type,mng_DEFI,4) == 0)
4986 {
4987 if (mng_type == 3)
cristyc82a27b2011-10-21 01:07:16 +00004988 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004989 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4990 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004991
cristy3ed852e2009-09-05 21:47:34 +00004992 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004993
cristy3ed852e2009-09-05 21:47:34 +00004994 if (mng_type == 2 && object_id != 0)
cristyc82a27b2011-10-21 01:07:16 +00004995 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004996 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4997 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004998
cristy3ed852e2009-09-05 21:47:34 +00004999 if (object_id > MNG_MAX_OBJECTS)
5000 {
5001 /*
5002 Instead ofsuing a warning we should allocate a larger
5003 MngInfo structure and continue.
5004 */
cristyc82a27b2011-10-21 01:07:16 +00005005 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005006 CoderError,"object id too large","`%s'",image->filename);
5007 object_id=MNG_MAX_OBJECTS;
5008 }
glennrp0fe50b42010-11-16 03:52:51 +00005009
cristy3ed852e2009-09-05 21:47:34 +00005010 if (mng_info->exists[object_id])
5011 if (mng_info->frozen[object_id])
5012 {
5013 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
cristyc82a27b2011-10-21 01:07:16 +00005014 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005015 GetMagickModule(),CoderError,
5016 "DEFI cannot redefine a frozen MNG object","`%s'",
5017 image->filename);
5018 continue;
5019 }
glennrp0fe50b42010-11-16 03:52:51 +00005020
cristy3ed852e2009-09-05 21:47:34 +00005021 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005022
cristy3ed852e2009-09-05 21:47:34 +00005023 if (length > 2)
5024 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00005025
cristy3ed852e2009-09-05 21:47:34 +00005026 /*
5027 Extract object offset info.
5028 */
5029 if (length > 11)
5030 {
glennrp0fe50b42010-11-16 03:52:51 +00005031 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5032 (p[5] << 16) | (p[6] << 8) | p[7]);
5033
5034 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5035 (p[9] << 16) | (p[10] << 8) | p[11]);
5036
cristy3ed852e2009-09-05 21:47:34 +00005037 if (logging != MagickFalse)
5038 {
5039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005040 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005041 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005042
cristy3ed852e2009-09-05 21:47:34 +00005043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005044 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005045 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005046 }
5047 }
glennrp0fe50b42010-11-16 03:52:51 +00005048
cristy3ed852e2009-09-05 21:47:34 +00005049 /*
5050 Extract object clipping info.
5051 */
5052 if (length > 27)
5053 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5054 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005055
cristy3ed852e2009-09-05 21:47:34 +00005056 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5057 continue;
5058 }
5059 if (memcmp(type,mng_bKGD,4) == 0)
5060 {
5061 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005062
cristy3ed852e2009-09-05 21:47:34 +00005063 if (length > 5)
5064 {
5065 mng_info->mng_global_bkgd.red=
5066 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005067
cristy3ed852e2009-09-05 21:47:34 +00005068 mng_info->mng_global_bkgd.green=
5069 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005070
cristy3ed852e2009-09-05 21:47:34 +00005071 mng_info->mng_global_bkgd.blue=
5072 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005073
cristy3ed852e2009-09-05 21:47:34 +00005074 mng_info->have_global_bkgd=MagickTrue;
5075 }
glennrp0fe50b42010-11-16 03:52:51 +00005076
cristy3ed852e2009-09-05 21:47:34 +00005077 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5078 continue;
5079 }
5080 if (memcmp(type,mng_BACK,4) == 0)
5081 {
5082#if defined(MNG_INSERT_LAYERS)
5083 if (length > 6)
5084 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005085
cristy3ed852e2009-09-05 21:47:34 +00005086 else
5087 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005088
cristy3ed852e2009-09-05 21:47:34 +00005089 if (mandatory_back && length > 5)
5090 {
5091 mng_background_color.red=
5092 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005093
cristy3ed852e2009-09-05 21:47:34 +00005094 mng_background_color.green=
5095 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005096
cristy3ed852e2009-09-05 21:47:34 +00005097 mng_background_color.blue=
5098 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005099
cristy4c08aed2011-07-01 19:47:50 +00005100 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005101 }
glennrp0fe50b42010-11-16 03:52:51 +00005102
cristy3ed852e2009-09-05 21:47:34 +00005103#ifdef MNG_OBJECT_BUFFERS
5104 if (length > 8)
5105 mng_background_object=(p[7] << 8) | p[8];
5106#endif
5107#endif
5108 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5109 continue;
5110 }
glennrp47b9dd52010-11-24 18:12:06 +00005111
cristy3ed852e2009-09-05 21:47:34 +00005112 if (memcmp(type,mng_PLTE,4) == 0)
5113 {
glennrp47b9dd52010-11-24 18:12:06 +00005114 /* Read global PLTE. */
5115
cristy3ed852e2009-09-05 21:47:34 +00005116 if (length && (length < 769))
5117 {
5118 if (mng_info->global_plte == (png_colorp) NULL)
5119 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5120 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005121
cristybb503372010-05-27 20:51:26 +00005122 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005123 {
5124 mng_info->global_plte[i].red=p[3*i];
5125 mng_info->global_plte[i].green=p[3*i+1];
5126 mng_info->global_plte[i].blue=p[3*i+2];
5127 }
glennrp0fe50b42010-11-16 03:52:51 +00005128
cristy35ef8242010-06-03 16:24:13 +00005129 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005130 }
5131#ifdef MNG_LOOSE
5132 for ( ; i < 256; i++)
5133 {
5134 mng_info->global_plte[i].red=i;
5135 mng_info->global_plte[i].green=i;
5136 mng_info->global_plte[i].blue=i;
5137 }
glennrp0fe50b42010-11-16 03:52:51 +00005138
cristy3ed852e2009-09-05 21:47:34 +00005139 if (length)
5140 mng_info->global_plte_length=256;
5141#endif
5142 else
5143 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005144
cristy3ed852e2009-09-05 21:47:34 +00005145 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5146 continue;
5147 }
glennrp47b9dd52010-11-24 18:12:06 +00005148
cristy3ed852e2009-09-05 21:47:34 +00005149 if (memcmp(type,mng_tRNS,4) == 0)
5150 {
5151 /* read global tRNS */
5152
5153 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005154 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005155 mng_info->global_trns[i]=p[i];
5156
5157#ifdef MNG_LOOSE
5158 for ( ; i < 256; i++)
5159 mng_info->global_trns[i]=255;
5160#endif
cristy12560f32010-06-03 16:51:08 +00005161 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005162 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5163 continue;
5164 }
5165 if (memcmp(type,mng_gAMA,4) == 0)
5166 {
5167 if (length == 4)
5168 {
cristybb503372010-05-27 20:51:26 +00005169 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005170 igamma;
5171
cristy8182b072010-05-30 20:10:53 +00005172 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005173 mng_info->global_gamma=((float) igamma)*0.00001;
5174 mng_info->have_global_gama=MagickTrue;
5175 }
glennrp0fe50b42010-11-16 03:52:51 +00005176
cristy3ed852e2009-09-05 21:47:34 +00005177 else
5178 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005179
cristy3ed852e2009-09-05 21:47:34 +00005180 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5181 continue;
5182 }
5183
5184 if (memcmp(type,mng_cHRM,4) == 0)
5185 {
glennrp47b9dd52010-11-24 18:12:06 +00005186 /* Read global cHRM */
5187
cristy3ed852e2009-09-05 21:47:34 +00005188 if (length == 32)
5189 {
cristy8182b072010-05-30 20:10:53 +00005190 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5191 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5192 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005193 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005194 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005195 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005196 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005197 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005198 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005199 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005200 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005201 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005202 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005203 mng_info->have_global_chrm=MagickTrue;
5204 }
5205 else
5206 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005207
cristy3ed852e2009-09-05 21:47:34 +00005208 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5209 continue;
5210 }
glennrp47b9dd52010-11-24 18:12:06 +00005211
cristy3ed852e2009-09-05 21:47:34 +00005212 if (memcmp(type,mng_sRGB,4) == 0)
5213 {
5214 /*
5215 Read global sRGB.
5216 */
5217 if (length)
5218 {
glennrpe610a072010-08-05 17:08:46 +00005219 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005220 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005221 mng_info->have_global_srgb=MagickTrue;
5222 }
5223 else
5224 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005225
cristy3ed852e2009-09-05 21:47:34 +00005226 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5227 continue;
5228 }
glennrp47b9dd52010-11-24 18:12:06 +00005229
cristy3ed852e2009-09-05 21:47:34 +00005230 if (memcmp(type,mng_iCCP,4) == 0)
5231 {
glennrpfd05d622011-02-25 04:10:33 +00005232 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005233
5234 /*
5235 Read global iCCP.
5236 */
5237 if (length)
5238 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005239
cristy3ed852e2009-09-05 21:47:34 +00005240 continue;
5241 }
glennrp47b9dd52010-11-24 18:12:06 +00005242
cristy3ed852e2009-09-05 21:47:34 +00005243 if (memcmp(type,mng_FRAM,4) == 0)
5244 {
5245 if (mng_type == 3)
cristyc82a27b2011-10-21 01:07:16 +00005246 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005247 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5248 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005249
cristy3ed852e2009-09-05 21:47:34 +00005250 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5251 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005252
cristy3ed852e2009-09-05 21:47:34 +00005253 frame_delay=default_frame_delay;
5254 frame_timeout=default_frame_timeout;
5255 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005256
cristy3ed852e2009-09-05 21:47:34 +00005257 if (length)
5258 if (p[0])
5259 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005260
cristy3ed852e2009-09-05 21:47:34 +00005261 if (logging != MagickFalse)
5262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5263 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005264
cristy3ed852e2009-09-05 21:47:34 +00005265 if (length > 6)
5266 {
glennrp47b9dd52010-11-24 18:12:06 +00005267 /* Note the delay and frame clipping boundaries. */
5268
cristy3ed852e2009-09-05 21:47:34 +00005269 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005270
cristybb503372010-05-27 20:51:26 +00005271 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005272 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005273
cristy3ed852e2009-09-05 21:47:34 +00005274 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005275
cristybb503372010-05-27 20:51:26 +00005276 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005277 {
5278 int
5279 change_delay,
5280 change_timeout,
5281 change_clipping;
5282
5283 change_delay=(*p++);
5284 change_timeout=(*p++);
5285 change_clipping=(*p++);
5286 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005287
cristy3ed852e2009-09-05 21:47:34 +00005288 if (change_delay)
5289 {
cristy8182b072010-05-30 20:10:53 +00005290 frame_delay=1UL*image->ticks_per_second*
5291 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005292
cristy8182b072010-05-30 20:10:53 +00005293 if (mng_info->ticks_per_second != 0)
5294 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005295
glennrpbb010dd2010-06-01 13:07:15 +00005296 else
5297 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005298
cristy3ed852e2009-09-05 21:47:34 +00005299 if (change_delay == 2)
5300 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005301
cristy3ed852e2009-09-05 21:47:34 +00005302 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005303
cristy3ed852e2009-09-05 21:47:34 +00005304 if (logging != MagickFalse)
5305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005306 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005307 }
glennrp47b9dd52010-11-24 18:12:06 +00005308
cristy3ed852e2009-09-05 21:47:34 +00005309 if (change_timeout)
5310 {
glennrpbb010dd2010-06-01 13:07:15 +00005311 frame_timeout=1UL*image->ticks_per_second*
5312 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005313
glennrpbb010dd2010-06-01 13:07:15 +00005314 if (mng_info->ticks_per_second != 0)
5315 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005316
glennrpbb010dd2010-06-01 13:07:15 +00005317 else
5318 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005319
cristy3ed852e2009-09-05 21:47:34 +00005320 if (change_delay == 2)
5321 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005322
cristy3ed852e2009-09-05 21:47:34 +00005323 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005324
cristy3ed852e2009-09-05 21:47:34 +00005325 if (logging != MagickFalse)
5326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005327 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005328 }
glennrp47b9dd52010-11-24 18:12:06 +00005329
cristy3ed852e2009-09-05 21:47:34 +00005330 if (change_clipping)
5331 {
5332 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5333 p+=17;
5334 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005335
cristy3ed852e2009-09-05 21:47:34 +00005336 if (logging != MagickFalse)
5337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005338 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005339 (double) fb.left,(double) fb.right,(double) fb.top,
5340 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005341
cristy3ed852e2009-09-05 21:47:34 +00005342 if (change_clipping == 2)
5343 default_fb=fb;
5344 }
5345 }
5346 }
5347 mng_info->clip=fb;
5348 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005349
cristybb503372010-05-27 20:51:26 +00005350 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005351 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005352
cristybb503372010-05-27 20:51:26 +00005353 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005354 -mng_info->clip.top);
5355 /*
5356 Insert a background layer behind the frame if framing_mode is 4.
5357 */
5358#if defined(MNG_INSERT_LAYERS)
5359 if (logging != MagickFalse)
5360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005361 " subframe_width=%.20g, subframe_height=%.20g",(double)
5362 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005363
cristy3ed852e2009-09-05 21:47:34 +00005364 if (insert_layers && (mng_info->framing_mode == 4) &&
5365 (subframe_width) && (subframe_height))
5366 {
glennrp47b9dd52010-11-24 18:12:06 +00005367 /* Allocate next image structure. */
cristy4c08aed2011-07-01 19:47:50 +00005368 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005369 {
cristy9950d572011-10-01 18:22:35 +00005370 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005371
cristy3ed852e2009-09-05 21:47:34 +00005372 if (GetNextImageInList(image) == (Image *) NULL)
5373 {
5374 image=DestroyImageList(image);
5375 MngInfoFreeStruct(mng_info,&have_mng_structure);
5376 return((Image *) NULL);
5377 }
glennrp47b9dd52010-11-24 18:12:06 +00005378
cristy3ed852e2009-09-05 21:47:34 +00005379 image=SyncNextImageInList(image);
5380 }
glennrp0fe50b42010-11-16 03:52:51 +00005381
cristy3ed852e2009-09-05 21:47:34 +00005382 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005383
cristy3ed852e2009-09-05 21:47:34 +00005384 if (term_chunk_found)
5385 {
5386 image->start_loop=MagickTrue;
5387 image->iterations=mng_iterations;
5388 term_chunk_found=MagickFalse;
5389 }
glennrp0fe50b42010-11-16 03:52:51 +00005390
cristy3ed852e2009-09-05 21:47:34 +00005391 else
5392 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005393
cristy3ed852e2009-09-05 21:47:34 +00005394 image->columns=subframe_width;
5395 image->rows=subframe_height;
5396 image->page.width=subframe_width;
5397 image->page.height=subframe_height;
5398 image->page.x=mng_info->clip.left;
5399 image->page.y=mng_info->clip.top;
5400 image->background_color=mng_background_color;
5401 image->matte=MagickFalse;
5402 image->delay=0;
cristyea1a8aa2011-10-20 13:24:06 +00005403 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005404
cristy3ed852e2009-09-05 21:47:34 +00005405 if (logging != MagickFalse)
5406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005407 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005408 (double) mng_info->clip.left,(double) mng_info->clip.right,
5409 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005410 }
5411#endif
5412 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5413 continue;
5414 }
5415 if (memcmp(type,mng_CLIP,4) == 0)
5416 {
5417 unsigned int
5418 first_object,
5419 last_object;
5420
5421 /*
5422 Read CLIP.
5423 */
5424 first_object=(p[0] << 8) | p[1];
5425 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005426
cristy3ed852e2009-09-05 21:47:34 +00005427 for (i=(int) first_object; i <= (int) last_object; i++)
5428 {
5429 if (mng_info->exists[i] && !mng_info->frozen[i])
5430 {
5431 MngBox
5432 box;
5433
5434 box=mng_info->object_clip[i];
5435 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5436 }
5437 }
glennrp47b9dd52010-11-24 18:12:06 +00005438
cristy3ed852e2009-09-05 21:47:34 +00005439 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5440 continue;
5441 }
5442 if (memcmp(type,mng_SAVE,4) == 0)
5443 {
5444 for (i=1; i < MNG_MAX_OBJECTS; i++)
5445 if (mng_info->exists[i])
5446 {
5447 mng_info->frozen[i]=MagickTrue;
5448#ifdef MNG_OBJECT_BUFFERS
5449 if (mng_info->ob[i] != (MngBuffer *) NULL)
5450 mng_info->ob[i]->frozen=MagickTrue;
5451#endif
5452 }
glennrp0fe50b42010-11-16 03:52:51 +00005453
cristy3ed852e2009-09-05 21:47:34 +00005454 if (length)
5455 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005456
cristy3ed852e2009-09-05 21:47:34 +00005457 continue;
5458 }
5459
5460 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5461 {
glennrp47b9dd52010-11-24 18:12:06 +00005462 /* Read DISC or SEEK. */
5463
cristy3ed852e2009-09-05 21:47:34 +00005464 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5465 {
5466 for (i=1; i < MNG_MAX_OBJECTS; i++)
5467 MngInfoDiscardObject(mng_info,i);
5468 }
glennrp0fe50b42010-11-16 03:52:51 +00005469
cristy3ed852e2009-09-05 21:47:34 +00005470 else
5471 {
cristybb503372010-05-27 20:51:26 +00005472 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005473 j;
5474
cristybb503372010-05-27 20:51:26 +00005475 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005476 {
5477 i=p[j] << 8 | p[j+1];
5478 MngInfoDiscardObject(mng_info,i);
5479 }
5480 }
glennrp0fe50b42010-11-16 03:52:51 +00005481
cristy3ed852e2009-09-05 21:47:34 +00005482 if (length)
5483 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005484
cristy3ed852e2009-09-05 21:47:34 +00005485 continue;
5486 }
glennrp47b9dd52010-11-24 18:12:06 +00005487
cristy3ed852e2009-09-05 21:47:34 +00005488 if (memcmp(type,mng_MOVE,4) == 0)
5489 {
cristybb503372010-05-27 20:51:26 +00005490 size_t
cristy3ed852e2009-09-05 21:47:34 +00005491 first_object,
5492 last_object;
5493
glennrp47b9dd52010-11-24 18:12:06 +00005494 /* read MOVE */
5495
cristy3ed852e2009-09-05 21:47:34 +00005496 first_object=(p[0] << 8) | p[1];
5497 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005498 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005499 {
5500 if (mng_info->exists[i] && !mng_info->frozen[i])
5501 {
5502 MngPair
5503 new_pair;
5504
5505 MngPair
5506 old_pair;
5507
5508 old_pair.a=mng_info->x_off[i];
5509 old_pair.b=mng_info->y_off[i];
5510 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5511 mng_info->x_off[i]=new_pair.a;
5512 mng_info->y_off[i]=new_pair.b;
5513 }
5514 }
glennrp47b9dd52010-11-24 18:12:06 +00005515
cristy3ed852e2009-09-05 21:47:34 +00005516 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5517 continue;
5518 }
5519
5520 if (memcmp(type,mng_LOOP,4) == 0)
5521 {
cristybb503372010-05-27 20:51:26 +00005522 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005523 loop_level=chunk[0];
5524 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005525
5526 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005527 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005528
cristy3ed852e2009-09-05 21:47:34 +00005529 if (logging != MagickFalse)
5530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005531 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5532 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005533
cristy3ed852e2009-09-05 21:47:34 +00005534 if (loop_iters == 0)
5535 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005536
cristy3ed852e2009-09-05 21:47:34 +00005537 else
5538 {
5539 mng_info->loop_jump[loop_level]=TellBlob(image);
5540 mng_info->loop_count[loop_level]=loop_iters;
5541 }
glennrp0fe50b42010-11-16 03:52:51 +00005542
cristy3ed852e2009-09-05 21:47:34 +00005543 mng_info->loop_iteration[loop_level]=0;
5544 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5545 continue;
5546 }
glennrp47b9dd52010-11-24 18:12:06 +00005547
cristy3ed852e2009-09-05 21:47:34 +00005548 if (memcmp(type,mng_ENDL,4) == 0)
5549 {
5550 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005551
cristy3ed852e2009-09-05 21:47:34 +00005552 if (skipping_loop > 0)
5553 {
5554 if (skipping_loop == loop_level)
5555 {
5556 /*
5557 Found end of zero-iteration loop.
5558 */
5559 skipping_loop=(-1);
5560 mng_info->loop_active[loop_level]=0;
5561 }
5562 }
glennrp47b9dd52010-11-24 18:12:06 +00005563
cristy3ed852e2009-09-05 21:47:34 +00005564 else
5565 {
5566 if (mng_info->loop_active[loop_level] == 1)
5567 {
5568 mng_info->loop_count[loop_level]--;
5569 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005570
cristy3ed852e2009-09-05 21:47:34 +00005571 if (logging != MagickFalse)
5572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005573 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005574 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005575 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005576
cristy3ed852e2009-09-05 21:47:34 +00005577 if (mng_info->loop_count[loop_level] != 0)
5578 {
5579 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5580 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005581
cristy3ed852e2009-09-05 21:47:34 +00005582 if (offset < 0)
5583 ThrowReaderException(CorruptImageError,
5584 "ImproperImageHeader");
5585 }
glennrp47b9dd52010-11-24 18:12:06 +00005586
cristy3ed852e2009-09-05 21:47:34 +00005587 else
5588 {
5589 short
5590 last_level;
5591
5592 /*
5593 Finished loop.
5594 */
5595 mng_info->loop_active[loop_level]=0;
5596 last_level=(-1);
5597 for (i=0; i < loop_level; i++)
5598 if (mng_info->loop_active[i] == 1)
5599 last_level=(short) i;
5600 loop_level=last_level;
5601 }
5602 }
5603 }
glennrp47b9dd52010-11-24 18:12:06 +00005604
cristy3ed852e2009-09-05 21:47:34 +00005605 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5606 continue;
5607 }
glennrp47b9dd52010-11-24 18:12:06 +00005608
cristy3ed852e2009-09-05 21:47:34 +00005609 if (memcmp(type,mng_CLON,4) == 0)
5610 {
5611 if (mng_info->clon_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005612 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005613 CoderError,"CLON is not implemented yet","`%s'",
5614 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005615
cristy3ed852e2009-09-05 21:47:34 +00005616 mng_info->clon_warning++;
5617 }
glennrp47b9dd52010-11-24 18:12:06 +00005618
cristy3ed852e2009-09-05 21:47:34 +00005619 if (memcmp(type,mng_MAGN,4) == 0)
5620 {
5621 png_uint_16
5622 magn_first,
5623 magn_last,
5624 magn_mb,
5625 magn_ml,
5626 magn_mr,
5627 magn_mt,
5628 magn_mx,
5629 magn_my,
5630 magn_methx,
5631 magn_methy;
5632
5633 if (length > 1)
5634 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005635
cristy3ed852e2009-09-05 21:47:34 +00005636 else
5637 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005638
cristy3ed852e2009-09-05 21:47:34 +00005639 if (length > 3)
5640 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005641
cristy3ed852e2009-09-05 21:47:34 +00005642 else
5643 magn_last=magn_first;
5644#ifndef MNG_OBJECT_BUFFERS
5645 if (magn_first || magn_last)
5646 if (mng_info->magn_warning == 0)
5647 {
cristyc82a27b2011-10-21 01:07:16 +00005648 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005649 GetMagickModule(),CoderError,
5650 "MAGN is not implemented yet for nonzero objects",
5651 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005652
cristy3ed852e2009-09-05 21:47:34 +00005653 mng_info->magn_warning++;
5654 }
5655#endif
5656 if (length > 4)
5657 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005658
cristy3ed852e2009-09-05 21:47:34 +00005659 else
5660 magn_methx=0;
5661
5662 if (length > 6)
5663 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005664
cristy3ed852e2009-09-05 21:47:34 +00005665 else
5666 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005667
cristy3ed852e2009-09-05 21:47:34 +00005668 if (magn_mx == 0)
5669 magn_mx=1;
5670
5671 if (length > 8)
5672 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005673
cristy3ed852e2009-09-05 21:47:34 +00005674 else
5675 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005676
cristy3ed852e2009-09-05 21:47:34 +00005677 if (magn_my == 0)
5678 magn_my=1;
5679
5680 if (length > 10)
5681 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005682
cristy3ed852e2009-09-05 21:47:34 +00005683 else
5684 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005685
cristy3ed852e2009-09-05 21:47:34 +00005686 if (magn_ml == 0)
5687 magn_ml=1;
5688
5689 if (length > 12)
5690 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005691
cristy3ed852e2009-09-05 21:47:34 +00005692 else
5693 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005694
cristy3ed852e2009-09-05 21:47:34 +00005695 if (magn_mr == 0)
5696 magn_mr=1;
5697
5698 if (length > 14)
5699 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005700
cristy3ed852e2009-09-05 21:47:34 +00005701 else
5702 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005703
cristy3ed852e2009-09-05 21:47:34 +00005704 if (magn_mt == 0)
5705 magn_mt=1;
5706
5707 if (length > 16)
5708 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005709
cristy3ed852e2009-09-05 21:47:34 +00005710 else
5711 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005712
cristy3ed852e2009-09-05 21:47:34 +00005713 if (magn_mb == 0)
5714 magn_mb=1;
5715
5716 if (length > 17)
5717 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005718
cristy3ed852e2009-09-05 21:47:34 +00005719 else
5720 magn_methy=magn_methx;
5721
glennrp47b9dd52010-11-24 18:12:06 +00005722
cristy3ed852e2009-09-05 21:47:34 +00005723 if (magn_methx > 5 || magn_methy > 5)
5724 if (mng_info->magn_warning == 0)
5725 {
cristyc82a27b2011-10-21 01:07:16 +00005726 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005727 GetMagickModule(),CoderError,
5728 "Unknown MAGN method in MNG datastream","`%s'",
5729 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005730
cristy3ed852e2009-09-05 21:47:34 +00005731 mng_info->magn_warning++;
5732 }
5733#ifdef MNG_OBJECT_BUFFERS
5734 /* Magnify existing objects in the range magn_first to magn_last */
5735#endif
5736 if (magn_first == 0 || magn_last == 0)
5737 {
5738 /* Save the magnification factors for object 0 */
5739 mng_info->magn_mb=magn_mb;
5740 mng_info->magn_ml=magn_ml;
5741 mng_info->magn_mr=magn_mr;
5742 mng_info->magn_mt=magn_mt;
5743 mng_info->magn_mx=magn_mx;
5744 mng_info->magn_my=magn_my;
5745 mng_info->magn_methx=magn_methx;
5746 mng_info->magn_methy=magn_methy;
5747 }
5748 }
glennrp47b9dd52010-11-24 18:12:06 +00005749
cristy3ed852e2009-09-05 21:47:34 +00005750 if (memcmp(type,mng_PAST,4) == 0)
5751 {
5752 if (mng_info->past_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005753 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005754 CoderError,"PAST is not implemented yet","`%s'",
5755 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005756
cristy3ed852e2009-09-05 21:47:34 +00005757 mng_info->past_warning++;
5758 }
glennrp47b9dd52010-11-24 18:12:06 +00005759
cristy3ed852e2009-09-05 21:47:34 +00005760 if (memcmp(type,mng_SHOW,4) == 0)
5761 {
5762 if (mng_info->show_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005763 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005764 CoderError,"SHOW is not implemented yet","`%s'",
5765 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005766
cristy3ed852e2009-09-05 21:47:34 +00005767 mng_info->show_warning++;
5768 }
glennrp47b9dd52010-11-24 18:12:06 +00005769
cristy3ed852e2009-09-05 21:47:34 +00005770 if (memcmp(type,mng_sBIT,4) == 0)
5771 {
5772 if (length < 4)
5773 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005774
cristy3ed852e2009-09-05 21:47:34 +00005775 else
5776 {
5777 mng_info->global_sbit.gray=p[0];
5778 mng_info->global_sbit.red=p[0];
5779 mng_info->global_sbit.green=p[1];
5780 mng_info->global_sbit.blue=p[2];
5781 mng_info->global_sbit.alpha=p[3];
5782 mng_info->have_global_sbit=MagickTrue;
5783 }
5784 }
5785 if (memcmp(type,mng_pHYs,4) == 0)
5786 {
5787 if (length > 8)
5788 {
5789 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005790 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005791 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005792 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005793 mng_info->global_phys_unit_type=p[8];
5794 mng_info->have_global_phys=MagickTrue;
5795 }
glennrp47b9dd52010-11-24 18:12:06 +00005796
cristy3ed852e2009-09-05 21:47:34 +00005797 else
5798 mng_info->have_global_phys=MagickFalse;
5799 }
5800 if (memcmp(type,mng_pHYg,4) == 0)
5801 {
5802 if (mng_info->phyg_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005803 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005804 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005805
cristy3ed852e2009-09-05 21:47:34 +00005806 mng_info->phyg_warning++;
5807 }
5808 if (memcmp(type,mng_BASI,4) == 0)
5809 {
5810 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005811
cristy3ed852e2009-09-05 21:47:34 +00005812 if (mng_info->basi_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005813 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005814 CoderError,"BASI is not implemented yet","`%s'",
5815 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005816
cristy3ed852e2009-09-05 21:47:34 +00005817 mng_info->basi_warning++;
5818#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005819 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005820 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005821 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005822 (p[6] << 8) | p[7]);
5823 basi_color_type=p[8];
5824 basi_compression_method=p[9];
5825 basi_filter_type=p[10];
5826 basi_interlace_method=p[11];
5827 if (length > 11)
5828 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005829
cristy3ed852e2009-09-05 21:47:34 +00005830 else
5831 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005832
cristy3ed852e2009-09-05 21:47:34 +00005833 if (length > 13)
5834 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005835
cristy3ed852e2009-09-05 21:47:34 +00005836 else
5837 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005838
cristy3ed852e2009-09-05 21:47:34 +00005839 if (length > 15)
5840 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005841
cristy3ed852e2009-09-05 21:47:34 +00005842 else
5843 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005844
cristy3ed852e2009-09-05 21:47:34 +00005845 if (length > 17)
5846 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005847
cristy3ed852e2009-09-05 21:47:34 +00005848 else
5849 {
5850 if (basi_sample_depth == 16)
5851 basi_alpha=65535L;
5852 else
5853 basi_alpha=255;
5854 }
glennrp47b9dd52010-11-24 18:12:06 +00005855
cristy3ed852e2009-09-05 21:47:34 +00005856 if (length > 19)
5857 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005858
cristy3ed852e2009-09-05 21:47:34 +00005859 else
5860 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005861
cristy3ed852e2009-09-05 21:47:34 +00005862#endif
5863 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5864 continue;
5865 }
glennrp47b9dd52010-11-24 18:12:06 +00005866
cristy3ed852e2009-09-05 21:47:34 +00005867 if (memcmp(type,mng_IHDR,4)
5868#if defined(JNG_SUPPORTED)
5869 && memcmp(type,mng_JHDR,4)
5870#endif
5871 )
5872 {
5873 /* Not an IHDR or JHDR chunk */
5874 if (length)
5875 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005876
cristy3ed852e2009-09-05 21:47:34 +00005877 continue;
5878 }
5879/* Process IHDR */
5880 if (logging != MagickFalse)
5881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5882 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005883
cristy3ed852e2009-09-05 21:47:34 +00005884 mng_info->exists[object_id]=MagickTrue;
5885 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005886
cristy3ed852e2009-09-05 21:47:34 +00005887 if (mng_info->invisible[object_id])
5888 {
5889 if (logging != MagickFalse)
5890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5891 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005892
cristy3ed852e2009-09-05 21:47:34 +00005893 skip_to_iend=MagickTrue;
5894 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5895 continue;
5896 }
5897#if defined(MNG_INSERT_LAYERS)
5898 if (length < 8)
5899 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005900
cristy8182b072010-05-30 20:10:53 +00005901 image_width=(size_t) mng_get_long(p);
5902 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005903#endif
5904 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5905
5906 /*
5907 Insert a transparent background layer behind the entire animation
5908 if it is not full screen.
5909 */
5910#if defined(MNG_INSERT_LAYERS)
5911 if (insert_layers && mng_type && first_mng_object)
5912 {
5913 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5914 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005915 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005916 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005917 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005918 {
cristy4c08aed2011-07-01 19:47:50 +00005919 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005920 {
5921 /*
5922 Allocate next image structure.
5923 */
cristy9950d572011-10-01 18:22:35 +00005924 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005925
cristy3ed852e2009-09-05 21:47:34 +00005926 if (GetNextImageInList(image) == (Image *) NULL)
5927 {
5928 image=DestroyImageList(image);
5929 MngInfoFreeStruct(mng_info,&have_mng_structure);
5930 return((Image *) NULL);
5931 }
glennrp47b9dd52010-11-24 18:12:06 +00005932
cristy3ed852e2009-09-05 21:47:34 +00005933 image=SyncNextImageInList(image);
5934 }
5935 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005936
cristy3ed852e2009-09-05 21:47:34 +00005937 if (term_chunk_found)
5938 {
5939 image->start_loop=MagickTrue;
5940 image->iterations=mng_iterations;
5941 term_chunk_found=MagickFalse;
5942 }
glennrp47b9dd52010-11-24 18:12:06 +00005943
cristy3ed852e2009-09-05 21:47:34 +00005944 else
5945 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005946
5947 /* Make a background rectangle. */
5948
cristy3ed852e2009-09-05 21:47:34 +00005949 image->delay=0;
5950 image->columns=mng_info->mng_width;
5951 image->rows=mng_info->mng_height;
5952 image->page.width=mng_info->mng_width;
5953 image->page.height=mng_info->mng_height;
5954 image->page.x=0;
5955 image->page.y=0;
5956 image->background_color=mng_background_color;
cristyea1a8aa2011-10-20 13:24:06 +00005957 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005958 if (logging != MagickFalse)
5959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005960 " Inserted transparent background layer, W=%.20g, H=%.20g",
5961 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005962 }
5963 }
5964 /*
5965 Insert a background layer behind the upcoming image if
5966 framing_mode is 3, and we haven't already inserted one.
5967 */
5968 if (insert_layers && (mng_info->framing_mode == 3) &&
5969 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5970 (simplicity & 0x08)))
5971 {
cristy4c08aed2011-07-01 19:47:50 +00005972 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005973 {
5974 /*
5975 Allocate next image structure.
5976 */
cristy9950d572011-10-01 18:22:35 +00005977 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005978
cristy3ed852e2009-09-05 21:47:34 +00005979 if (GetNextImageInList(image) == (Image *) NULL)
5980 {
5981 image=DestroyImageList(image);
5982 MngInfoFreeStruct(mng_info,&have_mng_structure);
5983 return((Image *) NULL);
5984 }
glennrp47b9dd52010-11-24 18:12:06 +00005985
cristy3ed852e2009-09-05 21:47:34 +00005986 image=SyncNextImageInList(image);
5987 }
glennrp0fe50b42010-11-16 03:52:51 +00005988
cristy3ed852e2009-09-05 21:47:34 +00005989 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005990
cristy3ed852e2009-09-05 21:47:34 +00005991 if (term_chunk_found)
5992 {
5993 image->start_loop=MagickTrue;
5994 image->iterations=mng_iterations;
5995 term_chunk_found=MagickFalse;
5996 }
glennrp0fe50b42010-11-16 03:52:51 +00005997
cristy3ed852e2009-09-05 21:47:34 +00005998 else
5999 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006000
cristy3ed852e2009-09-05 21:47:34 +00006001 image->delay=0;
6002 image->columns=subframe_width;
6003 image->rows=subframe_height;
6004 image->page.width=subframe_width;
6005 image->page.height=subframe_height;
6006 image->page.x=mng_info->clip.left;
6007 image->page.y=mng_info->clip.top;
6008 image->background_color=mng_background_color;
6009 image->matte=MagickFalse;
cristyea1a8aa2011-10-20 13:24:06 +00006010 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006011
cristy3ed852e2009-09-05 21:47:34 +00006012 if (logging != MagickFalse)
6013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00006014 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00006015 (double) mng_info->clip.left,(double) mng_info->clip.right,
6016 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00006017 }
6018#endif /* MNG_INSERT_LAYERS */
6019 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006020
cristy4c08aed2011-07-01 19:47:50 +00006021 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006022 {
6023 /*
6024 Allocate next image structure.
6025 */
cristy9950d572011-10-01 18:22:35 +00006026 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006027
cristy3ed852e2009-09-05 21:47:34 +00006028 if (GetNextImageInList(image) == (Image *) NULL)
6029 {
6030 image=DestroyImageList(image);
6031 MngInfoFreeStruct(mng_info,&have_mng_structure);
6032 return((Image *) NULL);
6033 }
glennrp47b9dd52010-11-24 18:12:06 +00006034
cristy3ed852e2009-09-05 21:47:34 +00006035 image=SyncNextImageInList(image);
6036 }
6037 mng_info->image=image;
6038 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6039 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006040
cristy3ed852e2009-09-05 21:47:34 +00006041 if (status == MagickFalse)
6042 break;
glennrp0fe50b42010-11-16 03:52:51 +00006043
cristy3ed852e2009-09-05 21:47:34 +00006044 if (term_chunk_found)
6045 {
6046 image->start_loop=MagickTrue;
6047 term_chunk_found=MagickFalse;
6048 }
glennrp0fe50b42010-11-16 03:52:51 +00006049
cristy3ed852e2009-09-05 21:47:34 +00006050 else
6051 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006052
cristy3ed852e2009-09-05 21:47:34 +00006053 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6054 {
6055 image->delay=frame_delay;
6056 frame_delay=default_frame_delay;
6057 }
glennrp0fe50b42010-11-16 03:52:51 +00006058
cristy3ed852e2009-09-05 21:47:34 +00006059 else
6060 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006061
cristy3ed852e2009-09-05 21:47:34 +00006062 image->page.width=mng_info->mng_width;
6063 image->page.height=mng_info->mng_height;
6064 image->page.x=mng_info->x_off[object_id];
6065 image->page.y=mng_info->y_off[object_id];
6066 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006067
cristy3ed852e2009-09-05 21:47:34 +00006068 /*
6069 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6070 */
glennrp47b9dd52010-11-24 18:12:06 +00006071
cristy3ed852e2009-09-05 21:47:34 +00006072 if (logging != MagickFalse)
6073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6074 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6075 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006076
cristybb503372010-05-27 20:51:26 +00006077 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006078
cristy3ed852e2009-09-05 21:47:34 +00006079 if (offset < 0)
6080 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6081 }
6082
6083 previous=image;
6084 mng_info->image=image;
6085 mng_info->mng_type=mng_type;
6086 mng_info->object_id=object_id;
6087
6088 if (memcmp(type,mng_IHDR,4) == 0)
6089 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006090
cristy3ed852e2009-09-05 21:47:34 +00006091#if defined(JNG_SUPPORTED)
6092 else
6093 image=ReadOneJNGImage(mng_info,image_info,exception);
6094#endif
6095
6096 if (image == (Image *) NULL)
6097 {
6098 if (IsImageObject(previous) != MagickFalse)
6099 {
6100 (void) DestroyImageList(previous);
6101 (void) CloseBlob(previous);
6102 }
glennrp47b9dd52010-11-24 18:12:06 +00006103
cristy3ed852e2009-09-05 21:47:34 +00006104 MngInfoFreeStruct(mng_info,&have_mng_structure);
6105 return((Image *) NULL);
6106 }
glennrp0fe50b42010-11-16 03:52:51 +00006107
cristy3ed852e2009-09-05 21:47:34 +00006108 if (image->columns == 0 || image->rows == 0)
6109 {
6110 (void) CloseBlob(image);
6111 image=DestroyImageList(image);
6112 MngInfoFreeStruct(mng_info,&have_mng_structure);
6113 return((Image *) NULL);
6114 }
glennrp0fe50b42010-11-16 03:52:51 +00006115
cristy3ed852e2009-09-05 21:47:34 +00006116 mng_info->image=image;
6117
6118 if (mng_type)
6119 {
6120 MngBox
6121 crop_box;
6122
6123 if (mng_info->magn_methx || mng_info->magn_methy)
6124 {
6125 png_uint_32
6126 magnified_height,
6127 magnified_width;
6128
6129 if (logging != MagickFalse)
6130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6131 " Processing MNG MAGN chunk");
6132
6133 if (mng_info->magn_methx == 1)
6134 {
6135 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006136
cristy3ed852e2009-09-05 21:47:34 +00006137 if (image->columns > 1)
6138 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006139
cristy3ed852e2009-09-05 21:47:34 +00006140 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006141 magnified_width += (png_uint_32)
6142 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006143 }
glennrp47b9dd52010-11-24 18:12:06 +00006144
cristy3ed852e2009-09-05 21:47:34 +00006145 else
6146 {
cristy4e5bc842010-06-09 13:56:01 +00006147 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006148
cristy3ed852e2009-09-05 21:47:34 +00006149 if (image->columns > 1)
6150 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006151
cristy3ed852e2009-09-05 21:47:34 +00006152 if (image->columns > 2)
6153 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006154
cristy3ed852e2009-09-05 21:47:34 +00006155 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006156 magnified_width += (png_uint_32)
6157 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006158 }
glennrp47b9dd52010-11-24 18:12:06 +00006159
cristy3ed852e2009-09-05 21:47:34 +00006160 if (mng_info->magn_methy == 1)
6161 {
6162 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006163
cristy3ed852e2009-09-05 21:47:34 +00006164 if (image->rows > 1)
6165 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006166
cristy3ed852e2009-09-05 21:47:34 +00006167 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006168 magnified_height += (png_uint_32)
6169 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006170 }
glennrp47b9dd52010-11-24 18:12:06 +00006171
cristy3ed852e2009-09-05 21:47:34 +00006172 else
6173 {
cristy4e5bc842010-06-09 13:56:01 +00006174 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006175
cristy3ed852e2009-09-05 21:47:34 +00006176 if (image->rows > 1)
6177 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006178
cristy3ed852e2009-09-05 21:47:34 +00006179 if (image->rows > 2)
6180 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006181
cristy3ed852e2009-09-05 21:47:34 +00006182 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006183 magnified_height += (png_uint_32)
6184 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006185 }
glennrp47b9dd52010-11-24 18:12:06 +00006186
cristy3ed852e2009-09-05 21:47:34 +00006187 if (magnified_height > image->rows ||
6188 magnified_width > image->columns)
6189 {
6190 Image
6191 *large_image;
6192
6193 int
6194 yy;
6195
cristy4c08aed2011-07-01 19:47:50 +00006196 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006197 *next,
6198 *prev;
6199
6200 png_uint_16
6201 magn_methx,
6202 magn_methy;
6203
cristy4c08aed2011-07-01 19:47:50 +00006204 ssize_t
6205 m,
6206 y;
6207
6208 register Quantum
6209 *n,
6210 *q;
6211
6212 register ssize_t
6213 x;
6214
glennrp47b9dd52010-11-24 18:12:06 +00006215 /* Allocate next image structure. */
6216
cristy3ed852e2009-09-05 21:47:34 +00006217 if (logging != MagickFalse)
6218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6219 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006220
cristy9950d572011-10-01 18:22:35 +00006221 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006222
cristy3ed852e2009-09-05 21:47:34 +00006223 if (GetNextImageInList(image) == (Image *) NULL)
6224 {
6225 image=DestroyImageList(image);
6226 MngInfoFreeStruct(mng_info,&have_mng_structure);
6227 return((Image *) NULL);
6228 }
6229
6230 large_image=SyncNextImageInList(image);
6231
6232 large_image->columns=magnified_width;
6233 large_image->rows=magnified_height;
6234
6235 magn_methx=mng_info->magn_methx;
6236 magn_methy=mng_info->magn_methy;
6237
glennrp3faa9a32011-04-23 14:00:25 +00006238#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006239#define QM unsigned short
6240 if (magn_methx != 1 || magn_methy != 1)
6241 {
6242 /*
6243 Scale pixels to unsigned shorts to prevent
6244 overflow of intermediate values of interpolations
6245 */
cristybb503372010-05-27 20:51:26 +00006246 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006247 {
6248 q=GetAuthenticPixels(image,0,y,image->columns,1,
6249 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006250
cristybb503372010-05-27 20:51:26 +00006251 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006252 {
cristy4c08aed2011-07-01 19:47:50 +00006253 SetPixelRed(image,ScaleQuantumToShort(
6254 GetPixelRed(image,q)),q);
6255 SetPixelGreen(image,ScaleQuantumToShort(
6256 GetPixelGreen(image,q)),q);
6257 SetPixelBlue(image,ScaleQuantumToShort(
6258 GetPixelBlue(image,q)),q);
6259 SetPixelAlpha(image,ScaleQuantumToShort(
6260 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006261 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006262 }
glennrp47b9dd52010-11-24 18:12:06 +00006263
cristy3ed852e2009-09-05 21:47:34 +00006264 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6265 break;
6266 }
6267 }
6268#else
6269#define QM Quantum
6270#endif
6271
6272 if (image->matte != MagickFalse)
cristyea1a8aa2011-10-20 13:24:06 +00006273 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006274
cristy3ed852e2009-09-05 21:47:34 +00006275 else
6276 {
cristy4c08aed2011-07-01 19:47:50 +00006277 large_image->background_color.alpha=OpaqueAlpha;
cristyea1a8aa2011-10-20 13:24:06 +00006278 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006279
cristy3ed852e2009-09-05 21:47:34 +00006280 if (magn_methx == 4)
6281 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006282
cristy3ed852e2009-09-05 21:47:34 +00006283 if (magn_methx == 5)
6284 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006285
cristy3ed852e2009-09-05 21:47:34 +00006286 if (magn_methy == 4)
6287 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006288
cristy3ed852e2009-09-05 21:47:34 +00006289 if (magn_methy == 5)
6290 magn_methy=3;
6291 }
6292
6293 /* magnify the rows into the right side of the large image */
6294
6295 if (logging != MagickFalse)
6296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006297 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006298 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006299 yy=0;
cristy8a20fa02011-12-27 15:54:31 +00006300 length=(size_t) image->columns*GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00006301 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6302 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006303
cristy4c08aed2011-07-01 19:47:50 +00006304 if ((prev == (Quantum *) NULL) ||
6305 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006306 {
6307 image=DestroyImageList(image);
6308 MngInfoFreeStruct(mng_info,&have_mng_structure);
6309 ThrowReaderException(ResourceLimitError,
6310 "MemoryAllocationFailed");
6311 }
glennrp47b9dd52010-11-24 18:12:06 +00006312
cristy3ed852e2009-09-05 21:47:34 +00006313 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6314 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006315
cristybb503372010-05-27 20:51:26 +00006316 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006317 {
6318 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006319 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006320
cristybb503372010-05-27 20:51:26 +00006321 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6322 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006323
cristybb503372010-05-27 20:51:26 +00006324 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6325 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006326
cristybb503372010-05-27 20:51:26 +00006327 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006328 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006329
cristy3ed852e2009-09-05 21:47:34 +00006330 else
cristybb503372010-05-27 20:51:26 +00006331 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006332
cristy3ed852e2009-09-05 21:47:34 +00006333 n=prev;
6334 prev=next;
6335 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006336
cristybb503372010-05-27 20:51:26 +00006337 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006338 {
6339 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6340 exception);
6341 (void) CopyMagickMemory(next,n,length);
6342 }
glennrp47b9dd52010-11-24 18:12:06 +00006343
cristy3ed852e2009-09-05 21:47:34 +00006344 for (i=0; i < m; i++, yy++)
6345 {
cristy4c08aed2011-07-01 19:47:50 +00006346 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006347 *pixels;
6348
cristybb503372010-05-27 20:51:26 +00006349 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006350 pixels=prev;
6351 n=next;
6352 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006353 1,exception);
cristy97707062011-12-27 18:25:00 +00006354 q+=(large_image->columns-image->columns)*
6355 GetPixelChannels(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006356
cristybb503372010-05-27 20:51:26 +00006357 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006358 {
glennrpfd05d622011-02-25 04:10:33 +00006359 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006360 /*
6361 if (image->storage_class == PseudoClass)
6362 {
6363 }
6364 */
6365
6366 if (magn_methy <= 1)
6367 {
glennrpbb4f99d2011-05-22 11:13:17 +00006368 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006369 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
glennrp847370c2011-07-05 17:37:15 +00006370 SetPixelGreen(large_image,GetPixelGreen(image,
6371 pixels),q);
6372 SetPixelBlue(large_image,GetPixelBlue(image,
6373 pixels),q);
6374 SetPixelAlpha(large_image,GetPixelAlpha(image,
6375 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006376 }
glennrp47b9dd52010-11-24 18:12:06 +00006377
cristy3ed852e2009-09-05 21:47:34 +00006378 else if (magn_methy == 2 || magn_methy == 4)
6379 {
6380 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006381 {
glennrp847370c2011-07-05 17:37:15 +00006382 SetPixelRed(large_image,GetPixelRed(image,
6383 pixels),q);
6384 SetPixelGreen(large_image,GetPixelGreen(image,
6385 pixels),q);
6386 SetPixelBlue(large_image,GetPixelBlue(image,
6387 pixels),q);
6388 SetPixelAlpha(large_image,GetPixelAlpha(image,
6389 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006390 }
glennrp47b9dd52010-11-24 18:12:06 +00006391
cristy3ed852e2009-09-05 21:47:34 +00006392 else
6393 {
6394 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006395 SetPixelRed(large_image,((QM) (((ssize_t)
6396 (2*i*(GetPixelRed(image,n)
6397 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006398 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006399 +GetPixelRed(image,pixels)))),q);
6400 SetPixelGreen(large_image,((QM) (((ssize_t)
6401 (2*i*(GetPixelGreen(image,n)
6402 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006403 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006404 +GetPixelGreen(image,pixels)))),q);
6405 SetPixelBlue(large_image,((QM) (((ssize_t)
6406 (2*i*(GetPixelBlue(image,n)
6407 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006408 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006409 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006410
cristy3ed852e2009-09-05 21:47:34 +00006411 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006412 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6413 (2*i*(GetPixelAlpha(image,n)
6414 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006415 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006416 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006417 }
glennrp47b9dd52010-11-24 18:12:06 +00006418
cristy3ed852e2009-09-05 21:47:34 +00006419 if (magn_methy == 4)
6420 {
6421 /* Replicate nearest */
6422 if (i <= ((m+1) << 1))
glennrp847370c2011-07-05 17:37:15 +00006423 SetPixelAlpha(large_image,GetPixelAlpha(image,
6424 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006425 else
glennrp847370c2011-07-05 17:37:15 +00006426 SetPixelAlpha(large_image,GetPixelAlpha(image,
6427 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006428 }
6429 }
glennrp47b9dd52010-11-24 18:12:06 +00006430
cristy3ed852e2009-09-05 21:47:34 +00006431 else /* if (magn_methy == 3 || magn_methy == 5) */
6432 {
6433 /* Replicate nearest */
6434 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006435 {
glennrp847370c2011-07-05 17:37:15 +00006436 SetPixelRed(large_image,GetPixelRed(image,
6437 pixels),q);
6438 SetPixelGreen(large_image,GetPixelGreen(image,
6439 pixels),q);
6440 SetPixelBlue(large_image,GetPixelBlue(image,
6441 pixels),q);
6442 SetPixelAlpha(large_image,GetPixelAlpha(image,
6443 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006444 }
glennrp47b9dd52010-11-24 18:12:06 +00006445
cristy3ed852e2009-09-05 21:47:34 +00006446 else
glennrpbb4f99d2011-05-22 11:13:17 +00006447 {
cristy4c08aed2011-07-01 19:47:50 +00006448 SetPixelRed(large_image,GetPixelRed(image,n),q);
glennrp847370c2011-07-05 17:37:15 +00006449 SetPixelGreen(large_image,GetPixelGreen(image,n),
6450 q);
6451 SetPixelBlue(large_image,GetPixelBlue(image,n),
6452 q);
6453 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6454 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006455 }
glennrp47b9dd52010-11-24 18:12:06 +00006456
cristy3ed852e2009-09-05 21:47:34 +00006457 if (magn_methy == 5)
6458 {
cristy4c08aed2011-07-01 19:47:50 +00006459 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6460 (GetPixelAlpha(image,n)
6461 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006462 +m))/((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006463 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006464 }
6465 }
cristyed231572011-07-14 02:18:59 +00006466 n+=GetPixelChannels(image);
6467 q+=GetPixelChannels(large_image);
6468 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006469 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006470
cristy3ed852e2009-09-05 21:47:34 +00006471 if (SyncAuthenticPixels(large_image,exception) == 0)
6472 break;
glennrp47b9dd52010-11-24 18:12:06 +00006473
cristy3ed852e2009-09-05 21:47:34 +00006474 } /* i */
6475 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006476
cristy4c08aed2011-07-01 19:47:50 +00006477 prev=(Quantum *) RelinquishMagickMemory(prev);
6478 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006479
6480 length=image->columns;
6481
6482 if (logging != MagickFalse)
6483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6484 " Delete original image");
6485
6486 DeleteImageFromList(&image);
6487
6488 image=large_image;
6489
6490 mng_info->image=image;
6491
6492 /* magnify the columns */
6493 if (logging != MagickFalse)
6494 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006495 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006496
cristybb503372010-05-27 20:51:26 +00006497 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006498 {
cristy4c08aed2011-07-01 19:47:50 +00006499 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006500 *pixels;
6501
6502 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristyed231572011-07-14 02:18:59 +00006503 pixels=q+(image->columns-length)*GetPixelChannels(image);
6504 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006505
cristybb503372010-05-27 20:51:26 +00006506 for (x=(ssize_t) (image->columns-length);
6507 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006508 {
cristyed231572011-07-14 02:18:59 +00006509 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006510
cristybb503372010-05-27 20:51:26 +00006511 if (x == (ssize_t) (image->columns-length))
6512 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006513
cristybb503372010-05-27 20:51:26 +00006514 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6515 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006516
cristybb503372010-05-27 20:51:26 +00006517 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6518 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006519
cristybb503372010-05-27 20:51:26 +00006520 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006521 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006522
cristy3ed852e2009-09-05 21:47:34 +00006523 else
cristybb503372010-05-27 20:51:26 +00006524 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006525
cristy3ed852e2009-09-05 21:47:34 +00006526 for (i=0; i < m; i++)
6527 {
6528 if (magn_methx <= 1)
6529 {
6530 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006531 SetPixelRed(image,GetPixelRed(image,pixels),q);
6532 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6533 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6534 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006535 }
glennrp47b9dd52010-11-24 18:12:06 +00006536
cristy3ed852e2009-09-05 21:47:34 +00006537 else if (magn_methx == 2 || magn_methx == 4)
6538 {
6539 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006540 {
cristy4c08aed2011-07-01 19:47:50 +00006541 SetPixelRed(image,GetPixelRed(image,pixels),q);
6542 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6543 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6544 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006545 }
glennrp47b9dd52010-11-24 18:12:06 +00006546
cristyed231572011-07-14 02:18:59 +00006547 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006548 else
6549 {
6550 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006551 SetPixelRed(image,(QM) ((2*i*(
6552 GetPixelRed(image,n)
6553 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006554 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006555 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006556
cristy4c08aed2011-07-01 19:47:50 +00006557 SetPixelGreen(image,(QM) ((2*i*(
6558 GetPixelGreen(image,n)
6559 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006560 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006561 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006562
cristy4c08aed2011-07-01 19:47:50 +00006563 SetPixelBlue(image,(QM) ((2*i*(
6564 GetPixelBlue(image,n)
6565 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006566 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006567 GetPixelBlue(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006568 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006569 SetPixelAlpha(image,(QM) ((2*i*(
6570 GetPixelAlpha(image,n)
6571 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006572 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006573 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006574 }
glennrp47b9dd52010-11-24 18:12:06 +00006575
cristy3ed852e2009-09-05 21:47:34 +00006576 if (magn_methx == 4)
6577 {
6578 /* Replicate nearest */
6579 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006580 {
cristy4c08aed2011-07-01 19:47:50 +00006581 SetPixelAlpha(image,
6582 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006583 }
cristy3ed852e2009-09-05 21:47:34 +00006584 else
glennrpbb4f99d2011-05-22 11:13:17 +00006585 {
cristy4c08aed2011-07-01 19:47:50 +00006586 SetPixelAlpha(image,
6587 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006588 }
cristy3ed852e2009-09-05 21:47:34 +00006589 }
6590 }
glennrp47b9dd52010-11-24 18:12:06 +00006591
cristy3ed852e2009-09-05 21:47:34 +00006592 else /* if (magn_methx == 3 || magn_methx == 5) */
6593 {
6594 /* Replicate nearest */
6595 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006596 {
cristy4c08aed2011-07-01 19:47:50 +00006597 SetPixelRed(image,GetPixelRed(image,pixels),q);
6598 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6599 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6600 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006601 }
glennrp47b9dd52010-11-24 18:12:06 +00006602
cristy3ed852e2009-09-05 21:47:34 +00006603 else
glennrpbb4f99d2011-05-22 11:13:17 +00006604 {
cristy4c08aed2011-07-01 19:47:50 +00006605 SetPixelRed(image,GetPixelRed(image,n),q);
6606 SetPixelGreen(image,GetPixelGreen(image,n),q);
6607 SetPixelBlue(image,GetPixelBlue(image,n),q);
6608 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006609 }
glennrp47b9dd52010-11-24 18:12:06 +00006610
cristy3ed852e2009-09-05 21:47:34 +00006611 if (magn_methx == 5)
6612 {
6613 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006614 SetPixelAlpha(image,
6615 (QM) ((2*i*( GetPixelAlpha(image,n)
6616 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006617 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006618 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006619 }
6620 }
cristyed231572011-07-14 02:18:59 +00006621 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006622 }
cristyed231572011-07-14 02:18:59 +00006623 n+=GetPixelChannels(image);
6624 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006625 }
glennrp47b9dd52010-11-24 18:12:06 +00006626
cristy3ed852e2009-09-05 21:47:34 +00006627 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6628 break;
6629 }
glennrp3faa9a32011-04-23 14:00:25 +00006630#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006631 if (magn_methx != 1 || magn_methy != 1)
6632 {
6633 /*
6634 Rescale pixels to Quantum
6635 */
cristybb503372010-05-27 20:51:26 +00006636 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006637 {
6638 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006639
cristybb503372010-05-27 20:51:26 +00006640 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006641 {
cristy4c08aed2011-07-01 19:47:50 +00006642 SetPixelRed(image,ScaleShortToQuantum(
6643 GetPixelRed(image,q)),q);
6644 SetPixelGreen(image,ScaleShortToQuantum(
6645 GetPixelGreen(image,q)),q);
6646 SetPixelBlue(image,ScaleShortToQuantum(
6647 GetPixelBlue(image,q)),q);
6648 SetPixelAlpha(image,ScaleShortToQuantum(
6649 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006650 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006651 }
glennrp47b9dd52010-11-24 18:12:06 +00006652
cristy3ed852e2009-09-05 21:47:34 +00006653 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6654 break;
6655 }
6656 }
6657#endif
6658 if (logging != MagickFalse)
6659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6660 " Finished MAGN processing");
6661 }
6662 }
6663
6664 /*
6665 Crop_box is with respect to the upper left corner of the MNG.
6666 */
6667 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6668 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6669 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6670 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6671 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6672 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6673 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6674 if ((crop_box.left != (mng_info->image_box.left
6675 +mng_info->x_off[object_id])) ||
6676 (crop_box.right != (mng_info->image_box.right
6677 +mng_info->x_off[object_id])) ||
6678 (crop_box.top != (mng_info->image_box.top
6679 +mng_info->y_off[object_id])) ||
6680 (crop_box.bottom != (mng_info->image_box.bottom
6681 +mng_info->y_off[object_id])))
6682 {
6683 if (logging != MagickFalse)
6684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6685 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006686
cristy3ed852e2009-09-05 21:47:34 +00006687 if ((crop_box.left < crop_box.right) &&
6688 (crop_box.top < crop_box.bottom))
6689 {
6690 Image
6691 *im;
6692
6693 RectangleInfo
6694 crop_info;
6695
6696 /*
6697 Crop_info is with respect to the upper left corner of
6698 the image.
6699 */
6700 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6701 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006702 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6703 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006704 image->page.width=image->columns;
6705 image->page.height=image->rows;
6706 image->page.x=0;
6707 image->page.y=0;
6708 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006709
cristy3ed852e2009-09-05 21:47:34 +00006710 if (im != (Image *) NULL)
6711 {
6712 image->columns=im->columns;
6713 image->rows=im->rows;
6714 im=DestroyImage(im);
6715 image->page.width=image->columns;
6716 image->page.height=image->rows;
6717 image->page.x=crop_box.left;
6718 image->page.y=crop_box.top;
6719 }
6720 }
glennrp47b9dd52010-11-24 18:12:06 +00006721
cristy3ed852e2009-09-05 21:47:34 +00006722 else
6723 {
6724 /*
6725 No pixels in crop area. The MNG spec still requires
6726 a layer, though, so make a single transparent pixel in
6727 the top left corner.
6728 */
6729 image->columns=1;
6730 image->rows=1;
6731 image->colors=2;
cristyea1a8aa2011-10-20 13:24:06 +00006732 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006733 image->page.width=1;
6734 image->page.height=1;
6735 image->page.x=0;
6736 image->page.y=0;
6737 }
6738 }
6739#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6740 image=mng_info->image;
6741#endif
6742 }
6743
glennrp2b013e42010-11-24 16:55:50 +00006744#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6745 /* PNG does not handle depths greater than 16 so reduce it even
6746 * if lossy
6747 */
6748 if (image->depth > 16)
6749 image->depth=16;
6750#endif
6751
glennrp3faa9a32011-04-23 14:00:25 +00006752#if (MAGICKCORE_QUANTUM_DEPTH > 8)
cristyc82a27b2011-10-21 01:07:16 +00006753 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00006754 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006755#endif
glennrpd6afd542010-11-19 01:53:05 +00006756
cristy3ed852e2009-09-05 21:47:34 +00006757 if (image_info->number_scenes != 0)
6758 {
6759 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006760 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006761 break;
6762 }
glennrpd6afd542010-11-19 01:53:05 +00006763
cristy3ed852e2009-09-05 21:47:34 +00006764 if (logging != MagickFalse)
6765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6766 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006767
cristy3ed852e2009-09-05 21:47:34 +00006768 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006769
cristy3ed852e2009-09-05 21:47:34 +00006770 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006771
cristy3ed852e2009-09-05 21:47:34 +00006772 if (logging != MagickFalse)
6773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6774 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006775
cristy3ed852e2009-09-05 21:47:34 +00006776#if defined(MNG_INSERT_LAYERS)
6777 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6778 (mng_info->mng_height))
6779 {
6780 /*
6781 Insert a background layer if nothing else was found.
6782 */
6783 if (logging != MagickFalse)
6784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6785 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006786
cristy4c08aed2011-07-01 19:47:50 +00006787 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006788 {
6789 /*
6790 Allocate next image structure.
6791 */
cristy9950d572011-10-01 18:22:35 +00006792 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006793 if (GetNextImageInList(image) == (Image *) NULL)
6794 {
6795 image=DestroyImageList(image);
6796 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006797
cristy3ed852e2009-09-05 21:47:34 +00006798 if (logging != MagickFalse)
6799 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6800 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006801
cristy3ed852e2009-09-05 21:47:34 +00006802 return((Image *) NULL);
6803 }
6804 image=SyncNextImageInList(image);
6805 }
6806 image->columns=mng_info->mng_width;
6807 image->rows=mng_info->mng_height;
6808 image->page.width=mng_info->mng_width;
6809 image->page.height=mng_info->mng_height;
6810 image->page.x=0;
6811 image->page.y=0;
6812 image->background_color=mng_background_color;
6813 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006814
cristy3ed852e2009-09-05 21:47:34 +00006815 if (image_info->ping == MagickFalse)
cristyea1a8aa2011-10-20 13:24:06 +00006816 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006817
cristy3ed852e2009-09-05 21:47:34 +00006818 mng_info->image_found++;
6819 }
6820#endif
6821 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006822
cristy3ed852e2009-09-05 21:47:34 +00006823 if (mng_iterations == 1)
6824 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006825
cristy3ed852e2009-09-05 21:47:34 +00006826 while (GetPreviousImageInList(image) != (Image *) NULL)
6827 {
6828 image_count++;
6829 if (image_count > 10*mng_info->image_found)
6830 {
6831 if (logging != MagickFalse)
6832 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006833
cristyc82a27b2011-10-21 01:07:16 +00006834 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006835 CoderError,"Linked list is corrupted, beginning of list not found",
6836 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006837
cristy3ed852e2009-09-05 21:47:34 +00006838 return((Image *) NULL);
6839 }
glennrp0fe50b42010-11-16 03:52:51 +00006840
cristy3ed852e2009-09-05 21:47:34 +00006841 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006842
cristy3ed852e2009-09-05 21:47:34 +00006843 if (GetNextImageInList(image) == (Image *) NULL)
6844 {
6845 if (logging != MagickFalse)
6846 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006847
cristyc82a27b2011-10-21 01:07:16 +00006848 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006849 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6850 image_info->filename);
6851 }
6852 }
glennrp47b9dd52010-11-24 18:12:06 +00006853
cristy3ed852e2009-09-05 21:47:34 +00006854 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6855 GetNextImageInList(image) ==
6856 (Image *) NULL)
6857 {
6858 if (logging != MagickFalse)
6859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6860 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006861
cristyc82a27b2011-10-21 01:07:16 +00006862 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006863 CoderError,"image->next for first image is NULL but shouldn't be.",
6864 "`%s'",image_info->filename);
6865 }
glennrp47b9dd52010-11-24 18:12:06 +00006866
cristy3ed852e2009-09-05 21:47:34 +00006867 if (mng_info->image_found == 0)
6868 {
6869 if (logging != MagickFalse)
6870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6871 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006872
cristyc82a27b2011-10-21 01:07:16 +00006873 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006874 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006875
cristy3ed852e2009-09-05 21:47:34 +00006876 if (image != (Image *) NULL)
6877 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006878
cristy3ed852e2009-09-05 21:47:34 +00006879 MngInfoFreeStruct(mng_info,&have_mng_structure);
6880 return((Image *) NULL);
6881 }
6882
6883 if (mng_info->ticks_per_second)
6884 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6885 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006886
cristy3ed852e2009-09-05 21:47:34 +00006887 else
6888 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006889
cristy3ed852e2009-09-05 21:47:34 +00006890 /* Find final nonzero image delay */
6891 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006892
cristy3ed852e2009-09-05 21:47:34 +00006893 while (GetNextImageInList(image) != (Image *) NULL)
6894 {
6895 if (image->delay)
6896 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006897
cristy3ed852e2009-09-05 21:47:34 +00006898 image=GetNextImageInList(image);
6899 }
glennrp0fe50b42010-11-16 03:52:51 +00006900
cristy3ed852e2009-09-05 21:47:34 +00006901 if (final_delay < final_image_delay)
6902 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006903
cristy3ed852e2009-09-05 21:47:34 +00006904 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006905
cristy3ed852e2009-09-05 21:47:34 +00006906 if (logging != MagickFalse)
6907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006908 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6909 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006910
cristy3ed852e2009-09-05 21:47:34 +00006911 if (logging != MagickFalse)
6912 {
6913 int
6914 scene;
6915
6916 scene=0;
6917 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006918
cristy3ed852e2009-09-05 21:47:34 +00006919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6920 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006921
cristy3ed852e2009-09-05 21:47:34 +00006922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006923 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006924
cristy3ed852e2009-09-05 21:47:34 +00006925 while (GetNextImageInList(image) != (Image *) NULL)
6926 {
6927 image=GetNextImageInList(image);
6928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006929 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006930 }
6931 }
6932
6933 image=GetFirstImageInList(image);
6934#ifdef MNG_COALESCE_LAYERS
6935 if (insert_layers)
6936 {
6937 Image
6938 *next_image,
6939 *next;
6940
cristybb503372010-05-27 20:51:26 +00006941 size_t
cristy3ed852e2009-09-05 21:47:34 +00006942 scene;
6943
6944 if (logging != MagickFalse)
6945 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006946
cristy3ed852e2009-09-05 21:47:34 +00006947 scene=image->scene;
cristyc82a27b2011-10-21 01:07:16 +00006948 next_image=CoalesceImages(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006949
cristy3ed852e2009-09-05 21:47:34 +00006950 if (next_image == (Image *) NULL)
6951 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006952
cristy3ed852e2009-09-05 21:47:34 +00006953 image=DestroyImageList(image);
6954 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006955
cristy3ed852e2009-09-05 21:47:34 +00006956 for (next=image; next != (Image *) NULL; next=next_image)
6957 {
6958 next->page.width=mng_info->mng_width;
6959 next->page.height=mng_info->mng_height;
6960 next->page.x=0;
6961 next->page.y=0;
6962 next->scene=scene++;
6963 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006964
cristy3ed852e2009-09-05 21:47:34 +00006965 if (next_image == (Image *) NULL)
6966 break;
glennrp47b9dd52010-11-24 18:12:06 +00006967
cristy3ed852e2009-09-05 21:47:34 +00006968 if (next->delay == 0)
6969 {
6970 scene--;
6971 next_image->previous=GetPreviousImageInList(next);
6972 if (GetPreviousImageInList(next) == (Image *) NULL)
6973 image=next_image;
6974 else
6975 next->previous->next=next_image;
6976 next=DestroyImage(next);
6977 }
6978 }
6979 }
6980#endif
6981
6982 while (GetNextImageInList(image) != (Image *) NULL)
6983 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006984
cristy3ed852e2009-09-05 21:47:34 +00006985 image->dispose=BackgroundDispose;
6986
6987 if (logging != MagickFalse)
6988 {
6989 int
6990 scene;
6991
6992 scene=0;
6993 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006994
cristy3ed852e2009-09-05 21:47:34 +00006995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6996 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006997
cristy3ed852e2009-09-05 21:47:34 +00006998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006999 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7000 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00007001
cristy3ed852e2009-09-05 21:47:34 +00007002 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00007003 {
7004 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007005
cristyf2faecf2010-05-28 19:19:36 +00007006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007007 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7008 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00007009 }
7010 }
glennrp47b9dd52010-11-24 18:12:06 +00007011
cristy3ed852e2009-09-05 21:47:34 +00007012 image=GetFirstImageInList(image);
7013 MngInfoFreeStruct(mng_info,&have_mng_structure);
7014 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00007015
cristy3ed852e2009-09-05 21:47:34 +00007016 if (logging != MagickFalse)
7017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00007018
cristy3ed852e2009-09-05 21:47:34 +00007019 return(GetFirstImageInList(image));
7020}
glennrp25c1e2b2010-03-25 01:39:56 +00007021#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007022static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7023{
7024 printf("Your PNG library is too old: You have libpng-%s\n",
7025 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00007026
cristy3ed852e2009-09-05 21:47:34 +00007027 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7028 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007029
cristy3ed852e2009-09-05 21:47:34 +00007030 return(Image *) NULL;
7031}
glennrp47b9dd52010-11-24 18:12:06 +00007032
cristy3ed852e2009-09-05 21:47:34 +00007033static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7034{
7035 return(ReadPNGImage(image_info,exception));
7036}
glennrp25c1e2b2010-03-25 01:39:56 +00007037#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007038#endif
7039
7040/*
7041%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7042% %
7043% %
7044% %
7045% R e g i s t e r P N G I m a g e %
7046% %
7047% %
7048% %
7049%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7050%
7051% RegisterPNGImage() adds properties for the PNG image format to
7052% the list of supported formats. The properties include the image format
7053% tag, a method to read and/or write the format, whether the format
7054% supports the saving of more than one frame to the same file or blob,
7055% whether the format supports native in-memory I/O, and a brief
7056% description of the format.
7057%
7058% The format of the RegisterPNGImage method is:
7059%
cristybb503372010-05-27 20:51:26 +00007060% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007061%
7062*/
cristybb503372010-05-27 20:51:26 +00007063ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007064{
7065 char
7066 version[MaxTextExtent];
7067
7068 MagickInfo
7069 *entry;
7070
7071 static const char
7072 *PNGNote=
7073 {
7074 "See http://www.libpng.org/ for details about the PNG format."
7075 },
glennrp47b9dd52010-11-24 18:12:06 +00007076
cristy3ed852e2009-09-05 21:47:34 +00007077 *JNGNote=
7078 {
7079 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7080 "format."
7081 },
glennrp47b9dd52010-11-24 18:12:06 +00007082
cristy3ed852e2009-09-05 21:47:34 +00007083 *MNGNote=
7084 {
7085 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7086 "format."
7087 };
7088
7089 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007090
cristy3ed852e2009-09-05 21:47:34 +00007091#if defined(PNG_LIBPNG_VER_STRING)
7092 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7093 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007094
cristy3ed852e2009-09-05 21:47:34 +00007095 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7096 {
7097 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7098 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7099 MaxTextExtent);
7100 }
7101#endif
glennrp47b9dd52010-11-24 18:12:06 +00007102
cristy3ed852e2009-09-05 21:47:34 +00007103 entry=SetMagickInfo("MNG");
7104 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007105
cristy3ed852e2009-09-05 21:47:34 +00007106#if defined(MAGICKCORE_PNG_DELEGATE)
7107 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7108 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7109#endif
glennrp47b9dd52010-11-24 18:12:06 +00007110
cristy3ed852e2009-09-05 21:47:34 +00007111 entry->magick=(IsImageFormatHandler *) IsMNG;
7112 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007113
cristy3ed852e2009-09-05 21:47:34 +00007114 if (*version != '\0')
7115 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007116
cristy3ed852e2009-09-05 21:47:34 +00007117 entry->module=ConstantString("PNG");
7118 entry->note=ConstantString(MNGNote);
7119 (void) RegisterMagickInfo(entry);
7120
7121 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007122
cristy3ed852e2009-09-05 21:47:34 +00007123#if defined(MAGICKCORE_PNG_DELEGATE)
7124 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7125 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7126#endif
glennrp47b9dd52010-11-24 18:12:06 +00007127
cristy3ed852e2009-09-05 21:47:34 +00007128 entry->magick=(IsImageFormatHandler *) IsPNG;
7129 entry->adjoin=MagickFalse;
7130 entry->description=ConstantString("Portable Network Graphics");
7131 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007132
cristy3ed852e2009-09-05 21:47:34 +00007133 if (*version != '\0')
7134 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007135
cristy3ed852e2009-09-05 21:47:34 +00007136 entry->note=ConstantString(PNGNote);
7137 (void) RegisterMagickInfo(entry);
7138
7139 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007140
cristy3ed852e2009-09-05 21:47:34 +00007141#if defined(MAGICKCORE_PNG_DELEGATE)
7142 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7143 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7144#endif
glennrp47b9dd52010-11-24 18:12:06 +00007145
cristy3ed852e2009-09-05 21:47:34 +00007146 entry->magick=(IsImageFormatHandler *) IsPNG;
7147 entry->adjoin=MagickFalse;
7148 entry->description=ConstantString(
7149 "8-bit indexed with optional binary transparency");
7150 entry->module=ConstantString("PNG");
7151 (void) RegisterMagickInfo(entry);
7152
7153 entry=SetMagickInfo("PNG24");
7154 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007155
cristy3ed852e2009-09-05 21:47:34 +00007156#if defined(ZLIB_VERSION)
7157 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7158 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007159
cristy3ed852e2009-09-05 21:47:34 +00007160 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7161 {
7162 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7163 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7164 }
7165#endif
glennrp47b9dd52010-11-24 18:12:06 +00007166
cristy3ed852e2009-09-05 21:47:34 +00007167 if (*version != '\0')
7168 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007169
cristy3ed852e2009-09-05 21:47:34 +00007170#if defined(MAGICKCORE_PNG_DELEGATE)
7171 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7172 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7173#endif
glennrp47b9dd52010-11-24 18:12:06 +00007174
cristy3ed852e2009-09-05 21:47:34 +00007175 entry->magick=(IsImageFormatHandler *) IsPNG;
7176 entry->adjoin=MagickFalse;
7177 entry->description=ConstantString("opaque 24-bit RGB");
7178 entry->module=ConstantString("PNG");
7179 (void) RegisterMagickInfo(entry);
7180
7181 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007182
cristy3ed852e2009-09-05 21:47:34 +00007183#if defined(MAGICKCORE_PNG_DELEGATE)
7184 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7185 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7186#endif
glennrp47b9dd52010-11-24 18:12:06 +00007187
cristy3ed852e2009-09-05 21:47:34 +00007188 entry->magick=(IsImageFormatHandler *) IsPNG;
7189 entry->adjoin=MagickFalse;
7190 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7191 entry->module=ConstantString("PNG");
7192 (void) RegisterMagickInfo(entry);
7193
7194 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007195
cristy3ed852e2009-09-05 21:47:34 +00007196#if defined(JNG_SUPPORTED)
7197#if defined(MAGICKCORE_PNG_DELEGATE)
7198 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7199 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7200#endif
7201#endif
glennrp47b9dd52010-11-24 18:12:06 +00007202
cristy3ed852e2009-09-05 21:47:34 +00007203 entry->magick=(IsImageFormatHandler *) IsJNG;
7204 entry->adjoin=MagickFalse;
7205 entry->description=ConstantString("JPEG Network Graphics");
7206 entry->module=ConstantString("PNG");
7207 entry->note=ConstantString(JNGNote);
7208 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007209
cristy18b17442009-10-25 18:36:48 +00007210#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007211 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007212#endif
glennrp47b9dd52010-11-24 18:12:06 +00007213
cristy3ed852e2009-09-05 21:47:34 +00007214 return(MagickImageCoderSignature);
7215}
7216
7217/*
7218%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7219% %
7220% %
7221% %
7222% U n r e g i s t e r P N G I m a g e %
7223% %
7224% %
7225% %
7226%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7227%
7228% UnregisterPNGImage() removes format registrations made by the
7229% PNG module from the list of supported formats.
7230%
7231% The format of the UnregisterPNGImage method is:
7232%
7233% UnregisterPNGImage(void)
7234%
7235*/
7236ModuleExport void UnregisterPNGImage(void)
7237{
7238 (void) UnregisterMagickInfo("MNG");
7239 (void) UnregisterMagickInfo("PNG");
7240 (void) UnregisterMagickInfo("PNG8");
7241 (void) UnregisterMagickInfo("PNG24");
7242 (void) UnregisterMagickInfo("PNG32");
7243 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007244
cristy3ed852e2009-09-05 21:47:34 +00007245#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007246 if (ping_semaphore != (SemaphoreInfo *) NULL)
7247 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007248#endif
7249}
7250
7251#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007252#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007253/*
7254%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7255% %
7256% %
7257% %
7258% W r i t e M N G I m a g e %
7259% %
7260% %
7261% %
7262%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7263%
7264% WriteMNGImage() writes an image in the Portable Network Graphics
7265% Group's "Multiple-image Network Graphics" encoded image format.
7266%
7267% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7268%
7269% The format of the WriteMNGImage method is:
7270%
cristy1e178e72011-08-28 19:44:34 +00007271% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7272% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007273%
7274% A description of each parameter follows.
7275%
7276% o image_info: the image info.
7277%
7278% o image: The image.
7279%
cristy1e178e72011-08-28 19:44:34 +00007280% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007281%
7282% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7283% "To do" under ReadPNGImage):
7284%
cristy3ed852e2009-09-05 21:47:34 +00007285% Preserve all unknown and not-yet-handled known chunks found in input
7286% PNG file and copy them into output PNG files according to the PNG
7287% copying rules.
7288%
7289% Write the iCCP chunk at MNG level when (icc profile length > 0)
7290%
7291% Improve selection of color type (use indexed-colour or indexed-colour
7292% with tRNS when 256 or fewer unique RGBA values are present).
7293%
7294% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7295% This will be complicated if we limit ourselves to generating MNG-LC
7296% files. For now we ignore disposal method 3 and simply overlay the next
7297% image on it.
7298%
7299% Check for identical PLTE's or PLTE/tRNS combinations and use a
7300% global MNG PLTE or PLTE/tRNS combination when appropriate.
7301% [mostly done 15 June 1999 but still need to take care of tRNS]
7302%
7303% Check for identical sRGB and replace with a global sRGB (and remove
7304% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7305% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7306% local gAMA/cHRM with local sRGB if appropriate).
7307%
7308% Check for identical sBIT chunks and write global ones.
7309%
7310% Provide option to skip writing the signature tEXt chunks.
7311%
7312% Use signatures to detect identical objects and reuse the first
7313% instance of such objects instead of writing duplicate objects.
7314%
7315% Use a smaller-than-32k value of compression window size when
7316% appropriate.
7317%
7318% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7319% ancillary text chunks and save profiles.
7320%
7321% Provide an option to force LC files (to ensure exact framing rate)
7322% instead of VLC.
7323%
7324% Provide an option to force VLC files instead of LC, even when offsets
7325% are present. This will involve expanding the embedded images with a
7326% transparent region at the top and/or left.
7327*/
7328
cristy3ed852e2009-09-05 21:47:34 +00007329static void
glennrpcf002022011-01-30 02:38:15 +00007330Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007331 png_info *ping_info, unsigned char *profile_type, unsigned char
7332 *profile_description, unsigned char *profile_data, png_uint_32 length)
7333{
cristy3ed852e2009-09-05 21:47:34 +00007334 png_textp
7335 text;
7336
cristybb503372010-05-27 20:51:26 +00007337 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007338 i;
7339
7340 unsigned char
7341 *sp;
7342
7343 png_charp
7344 dp;
7345
7346 png_uint_32
7347 allocated_length,
7348 description_length;
7349
7350 unsigned char
7351 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007352
cristy3ed852e2009-09-05 21:47:34 +00007353 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7354 return;
7355
7356 if (image_info->verbose)
7357 {
glennrp0fe50b42010-11-16 03:52:51 +00007358 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7359 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007360 }
glennrp0fe50b42010-11-16 03:52:51 +00007361
cristy3ed852e2009-09-05 21:47:34 +00007362 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7363 description_length=(png_uint_32) strlen((const char *) profile_description);
7364 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7365 + description_length);
7366 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7367 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7368 text[0].key[0]='\0';
7369 (void) ConcatenateMagickString(text[0].key,
7370 "Raw profile type ",MaxTextExtent);
7371 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7372 sp=profile_data;
7373 dp=text[0].text;
7374 *dp++='\n';
7375 (void) CopyMagickString(dp,(const char *) profile_description,
7376 allocated_length);
7377 dp+=description_length;
7378 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007379 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007380 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007381 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007382
cristybb503372010-05-27 20:51:26 +00007383 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007384 {
7385 if (i%36 == 0)
7386 *dp++='\n';
7387 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7388 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7389 }
glennrp47b9dd52010-11-24 18:12:06 +00007390
cristy3ed852e2009-09-05 21:47:34 +00007391 *dp++='\n';
7392 *dp='\0';
7393 text[0].text_length=(png_size_t) (dp-text[0].text);
7394 text[0].compression=image_info->compression == NoCompression ||
7395 (image_info->compression == UndefinedCompression &&
7396 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007397
cristy3ed852e2009-09-05 21:47:34 +00007398 if (text[0].text_length <= allocated_length)
7399 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007400
cristy3ed852e2009-09-05 21:47:34 +00007401 png_free(ping,text[0].text);
7402 png_free(ping,text[0].key);
7403 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007404}
7405
glennrpcf002022011-01-30 02:38:15 +00007406static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007407 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007408{
7409 char
7410 *name;
7411
7412 const StringInfo
7413 *profile;
7414
7415 unsigned char
7416 *data;
7417
7418 png_uint_32 length;
7419
7420 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007421
7422 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7423 {
cristy3ed852e2009-09-05 21:47:34 +00007424 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007425
cristy3ed852e2009-09-05 21:47:34 +00007426 if (profile != (const StringInfo *) NULL)
7427 {
7428 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007429 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007430
glennrp47b9dd52010-11-24 18:12:06 +00007431 if (LocaleNCompare(name,string,11) == 0)
7432 {
7433 if (logging != MagickFalse)
7434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7435 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007436
glennrpcf002022011-01-30 02:38:15 +00007437 ping_profile=CloneStringInfo(profile);
7438 data=GetStringInfoDatum(ping_profile),
7439 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007440 data[4]=data[3];
7441 data[3]=data[2];
7442 data[2]=data[1];
7443 data[1]=data[0];
7444 (void) WriteBlobMSBULong(image,length-5); /* data length */
7445 (void) WriteBlob(image,length-1,data+1);
7446 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007447 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007448 }
cristy3ed852e2009-09-05 21:47:34 +00007449 }
glennrp47b9dd52010-11-24 18:12:06 +00007450
cristy3ed852e2009-09-05 21:47:34 +00007451 name=GetNextImageProfile(image);
7452 }
glennrp47b9dd52010-11-24 18:12:06 +00007453
cristy3ed852e2009-09-05 21:47:34 +00007454 return(MagickTrue);
7455}
7456
glennrpb9cfe272010-12-21 15:08:06 +00007457
cristy3ed852e2009-09-05 21:47:34 +00007458/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007459static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +00007460 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007461{
7462 Image
7463 *image;
7464
7465 ImageInfo
7466 *image_info;
7467
cristy3ed852e2009-09-05 21:47:34 +00007468 char
7469 s[2];
7470
7471 const char
7472 *name,
7473 *property,
7474 *value;
7475
7476 const StringInfo
7477 *profile;
7478
cristy3ed852e2009-09-05 21:47:34 +00007479 int
cristy3ed852e2009-09-05 21:47:34 +00007480 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007481 pass;
7482
glennrpe9c26dc2010-05-30 01:56:35 +00007483 png_byte
7484 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007485
glennrp39992b42010-11-14 00:03:43 +00007486 png_color
7487 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007488
glennrp5af765f2010-03-30 11:12:18 +00007489 png_color_16
7490 ping_background,
7491 ping_trans_color;
7492
cristy3ed852e2009-09-05 21:47:34 +00007493 png_info
7494 *ping_info;
7495
7496 png_struct
7497 *ping;
7498
glennrp5af765f2010-03-30 11:12:18 +00007499 png_uint_32
7500 ping_height,
7501 ping_width;
7502
cristybb503372010-05-27 20:51:26 +00007503 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007504 y;
7505
7506 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007507 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007508 logging,
glennrp58e01762011-01-07 15:28:54 +00007509 matte,
7510
glennrpda8f3a72011-02-27 23:54:12 +00007511 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007512 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007513 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007514 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007515 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007516 ping_have_bKGD,
7517 ping_have_pHYs,
7518 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007519
7520 ping_exclude_bKGD,
7521 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007522 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007523 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007524 ping_exclude_gAMA,
7525 ping_exclude_iCCP,
7526 /* ping_exclude_iTXt, */
7527 ping_exclude_oFFs,
7528 ping_exclude_pHYs,
7529 ping_exclude_sRGB,
7530 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007531 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007532 ping_exclude_vpAg,
7533 ping_exclude_zCCP, /* hex-encoded iCCP */
7534 ping_exclude_zTXt,
7535
glennrp8d3d6e52011-04-19 04:39:51 +00007536 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007537 ping_need_colortype_warning,
7538
glennrp82b3c532011-03-22 19:20:54 +00007539 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007540 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007541 tried_333,
7542 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007543
7544 QuantumInfo
7545 *quantum_info;
7546
cristyc82a27b2011-10-21 01:07:16 +00007547 PNGErrorInfo
7548 error_info;
7549
cristybb503372010-05-27 20:51:26 +00007550 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007551 i,
7552 x;
7553
7554 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007555 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007556
glennrp5af765f2010-03-30 11:12:18 +00007557 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007558 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007559 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007560 ping_color_type,
7561 ping_interlace_method,
7562 ping_compression_method,
7563 ping_filter_method,
7564 ping_num_trans;
7565
cristybb503372010-05-27 20:51:26 +00007566 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007567 image_depth,
7568 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007569
cristybb503372010-05-27 20:51:26 +00007570 size_t
cristy3ed852e2009-09-05 21:47:34 +00007571 quality,
7572 rowbytes,
7573 save_image_depth;
7574
glennrpdfd70802010-11-14 01:23:35 +00007575 int
glennrpfd05d622011-02-25 04:10:33 +00007576 j,
glennrpf09bded2011-01-08 01:15:59 +00007577 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007578 number_opaque,
7579 number_semitransparent,
7580 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007581 ping_pHYs_unit_type;
7582
7583 png_uint_32
7584 ping_pHYs_x_resolution,
7585 ping_pHYs_y_resolution;
7586
cristy3ed852e2009-09-05 21:47:34 +00007587 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007588 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007589
cristyc82a27b2011-10-21 01:07:16 +00007590 image = CloneImage(IMimage,0,0,MagickFalse,exception);
glennrpb9cfe272010-12-21 15:08:06 +00007591 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007592 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007593 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007594
cristy3ed852e2009-09-05 21:47:34 +00007595#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007596 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007597#endif
7598
glennrp5af765f2010-03-30 11:12:18 +00007599 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007600 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007601 ping_color_type=0,
7602 ping_interlace_method=0,
7603 ping_compression_method=0,
7604 ping_filter_method=0,
7605 ping_num_trans = 0;
7606
7607 ping_background.red = 0;
7608 ping_background.green = 0;
7609 ping_background.blue = 0;
7610 ping_background.gray = 0;
7611 ping_background.index = 0;
7612
7613 ping_trans_color.red=0;
7614 ping_trans_color.green=0;
7615 ping_trans_color.blue=0;
7616 ping_trans_color.gray=0;
7617
glennrpdfd70802010-11-14 01:23:35 +00007618 ping_pHYs_unit_type = 0;
7619 ping_pHYs_x_resolution = 0;
7620 ping_pHYs_y_resolution = 0;
7621
glennrpda8f3a72011-02-27 23:54:12 +00007622 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007623 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007624 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007625 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007626 ping_have_bKGD=MagickFalse;
7627 ping_have_pHYs=MagickFalse;
7628 ping_have_tRNS=MagickFalse;
7629
glennrp0e8ea192010-12-24 18:00:33 +00007630 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7631 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007632 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007633 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007634 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007635 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7636 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7637 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7638 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7639 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7640 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007641 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007642 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7643 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7644 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7645
glennrp8d3d6e52011-04-19 04:39:51 +00007646 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007647 ping_need_colortype_warning = MagickFalse;
7648
cristy0d57eec2011-09-04 22:13:56 +00007649 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7650 * i.e., eliminate the ICC profile and set image->rendering_intent.
7651 * Note that this will not involve any changes to the actual pixels
7652 * but merely passes information to applications that read the resulting
7653 * PNG image.
7654 */
7655 if (ping_exclude_sRGB == MagickFalse)
7656 {
7657 char
7658 *name;
7659
7660 const StringInfo
7661 *profile;
7662
7663 ResetImageProfileIterator(image);
7664 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7665 {
7666 profile=GetImageProfile(image,name);
7667
7668 if (profile != (StringInfo *) NULL)
7669 {
7670 if ((LocaleCompare(name,"ICC") == 0) ||
glennrp29a106e2011-09-06 17:11:42 +00007671 (LocaleCompare(name,"ICM") == 0))
7672 {
glennrpee7b4c02011-10-04 01:21:09 +00007673 int
7674 icheck;
7675
7676 /* 0: not a known sRGB profile
7677 * 1: HP-Microsoft sRGB v2
7678 * 2: ICC sRGB v4 perceptual
7679 * 3: ICC sRGB v2 perceptual no black-compensation
7680 */
7681 png_uint_32
7682 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7683 check_len[4] = {0, 3144, 60960, 3052};
7684
7685 png_uint_32
7686 length,
7687 profile_crc;
7688
cristy0d57eec2011-09-04 22:13:56 +00007689 unsigned char
7690 *data;
7691
glennrp29a106e2011-09-06 17:11:42 +00007692 length=(png_uint_32) GetStringInfoLength(profile);
7693
glennrpee7b4c02011-10-04 01:21:09 +00007694 for (icheck=3; icheck > 0; icheck--)
cristy0d57eec2011-09-04 22:13:56 +00007695 {
glennrpee7b4c02011-10-04 01:21:09 +00007696 if (length == check_len[icheck])
glennrp29a106e2011-09-06 17:11:42 +00007697 {
glennrpee7b4c02011-10-04 01:21:09 +00007698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7699 " Got a %lu-byte ICC profile (potentially sRGB)",
7700 (unsigned long) length);
glennrp29a106e2011-09-06 17:11:42 +00007701
glennrpee7b4c02011-10-04 01:21:09 +00007702 data=GetStringInfoDatum(profile);
7703 profile_crc=crc32(0,data,length);
glennrp29a106e2011-09-06 17:11:42 +00007704
glennrpee7b4c02011-10-04 01:21:09 +00007705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe54cd4b2011-10-04 01:31:35 +00007706 " with crc=%8x",(unsigned int) profile_crc);
glennrpee7b4c02011-10-04 01:21:09 +00007707
7708 if (profile_crc == check_crc[icheck])
7709 {
7710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7711 " It is sRGB.");
7712 if (image->rendering_intent==UndefinedIntent)
7713 image->rendering_intent=PerceptualIntent;
7714 break;
7715 }
glennrp29a106e2011-09-06 17:11:42 +00007716 }
glennrp29a106e2011-09-06 17:11:42 +00007717 }
glennrpee7b4c02011-10-04 01:21:09 +00007718 if (icheck == 0)
glennrp29a106e2011-09-06 17:11:42 +00007719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpee7b4c02011-10-04 01:21:09 +00007720 " Got a %lu-byte ICC profile",
glennrp29a106e2011-09-06 17:11:42 +00007721 (unsigned long) length);
7722 }
cristy0d57eec2011-09-04 22:13:56 +00007723 }
7724 name=GetNextImageProfile(image);
7725 }
7726 }
7727
glennrp8bb3a022010-12-13 20:40:04 +00007728 number_opaque = 0;
7729 number_semitransparent = 0;
7730 number_transparent = 0;
7731
glennrpfd05d622011-02-25 04:10:33 +00007732 if (logging != MagickFalse)
7733 {
7734 if (image->storage_class == UndefinedClass)
7735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7736 " storage_class=UndefinedClass");
7737 if (image->storage_class == DirectClass)
7738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7739 " storage_class=DirectClass");
7740 if (image->storage_class == PseudoClass)
7741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7742 " storage_class=PseudoClass");
7743 }
glennrp28af3712011-04-06 18:07:30 +00007744
glennrp7e65e932011-08-19 02:31:16 +00007745 if (image->storage_class == PseudoClass &&
7746 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7747 (mng_info->write_png_colortype != 0 &&
7748 mng_info->write_png_colortype != 4)))
7749 {
cristyea1a8aa2011-10-20 13:24:06 +00007750 (void) SyncImage(image,exception);
glennrp7e65e932011-08-19 02:31:16 +00007751 image->storage_class = DirectClass;
7752 }
7753
glennrpc6c391a2011-04-27 02:23:56 +00007754 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007755 {
glennrpc6c391a2011-04-27 02:23:56 +00007756 if (image->storage_class != PseudoClass && image->colormap != NULL)
7757 {
7758 /* Free the bogus colormap; it can cause trouble later */
7759 if (logging != MagickFalse)
7760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7761 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00007762 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00007763 image->colormap=NULL;
7764 }
glennrp28af3712011-04-06 18:07:30 +00007765 }
glennrpbb4f99d2011-05-22 11:13:17 +00007766
cristy510d06a2011-07-06 23:43:54 +00007767 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristye941a752011-10-15 01:52:48 +00007768 (void) TransformImageColorspace(image,RGBColorspace,exception);
glennrp0fe50b42010-11-16 03:52:51 +00007769
glennrp3241bd02010-12-12 04:36:28 +00007770 /*
7771 Sometimes we get PseudoClass images whose RGB values don't match
7772 the colors in the colormap. This code syncs the RGB values.
7773 */
7774 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
cristyea1a8aa2011-10-20 13:24:06 +00007775 (void) SyncImage(image,exception);
glennrp3241bd02010-12-12 04:36:28 +00007776
glennrpa6a06632011-01-19 15:15:34 +00007777#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7778 if (image->depth > 8)
7779 {
7780 if (logging != MagickFalse)
7781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7782 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7783
7784 image->depth=8;
7785 }
7786#endif
7787
glennrp8e58efd2011-05-20 12:16:29 +00007788 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007789 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7790 {
cristy4c08aed2011-07-01 19:47:50 +00007791 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007792 *r;
7793
glennrp8e58efd2011-05-20 12:16:29 +00007794 if (image->depth > 8)
7795 {
7796#if MAGICKCORE_QUANTUM_DEPTH > 16
7797 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007798 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007799
7800 for (y=0; y < (ssize_t) image->rows; y++)
7801 {
cristy8a20fa02011-12-27 15:54:31 +00007802 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007803
cristy4c08aed2011-07-01 19:47:50 +00007804 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007805 break;
7806
7807 for (x=0; x < (ssize_t) image->columns; x++)
7808 {
glennrp54cf7972011-08-06 14:28:09 +00007809 LBR16PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007810 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007811 }
glennrpbb4f99d2011-05-22 11:13:17 +00007812
glennrp8e58efd2011-05-20 12:16:29 +00007813 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7814 break;
7815 }
7816
7817 if (image->storage_class == PseudoClass && image->colormap != NULL)
7818 {
cristy3e08f112011-05-24 13:19:30 +00007819 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007820 {
glennrp91d99252011-06-25 14:30:13 +00007821 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007822 }
7823 }
7824#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7825 }
7826
7827 else if (image->depth > 4)
7828 {
7829#if MAGICKCORE_QUANTUM_DEPTH > 8
7830 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007831 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007832
7833 for (y=0; y < (ssize_t) image->rows; y++)
7834 {
cristyc82a27b2011-10-21 01:07:16 +00007835 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007836
cristy4c08aed2011-07-01 19:47:50 +00007837 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007838 break;
7839
7840 for (x=0; x < (ssize_t) image->columns; x++)
7841 {
glennrp54cf7972011-08-06 14:28:09 +00007842 LBR08PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007843 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007844 }
glennrpbb4f99d2011-05-22 11:13:17 +00007845
glennrp8e58efd2011-05-20 12:16:29 +00007846 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7847 break;
7848 }
7849
7850 if (image->storage_class == PseudoClass && image->colormap != NULL)
7851 {
cristy3e08f112011-05-24 13:19:30 +00007852 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007853 {
glennrp91d99252011-06-25 14:30:13 +00007854 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007855 }
7856 }
7857#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7858 }
7859 else
7860 if (image->depth > 2)
7861 {
7862 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007863 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007864
7865 for (y=0; y < (ssize_t) image->rows; y++)
7866 {
cristy8a20fa02011-12-27 15:54:31 +00007867 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007868
cristy4c08aed2011-07-01 19:47:50 +00007869 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007870 break;
7871
7872 for (x=0; x < (ssize_t) image->columns; x++)
7873 {
glennrp54cf7972011-08-06 14:28:09 +00007874 LBR04PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007875 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007876 }
glennrpbb4f99d2011-05-22 11:13:17 +00007877
glennrp8e58efd2011-05-20 12:16:29 +00007878 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7879 break;
7880 }
7881
7882 if (image->storage_class == PseudoClass && image->colormap != NULL)
7883 {
cristy3e08f112011-05-24 13:19:30 +00007884 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007885 {
glennrp91d99252011-06-25 14:30:13 +00007886 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007887 }
7888 }
7889 }
7890
7891 else if (image->depth > 1)
7892 {
7893 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007894 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007895
7896 for (y=0; y < (ssize_t) image->rows; y++)
7897 {
cristy8a20fa02011-12-27 15:54:31 +00007898 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007899
cristy4c08aed2011-07-01 19:47:50 +00007900 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007901 break;
7902
7903 for (x=0; x < (ssize_t) image->columns; x++)
7904 {
glennrp54cf7972011-08-06 14:28:09 +00007905 LBR02PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007906 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007907 }
glennrpbb4f99d2011-05-22 11:13:17 +00007908
glennrp8e58efd2011-05-20 12:16:29 +00007909 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7910 break;
7911 }
7912
7913 if (image->storage_class == PseudoClass && image->colormap != NULL)
7914 {
cristy3e08f112011-05-24 13:19:30 +00007915 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007916 {
glennrp91d99252011-06-25 14:30:13 +00007917 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007918 }
7919 }
7920 }
7921 else
7922 {
7923 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007924 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007925
7926 for (y=0; y < (ssize_t) image->rows; y++)
7927 {
cristy8a20fa02011-12-27 15:54:31 +00007928 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007929
cristy4c08aed2011-07-01 19:47:50 +00007930 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007931 break;
7932
7933 for (x=0; x < (ssize_t) image->columns; x++)
7934 {
glennrp54cf7972011-08-06 14:28:09 +00007935 LBR01PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007936 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007937 }
glennrpbb4f99d2011-05-22 11:13:17 +00007938
glennrp8e58efd2011-05-20 12:16:29 +00007939 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7940 break;
7941 }
7942
7943 if (image->storage_class == PseudoClass && image->colormap != NULL)
7944 {
cristy3e08f112011-05-24 13:19:30 +00007945 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007946 {
glennrp91d99252011-06-25 14:30:13 +00007947 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007948 }
7949 }
7950 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007951 }
7952
glennrp67b9c1a2011-04-22 18:47:36 +00007953 /* To do: set to next higher multiple of 8 */
7954 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007955 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007956
glennrp2b013e42010-11-24 16:55:50 +00007957#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7958 /* PNG does not handle depths greater than 16 so reduce it even
7959 * if lossy
7960 */
glennrp8e58efd2011-05-20 12:16:29 +00007961 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007962 image->depth=16;
7963#endif
7964
glennrp3faa9a32011-04-23 14:00:25 +00007965#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007966 if (image->depth == 16 && mng_info->write_png_depth != 16)
cristyc82a27b2011-10-21 01:07:16 +00007967 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007968 image->depth = 8;
7969#endif
7970
glennrpc8c2f062011-02-25 19:00:33 +00007971 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007972 * we reduce the transparency to binary and run again, then if there
7973 * 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 +00007974 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7975 * palette. Then (To do) we take care of a final reduction that is only
7976 * needed if there are still 256 colors present and one of them has both
7977 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007978 */
glennrp82b3c532011-03-22 19:20:54 +00007979
glennrp8ca51ad2011-05-12 21:22:32 +00007980 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007981 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007982 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007983
glennrp8ca51ad2011-05-12 21:22:32 +00007984 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007985 {
7986 /* BUILD_PALETTE
7987 *
7988 * Sometimes we get DirectClass images that have 256 colors or fewer.
7989 * This code will build a colormap.
7990 *
7991 * Also, sometimes we get PseudoClass images with an out-of-date
7992 * colormap. This code will replace the colormap with a new one.
7993 * Sometimes we get PseudoClass images that have more than 256 colors.
7994 * This code will delete the colormap and change the image to
7995 * DirectClass.
7996 *
cristy4c08aed2011-07-01 19:47:50 +00007997 * If image->matte is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00007998 * even though it sometimes contains left-over non-opaque values.
7999 *
8000 * Also we gather some information (number of opaque, transparent,
8001 * and semitransparent pixels, and whether the image has any non-gray
8002 * pixels or only black-and-white pixels) that we might need later.
8003 *
8004 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8005 * we need to check for bogus non-opaque values, at least.
8006 */
glennrp3c218112010-11-27 15:31:26 +00008007
glennrpd71e86a2011-02-24 01:28:37 +00008008 int
8009 n;
glennrp3c218112010-11-27 15:31:26 +00008010
cristy101ab702011-10-13 13:06:32 +00008011 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008012 opaque[260],
8013 semitransparent[260],
8014 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00008015
cristy4c08aed2011-07-01 19:47:50 +00008016 register const Quantum
8017 *s;
glennrp8bb3a022010-12-13 20:40:04 +00008018
cristy4c08aed2011-07-01 19:47:50 +00008019 register Quantum
8020 *q,
glennrpfd05d622011-02-25 04:10:33 +00008021 *r;
8022
glennrpd71e86a2011-02-24 01:28:37 +00008023 if (logging != MagickFalse)
8024 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8025 " Enter BUILD_PALETTE:");
8026
8027 if (logging != MagickFalse)
8028 {
glennrp03812ae2010-12-24 01:31:34 +00008029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008030 " image->columns=%.20g",(double) image->columns);
8031 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8032 " image->rows=%.20g",(double) image->rows);
8033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8034 " image->matte=%.20g",(double) image->matte);
8035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8036 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008037
glennrpfd05d622011-02-25 04:10:33 +00008038 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008039 {
8040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008041 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008043 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008044
glennrpd71e86a2011-02-24 01:28:37 +00008045 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008046 {
glennrpd71e86a2011-02-24 01:28:37 +00008047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8048 " %d (%d,%d,%d,%d)",
8049 (int) i,
8050 (int) image->colormap[i].red,
8051 (int) image->colormap[i].green,
8052 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008053 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008054 }
glennrp2cc891a2010-12-24 13:44:32 +00008055
glennrpd71e86a2011-02-24 01:28:37 +00008056 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8057 {
8058 if (i > 255)
8059 {
8060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8061 " %d (%d,%d,%d,%d)",
8062 (int) i,
8063 (int) image->colormap[i].red,
8064 (int) image->colormap[i].green,
8065 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008066 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008067 }
8068 }
glennrp03812ae2010-12-24 01:31:34 +00008069 }
glennrp7ddcc222010-12-11 05:01:05 +00008070
glennrpd71e86a2011-02-24 01:28:37 +00008071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8072 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008073
glennrpd71e86a2011-02-24 01:28:37 +00008074 if (image->colors == 0)
cristyc458f912011-12-27 20:26:40 +00008075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8076 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008077
glennrp8d3d6e52011-04-19 04:39:51 +00008078 if (ping_preserve_colormap == MagickFalse)
8079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8080 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008081 }
8082
glennrpd71e86a2011-02-24 01:28:37 +00008083 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008084 number_opaque = 0;
8085 number_semitransparent = 0;
8086 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008087
8088 for (y=0; y < (ssize_t) image->rows; y++)
8089 {
8090 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8091
cristyacd2ed22011-08-30 01:44:23 +00008092 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008093 break;
8094
8095 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008096 {
glennrp4737d522011-04-29 03:33:42 +00008097 if (image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008098 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008099 {
8100 if (number_opaque < 259)
8101 {
8102 if (number_opaque == 0)
8103 {
cristy101ab702011-10-13 13:06:32 +00008104 GetPixelInfoPixel(image, q, opaque);
cristy4c08aed2011-07-01 19:47:50 +00008105 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008106 number_opaque=1;
8107 }
glennrp2cc891a2010-12-24 13:44:32 +00008108
glennrpd71e86a2011-02-24 01:28:37 +00008109 for (i=0; i< (ssize_t) number_opaque; i++)
8110 {
cristy4c08aed2011-07-01 19:47:50 +00008111 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008112 break;
8113 }
glennrp7ddcc222010-12-11 05:01:05 +00008114
cristyc458f912011-12-27 20:26:40 +00008115 if (i == (ssize_t) number_opaque && number_opaque < 259)
glennrpd71e86a2011-02-24 01:28:37 +00008116 {
8117 number_opaque++;
cristy101ab702011-10-13 13:06:32 +00008118 GetPixelInfoPixel(image, q, opaque+i);
cristy4c08aed2011-07-01 19:47:50 +00008119 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008120 }
8121 }
8122 }
cristy4c08aed2011-07-01 19:47:50 +00008123 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008124 {
8125 if (number_transparent < 259)
8126 {
8127 if (number_transparent == 0)
8128 {
cristy101ab702011-10-13 13:06:32 +00008129 GetPixelInfoPixel(image, q, transparent);
cristy4c08aed2011-07-01 19:47:50 +00008130 ping_trans_color.red=(unsigned short)
8131 GetPixelRed(image,q);
8132 ping_trans_color.green=(unsigned short)
8133 GetPixelGreen(image,q);
8134 ping_trans_color.blue=(unsigned short)
8135 GetPixelBlue(image,q);
8136 ping_trans_color.gray=(unsigned short)
8137 GetPixelRed(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008138 number_transparent = 1;
8139 }
8140
8141 for (i=0; i< (ssize_t) number_transparent; i++)
8142 {
cristy4c08aed2011-07-01 19:47:50 +00008143 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008144 break;
8145 }
8146
8147 if (i == (ssize_t) number_transparent &&
8148 number_transparent < 259)
8149 {
8150 number_transparent++;
cristy101ab702011-10-13 13:06:32 +00008151 GetPixelInfoPixel(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008152 }
8153 }
8154 }
8155 else
8156 {
8157 if (number_semitransparent < 259)
8158 {
8159 if (number_semitransparent == 0)
8160 {
cristy101ab702011-10-13 13:06:32 +00008161 GetPixelInfoPixel(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008162 number_semitransparent = 1;
8163 }
8164
8165 for (i=0; i< (ssize_t) number_semitransparent; i++)
8166 {
cristy4c08aed2011-07-01 19:47:50 +00008167 if (IsPixelEquivalent(image,q, semitransparent+i)
8168 && GetPixelAlpha(image,q) ==
8169 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008170 break;
8171 }
8172
8173 if (i == (ssize_t) number_semitransparent &&
8174 number_semitransparent < 259)
8175 {
8176 number_semitransparent++;
cristy101ab702011-10-13 13:06:32 +00008177 GetPixelInfoPixel(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008178 }
8179 }
8180 }
cristyed231572011-07-14 02:18:59 +00008181 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008182 }
8183 }
8184
cristy4054bfb2011-08-29 23:41:39 +00008185 if (mng_info->write_png8 == MagickFalse &&
8186 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008187 {
8188 /* Add the background color to the palette, if it
8189 * isn't already there.
8190 */
glennrpc6c391a2011-04-27 02:23:56 +00008191 if (logging != MagickFalse)
8192 {
8193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8194 " Check colormap for background (%d,%d,%d)",
8195 (int) image->background_color.red,
8196 (int) image->background_color.green,
8197 (int) image->background_color.blue);
8198 }
glennrpd71e86a2011-02-24 01:28:37 +00008199 for (i=0; i<number_opaque; i++)
8200 {
glennrpca7ad3a2011-04-26 04:44:54 +00008201 if (opaque[i].red == image->background_color.red &&
8202 opaque[i].green == image->background_color.green &&
8203 opaque[i].blue == image->background_color.blue)
8204 break;
glennrpd71e86a2011-02-24 01:28:37 +00008205 }
glennrpd71e86a2011-02-24 01:28:37 +00008206 if (number_opaque < 259 && i == number_opaque)
8207 {
glennrp8e045c82011-04-27 16:40:27 +00008208 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008209 ping_background.index = i;
8210 if (logging != MagickFalse)
8211 {
8212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8213 " background_color index is %d",(int) i);
8214 }
8215
glennrpd71e86a2011-02-24 01:28:37 +00008216 }
glennrpa080bc32011-03-11 18:03:44 +00008217 else if (logging != MagickFalse)
8218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8219 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008220 }
8221
8222 image_colors=number_opaque+number_transparent+number_semitransparent;
8223
glennrpa080bc32011-03-11 18:03:44 +00008224 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8225 {
8226 /* No room for the background color; remove it. */
8227 number_opaque--;
8228 image_colors--;
8229 }
8230
glennrpd71e86a2011-02-24 01:28:37 +00008231 if (logging != MagickFalse)
8232 {
8233 if (image_colors > 256)
8234 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8235 " image has more than 256 colors");
8236
8237 else
8238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8239 " image has %d colors",image_colors);
8240 }
8241
glennrp8d3d6e52011-04-19 04:39:51 +00008242 if (ping_preserve_colormap != MagickFalse)
8243 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008244
glennrpfd05d622011-02-25 04:10:33 +00008245 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008246 {
8247 ping_have_color=MagickFalse;
8248 ping_have_non_bw=MagickFalse;
8249
8250 if(image_colors > 256)
8251 {
8252 for (y=0; y < (ssize_t) image->rows; y++)
8253 {
8254 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8255
cristyacd2ed22011-08-30 01:44:23 +00008256 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008257 break;
8258
glennrpe5e6b802011-07-20 14:44:40 +00008259 s=q;
8260 for (x=0; x < (ssize_t) image->columns; x++)
8261 {
8262 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8263 GetPixelRed(image,s) != GetPixelBlue(image,s))
8264 {
8265 ping_have_color=MagickTrue;
8266 ping_have_non_bw=MagickTrue;
8267 break;
8268 }
8269 s+=GetPixelChannels(image);
8270 }
8271
8272 if (ping_have_color != MagickFalse)
8273 break;
8274
glennrpd71e86a2011-02-24 01:28:37 +00008275 /* Worst case is black-and-white; we are looking at every
8276 * pixel twice.
8277 */
8278
glennrpd71e86a2011-02-24 01:28:37 +00008279 if (ping_have_non_bw == MagickFalse)
8280 {
8281 s=q;
8282 for (x=0; x < (ssize_t) image->columns; x++)
8283 {
cristy4c08aed2011-07-01 19:47:50 +00008284 if (GetPixelRed(image,s) != 0 &&
8285 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008286 {
8287 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008288 break;
glennrpd71e86a2011-02-24 01:28:37 +00008289 }
cristyed231572011-07-14 02:18:59 +00008290 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008291 }
glennrpe5e6b802011-07-20 14:44:40 +00008292 }
glennrpd71e86a2011-02-24 01:28:37 +00008293 }
glennrpbb4f99d2011-05-22 11:13:17 +00008294 }
8295 }
glennrpd71e86a2011-02-24 01:28:37 +00008296
8297 if (image_colors < 257)
8298 {
cristy101ab702011-10-13 13:06:32 +00008299 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008300 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008301
glennrpd71e86a2011-02-24 01:28:37 +00008302 /*
8303 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008304 */
8305
glennrpd71e86a2011-02-24 01:28:37 +00008306 if (logging != MagickFalse)
8307 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8308 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008309
glennrpd71e86a2011-02-24 01:28:37 +00008310 /* Sort palette, transparent first */;
8311
8312 n = 0;
8313
8314 for (i=0; i<number_transparent; i++)
8315 colormap[n++] = transparent[i];
8316
8317 for (i=0; i<number_semitransparent; i++)
8318 colormap[n++] = semitransparent[i];
8319
8320 for (i=0; i<number_opaque; i++)
8321 colormap[n++] = opaque[i];
8322
glennrpc6c391a2011-04-27 02:23:56 +00008323 ping_background.index +=
8324 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008325
glennrpd71e86a2011-02-24 01:28:37 +00008326 /* image_colors < 257; search the colormap instead of the pixels
8327 * to get ping_have_color and ping_have_non_bw
8328 */
8329 for (i=0; i<n; i++)
8330 {
8331 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008332 {
glennrpd71e86a2011-02-24 01:28:37 +00008333 if (colormap[i].red != colormap[i].green ||
8334 colormap[i].red != colormap[i].blue)
8335 {
8336 ping_have_color=MagickTrue;
8337 ping_have_non_bw=MagickTrue;
8338 break;
8339 }
8340 }
8341
8342 if (ping_have_non_bw == MagickFalse)
8343 {
8344 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008345 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008346 }
glennrp8bb3a022010-12-13 20:40:04 +00008347 }
8348
glennrpd71e86a2011-02-24 01:28:37 +00008349 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8350 (number_transparent == 0 && number_semitransparent == 0)) &&
8351 (((mng_info->write_png_colortype-1) ==
8352 PNG_COLOR_TYPE_PALETTE) ||
8353 (mng_info->write_png_colortype == 0)))
8354 {
glennrp6185c532011-01-14 17:58:40 +00008355 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008356 {
glennrpd71e86a2011-02-24 01:28:37 +00008357 if (n != (ssize_t) image_colors)
8358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8359 " image_colors (%d) and n (%d) don't match",
8360 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008361
glennrpd71e86a2011-02-24 01:28:37 +00008362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8363 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008364 }
glennrp03812ae2010-12-24 01:31:34 +00008365
glennrpd71e86a2011-02-24 01:28:37 +00008366 image->colors = image_colors;
8367
cristy018f07f2011-09-04 21:15:19 +00008368 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008369 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008370 ThrowWriterException(ResourceLimitError,
8371 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008372
8373 for (i=0; i< (ssize_t) image_colors; i++)
8374 image->colormap[i] = colormap[i];
8375
8376 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008377 {
glennrpd71e86a2011-02-24 01:28:37 +00008378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8379 " image->colors=%d (%d)",
8380 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008381
glennrpd71e86a2011-02-24 01:28:37 +00008382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8383 " Update the pixel indexes");
8384 }
glennrp6185c532011-01-14 17:58:40 +00008385
glennrpfd05d622011-02-25 04:10:33 +00008386 /* Sync the pixel indices with the new colormap */
8387
glennrpd71e86a2011-02-24 01:28:37 +00008388 for (y=0; y < (ssize_t) image->rows; y++)
8389 {
cristy97707062011-12-27 18:25:00 +00008390 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp6185c532011-01-14 17:58:40 +00008391
cristyacd2ed22011-08-30 01:44:23 +00008392 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008393 break;
glennrp6185c532011-01-14 17:58:40 +00008394
glennrpd71e86a2011-02-24 01:28:37 +00008395 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008396 {
glennrpd71e86a2011-02-24 01:28:37 +00008397 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008398 {
glennrpd71e86a2011-02-24 01:28:37 +00008399 if ((image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008400 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8401 image->colormap[i].red == GetPixelRed(image,q) &&
8402 image->colormap[i].green == GetPixelGreen(image,q) &&
8403 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008404 {
cristy4c08aed2011-07-01 19:47:50 +00008405 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008406 break;
glennrp6185c532011-01-14 17:58:40 +00008407 }
glennrp6185c532011-01-14 17:58:40 +00008408 }
cristyed231572011-07-14 02:18:59 +00008409 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008410 }
glennrp6185c532011-01-14 17:58:40 +00008411
glennrpd71e86a2011-02-24 01:28:37 +00008412 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8413 break;
8414 }
8415 }
8416 }
8417
8418 if (logging != MagickFalse)
8419 {
8420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8421 " image->colors=%d", (int) image->colors);
8422
8423 if (image->colormap != NULL)
8424 {
8425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008426 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008427
8428 for (i=0; i < (ssize_t) image->colors; i++)
8429 {
cristy72988482011-03-29 16:34:38 +00008430 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008431 {
8432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8433 " %d (%d,%d,%d,%d)",
8434 (int) i,
8435 (int) image->colormap[i].red,
8436 (int) image->colormap[i].green,
8437 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008438 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008439 }
glennrp6185c532011-01-14 17:58:40 +00008440 }
8441 }
glennrp03812ae2010-12-24 01:31:34 +00008442
glennrpd71e86a2011-02-24 01:28:37 +00008443 if (number_transparent < 257)
8444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8445 " number_transparent = %d",
8446 number_transparent);
8447 else
glennrp03812ae2010-12-24 01:31:34 +00008448
glennrpd71e86a2011-02-24 01:28:37 +00008449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8450 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008451
glennrpd71e86a2011-02-24 01:28:37 +00008452 if (number_opaque < 257)
8453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8454 " number_opaque = %d",
8455 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008456
glennrpd71e86a2011-02-24 01:28:37 +00008457 else
8458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8459 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008460
glennrpd71e86a2011-02-24 01:28:37 +00008461 if (number_semitransparent < 257)
8462 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8463 " number_semitransparent = %d",
8464 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008465
glennrpd71e86a2011-02-24 01:28:37 +00008466 else
8467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8468 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008469
glennrpd71e86a2011-02-24 01:28:37 +00008470 if (ping_have_non_bw == MagickFalse)
8471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8472 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008473
glennrpd71e86a2011-02-24 01:28:37 +00008474 else if (ping_have_color == MagickFalse)
8475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8476 " All pixels and the background are gray");
8477
8478 else
8479 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8480 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008481
glennrp03812ae2010-12-24 01:31:34 +00008482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8483 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008484 }
glennrpfd05d622011-02-25 04:10:33 +00008485
glennrpc8c2f062011-02-25 19:00:33 +00008486 if (mng_info->write_png8 == MagickFalse)
8487 break;
glennrpfd05d622011-02-25 04:10:33 +00008488
glennrpc8c2f062011-02-25 19:00:33 +00008489 /* Make any reductions necessary for the PNG8 format */
8490 if (image_colors <= 256 &&
8491 image_colors != 0 && image->colormap != NULL &&
8492 number_semitransparent == 0 &&
8493 number_transparent <= 1)
8494 break;
8495
8496 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008497 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8498 * transparent color so if more than one is transparent we merge
8499 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008500 */
glennrp130fc452011-08-20 03:43:18 +00008501 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008502 {
8503 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8504 " Thresholding the alpha channel to binary");
8505
8506 for (y=0; y < (ssize_t) image->rows; y++)
8507 {
cristy8a20fa02011-12-27 15:54:31 +00008508 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008509
cristy4c08aed2011-07-01 19:47:50 +00008510 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008511 break;
8512
8513 for (x=0; x < (ssize_t) image->columns; x++)
8514 {
glennrpf73547f2011-08-20 04:40:26 +00008515 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008516 {
cristy803640d2011-11-17 02:11:32 +00008517 SetPixelInfoPixel(image,&image->background_color,r);
cristy4c08aed2011-07-01 19:47:50 +00008518 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008519 }
8520 else
cristy4c08aed2011-07-01 19:47:50 +00008521 SetPixelAlpha(image,OpaqueAlpha,r);
cristyed231572011-07-14 02:18:59 +00008522 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008523 }
glennrpbb4f99d2011-05-22 11:13:17 +00008524
glennrpc8c2f062011-02-25 19:00:33 +00008525 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8526 break;
8527
8528 if (image_colors != 0 && image_colors <= 256 &&
8529 image->colormap != NULL)
8530 for (i=0; i<image_colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00008531 image->colormap[i].alpha =
8532 (image->colormap[i].alpha > TransparentAlpha/2 ?
8533 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008534 }
8535 continue;
8536 }
8537
8538 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008539 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8540 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8541 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008542 */
glennrpd3371642011-03-22 19:42:23 +00008543 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8544 {
8545 if (logging != MagickFalse)
8546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8547 " Quantizing the background color to 4-4-4");
8548
8549 tried_444 = MagickTrue;
8550
glennrp91d99252011-06-25 14:30:13 +00008551 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008552
8553 if (logging != MagickFalse)
8554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8555 " Quantizing the pixel colors to 4-4-4");
8556
8557 if (image->colormap == NULL)
8558 {
8559 for (y=0; y < (ssize_t) image->rows; y++)
8560 {
cristy8a20fa02011-12-27 15:54:31 +00008561 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpd3371642011-03-22 19:42:23 +00008562
cristy4c08aed2011-07-01 19:47:50 +00008563 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008564 break;
8565
8566 for (x=0; x < (ssize_t) image->columns; x++)
8567 {
cristy4c08aed2011-07-01 19:47:50 +00008568 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008569 LBR04PixelRGB(r);
cristy8a20fa02011-12-27 15:54:31 +00008570 r+=GetPixelChannels(image);
glennrpd3371642011-03-22 19:42:23 +00008571 }
glennrpbb4f99d2011-05-22 11:13:17 +00008572
glennrpd3371642011-03-22 19:42:23 +00008573 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8574 break;
8575 }
8576 }
8577
8578 else /* Should not reach this; colormap already exists and
8579 must be <= 256 */
8580 {
8581 if (logging != MagickFalse)
8582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8583 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008584
glennrpd3371642011-03-22 19:42:23 +00008585 for (i=0; i<image_colors; i++)
8586 {
glennrp91d99252011-06-25 14:30:13 +00008587 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008588 }
8589 }
8590 continue;
8591 }
8592
glennrp82b3c532011-03-22 19:20:54 +00008593 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8594 {
8595 if (logging != MagickFalse)
8596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8597 " Quantizing the background color to 3-3-3");
8598
8599 tried_333 = MagickTrue;
8600
glennrp91d99252011-06-25 14:30:13 +00008601 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008602
8603 if (logging != MagickFalse)
8604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008605 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008606
8607 if (image->colormap == NULL)
8608 {
8609 for (y=0; y < (ssize_t) image->rows; y++)
8610 {
cristy8a20fa02011-12-27 15:54:31 +00008611 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp82b3c532011-03-22 19:20:54 +00008612
cristy4c08aed2011-07-01 19:47:50 +00008613 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008614 break;
8615
8616 for (x=0; x < (ssize_t) image->columns; x++)
8617 {
cristy4c08aed2011-07-01 19:47:50 +00008618 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8619 LBR03RGB(r);
cristy8a20fa02011-12-27 15:54:31 +00008620 r+=GetPixelChannels(image);
glennrp82b3c532011-03-22 19:20:54 +00008621 }
glennrpbb4f99d2011-05-22 11:13:17 +00008622
glennrp82b3c532011-03-22 19:20:54 +00008623 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8624 break;
8625 }
8626 }
8627
8628 else /* Should not reach this; colormap already exists and
8629 must be <= 256 */
8630 {
8631 if (logging != MagickFalse)
8632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008633 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008634 for (i=0; i<image_colors; i++)
8635 {
glennrp91d99252011-06-25 14:30:13 +00008636 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008637 }
glennrpd3371642011-03-22 19:42:23 +00008638 }
8639 continue;
glennrp82b3c532011-03-22 19:20:54 +00008640 }
glennrpc8c2f062011-02-25 19:00:33 +00008641
glennrp8ca51ad2011-05-12 21:22:32 +00008642 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008643 {
8644 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008646 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008647
glennrp8ca51ad2011-05-12 21:22:32 +00008648 tried_332 = MagickTrue;
8649
glennrp3faa9a32011-04-23 14:00:25 +00008650 /* Red and green were already done so we only quantize the blue
8651 * channel
8652 */
8653
glennrp91d99252011-06-25 14:30:13 +00008654 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008655
glennrpc8c2f062011-02-25 19:00:33 +00008656 if (logging != MagickFalse)
8657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008658 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008659
glennrpc8c2f062011-02-25 19:00:33 +00008660 if (image->colormap == NULL)
8661 {
8662 for (y=0; y < (ssize_t) image->rows; y++)
8663 {
cristy8a20fa02011-12-27 15:54:31 +00008664 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008665
cristy4c08aed2011-07-01 19:47:50 +00008666 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008667 break;
8668
8669 for (x=0; x < (ssize_t) image->columns; x++)
8670 {
cristy4c08aed2011-07-01 19:47:50 +00008671 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008672 LBR02PixelBlue(r);
cristy8a20fa02011-12-27 15:54:31 +00008673 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008674 }
glennrpbb4f99d2011-05-22 11:13:17 +00008675
glennrpc8c2f062011-02-25 19:00:33 +00008676 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8677 break;
8678 }
8679 }
glennrpfd05d622011-02-25 04:10:33 +00008680
glennrpc8c2f062011-02-25 19:00:33 +00008681 else /* Should not reach this; colormap already exists and
8682 must be <= 256 */
8683 {
8684 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008686 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008687 for (i=0; i<image_colors; i++)
8688 {
glennrp91d99252011-06-25 14:30:13 +00008689 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008690 }
8691 }
8692 continue;
8693 }
8694 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008695
8696 if (image_colors == 0 || image_colors > 256)
8697 {
8698 /* Take care of special case with 256 colors + 1 transparent
8699 * color. We don't need to quantize to 2-3-2-1; we only need to
8700 * eliminate one color, so we'll merge the two darkest red
8701 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8702 */
8703 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8704 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8705 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8706 {
8707 image->background_color.red=ScaleCharToQuantum(0x24);
8708 }
glennrpbb4f99d2011-05-22 11:13:17 +00008709
glennrp8ca51ad2011-05-12 21:22:32 +00008710 if (image->colormap == NULL)
8711 {
8712 for (y=0; y < (ssize_t) image->rows; y++)
8713 {
cristy8a20fa02011-12-27 15:54:31 +00008714 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008715
cristy4c08aed2011-07-01 19:47:50 +00008716 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008717 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008718
glennrp8ca51ad2011-05-12 21:22:32 +00008719 for (x=0; x < (ssize_t) image->columns; x++)
8720 {
cristy4c08aed2011-07-01 19:47:50 +00008721 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8722 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8723 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8724 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008725 {
cristy4c08aed2011-07-01 19:47:50 +00008726 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008727 }
cristyed231572011-07-14 02:18:59 +00008728 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008729 }
glennrpbb4f99d2011-05-22 11:13:17 +00008730
glennrp8ca51ad2011-05-12 21:22:32 +00008731 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8732 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008733
glennrp8ca51ad2011-05-12 21:22:32 +00008734 }
8735 }
8736
8737 else
8738 {
8739 for (i=0; i<image_colors; i++)
8740 {
8741 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8742 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8743 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8744 {
8745 image->colormap[i].red=ScaleCharToQuantum(0x24);
8746 }
8747 }
8748 }
8749 }
glennrpd71e86a2011-02-24 01:28:37 +00008750 }
glennrpfd05d622011-02-25 04:10:33 +00008751 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008752
glennrpfd05d622011-02-25 04:10:33 +00008753 /* If we are excluding the tRNS chunk and there is transparency,
8754 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8755 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008756 */
glennrp0e8ea192010-12-24 18:00:33 +00008757 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8758 (number_transparent != 0 || number_semitransparent != 0))
8759 {
glennrpd17915c2011-04-29 14:24:22 +00008760 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008761
8762 if (ping_have_color == MagickFalse)
8763 mng_info->write_png_colortype = 5;
8764
8765 else
8766 mng_info->write_png_colortype = 7;
8767
glennrp8d579662011-02-23 02:05:02 +00008768 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008769 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008770 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008771
glennrp0e8ea192010-12-24 18:00:33 +00008772 }
8773
glennrpfd05d622011-02-25 04:10:33 +00008774 /* See if cheap transparency is possible. It is only possible
8775 * when there is a single transparent color, no semitransparent
8776 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008777 * as the transparent color. We only need this information if
8778 * we are writing a PNG with colortype 0 or 2, and we have not
8779 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008780 */
glennrp5a39f372011-02-25 04:52:16 +00008781 if (number_transparent == 1 &&
8782 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008783 {
8784 ping_have_cheap_transparency = MagickTrue;
8785
8786 if (number_semitransparent != 0)
8787 ping_have_cheap_transparency = MagickFalse;
8788
8789 else if (image_colors == 0 || image_colors > 256 ||
8790 image->colormap == NULL)
8791 {
cristy4c08aed2011-07-01 19:47:50 +00008792 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008793 *q;
8794
glennrpfd05d622011-02-25 04:10:33 +00008795 for (y=0; y < (ssize_t) image->rows; y++)
8796 {
8797 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8798
cristyacd2ed22011-08-30 01:44:23 +00008799 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008800 break;
8801
8802 for (x=0; x < (ssize_t) image->columns; x++)
8803 {
cristy4c08aed2011-07-01 19:47:50 +00008804 if (GetPixelAlpha(image,q) != TransparentAlpha &&
glennrp847370c2011-07-05 17:37:15 +00008805 (unsigned short) GetPixelRed(image,q) ==
8806 ping_trans_color.red &&
8807 (unsigned short) GetPixelGreen(image,q) ==
8808 ping_trans_color.green &&
8809 (unsigned short) GetPixelBlue(image,q) ==
8810 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008811 {
8812 ping_have_cheap_transparency = MagickFalse;
8813 break;
8814 }
8815
cristyed231572011-07-14 02:18:59 +00008816 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008817 }
glennrpbb4f99d2011-05-22 11:13:17 +00008818
glennrpfd05d622011-02-25 04:10:33 +00008819 if (ping_have_cheap_transparency == MagickFalse)
8820 break;
8821 }
8822 }
8823 else
8824 {
glennrp67b9c1a2011-04-22 18:47:36 +00008825 /* Assuming that image->colormap[0] is the one transparent color
8826 * and that all others are opaque.
8827 */
glennrpfd05d622011-02-25 04:10:33 +00008828 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008829 for (i=1; i<image_colors; i++)
8830 if (image->colormap[i].red == image->colormap[0].red &&
8831 image->colormap[i].green == image->colormap[0].green &&
8832 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008833 {
glennrp67b9c1a2011-04-22 18:47:36 +00008834 ping_have_cheap_transparency = MagickFalse;
8835 break;
glennrpfd05d622011-02-25 04:10:33 +00008836 }
8837 }
glennrpbb4f99d2011-05-22 11:13:17 +00008838
glennrpfd05d622011-02-25 04:10:33 +00008839 if (logging != MagickFalse)
8840 {
8841 if (ping_have_cheap_transparency == MagickFalse)
8842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8843 " Cheap transparency is not possible.");
8844
8845 else
8846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8847 " Cheap transparency is possible.");
8848 }
8849 }
8850 else
8851 ping_have_cheap_transparency = MagickFalse;
8852
glennrp8640fb52010-11-23 15:48:26 +00008853 image_depth=image->depth;
8854
glennrp26c990a2010-11-23 02:23:20 +00008855 quantum_info = (QuantumInfo *) NULL;
8856 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008857 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008858 image_matte=image->matte;
8859
glennrp0fe50b42010-11-16 03:52:51 +00008860 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008861 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008862
glennrp52a479c2011-02-26 21:14:38 +00008863 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8864 (image->colors == 0 || image->colormap == NULL))
8865 {
glennrp52a479c2011-02-26 21:14:38 +00008866 image_info=DestroyImageInfo(image_info);
8867 image=DestroyImage(image);
cristyc82a27b2011-10-21 01:07:16 +00008868 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
glennrp15e01552011-03-06 23:29:17 +00008869 "Cannot write PNG8 or color-type 3; colormap is NULL",
8870 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008871#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8872 UnlockSemaphoreInfo(ping_semaphore);
8873#endif
8874 return(MagickFalse);
8875 }
8876
cristy3ed852e2009-09-05 21:47:34 +00008877 /*
8878 Allocate the PNG structures
8879 */
8880#ifdef PNG_USER_MEM_SUPPORTED
cristyc82a27b2011-10-21 01:07:16 +00008881 error_info.image=image;
8882 error_info.exception=exception;
8883 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008884 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8885 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008886
cristy3ed852e2009-09-05 21:47:34 +00008887#else
cristyc82a27b2011-10-21 01:07:16 +00008888 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008889 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008890
cristy3ed852e2009-09-05 21:47:34 +00008891#endif
8892 if (ping == (png_struct *) NULL)
8893 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008894
cristy3ed852e2009-09-05 21:47:34 +00008895 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008896
cristy3ed852e2009-09-05 21:47:34 +00008897 if (ping_info == (png_info *) NULL)
8898 {
8899 png_destroy_write_struct(&ping,(png_info **) NULL);
8900 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8901 }
glennrp0fe50b42010-11-16 03:52:51 +00008902
cristy3ed852e2009-09-05 21:47:34 +00008903 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008904 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008905
glennrp5af765f2010-03-30 11:12:18 +00008906 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008907 {
8908 /*
8909 PNG write failed.
8910 */
8911#ifdef PNG_DEBUG
8912 if (image_info->verbose)
8913 (void) printf("PNG write has failed.\n");
8914#endif
8915 png_destroy_write_struct(&ping,&ping_info);
8916#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008917 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008918#endif
glennrpda8f3a72011-02-27 23:54:12 +00008919 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008920 (void) CloseBlob(image);
8921 image_info=DestroyImageInfo(image_info);
8922 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008923 return(MagickFalse);
8924 }
8925 /*
8926 Prepare PNG for writing.
8927 */
8928#if defined(PNG_MNG_FEATURES_SUPPORTED)
8929 if (mng_info->write_mng)
8930 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008931
cristy3ed852e2009-09-05 21:47:34 +00008932#else
8933# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8934 if (mng_info->write_mng)
8935 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008936
cristy3ed852e2009-09-05 21:47:34 +00008937# endif
8938#endif
glennrp2b013e42010-11-24 16:55:50 +00008939
cristy3ed852e2009-09-05 21:47:34 +00008940 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008941
cristy4e5bc842010-06-09 13:56:01 +00008942 ping_width=(png_uint_32) image->columns;
8943 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008944
cristy3ed852e2009-09-05 21:47:34 +00008945 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8946 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008947
cristy3ed852e2009-09-05 21:47:34 +00008948 if (mng_info->write_png_depth != 0)
8949 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008950
cristy3ed852e2009-09-05 21:47:34 +00008951 /* Adjust requested depth to next higher valid depth if necessary */
8952 if (image_depth > 8)
8953 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008954
cristy3ed852e2009-09-05 21:47:34 +00008955 if ((image_depth > 4) && (image_depth < 8))
8956 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008957
cristy3ed852e2009-09-05 21:47:34 +00008958 if (image_depth == 3)
8959 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008960
cristy3ed852e2009-09-05 21:47:34 +00008961 if (logging != MagickFalse)
8962 {
8963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008964 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008966 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008968 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008970 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008972 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008973 }
glennrp8640fb52010-11-23 15:48:26 +00008974
cristy3ed852e2009-09-05 21:47:34 +00008975 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008976 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008977
glennrp26f37912010-12-23 16:22:42 +00008978
cristy3ed852e2009-09-05 21:47:34 +00008979#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008980 if (ping_exclude_pHYs == MagickFalse)
8981 {
cristy2a11bef2011-10-28 18:33:11 +00008982 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00008983 (!mng_info->write_mng || !mng_info->equal_physs))
8984 {
glennrp0fe50b42010-11-16 03:52:51 +00008985 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8987 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008988
8989 if (image->units == PixelsPerInchResolution)
8990 {
glennrpdfd70802010-11-14 01:23:35 +00008991 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008992 ping_pHYs_x_resolution=
cristy2a11bef2011-10-28 18:33:11 +00008993 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
glennrp823b55c2011-03-14 18:46:46 +00008994 ping_pHYs_y_resolution=
cristy2a11bef2011-10-28 18:33:11 +00008995 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008996 }
glennrpdfd70802010-11-14 01:23:35 +00008997
cristy3ed852e2009-09-05 21:47:34 +00008998 else if (image->units == PixelsPerCentimeterResolution)
8999 {
glennrpdfd70802010-11-14 01:23:35 +00009000 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
cristy2a11bef2011-10-28 18:33:11 +00009001 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9002 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00009003 }
glennrp991d11d2010-11-12 21:55:28 +00009004
cristy3ed852e2009-09-05 21:47:34 +00009005 else
9006 {
glennrpdfd70802010-11-14 01:23:35 +00009007 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
cristy2a11bef2011-10-28 18:33:11 +00009008 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9009 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00009010 }
glennrp991d11d2010-11-12 21:55:28 +00009011
glennrp823b55c2011-03-14 18:46:46 +00009012 if (logging != MagickFalse)
9013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9014 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9015 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9016 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009017 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009018 }
glennrp26f37912010-12-23 16:22:42 +00009019 }
cristy3ed852e2009-09-05 21:47:34 +00009020#endif
glennrpa521b2f2010-10-29 04:11:03 +00009021
glennrp26f37912010-12-23 16:22:42 +00009022 if (ping_exclude_bKGD == MagickFalse)
9023 {
glennrpa521b2f2010-10-29 04:11:03 +00009024 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009025 {
glennrpa521b2f2010-10-29 04:11:03 +00009026 unsigned int
9027 mask;
cristy3ed852e2009-09-05 21:47:34 +00009028
glennrpa521b2f2010-10-29 04:11:03 +00009029 mask=0xffff;
9030 if (ping_bit_depth == 8)
9031 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009032
glennrpa521b2f2010-10-29 04:11:03 +00009033 if (ping_bit_depth == 4)
9034 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009035
glennrpa521b2f2010-10-29 04:11:03 +00009036 if (ping_bit_depth == 2)
9037 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009038
glennrpa521b2f2010-10-29 04:11:03 +00009039 if (ping_bit_depth == 1)
9040 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009041
glennrpa521b2f2010-10-29 04:11:03 +00009042 ping_background.red=(png_uint_16)
9043 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009044
glennrpa521b2f2010-10-29 04:11:03 +00009045 ping_background.green=(png_uint_16)
9046 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009047
glennrpa521b2f2010-10-29 04:11:03 +00009048 ping_background.blue=(png_uint_16)
9049 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009050
9051 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009052 }
cristy3ed852e2009-09-05 21:47:34 +00009053
glennrp0fe50b42010-11-16 03:52:51 +00009054 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009055 {
9056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9057 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9059 " background_color index is %d",
9060 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009061
9062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9063 " ping_bit_depth=%d",ping_bit_depth);
9064 }
glennrp0fe50b42010-11-16 03:52:51 +00009065
9066 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009067 }
glennrp0fe50b42010-11-16 03:52:51 +00009068
cristy3ed852e2009-09-05 21:47:34 +00009069 /*
9070 Select the color type.
9071 */
9072 matte=image_matte;
9073 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009074
glennrp1273f7b2011-02-24 03:20:30 +00009075 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009076 {
glennrp0fe50b42010-11-16 03:52:51 +00009077
glennrpfd05d622011-02-25 04:10:33 +00009078 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009079 for reducing the sample depth from 8. */
9080
glennrp0fe50b42010-11-16 03:52:51 +00009081 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009082
glennrp8bb3a022010-12-13 20:40:04 +00009083 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009084
9085 /*
9086 Set image palette.
9087 */
9088 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9089
glennrp0fe50b42010-11-16 03:52:51 +00009090 if (logging != MagickFalse)
9091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9092 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009093 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009094
9095 for (i=0; i < (ssize_t) number_colors; i++)
9096 {
9097 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9098 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9099 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9100 if (logging != MagickFalse)
9101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009102#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009103 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009104#else
9105 " %5ld (%5d,%5d,%5d)",
9106#endif
glennrp0fe50b42010-11-16 03:52:51 +00009107 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9108
9109 }
glennrp2b013e42010-11-24 16:55:50 +00009110
glennrp8bb3a022010-12-13 20:40:04 +00009111 ping_have_PLTE=MagickTrue;
9112 image_depth=ping_bit_depth;
9113 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009114
glennrp58e01762011-01-07 15:28:54 +00009115 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009116 {
glennrp0fe50b42010-11-16 03:52:51 +00009117 /*
9118 Identify which colormap entry is transparent.
9119 */
9120 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009121 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009122
glennrp8bb3a022010-12-13 20:40:04 +00009123 for (i=0; i < (ssize_t) number_transparent; i++)
9124 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009125
glennrp0fe50b42010-11-16 03:52:51 +00009126
glennrp2cc891a2010-12-24 13:44:32 +00009127 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009128 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009129
9130 if (ping_num_trans == 0)
9131 ping_have_tRNS=MagickFalse;
9132
glennrp8bb3a022010-12-13 20:40:04 +00009133 else
9134 ping_have_tRNS=MagickTrue;
9135 }
glennrp0fe50b42010-11-16 03:52:51 +00009136
glennrp1273f7b2011-02-24 03:20:30 +00009137 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009138 {
glennrp1273f7b2011-02-24 03:20:30 +00009139 /*
9140 * Identify which colormap entry is the background color.
9141 */
9142
glennrp4f25bd02011-01-01 18:51:28 +00009143 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9144 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9145 break;
glennrp0fe50b42010-11-16 03:52:51 +00009146
glennrp4f25bd02011-01-01 18:51:28 +00009147 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009148
9149 if (logging != MagickFalse)
9150 {
9151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9152 " background_color index is %d",
9153 (int) ping_background.index);
9154 }
glennrp4f25bd02011-01-01 18:51:28 +00009155 }
cristy3ed852e2009-09-05 21:47:34 +00009156 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009157
glennrp7e65e932011-08-19 02:31:16 +00009158 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009159 {
9160 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009161 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009162 }
glennrp0fe50b42010-11-16 03:52:51 +00009163
glennrp7e65e932011-08-19 02:31:16 +00009164 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009165 {
9166 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009167 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009168 }
glennrp0fe50b42010-11-16 03:52:51 +00009169
glennrp8bb3a022010-12-13 20:40:04 +00009170 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009171 {
glennrp5af765f2010-03-30 11:12:18 +00009172 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009173
glennrp8bb3a022010-12-13 20:40:04 +00009174 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009175 {
glennrp5af765f2010-03-30 11:12:18 +00009176 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009177
glennrp5af765f2010-03-30 11:12:18 +00009178 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9179 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009180 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009181
glennrp8bb3a022010-12-13 20:40:04 +00009182 else
9183 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009184
9185 if (logging != MagickFalse)
9186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9187 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009188 }
glennrp0fe50b42010-11-16 03:52:51 +00009189
glennrp7c4c9e62011-03-21 20:23:32 +00009190 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009191 {
9192 if (logging != MagickFalse)
9193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009194 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009195
glennrpd6bf1612010-12-17 17:28:54 +00009196 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009197 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009198
glennrpd6bf1612010-12-17 17:28:54 +00009199 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009200 {
glennrp5af765f2010-03-30 11:12:18 +00009201 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009202 image_matte=MagickFalse;
9203 }
glennrp0fe50b42010-11-16 03:52:51 +00009204
glennrpd6bf1612010-12-17 17:28:54 +00009205 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009206 {
glennrp5af765f2010-03-30 11:12:18 +00009207 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009208 image_matte=MagickTrue;
9209 }
glennrp0fe50b42010-11-16 03:52:51 +00009210
glennrp5aa37f62011-01-02 03:07:57 +00009211 if (image_info->type == PaletteType ||
9212 image_info->type == PaletteMatteType)
9213 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9214
glennrp7c4c9e62011-03-21 20:23:32 +00009215 if (mng_info->write_png_colortype == 0 &&
9216 (image_info->type == UndefinedType ||
9217 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009218 {
glennrp5aa37f62011-01-02 03:07:57 +00009219 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009220 {
glennrp5aa37f62011-01-02 03:07:57 +00009221 if (image_matte == MagickFalse)
9222 {
9223 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9224 image_matte=MagickFalse;
9225 }
glennrp0fe50b42010-11-16 03:52:51 +00009226
glennrp0b206f52011-01-07 04:55:32 +00009227 else
glennrp5aa37f62011-01-02 03:07:57 +00009228 {
9229 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9230 image_matte=MagickTrue;
9231 }
9232 }
9233 else
glennrp8bb3a022010-12-13 20:40:04 +00009234 {
glennrp5aa37f62011-01-02 03:07:57 +00009235 if (image_matte == MagickFalse)
9236 {
9237 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9238 image_matte=MagickFalse;
9239 }
glennrp8bb3a022010-12-13 20:40:04 +00009240
glennrp0b206f52011-01-07 04:55:32 +00009241 else
glennrp5aa37f62011-01-02 03:07:57 +00009242 {
9243 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9244 image_matte=MagickTrue;
9245 }
9246 }
glennrp0fe50b42010-11-16 03:52:51 +00009247 }
glennrp5aa37f62011-01-02 03:07:57 +00009248
cristy3ed852e2009-09-05 21:47:34 +00009249 }
glennrp0fe50b42010-11-16 03:52:51 +00009250
cristy3ed852e2009-09-05 21:47:34 +00009251 if (logging != MagickFalse)
9252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009253 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009254
glennrp5af765f2010-03-30 11:12:18 +00009255 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009256 {
9257 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9258 ping_color_type == PNG_COLOR_TYPE_RGB ||
9259 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9260 ping_bit_depth=8;
9261 }
cristy3ed852e2009-09-05 21:47:34 +00009262
glennrpd6bf1612010-12-17 17:28:54 +00009263 old_bit_depth=ping_bit_depth;
9264
glennrp5af765f2010-03-30 11:12:18 +00009265 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009266 {
glennrp8d579662011-02-23 02:05:02 +00009267 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9268 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009269 }
glennrp8640fb52010-11-23 15:48:26 +00009270
glennrp5af765f2010-03-30 11:12:18 +00009271 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009272 {
cristy35ef8242010-06-03 16:24:13 +00009273 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009274 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009275
9276 if (image->colors == 0)
9277 {
glennrp0fe50b42010-11-16 03:52:51 +00009278 /* DO SOMETHING */
cristyc82a27b2011-10-21 01:07:16 +00009279 (void) ThrowMagickException(exception,
glennrp0f111982010-07-07 20:18:33 +00009280 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009281 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009282 }
9283
cristy35ef8242010-06-03 16:24:13 +00009284 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009285 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009286 }
glennrp2b013e42010-11-24 16:55:50 +00009287
glennrpd6bf1612010-12-17 17:28:54 +00009288 if (logging != MagickFalse)
9289 {
9290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9291 " Number of colors: %.20g",(double) image_colors);
9292
9293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9294 " Tentative PNG bit depth: %d",ping_bit_depth);
9295 }
9296
9297 if (ping_bit_depth < (int) mng_info->write_png_depth)
9298 ping_bit_depth = mng_info->write_png_depth;
9299 }
glennrp2cc891a2010-12-24 13:44:32 +00009300
glennrp5af765f2010-03-30 11:12:18 +00009301 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009302
cristy3ed852e2009-09-05 21:47:34 +00009303 if (logging != MagickFalse)
9304 {
9305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009306 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009307
cristy3ed852e2009-09-05 21:47:34 +00009308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009309 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009310
cristy3ed852e2009-09-05 21:47:34 +00009311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009312 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009313
cristy3ed852e2009-09-05 21:47:34 +00009314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009315
glennrp8640fb52010-11-23 15:48:26 +00009316 " image->depth: %.20g",(double) image->depth);
9317
9318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009319 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009320 }
9321
glennrp58e01762011-01-07 15:28:54 +00009322 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009323 {
glennrp4f25bd02011-01-01 18:51:28 +00009324 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009325 {
glennrp7c4c9e62011-03-21 20:23:32 +00009326 if (mng_info->write_png_colortype == 0)
9327 {
9328 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009329
glennrp7c4c9e62011-03-21 20:23:32 +00009330 if (ping_have_color != MagickFalse)
9331 ping_color_type=PNG_COLOR_TYPE_RGBA;
9332 }
glennrp4f25bd02011-01-01 18:51:28 +00009333
9334 /*
9335 * Determine if there is any transparent color.
9336 */
9337 if (number_transparent + number_semitransparent == 0)
9338 {
9339 /*
9340 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9341 */
glennrpa6a06632011-01-19 15:15:34 +00009342
glennrp4f25bd02011-01-01 18:51:28 +00009343 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009344
9345 if (mng_info->write_png_colortype == 0)
9346 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009347 }
9348
9349 else
9350 {
9351 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009352 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009353
9354 mask=0xffff;
9355
9356 if (ping_bit_depth == 8)
9357 mask=0x00ff;
9358
9359 if (ping_bit_depth == 4)
9360 mask=0x000f;
9361
9362 if (ping_bit_depth == 2)
9363 mask=0x0003;
9364
9365 if (ping_bit_depth == 1)
9366 mask=0x0001;
9367
9368 ping_trans_color.red=(png_uint_16)
9369 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9370
9371 ping_trans_color.green=(png_uint_16)
9372 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9373
9374 ping_trans_color.blue=(png_uint_16)
9375 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9376
9377 ping_trans_color.gray=(png_uint_16)
cristy101ab702011-10-13 13:06:32 +00009378 (ScaleQuantumToShort(GetPixelInfoIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009379 image->colormap)) & mask);
9380
9381 ping_trans_color.index=(png_byte) 0;
9382
9383 ping_have_tRNS=MagickTrue;
9384 }
9385
9386 if (ping_have_tRNS != MagickFalse)
9387 {
9388 /*
glennrpfd05d622011-02-25 04:10:33 +00009389 * Determine if there is one and only one transparent color
9390 * and if so if it is fully transparent.
9391 */
9392 if (ping_have_cheap_transparency == MagickFalse)
9393 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009394 }
9395
9396 if (ping_have_tRNS != MagickFalse)
9397 {
glennrp7c4c9e62011-03-21 20:23:32 +00009398 if (mng_info->write_png_colortype == 0)
9399 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009400
9401 if (image_depth == 8)
9402 {
9403 ping_trans_color.red&=0xff;
9404 ping_trans_color.green&=0xff;
9405 ping_trans_color.blue&=0xff;
9406 ping_trans_color.gray&=0xff;
9407 }
9408 }
9409 }
cristy3ed852e2009-09-05 21:47:34 +00009410 else
9411 {
cristy3ed852e2009-09-05 21:47:34 +00009412 if (image_depth == 8)
9413 {
glennrp5af765f2010-03-30 11:12:18 +00009414 ping_trans_color.red&=0xff;
9415 ping_trans_color.green&=0xff;
9416 ping_trans_color.blue&=0xff;
9417 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009418 }
9419 }
9420 }
glennrp8640fb52010-11-23 15:48:26 +00009421
cristy3ed852e2009-09-05 21:47:34 +00009422 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009423
glennrp2e09f552010-11-14 00:38:48 +00009424 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009425 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009426
glennrp39992b42010-11-14 00:03:43 +00009427 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009428 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009429 ping_have_color == MagickFalse &&
9430 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009431 {
cristy35ef8242010-06-03 16:24:13 +00009432 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009433
cristy3ed852e2009-09-05 21:47:34 +00009434 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009435 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009436
glennrp7c4c9e62011-03-21 20:23:32 +00009437 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009438 {
glennrp5af765f2010-03-30 11:12:18 +00009439 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009440
cristy3ed852e2009-09-05 21:47:34 +00009441 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009442 {
9443 if (logging != MagickFalse)
9444 {
9445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9446 " Scaling ping_trans_color (0)");
9447 }
9448 ping_trans_color.gray*=0x0101;
9449 }
cristy3ed852e2009-09-05 21:47:34 +00009450 }
glennrp0fe50b42010-11-16 03:52:51 +00009451
cristy3ed852e2009-09-05 21:47:34 +00009452 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9453 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009454
glennrp136ee3a2011-04-27 15:47:45 +00009455 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009456 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009457 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009458
cristy3ed852e2009-09-05 21:47:34 +00009459 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009460 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009461
cristy3ed852e2009-09-05 21:47:34 +00009462 else
9463 {
glennrp5af765f2010-03-30 11:12:18 +00009464 ping_bit_depth=8;
9465 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009466 {
9467 if(!mng_info->write_png_depth)
9468 {
glennrp5af765f2010-03-30 11:12:18 +00009469 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009470
cristy35ef8242010-06-03 16:24:13 +00009471 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009472 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009473 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009474 }
9475 }
glennrp2b013e42010-11-24 16:55:50 +00009476
glennrp0fe50b42010-11-16 03:52:51 +00009477 else if (ping_color_type ==
9478 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009479 mng_info->IsPalette)
9480 {
cristy3ed852e2009-09-05 21:47:34 +00009481 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009482
cristy3ed852e2009-09-05 21:47:34 +00009483 int
9484 depth_4_ok=MagickTrue,
9485 depth_2_ok=MagickTrue,
9486 depth_1_ok=MagickTrue;
9487
cristybb503372010-05-27 20:51:26 +00009488 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009489 {
9490 unsigned char
9491 intensity;
9492
9493 intensity=ScaleQuantumToChar(image->colormap[i].red);
9494
9495 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9496 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9497 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9498 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009499 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009500 depth_1_ok=MagickFalse;
9501 }
glennrp2b013e42010-11-24 16:55:50 +00009502
cristy3ed852e2009-09-05 21:47:34 +00009503 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009504 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009505
cristy3ed852e2009-09-05 21:47:34 +00009506 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009507 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009508
cristy3ed852e2009-09-05 21:47:34 +00009509 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009510 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009511 }
9512 }
glennrp2b013e42010-11-24 16:55:50 +00009513
glennrp5af765f2010-03-30 11:12:18 +00009514 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009515 }
glennrp0fe50b42010-11-16 03:52:51 +00009516
cristy3ed852e2009-09-05 21:47:34 +00009517 else
glennrp0fe50b42010-11-16 03:52:51 +00009518
cristy3ed852e2009-09-05 21:47:34 +00009519 if (mng_info->IsPalette)
9520 {
glennrp17a14852010-05-10 03:01:59 +00009521 number_colors=image_colors;
9522
cristy3ed852e2009-09-05 21:47:34 +00009523 if (image_depth <= 8)
9524 {
cristy3ed852e2009-09-05 21:47:34 +00009525 /*
9526 Set image palette.
9527 */
glennrp5af765f2010-03-30 11:12:18 +00009528 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009529
glennrp58e01762011-01-07 15:28:54 +00009530 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009531 {
glennrp9c1eb072010-06-06 22:19:15 +00009532 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009533
glennrp3b51f0e2010-11-27 18:14:08 +00009534 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9536 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009537 }
glennrp0fe50b42010-11-16 03:52:51 +00009538
cristy3ed852e2009-09-05 21:47:34 +00009539 else
9540 {
cristybb503372010-05-27 20:51:26 +00009541 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009542 {
9543 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9544 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9545 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9546 }
glennrp0fe50b42010-11-16 03:52:51 +00009547
glennrp3b51f0e2010-11-27 18:14:08 +00009548 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009550 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009551 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009552
glennrp39992b42010-11-14 00:03:43 +00009553 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009554 }
glennrp0fe50b42010-11-16 03:52:51 +00009555
cristy3ed852e2009-09-05 21:47:34 +00009556 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009557 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009558 {
cristybefe4d22010-06-07 01:18:58 +00009559 size_t
9560 one;
9561
glennrp5af765f2010-03-30 11:12:18 +00009562 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009563 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009564
cristy94b11832011-09-08 19:46:03 +00009565 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009566 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009567 }
glennrp0fe50b42010-11-16 03:52:51 +00009568
glennrp5af765f2010-03-30 11:12:18 +00009569 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009570
glennrp58e01762011-01-07 15:28:54 +00009571 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009572 {
glennrp0fe50b42010-11-16 03:52:51 +00009573 /*
glennrpd6bf1612010-12-17 17:28:54 +00009574 * Set up trans_colors array.
9575 */
glennrp0fe50b42010-11-16 03:52:51 +00009576 assert(number_colors <= 256);
9577
glennrpd6bf1612010-12-17 17:28:54 +00009578 ping_num_trans=(unsigned short) (number_transparent +
9579 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009580
9581 if (ping_num_trans == 0)
9582 ping_have_tRNS=MagickFalse;
9583
glennrpd6bf1612010-12-17 17:28:54 +00009584 else
glennrp0fe50b42010-11-16 03:52:51 +00009585 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009586 if (logging != MagickFalse)
9587 {
9588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9589 " Scaling ping_trans_color (1)");
9590 }
glennrpd6bf1612010-12-17 17:28:54 +00009591 ping_have_tRNS=MagickTrue;
9592
9593 for (i=0; i < ping_num_trans; i++)
9594 {
cristy4c08aed2011-07-01 19:47:50 +00009595 ping_trans_alpha[i]= (png_byte)
9596 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009597 }
glennrp0fe50b42010-11-16 03:52:51 +00009598 }
9599 }
cristy3ed852e2009-09-05 21:47:34 +00009600 }
9601 }
glennrp0fe50b42010-11-16 03:52:51 +00009602
cristy3ed852e2009-09-05 21:47:34 +00009603 else
9604 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009605
cristy3ed852e2009-09-05 21:47:34 +00009606 if (image_depth < 8)
9607 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009608
cristy3ed852e2009-09-05 21:47:34 +00009609 if ((save_image_depth == 16) && (image_depth == 8))
9610 {
glennrp4f25bd02011-01-01 18:51:28 +00009611 if (logging != MagickFalse)
9612 {
9613 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9614 " Scaling ping_trans_color from (%d,%d,%d)",
9615 (int) ping_trans_color.red,
9616 (int) ping_trans_color.green,
9617 (int) ping_trans_color.blue);
9618 }
9619
glennrp5af765f2010-03-30 11:12:18 +00009620 ping_trans_color.red*=0x0101;
9621 ping_trans_color.green*=0x0101;
9622 ping_trans_color.blue*=0x0101;
9623 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009624
9625 if (logging != MagickFalse)
9626 {
9627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9628 " to (%d,%d,%d)",
9629 (int) ping_trans_color.red,
9630 (int) ping_trans_color.green,
9631 (int) ping_trans_color.blue);
9632 }
cristy3ed852e2009-09-05 21:47:34 +00009633 }
9634 }
9635
cristy4383ec82011-01-05 15:42:32 +00009636 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9637 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009638
cristy3ed852e2009-09-05 21:47:34 +00009639 /*
9640 Adjust background and transparency samples in sub-8-bit grayscale files.
9641 */
glennrp5af765f2010-03-30 11:12:18 +00009642 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009643 PNG_COLOR_TYPE_GRAY)
9644 {
9645 png_uint_16
9646 maxval;
9647
cristy35ef8242010-06-03 16:24:13 +00009648 size_t
9649 one=1;
9650
cristy22ffd972010-06-03 16:51:47 +00009651 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009652
glennrp4f25bd02011-01-01 18:51:28 +00009653 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009654 {
cristy3ed852e2009-09-05 21:47:34 +00009655
glennrp9f0fa852011-12-15 12:20:50 +00009656 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9657 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9658 &image->background_color))) +.5)));
cristy3ed852e2009-09-05 21:47:34 +00009659
9660 if (logging != MagickFalse)
9661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009662 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9664 " background_color index is %d",
9665 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009666
glennrp991d11d2010-11-12 21:55:28 +00009667 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009668 }
cristy3ed852e2009-09-05 21:47:34 +00009669
glennrp3e3e20f2011-06-09 04:21:43 +00009670 if (logging != MagickFalse)
9671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9672 " Scaling ping_trans_color.gray from %d",
9673 (int)ping_trans_color.gray);
9674
glennrp9be9b1c2011-06-09 12:21:45 +00009675 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009676 ping_trans_color.gray)+.5);
9677
9678 if (logging != MagickFalse)
9679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9680 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009681 }
glennrp17a14852010-05-10 03:01:59 +00009682
glennrp26f37912010-12-23 16:22:42 +00009683 if (ping_exclude_bKGD == MagickFalse)
9684 {
glennrp1273f7b2011-02-24 03:20:30 +00009685 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009686 {
9687 /*
9688 Identify which colormap entry is the background color.
9689 */
9690
glennrp17a14852010-05-10 03:01:59 +00009691 number_colors=image_colors;
9692
glennrpa521b2f2010-10-29 04:11:03 +00009693 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9694 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009695 break;
9696
9697 ping_background.index=(png_byte) i;
9698
glennrp3b51f0e2010-11-27 18:14:08 +00009699 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009700 {
9701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009702 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009703 }
glennrp0fe50b42010-11-16 03:52:51 +00009704
cristy13d07042010-11-21 20:56:18 +00009705 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009706 {
9707 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009708
9709 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009710 {
9711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9712 " background =(%d,%d,%d)",
9713 (int) ping_background.red,
9714 (int) ping_background.green,
9715 (int) ping_background.blue);
9716 }
9717 }
glennrpa521b2f2010-10-29 04:11:03 +00009718
glennrpd6bf1612010-12-17 17:28:54 +00009719 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009720 {
glennrp3b51f0e2010-11-27 18:14:08 +00009721 if (logging != MagickFalse)
9722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9723 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009724 ping_have_bKGD = MagickFalse;
9725 }
glennrp17a14852010-05-10 03:01:59 +00009726 }
glennrp26f37912010-12-23 16:22:42 +00009727 }
glennrp17a14852010-05-10 03:01:59 +00009728
cristy3ed852e2009-09-05 21:47:34 +00009729 if (logging != MagickFalse)
9730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009731 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009732 /*
9733 Initialize compression level and filtering.
9734 */
9735 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009736 {
9737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9738 " Setting up deflate compression");
9739
9740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9741 " Compression buffer size: 32768");
9742 }
9743
cristy3ed852e2009-09-05 21:47:34 +00009744 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009745
cristy3ed852e2009-09-05 21:47:34 +00009746 if (logging != MagickFalse)
9747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9748 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009749
cristy4054bfb2011-08-29 23:41:39 +00009750 png_set_compression_mem_level(ping, 9);
9751
glennrp10d739e2011-06-29 18:00:52 +00009752 /* Untangle the "-quality" setting:
9753
9754 Undefined is 0; the default is used.
9755 Default is 75
9756
9757 10's digit:
9758
9759 0: Use Z_HUFFMAN_ONLY strategy with the
9760 zlib default compression level
9761
9762 1-9: the zlib compression level
9763
9764 1's digit:
9765
9766 0-4: the PNG filter method
9767
9768 5: libpng adaptive filtering if compression level > 5
9769 libpng filter type "none" if compression level <= 5
9770 or if image is grayscale or palette
9771
9772 6: libpng adaptive filtering
9773
9774 7: "LOCO" filtering (intrapixel differing) if writing
9775 a MNG, othewise "none". Did not work in IM-6.7.0-9
9776 and earlier because of a missing "else".
9777
9778 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009779 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009780
9781 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009782 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009783
9784 Note that using the -quality option, not all combinations of
9785 PNG filter type, zlib compression level, and zlib compression
cristy5e6be1e2011-07-16 01:23:39 +00009786 strategy are possible. This will be addressed soon in a
cristy5d6fc9c2011-12-27 03:10:42 +00009787 release that accomodates "-define png:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +00009788
9789 */
9790
cristy3ed852e2009-09-05 21:47:34 +00009791 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9792 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009793
glennrp18682582011-06-30 18:11:47 +00009794 if (quality <= 9)
9795 {
9796 if (mng_info->write_png_compression_strategy == 0)
9797 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9798 }
9799
9800 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009801 {
9802 int
9803 level;
9804
cristybb503372010-05-27 20:51:26 +00009805 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009806
glennrp18682582011-06-30 18:11:47 +00009807 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009808 }
glennrp0fe50b42010-11-16 03:52:51 +00009809
glennrp18682582011-06-30 18:11:47 +00009810 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009811 {
glennrp18682582011-06-30 18:11:47 +00009812 if ((quality %10) == 8 || (quality %10) == 9)
9813 mng_info->write_png_compression_strategy=Z_RLE;
cristy3ed852e2009-09-05 21:47:34 +00009814 }
glennrp0fe50b42010-11-16 03:52:51 +00009815
glennrp18682582011-06-30 18:11:47 +00009816 if (mng_info->write_png_compression_filter == 0)
9817 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9818
cristy3ed852e2009-09-05 21:47:34 +00009819 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009820 {
glennrp18682582011-06-30 18:11:47 +00009821 if (mng_info->write_png_compression_level)
9822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9823 " Compression level: %d",
9824 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009825
glennrp18682582011-06-30 18:11:47 +00009826 if (mng_info->write_png_compression_strategy)
9827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9828 " Compression strategy: %d",
9829 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009830
glennrp18682582011-06-30 18:11:47 +00009831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9832 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009833
cristy4054bfb2011-08-29 23:41:39 +00009834 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +00009835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9836 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +00009837 else if (mng_info->write_png_compression_filter == 0 ||
9838 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +00009839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9840 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009841 else
9842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9843 " Base filter method: %d",
9844 (int) mng_info->write_png_compression_filter-1);
9845 }
glennrp2b013e42010-11-24 16:55:50 +00009846
glennrp18682582011-06-30 18:11:47 +00009847 if (mng_info->write_png_compression_level != 0)
9848 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9849
9850 if (mng_info->write_png_compression_filter == 6)
9851 {
9852 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9853 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9854 (quality < 50))
9855 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9856 else
9857 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9858 }
cristy4054bfb2011-08-29 23:41:39 +00009859 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +00009860 mng_info->write_png_compression_filter == 10)
9861 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9862
9863 else if (mng_info->write_png_compression_filter == 8)
9864 {
9865#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9866 if (mng_info->write_mng)
9867 {
9868 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9869 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9870 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9871 }
9872#endif
cristy4054bfb2011-08-29 23:41:39 +00009873 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +00009874 }
9875
9876 else if (mng_info->write_png_compression_filter == 9)
9877 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9878
9879 else if (mng_info->write_png_compression_filter != 0)
9880 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9881 mng_info->write_png_compression_filter-1);
9882
9883 if (mng_info->write_png_compression_strategy != 0)
9884 png_set_compression_strategy(ping,
9885 mng_info->write_png_compression_strategy-1);
9886
cristy0d57eec2011-09-04 22:13:56 +00009887 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9888 if (ping_exclude_sRGB != MagickFalse ||
9889 (image->rendering_intent == UndefinedIntent))
9890 {
9891 if ((ping_exclude_tEXt == MagickFalse ||
9892 ping_exclude_zTXt == MagickFalse) &&
9893 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009894 {
9895 ResetImageProfileIterator(image);
9896 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009897 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009898 profile=GetImageProfile(image,name);
9899
9900 if (profile != (StringInfo *) NULL)
9901 {
glennrp5af765f2010-03-30 11:12:18 +00009902#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009903 if ((LocaleCompare(name,"ICC") == 0) ||
9904 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009905 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009906
9907 if (ping_exclude_iCCP == MagickFalse)
9908 {
cristy9f027d12011-09-21 01:17:17 +00009909 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009910#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009911 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009912#else
9913 (png_const_bytep) GetStringInfoDatum(profile),
9914#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009915 (png_uint_32) GetStringInfoLength(profile));
9916 }
glennrp26f37912010-12-23 16:22:42 +00009917 }
glennrp0fe50b42010-11-16 03:52:51 +00009918
glennrpc8cbc5d2011-01-01 00:12:34 +00009919 else
cristy3ed852e2009-09-05 21:47:34 +00009920#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009921 if (ping_exclude_zCCP == MagickFalse)
9922 {
glennrpcf002022011-01-30 02:38:15 +00009923 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009924 (unsigned char *) name,(unsigned char *) name,
9925 GetStringInfoDatum(profile),
9926 (png_uint_32) GetStringInfoLength(profile));
9927 }
9928 }
glennrp0b206f52011-01-07 04:55:32 +00009929
glennrpc8cbc5d2011-01-01 00:12:34 +00009930 if (logging != MagickFalse)
9931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9932 " Setting up text chunk with %s profile",name);
9933
9934 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009935 }
cristy0d57eec2011-09-04 22:13:56 +00009936 }
cristy3ed852e2009-09-05 21:47:34 +00009937 }
9938
9939#if defined(PNG_WRITE_sRGB_SUPPORTED)
9940 if ((mng_info->have_write_global_srgb == 0) &&
9941 ((image->rendering_intent != UndefinedIntent) ||
9942 (image->colorspace == sRGBColorspace)))
9943 {
glennrp26f37912010-12-23 16:22:42 +00009944 if (ping_exclude_sRGB == MagickFalse)
9945 {
9946 /*
9947 Note image rendering intent.
9948 */
9949 if (logging != MagickFalse)
9950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9951 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009952
glennrp26f37912010-12-23 16:22:42 +00009953 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009954 Magick_RenderingIntent_to_PNG_RenderingIntent(
9955 image->rendering_intent)));
glennrp26f37912010-12-23 16:22:42 +00009956 }
cristy3ed852e2009-09-05 21:47:34 +00009957 }
glennrp26f37912010-12-23 16:22:42 +00009958
glennrp5af765f2010-03-30 11:12:18 +00009959 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009960#endif
9961 {
glennrp2cc891a2010-12-24 13:44:32 +00009962 if (ping_exclude_gAMA == MagickFalse &&
9963 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009964 (image->gamma < .45 || image->gamma > .46)))
9965 {
cristy3ed852e2009-09-05 21:47:34 +00009966 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9967 {
9968 /*
9969 Note image gamma.
9970 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9971 */
9972 if (logging != MagickFalse)
9973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9974 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009975
cristy3ed852e2009-09-05 21:47:34 +00009976 png_set_gAMA(ping,ping_info,image->gamma);
9977 }
glennrp26f37912010-12-23 16:22:42 +00009978 }
glennrp2b013e42010-11-24 16:55:50 +00009979
glennrp26f37912010-12-23 16:22:42 +00009980 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009981 {
glennrp26f37912010-12-23 16:22:42 +00009982 if ((mng_info->have_write_global_chrm == 0) &&
9983 (image->chromaticity.red_primary.x != 0.0))
9984 {
9985 /*
9986 Note image chromaticity.
9987 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9988 */
9989 PrimaryInfo
9990 bp,
9991 gp,
9992 rp,
9993 wp;
cristy3ed852e2009-09-05 21:47:34 +00009994
glennrp26f37912010-12-23 16:22:42 +00009995 wp=image->chromaticity.white_point;
9996 rp=image->chromaticity.red_primary;
9997 gp=image->chromaticity.green_primary;
9998 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009999
glennrp26f37912010-12-23 16:22:42 +000010000 if (logging != MagickFalse)
10001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10002 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010003
glennrp26f37912010-12-23 16:22:42 +000010004 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10005 bp.x,bp.y);
10006 }
10007 }
cristy3ed852e2009-09-05 21:47:34 +000010008 }
glennrpdfd70802010-11-14 01:23:35 +000010009
glennrp5af765f2010-03-30 11:12:18 +000010010 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +000010011
10012 if (mng_info->write_mng)
10013 png_set_sig_bytes(ping,8);
10014
cristy5d6fc9c2011-12-27 03:10:42 +000010015 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
cristy3ed852e2009-09-05 21:47:34 +000010016
glennrpd6bf1612010-12-17 17:28:54 +000010017 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +000010018 {
10019 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +000010020 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010021 {
glennrp5af765f2010-03-30 11:12:18 +000010022 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +000010023
glennrp5af765f2010-03-30 11:12:18 +000010024 if (ping_bit_depth < 8)
10025 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +000010026 }
glennrp0fe50b42010-11-16 03:52:51 +000010027
cristy3ed852e2009-09-05 21:47:34 +000010028 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +000010029 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +000010030 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +000010031 }
10032
glennrp0e8ea192010-12-24 18:00:33 +000010033 if (ping_need_colortype_warning != MagickFalse ||
10034 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +000010035 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +000010036 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +000010037 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +000010038 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +000010039 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +000010040 {
10041 if (logging != MagickFalse)
10042 {
glennrp0e8ea192010-12-24 18:00:33 +000010043 if (ping_need_colortype_warning != MagickFalse)
10044 {
10045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10046 " Image has transparency but tRNS chunk was excluded");
10047 }
10048
cristy3ed852e2009-09-05 21:47:34 +000010049 if (mng_info->write_png_depth)
10050 {
10051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010052 " Defined png:bit-depth=%u, Computed depth=%u",
cristy3ed852e2009-09-05 21:47:34 +000010053 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +000010054 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +000010055 }
glennrp0e8ea192010-12-24 18:00:33 +000010056
cristy3ed852e2009-09-05 21:47:34 +000010057 if (mng_info->write_png_colortype)
10058 {
10059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010060 " Defined png:color-type=%u, Computed color type=%u",
cristy3ed852e2009-09-05 21:47:34 +000010061 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +000010062 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010063 }
10064 }
glennrp0e8ea192010-12-24 18:00:33 +000010065
glennrp3bd2e412010-08-10 13:34:52 +000010066 png_warning(ping,
cristy5d6fc9c2011-12-27 03:10:42 +000010067 "Cannot write image with defined png:bit-depth or png:color-type.");
cristy3ed852e2009-09-05 21:47:34 +000010068 }
10069
glennrp58e01762011-01-07 15:28:54 +000010070 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010071 {
10072 /* Add an opaque matte channel */
10073 image->matte = MagickTrue;
cristye941a752011-10-15 01:52:48 +000010074 (void) SetImageAlpha(image,OpaqueAlpha,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010075
glennrpb4a13412010-05-05 12:47:19 +000010076 if (logging != MagickFalse)
10077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10078 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +000010079 }
10080
glennrp0e319732011-01-25 21:53:13 +000010081 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +000010082 {
glennrp991d11d2010-11-12 21:55:28 +000010083 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +000010084 {
glennrp991d11d2010-11-12 21:55:28 +000010085 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010086 if (logging != MagickFalse)
10087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10088 " Setting ping_have_tRNS=MagickTrue.");
10089 }
glennrpe9c26dc2010-05-30 01:56:35 +000010090 }
10091
cristy3ed852e2009-09-05 21:47:34 +000010092 if (logging != MagickFalse)
10093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10094 " Writing PNG header chunks");
10095
glennrp5af765f2010-03-30 11:12:18 +000010096 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10097 ping_bit_depth,ping_color_type,
10098 ping_interlace_method,ping_compression_method,
10099 ping_filter_method);
10100
glennrp39992b42010-11-14 00:03:43 +000010101 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10102 {
glennrpf09bded2011-01-08 01:15:59 +000010103 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010104
glennrp3b51f0e2010-11-27 18:14:08 +000010105 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +000010106 {
glennrp8640fb52010-11-23 15:48:26 +000010107 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +000010108 {
glennrpd6bf1612010-12-17 17:28:54 +000010109 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +000010110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010111 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10112 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010113 (int) palette[i].red,
10114 (int) palette[i].green,
10115 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +000010116 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010117 (int) ping_trans_alpha[i]);
10118 else
10119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010120 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010121 (int) i,
10122 (int) palette[i].red,
10123 (int) palette[i].green,
10124 (int) palette[i].blue);
10125 }
glennrp39992b42010-11-14 00:03:43 +000010126 }
glennrp39992b42010-11-14 00:03:43 +000010127 }
10128
glennrp26f37912010-12-23 16:22:42 +000010129 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010130 {
glennrp26f37912010-12-23 16:22:42 +000010131 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010132 {
glennrp26f37912010-12-23 16:22:42 +000010133 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010134 if (logging)
10135 {
10136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10137 " Setting up bKGD chunk");
10138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10139 " background color = (%d,%d,%d)",
10140 (int) ping_background.red,
10141 (int) ping_background.green,
10142 (int) ping_background.blue);
10143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10144 " index = %d, gray=%d",
10145 (int) ping_background.index,
10146 (int) ping_background.gray);
10147 }
10148 }
glennrp26f37912010-12-23 16:22:42 +000010149 }
10150
10151 if (ping_exclude_pHYs == MagickFalse)
10152 {
10153 if (ping_have_pHYs != MagickFalse)
10154 {
10155 png_set_pHYs(ping,ping_info,
10156 ping_pHYs_x_resolution,
10157 ping_pHYs_y_resolution,
10158 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010159
10160 if (logging)
10161 {
10162 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10163 " Setting up pHYs chunk");
10164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10165 " x_resolution=%lu",
10166 (unsigned long) ping_pHYs_x_resolution);
10167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10168 " y_resolution=%lu",
10169 (unsigned long) ping_pHYs_y_resolution);
10170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10171 " unit_type=%lu",
10172 (unsigned long) ping_pHYs_unit_type);
10173 }
glennrp26f37912010-12-23 16:22:42 +000010174 }
glennrpdfd70802010-11-14 01:23:35 +000010175 }
10176
10177#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010178 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010179 {
glennrp26f37912010-12-23 16:22:42 +000010180 if (image->page.x || image->page.y)
10181 {
10182 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10183 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010184
glennrp26f37912010-12-23 16:22:42 +000010185 if (logging != MagickFalse)
10186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10187 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10188 (int) image->page.x, (int) image->page.y);
10189 }
glennrpdfd70802010-11-14 01:23:35 +000010190 }
10191#endif
10192
glennrpda8f3a72011-02-27 23:54:12 +000010193 if (mng_info->need_blob != MagickFalse)
10194 {
cristyc82a27b2011-10-21 01:07:16 +000010195 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
glennrpda8f3a72011-02-27 23:54:12 +000010196 MagickFalse)
10197 png_error(ping,"WriteBlob Failed");
10198
10199 ping_have_blob=MagickTrue;
10200 }
10201
cristy3ed852e2009-09-05 21:47:34 +000010202 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010203
glennrp39992b42010-11-14 00:03:43 +000010204 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010205 {
glennrp3b51f0e2010-11-27 18:14:08 +000010206 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010207 {
10208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10209 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10210 }
10211
10212 if (ping_color_type == 3)
10213 (void) png_set_tRNS(ping, ping_info,
10214 ping_trans_alpha,
10215 ping_num_trans,
10216 NULL);
10217
10218 else
10219 {
10220 (void) png_set_tRNS(ping, ping_info,
10221 NULL,
10222 0,
10223 &ping_trans_color);
10224
glennrp3b51f0e2010-11-27 18:14:08 +000010225 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010226 {
10227 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010228 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010229 (int) ping_trans_color.red,
10230 (int) ping_trans_color.green,
10231 (int) ping_trans_color.blue);
10232 }
10233 }
glennrp991d11d2010-11-12 21:55:28 +000010234 }
10235
cristy3ed852e2009-09-05 21:47:34 +000010236 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010237 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010238
cristy3ed852e2009-09-05 21:47:34 +000010239 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010240
cristy3ed852e2009-09-05 21:47:34 +000010241 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010242 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010243
glennrp26f37912010-12-23 16:22:42 +000010244 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010245 {
glennrp4f25bd02011-01-01 18:51:28 +000010246 if ((image->page.width != 0 && image->page.width != image->columns) ||
10247 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010248 {
10249 unsigned char
10250 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010251
glennrp26f37912010-12-23 16:22:42 +000010252 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10253 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010254 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010255 PNGLong(chunk+4,(png_uint_32) image->page.width);
10256 PNGLong(chunk+8,(png_uint_32) image->page.height);
10257 chunk[12]=0; /* unit = pixels */
10258 (void) WriteBlob(image,13,chunk);
10259 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10260 }
cristy3ed852e2009-09-05 21:47:34 +000010261 }
10262
10263#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010264 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010265#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010266 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010267#undef PNG_HAVE_IDAT
10268#endif
10269
10270 png_set_packing(ping);
10271 /*
10272 Allocate memory.
10273 */
10274 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010275 if (image_depth > 8)
10276 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010277 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010278 {
glennrpb4a13412010-05-05 12:47:19 +000010279 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010280 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010281 break;
glennrp0fe50b42010-11-16 03:52:51 +000010282
glennrpb4a13412010-05-05 12:47:19 +000010283 case PNG_COLOR_TYPE_GRAY_ALPHA:
10284 rowbytes*=2;
10285 break;
glennrp0fe50b42010-11-16 03:52:51 +000010286
glennrpb4a13412010-05-05 12:47:19 +000010287 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010288 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010289 break;
glennrp0fe50b42010-11-16 03:52:51 +000010290
glennrpb4a13412010-05-05 12:47:19 +000010291 default:
10292 break;
cristy3ed852e2009-09-05 21:47:34 +000010293 }
glennrp3b51f0e2010-11-27 18:14:08 +000010294
10295 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010296 {
10297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10298 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010299
glennrpb4a13412010-05-05 12:47:19 +000010300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010301 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010302 }
glennrpcf002022011-01-30 02:38:15 +000010303 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10304 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010305
glennrpcf002022011-01-30 02:38:15 +000010306 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010307 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010308
cristy3ed852e2009-09-05 21:47:34 +000010309 /*
10310 Initialize image scanlines.
10311 */
glennrp5af765f2010-03-30 11:12:18 +000010312 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010313 {
10314 /*
10315 PNG write failed.
10316 */
10317#ifdef PNG_DEBUG
10318 if (image_info->verbose)
10319 (void) printf("PNG write has failed.\n");
10320#endif
10321 png_destroy_write_struct(&ping,&ping_info);
10322 if (quantum_info != (QuantumInfo *) NULL)
10323 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010324 if (ping_pixels != (unsigned char *) NULL)
10325 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010326#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010327 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010328#endif
glennrpda8f3a72011-02-27 23:54:12 +000010329 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010330 (void) CloseBlob(image);
10331 image_info=DestroyImageInfo(image_info);
10332 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010333 return(MagickFalse);
10334 }
cristyed552522009-10-16 14:04:35 +000010335 quantum_info=AcquireQuantumInfo(image_info,image);
10336 if (quantum_info == (QuantumInfo *) NULL)
10337 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010338 quantum_info->format=UndefinedQuantumFormat;
10339 quantum_info->depth=image_depth;
10340 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010341
cristy3ed852e2009-09-05 21:47:34 +000010342 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010343 !mng_info->write_png32) &&
10344 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010345 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010346 image_matte == MagickFalse &&
10347 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010348 {
glennrp8bb3a022010-12-13 20:40:04 +000010349 /* Palette, Bilevel, or Opaque Monochrome */
cristy4c08aed2011-07-01 19:47:50 +000010350 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010351 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010352
cristy3ed852e2009-09-05 21:47:34 +000010353 quantum_info->depth=8;
10354 for (pass=0; pass < num_passes; pass++)
10355 {
10356 /*
10357 Convert PseudoClass image to a PNG monochrome image.
10358 */
cristybb503372010-05-27 20:51:26 +000010359 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010360 {
glennrpd71e86a2011-02-24 01:28:37 +000010361 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10363 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010364
cristyc82a27b2011-10-21 01:07:16 +000010365 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010366
cristy4c08aed2011-07-01 19:47:50 +000010367 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010368 break;
glennrp0fe50b42010-11-16 03:52:51 +000010369
cristy3ed852e2009-09-05 21:47:34 +000010370 if (mng_info->IsPalette)
10371 {
cristy4c08aed2011-07-01 19:47:50 +000010372 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010373 quantum_info,GrayQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010374 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10375 mng_info->write_png_depth &&
10376 mng_info->write_png_depth != old_bit_depth)
10377 {
10378 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010379 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010380 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010381 >> (8-old_bit_depth));
10382 }
10383 }
glennrp0fe50b42010-11-16 03:52:51 +000010384
cristy3ed852e2009-09-05 21:47:34 +000010385 else
10386 {
cristy4c08aed2011-07-01 19:47:50 +000010387 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010388 quantum_info,RedQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010389 }
glennrp0fe50b42010-11-16 03:52:51 +000010390
cristy3ed852e2009-09-05 21:47:34 +000010391 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010392 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010393 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010394 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010395
glennrp3b51f0e2010-11-27 18:14:08 +000010396 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10398 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010399
glennrpcf002022011-01-30 02:38:15 +000010400 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010401 }
10402 if (image->previous == (Image *) NULL)
10403 {
10404 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10405 if (status == MagickFalse)
10406 break;
10407 }
10408 }
10409 }
glennrp0fe50b42010-11-16 03:52:51 +000010410
glennrp8bb3a022010-12-13 20:40:04 +000010411 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010412 {
glennrp0fe50b42010-11-16 03:52:51 +000010413 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010414 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010415 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010416 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010417 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010418 {
cristy4c08aed2011-07-01 19:47:50 +000010419 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010420 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010421
glennrp8bb3a022010-12-13 20:40:04 +000010422 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010423 {
glennrp8bb3a022010-12-13 20:40:04 +000010424
cristybb503372010-05-27 20:51:26 +000010425 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010426 {
cristyc82a27b2011-10-21 01:07:16 +000010427 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010428
cristy4c08aed2011-07-01 19:47:50 +000010429 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010430 break;
glennrp2cc891a2010-12-24 13:44:32 +000010431
glennrp5af765f2010-03-30 11:12:18 +000010432 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010433 {
glennrp8bb3a022010-12-13 20:40:04 +000010434 if (mng_info->IsPalette)
cristy4c08aed2011-07-01 19:47:50 +000010435 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010436 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010437
glennrp8bb3a022010-12-13 20:40:04 +000010438 else
cristy4c08aed2011-07-01 19:47:50 +000010439 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010440 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010441
glennrp3b51f0e2010-11-27 18:14:08 +000010442 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010444 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010445 }
glennrp2cc891a2010-12-24 13:44:32 +000010446
glennrp8bb3a022010-12-13 20:40:04 +000010447 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10448 {
10449 if (logging != MagickFalse && y == 0)
10450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10451 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010452
cristy4c08aed2011-07-01 19:47:50 +000010453 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010454 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010455 }
glennrp2cc891a2010-12-24 13:44:32 +000010456
glennrp3b51f0e2010-11-27 18:14:08 +000010457 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010459 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010460
glennrpcf002022011-01-30 02:38:15 +000010461 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010462 }
glennrp2cc891a2010-12-24 13:44:32 +000010463
glennrp8bb3a022010-12-13 20:40:04 +000010464 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010465 {
glennrp8bb3a022010-12-13 20:40:04 +000010466 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10467 if (status == MagickFalse)
10468 break;
cristy3ed852e2009-09-05 21:47:34 +000010469 }
cristy3ed852e2009-09-05 21:47:34 +000010470 }
10471 }
glennrp8bb3a022010-12-13 20:40:04 +000010472
10473 else
10474 {
cristy4c08aed2011-07-01 19:47:50 +000010475 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010476 *p;
10477
10478 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010479 {
glennrp8bb3a022010-12-13 20:40:04 +000010480 if ((image_depth > 8) || (mng_info->write_png24 ||
10481 mng_info->write_png32 ||
10482 (!mng_info->write_png8 && !mng_info->IsPalette)))
10483 {
10484 for (y=0; y < (ssize_t) image->rows; y++)
10485 {
10486 p=GetVirtualPixels(image,0,y,image->columns,1,
cristyc82a27b2011-10-21 01:07:16 +000010487 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010488
cristy4c08aed2011-07-01 19:47:50 +000010489 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010490 break;
glennrp2cc891a2010-12-24 13:44:32 +000010491
glennrp8bb3a022010-12-13 20:40:04 +000010492 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10493 {
10494 if (image->storage_class == DirectClass)
cristy4c08aed2011-07-01 19:47:50 +000010495 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010496 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010497
glennrp8bb3a022010-12-13 20:40:04 +000010498 else
cristy4c08aed2011-07-01 19:47:50 +000010499 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010500 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010501 }
glennrp2cc891a2010-12-24 13:44:32 +000010502
glennrp8bb3a022010-12-13 20:40:04 +000010503 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10504 {
cristy4c08aed2011-07-01 19:47:50 +000010505 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010506 quantum_info,GrayAlphaQuantum,ping_pixels,
cristyc82a27b2011-10-21 01:07:16 +000010507 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010508
glennrp8bb3a022010-12-13 20:40:04 +000010509 if (logging != MagickFalse && y == 0)
10510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10511 " Writing GRAY_ALPHA PNG pixels (3)");
10512 }
glennrp2cc891a2010-12-24 13:44:32 +000010513
glennrp8bb3a022010-12-13 20:40:04 +000010514 else if (image_matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +000010515 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010516 quantum_info,RGBAQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010517
glennrp8bb3a022010-12-13 20:40:04 +000010518 else
cristy4c08aed2011-07-01 19:47:50 +000010519 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010520 quantum_info,RGBQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010521
glennrp8bb3a022010-12-13 20:40:04 +000010522 if (logging != MagickFalse && y == 0)
10523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10524 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010525
glennrpcf002022011-01-30 02:38:15 +000010526 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010527 }
10528 }
glennrp2cc891a2010-12-24 13:44:32 +000010529
glennrp8bb3a022010-12-13 20:40:04 +000010530 else
10531 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10532 mng_info->write_png32 ||
10533 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10534 {
10535 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10536 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10537 {
10538 if (logging != MagickFalse)
10539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10540 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010541
glennrp8bb3a022010-12-13 20:40:04 +000010542 quantum_info->depth=8;
10543 image_depth=8;
10544 }
glennrp2cc891a2010-12-24 13:44:32 +000010545
glennrp8bb3a022010-12-13 20:40:04 +000010546 for (y=0; y < (ssize_t) image->rows; y++)
10547 {
10548 if (logging != MagickFalse && y == 0)
10549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10550 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010551
cristy97707062011-12-27 18:25:00 +000010552 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000010553
cristy4c08aed2011-07-01 19:47:50 +000010554 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010555 break;
glennrp2cc891a2010-12-24 13:44:32 +000010556
glennrp8bb3a022010-12-13 20:40:04 +000010557 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010558 {
glennrp4bf89732011-03-21 13:48:28 +000010559 quantum_info->depth=image->depth;
10560
cristy4c08aed2011-07-01 19:47:50 +000010561 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010562 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp44757ab2011-03-17 12:57:03 +000010563 }
glennrp2cc891a2010-12-24 13:44:32 +000010564
glennrp8bb3a022010-12-13 20:40:04 +000010565 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10566 {
10567 if (logging != MagickFalse && y == 0)
10568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10569 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010570
cristy4c08aed2011-07-01 19:47:50 +000010571 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010572 quantum_info,GrayAlphaQuantum,ping_pixels,
cristyc82a27b2011-10-21 01:07:16 +000010573 exception);
glennrp8bb3a022010-12-13 20:40:04 +000010574 }
glennrp2cc891a2010-12-24 13:44:32 +000010575
glennrp8bb3a022010-12-13 20:40:04 +000010576 else
glennrp8bb3a022010-12-13 20:40:04 +000010577 {
cristy4c08aed2011-07-01 19:47:50 +000010578 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010579 quantum_info,IndexQuantum,ping_pixels,exception);
glennrp5eae7602011-02-22 15:21:32 +000010580
10581 if (logging != MagickFalse && y <= 2)
10582 {
10583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010584 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010585
10586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10587 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10588 (int)ping_pixels[0],(int)ping_pixels[1]);
10589 }
glennrp8bb3a022010-12-13 20:40:04 +000010590 }
glennrpcf002022011-01-30 02:38:15 +000010591 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010592 }
10593 }
glennrp2cc891a2010-12-24 13:44:32 +000010594
glennrp8bb3a022010-12-13 20:40:04 +000010595 if (image->previous == (Image *) NULL)
10596 {
10597 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10598 if (status == MagickFalse)
10599 break;
10600 }
cristy3ed852e2009-09-05 21:47:34 +000010601 }
glennrp8bb3a022010-12-13 20:40:04 +000010602 }
10603 }
10604
cristyb32b90a2009-09-07 21:45:48 +000010605 if (quantum_info != (QuantumInfo *) NULL)
10606 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010607
10608 if (logging != MagickFalse)
10609 {
10610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010611 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010612
cristy3ed852e2009-09-05 21:47:34 +000010613 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010614 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010615
cristy3ed852e2009-09-05 21:47:34 +000010616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010617 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010618
cristy3ed852e2009-09-05 21:47:34 +000010619 if (mng_info->write_png_depth)
10620 {
10621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010622 " Defined png:bit-depth: %d",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010623 }
glennrp0fe50b42010-11-16 03:52:51 +000010624
cristy3ed852e2009-09-05 21:47:34 +000010625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010626 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010627
cristy3ed852e2009-09-05 21:47:34 +000010628 if (mng_info->write_png_colortype)
10629 {
10630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010631 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010632 }
glennrp0fe50b42010-11-16 03:52:51 +000010633
cristy3ed852e2009-09-05 21:47:34 +000010634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010635 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010636
cristy3ed852e2009-09-05 21:47:34 +000010637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010638 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010639 }
10640 /*
glennrpa0ed0092011-04-18 16:36:29 +000010641 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010642 */
glennrp823b55c2011-03-14 18:46:46 +000010643 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010644 {
glennrp26f37912010-12-23 16:22:42 +000010645 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010646 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010647 while (property != (const char *) NULL)
10648 {
10649 png_textp
10650 text;
glennrp2cc891a2010-12-24 13:44:32 +000010651
cristyd15e6592011-10-15 00:13:06 +000010652 value=GetImageProperty(image,property,exception);
glennrpa0ed0092011-04-18 16:36:29 +000010653
10654 /* Don't write any "png:" properties; those are just for "identify" */
10655 if (LocaleNCompare(property,"png:",4) != 0 &&
10656
10657 /* Suppress density and units if we wrote a pHYs chunk */
10658 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010659 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010660 LocaleCompare(property,"units") != 0) &&
10661
10662 /* Suppress the IM-generated Date:create and Date:modify */
10663 (ping_exclude_date == MagickFalse ||
10664 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010665 {
glennrpc70af4a2011-03-07 00:08:23 +000010666 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010667 {
glennrpc70af4a2011-03-07 00:08:23 +000010668 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10669 text[0].key=(char *) property;
10670 text[0].text=(char *) value;
10671 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010672
glennrpc70af4a2011-03-07 00:08:23 +000010673 if (ping_exclude_tEXt != MagickFalse)
10674 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10675
10676 else if (ping_exclude_zTXt != MagickFalse)
10677 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10678
10679 else
glennrp26f37912010-12-23 16:22:42 +000010680 {
glennrpc70af4a2011-03-07 00:08:23 +000010681 text[0].compression=image_info->compression == NoCompression ||
10682 (image_info->compression == UndefinedCompression &&
10683 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10684 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010685 }
glennrp2cc891a2010-12-24 13:44:32 +000010686
glennrpc70af4a2011-03-07 00:08:23 +000010687 if (logging != MagickFalse)
10688 {
10689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10690 " Setting up text chunk");
10691
10692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10693 " keyword: %s",text[0].key);
10694 }
10695
10696 png_set_text(ping,ping_info,text,1);
10697 png_free(ping,text);
10698 }
glennrp26f37912010-12-23 16:22:42 +000010699 }
10700 property=GetNextImageProperty(image);
10701 }
cristy3ed852e2009-09-05 21:47:34 +000010702 }
10703
10704 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010705 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010706
10707 if (logging != MagickFalse)
10708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10709 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010710
cristy3ed852e2009-09-05 21:47:34 +000010711 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010712
cristy3ed852e2009-09-05 21:47:34 +000010713 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10714 {
10715 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010716 (ping_width != mng_info->page.width) ||
10717 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010718 {
10719 unsigned char
10720 chunk[32];
10721
10722 /*
10723 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10724 */
10725 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10726 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010727 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010728 chunk[4]=4;
10729 chunk[5]=0; /* frame name separator (no name) */
10730 chunk[6]=1; /* flag for changing delay, for next frame only */
10731 chunk[7]=0; /* flag for changing frame timeout */
10732 chunk[8]=1; /* flag for changing frame clipping for next frame */
10733 chunk[9]=0; /* flag for changing frame sync_id */
10734 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10735 chunk[14]=0; /* clipping boundaries delta type */
10736 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10737 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010738 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010739 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10740 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010741 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010742 (void) WriteBlob(image,31,chunk);
10743 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10744 mng_info->old_framing_mode=4;
10745 mng_info->framing_mode=1;
10746 }
glennrp0fe50b42010-11-16 03:52:51 +000010747
cristy3ed852e2009-09-05 21:47:34 +000010748 else
10749 mng_info->framing_mode=3;
10750 }
10751 if (mng_info->write_mng && !mng_info->need_fram &&
10752 ((int) image->dispose == 3))
cristyc82a27b2011-10-21 01:07:16 +000010753 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010754 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010755 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010756
cristy3ed852e2009-09-05 21:47:34 +000010757 /*
10758 Free PNG resources.
10759 */
glennrp5af765f2010-03-30 11:12:18 +000010760
cristy3ed852e2009-09-05 21:47:34 +000010761 png_destroy_write_struct(&ping,&ping_info);
10762
glennrpcf002022011-01-30 02:38:15 +000010763 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010764
10765#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010766 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010767#endif
10768
glennrpda8f3a72011-02-27 23:54:12 +000010769 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010770 (void) CloseBlob(image);
10771
10772 image_info=DestroyImageInfo(image_info);
10773 image=DestroyImage(image);
10774
10775 /* Store bit depth actually written */
10776 s[0]=(char) ping_bit_depth;
10777 s[1]='\0';
10778
cristyd15e6592011-10-15 00:13:06 +000010779 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
glennrpb9cfe272010-12-21 15:08:06 +000010780
cristy3ed852e2009-09-05 21:47:34 +000010781 if (logging != MagickFalse)
10782 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10783 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010784
cristy3ed852e2009-09-05 21:47:34 +000010785 return(MagickTrue);
10786/* End write one PNG image */
10787}
10788
10789/*
10790%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10791% %
10792% %
10793% %
10794% W r i t e P N G I m a g e %
10795% %
10796% %
10797% %
10798%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10799%
10800% WritePNGImage() writes a Portable Network Graphics (PNG) or
10801% Multiple-image Network Graphics (MNG) image file.
10802%
10803% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10804%
10805% The format of the WritePNGImage method is:
10806%
cristy1e178e72011-08-28 19:44:34 +000010807% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10808% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010809%
10810% A description of each parameter follows:
10811%
10812% o image_info: the image info.
10813%
10814% o image: The image.
10815%
cristy1e178e72011-08-28 19:44:34 +000010816% o exception: return any errors or warnings in this structure.
10817%
cristy3ed852e2009-09-05 21:47:34 +000010818% Returns MagickTrue on success, MagickFalse on failure.
10819%
10820% Communicating with the PNG encoder:
10821%
10822% While the datastream written is always in PNG format and normally would
10823% be given the "png" file extension, this method also writes the following
cristy5d6fc9c2011-12-27 03:10:42 +000010824% pseudo-formats which are subsets of png:
cristy3ed852e2009-09-05 21:47:34 +000010825%
glennrp5a39f372011-02-25 04:52:16 +000010826% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10827% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010828% is present, the tRNS chunk must only have values 0 and 255
10829% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010830% transparent). If other values are present they will be
10831% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010832% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000010833% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10834% of any resulting fully-transparent pixels is changed to
10835% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000010836%
10837% If you want better quantization or dithering of the colors
10838% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010839% PNG encoder. The pixels contain 8-bit indices even if
10840% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010841% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010842% PNG grayscale type might be slightly more efficient. Please
10843% note that writing to the PNG8 format may result in loss
10844% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010845%
10846% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10847% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010848% one of the colors as transparent. The only loss incurred
10849% is reduction of sample depth to 8. If the image has more
10850% than one transparent color, has semitransparent pixels, or
10851% has an opaque pixel with the same RGB components as the
10852% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010853%
10854% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10855% transparency is permitted, i.e., the alpha sample for
10856% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010857% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010858% The only loss in data is the reduction of the sample depth
10859% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010860%
10861% o -define: For more precise control of the PNG output, you can use the
10862% Image options "png:bit-depth" and "png:color-type". These
10863% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010864% from the application programming interfaces. The options
10865% are case-independent and are converted to lowercase before
10866% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010867%
10868% png:color-type can be 0, 2, 3, 4, or 6.
10869%
10870% When png:color-type is 0 (Grayscale), png:bit-depth can
10871% be 1, 2, 4, 8, or 16.
10872%
10873% When png:color-type is 2 (RGB), png:bit-depth can
10874% be 8 or 16.
10875%
10876% When png:color-type is 3 (Indexed), png:bit-depth can
10877% be 1, 2, 4, or 8. This refers to the number of bits
10878% used to store the index. The color samples always have
10879% bit-depth 8 in indexed PNG files.
10880%
10881% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10882% png:bit-depth can be 8 or 16.
10883%
glennrp5a39f372011-02-25 04:52:16 +000010884% If the image cannot be written without loss with the requested bit-depth
10885% and color-type, a PNG file will not be written, and the encoder will
10886% return MagickFalse.
10887%
cristy3ed852e2009-09-05 21:47:34 +000010888% Since image encoders should not be responsible for the "heavy lifting",
10889% the user should make sure that ImageMagick has already reduced the
10890% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010891% transparency prior to attempting to write the image with depth, color,
glennrp54dc0692011-07-01 19:02:13 +000010892% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010893%
cristy3ed852e2009-09-05 21:47:34 +000010894% Note that another definition, "png:bit-depth-written" exists, but it
10895% is not intended for external use. It is only used internally by the
10896% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10897%
10898% It is possible to request that the PNG encoder write previously-formatted
10899% ancillary chunks in the output PNG file, using the "-profile" commandline
10900% option as shown below or by setting the profile via a programming
10901% interface:
10902%
10903% -profile PNG-chunk-x:<file>
10904%
10905% where x is a location flag and <file> is a file containing the chunk
10906% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010907% This encoder will compute the chunk length and CRC, so those must not
10908% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010909%
10910% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10911% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10912% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010913% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010914%
glennrpbb8a7332010-11-13 15:17:35 +000010915% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010916%
glennrp3241bd02010-12-12 04:36:28 +000010917% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010918%
glennrpd6afd542010-11-19 01:53:05 +000010919% o 32-bit depth is reduced to 16.
10920% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10921% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010922% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010923% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010924% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010925% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10926% this can be done without loss and a larger bit depth N was not
cristy5d6fc9c2011-12-27 03:10:42 +000010927% requested via the "-define png:bit-depth=N" option.
glennrpd6afd542010-11-19 01:53:05 +000010928% o If matte channel is present but only one transparent color is
10929% present, RGB+tRNS is written instead of RGBA
10930% o Opaque matte channel is removed (or added, if color-type 4 or 6
10931% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010932%
cristy3ed852e2009-09-05 21:47:34 +000010933%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10934*/
10935static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +000010936 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010937{
10938 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010939 excluding,
10940 logging,
10941 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010942 status;
10943
10944 MngInfo
10945 *mng_info;
10946
10947 const char
10948 *value;
10949
10950 int
glennrp21f0e622011-01-07 16:20:57 +000010951 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010952 source;
10953
cristy3ed852e2009-09-05 21:47:34 +000010954 /*
10955 Open image file.
10956 */
10957 assert(image_info != (const ImageInfo *) NULL);
10958 assert(image_info->signature == MagickSignature);
10959 assert(image != (Image *) NULL);
10960 assert(image->signature == MagickSignature);
10961 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010962 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010963 /*
10964 Allocate a MngInfo structure.
10965 */
10966 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010967 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010968
cristy3ed852e2009-09-05 21:47:34 +000010969 if (mng_info == (MngInfo *) NULL)
10970 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010971
cristy3ed852e2009-09-05 21:47:34 +000010972 /*
10973 Initialize members of the MngInfo structure.
10974 */
10975 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10976 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010977 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010978 have_mng_structure=MagickTrue;
10979
10980 /* See if user has requested a specific PNG subformat */
10981
10982 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10983 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10984 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10985
10986 if (mng_info->write_png8)
10987 {
glennrp9c1eb072010-06-06 22:19:15 +000010988 mng_info->write_png_colortype = /* 3 */ 4;
10989 mng_info->write_png_depth = 8;
10990 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010991 }
10992
10993 if (mng_info->write_png24)
10994 {
glennrp9c1eb072010-06-06 22:19:15 +000010995 mng_info->write_png_colortype = /* 2 */ 3;
10996 mng_info->write_png_depth = 8;
10997 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010998
glennrp9c1eb072010-06-06 22:19:15 +000010999 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011000 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011001
glennrp9c1eb072010-06-06 22:19:15 +000011002 else
cristy018f07f2011-09-04 21:15:19 +000011003 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011004
cristyea1a8aa2011-10-20 13:24:06 +000011005 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011006 }
11007
11008 if (mng_info->write_png32)
11009 {
glennrp9c1eb072010-06-06 22:19:15 +000011010 mng_info->write_png_colortype = /* 6 */ 7;
11011 mng_info->write_png_depth = 8;
11012 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011013
glennrp9c1eb072010-06-06 22:19:15 +000011014 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011015 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011016
glennrp9c1eb072010-06-06 22:19:15 +000011017 else
cristy018f07f2011-09-04 21:15:19 +000011018 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011019
cristyea1a8aa2011-10-20 13:24:06 +000011020 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011021 }
11022
11023 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011024
cristy3ed852e2009-09-05 21:47:34 +000011025 if (value != (char *) NULL)
11026 {
11027 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011028 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011029
cristy3ed852e2009-09-05 21:47:34 +000011030 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011031 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011032
cristy3ed852e2009-09-05 21:47:34 +000011033 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011034 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011035
cristy3ed852e2009-09-05 21:47:34 +000011036 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011037 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011038
cristy3ed852e2009-09-05 21:47:34 +000011039 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011040 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011041
glennrpbb8a7332010-11-13 15:17:35 +000011042 else
cristyc82a27b2011-10-21 01:07:16 +000011043 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011044 GetMagickModule(),CoderWarning,
11045 "ignoring invalid defined png:bit-depth",
11046 "=%s",value);
11047
cristy3ed852e2009-09-05 21:47:34 +000011048 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011050 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011051 }
glennrp0fe50b42010-11-16 03:52:51 +000011052
cristy3ed852e2009-09-05 21:47:34 +000011053 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011054
cristy3ed852e2009-09-05 21:47:34 +000011055 if (value != (char *) NULL)
11056 {
11057 /* We must store colortype+1 because 0 is a valid colortype */
11058 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011059 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011060
cristy3ed852e2009-09-05 21:47:34 +000011061 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011062 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011063
cristy3ed852e2009-09-05 21:47:34 +000011064 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011065 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011066
cristy3ed852e2009-09-05 21:47:34 +000011067 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011068 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011069
cristy3ed852e2009-09-05 21:47:34 +000011070 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011071 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011072
glennrpbb8a7332010-11-13 15:17:35 +000011073 else
cristyc82a27b2011-10-21 01:07:16 +000011074 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011075 GetMagickModule(),CoderWarning,
11076 "ignoring invalid defined png:color-type",
11077 "=%s",value);
11078
cristy3ed852e2009-09-05 21:47:34 +000011079 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011081 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011082 }
11083
glennrp0e8ea192010-12-24 18:00:33 +000011084 /* Check for chunks to be excluded:
11085 *
glennrp0dff56c2011-01-29 19:10:02 +000011086 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011087 * listed in the "unused_chunks" array, above.
11088 *
cristy5d6fc9c2011-12-27 03:10:42 +000011089 * Chunks can be listed for exclusion via a "png:exclude-chunk"
glennrp0e8ea192010-12-24 18:00:33 +000011090 * define (in the image properties or in the image artifacts)
11091 * or via a mng_info member. For convenience, in addition
11092 * to or instead of a comma-separated list of chunks, the
11093 * "exclude-chunk" string can be simply "all" or "none".
11094 *
11095 * The exclude-chunk define takes priority over the mng_info.
11096 *
cristy5d6fc9c2011-12-27 03:10:42 +000011097 * A "png:include-chunk" define takes priority over both the
11098 * mng_info and the "png:exclude-chunk" define. Like the
glennrp0e8ea192010-12-24 18:00:33 +000011099 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011100 * well as a comma-separated list. Chunks that are unknown to
11101 * ImageMagick are always excluded, regardless of their "copy-safe"
11102 * status according to the PNG specification, and even if they
11103 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000011104 *
11105 * Finally, all chunks listed in the "unused_chunks" array are
11106 * automatically excluded, regardless of the other instructions
11107 * or lack thereof.
11108 *
11109 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11110 * will not be written and the gAMA chunk will only be written if it
11111 * is not between .45 and .46, or approximately (1.0/2.2).
11112 *
11113 * If you exclude tRNS and the image has transparency, the colortype
11114 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11115 *
11116 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011117 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011118 */
11119
glennrp26f37912010-12-23 16:22:42 +000011120 mng_info->ping_exclude_bKGD=MagickFalse;
11121 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011122 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011123 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11124 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011125 mng_info->ping_exclude_iCCP=MagickFalse;
11126 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11127 mng_info->ping_exclude_oFFs=MagickFalse;
11128 mng_info->ping_exclude_pHYs=MagickFalse;
11129 mng_info->ping_exclude_sRGB=MagickFalse;
11130 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011131 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011132 mng_info->ping_exclude_vpAg=MagickFalse;
11133 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11134 mng_info->ping_exclude_zTXt=MagickFalse;
11135
glennrp8d3d6e52011-04-19 04:39:51 +000011136 mng_info->ping_preserve_colormap=MagickFalse;
11137
11138 value=GetImageArtifact(image,"png:preserve-colormap");
11139 if (value == NULL)
11140 value=GetImageOption(image_info,"png:preserve-colormap");
11141 if (value != NULL)
11142 mng_info->ping_preserve_colormap=MagickTrue;
11143
glennrp18682582011-06-30 18:11:47 +000011144 /* Thes compression-level, compression-strategy, and compression-filter
11145 * defines take precedence over values from the -quality option.
11146 */
11147 value=GetImageArtifact(image,"png:compression-level");
11148 if (value == NULL)
11149 value=GetImageOption(image_info,"png:compression-level");
11150 if (value != NULL)
11151 {
glennrp18682582011-06-30 18:11:47 +000011152 /* We have to add 1 to everything because 0 is a valid input,
11153 * and we want to use 0 (the default) to mean undefined.
11154 */
11155 if (LocaleCompare(value,"0") == 0)
11156 mng_info->write_png_compression_level = 1;
11157
11158 if (LocaleCompare(value,"1") == 0)
11159 mng_info->write_png_compression_level = 2;
11160
11161 else if (LocaleCompare(value,"2") == 0)
11162 mng_info->write_png_compression_level = 3;
11163
11164 else if (LocaleCompare(value,"3") == 0)
11165 mng_info->write_png_compression_level = 4;
11166
11167 else if (LocaleCompare(value,"4") == 0)
11168 mng_info->write_png_compression_level = 5;
11169
11170 else if (LocaleCompare(value,"5") == 0)
11171 mng_info->write_png_compression_level = 6;
11172
11173 else if (LocaleCompare(value,"6") == 0)
11174 mng_info->write_png_compression_level = 7;
11175
11176 else if (LocaleCompare(value,"7") == 0)
11177 mng_info->write_png_compression_level = 8;
11178
11179 else if (LocaleCompare(value,"8") == 0)
11180 mng_info->write_png_compression_level = 9;
11181
11182 else if (LocaleCompare(value,"9") == 0)
11183 mng_info->write_png_compression_level = 10;
11184
11185 else
cristyc82a27b2011-10-21 01:07:16 +000011186 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011187 GetMagickModule(),CoderWarning,
11188 "ignoring invalid defined png:compression-level",
11189 "=%s",value);
11190 }
11191
11192 value=GetImageArtifact(image,"png:compression-strategy");
11193 if (value == NULL)
11194 value=GetImageOption(image_info,"png:compression-strategy");
11195 if (value != NULL)
11196 {
11197
11198 if (LocaleCompare(value,"0") == 0)
11199 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11200
11201 else if (LocaleCompare(value,"1") == 0)
11202 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11203
11204 else if (LocaleCompare(value,"2") == 0)
11205 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11206
11207 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011208#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011209 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011210#else
11211 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11212#endif
glennrp18682582011-06-30 18:11:47 +000011213
11214 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011215#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011216 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011217#else
11218 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11219#endif
glennrp18682582011-06-30 18:11:47 +000011220
11221 else
cristyc82a27b2011-10-21 01:07:16 +000011222 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011223 GetMagickModule(),CoderWarning,
11224 "ignoring invalid defined png:compression-strategy",
11225 "=%s",value);
11226 }
11227
11228 value=GetImageArtifact(image,"png:compression-filter");
11229 if (value == NULL)
11230 value=GetImageOption(image_info,"png:compression-filter");
11231 if (value != NULL)
11232 {
11233
11234 /* To do: combinations of filters allowed by libpng
11235 * masks 0x08 through 0xf8
11236 *
11237 * Implement this as a comma-separated list of 0,1,2,3,4,5
11238 * where 5 is a special case meaning PNG_ALL_FILTERS.
11239 */
11240
11241 if (LocaleCompare(value,"0") == 0)
11242 mng_info->write_png_compression_filter = 1;
11243
11244 if (LocaleCompare(value,"1") == 0)
11245 mng_info->write_png_compression_filter = 2;
11246
11247 else if (LocaleCompare(value,"2") == 0)
11248 mng_info->write_png_compression_filter = 3;
11249
11250 else if (LocaleCompare(value,"3") == 0)
11251 mng_info->write_png_compression_filter = 4;
11252
11253 else if (LocaleCompare(value,"4") == 0)
11254 mng_info->write_png_compression_filter = 5;
11255
11256 else if (LocaleCompare(value,"5") == 0)
11257 mng_info->write_png_compression_filter = 6;
11258
glennrp18682582011-06-30 18:11:47 +000011259 else
cristyc82a27b2011-10-21 01:07:16 +000011260 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011261 GetMagickModule(),CoderWarning,
11262 "ignoring invalid defined png:compression-filter",
11263 "=%s",value);
11264 }
11265
glennrp03812ae2010-12-24 01:31:34 +000011266 excluding=MagickFalse;
11267
glennrp5c7cf4e2010-12-24 00:30:00 +000011268 for (source=0; source<1; source++)
11269 {
11270 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011271 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011272 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011273
11274 if (value == NULL)
11275 value=GetImageArtifact(image,"png:exclude-chunks");
11276 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011277 else
glennrpacba0042010-12-24 14:27:26 +000011278 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011279 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011280
glennrpacba0042010-12-24 14:27:26 +000011281 if (value == NULL)
11282 value=GetImageOption(image_info,"png:exclude-chunks");
11283 }
11284
glennrp03812ae2010-12-24 01:31:34 +000011285 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011286 {
glennrp03812ae2010-12-24 01:31:34 +000011287
11288 size_t
11289 last;
11290
11291 excluding=MagickTrue;
11292
11293 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011294 {
11295 if (source == 0)
11296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11297 " png:exclude-chunk=%s found in image artifacts.\n", value);
11298 else
11299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11300 " png:exclude-chunk=%s found in image properties.\n", value);
11301 }
glennrp03812ae2010-12-24 01:31:34 +000011302
11303 last=strlen(value);
11304
11305 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011306 {
glennrp03812ae2010-12-24 01:31:34 +000011307
11308 if (LocaleNCompare(value+i,"all",3) == 0)
11309 {
11310 mng_info->ping_exclude_bKGD=MagickTrue;
11311 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011312 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011313 mng_info->ping_exclude_EXIF=MagickTrue;
11314 mng_info->ping_exclude_gAMA=MagickTrue;
11315 mng_info->ping_exclude_iCCP=MagickTrue;
11316 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11317 mng_info->ping_exclude_oFFs=MagickTrue;
11318 mng_info->ping_exclude_pHYs=MagickTrue;
11319 mng_info->ping_exclude_sRGB=MagickTrue;
11320 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011321 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011322 mng_info->ping_exclude_vpAg=MagickTrue;
11323 mng_info->ping_exclude_zCCP=MagickTrue;
11324 mng_info->ping_exclude_zTXt=MagickTrue;
11325 i--;
11326 }
glennrp2cc891a2010-12-24 13:44:32 +000011327
glennrp03812ae2010-12-24 01:31:34 +000011328 if (LocaleNCompare(value+i,"none",4) == 0)
11329 {
11330 mng_info->ping_exclude_bKGD=MagickFalse;
11331 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011332 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011333 mng_info->ping_exclude_EXIF=MagickFalse;
11334 mng_info->ping_exclude_gAMA=MagickFalse;
11335 mng_info->ping_exclude_iCCP=MagickFalse;
11336 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11337 mng_info->ping_exclude_oFFs=MagickFalse;
11338 mng_info->ping_exclude_pHYs=MagickFalse;
11339 mng_info->ping_exclude_sRGB=MagickFalse;
11340 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011341 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011342 mng_info->ping_exclude_vpAg=MagickFalse;
11343 mng_info->ping_exclude_zCCP=MagickFalse;
11344 mng_info->ping_exclude_zTXt=MagickFalse;
11345 }
glennrp2cc891a2010-12-24 13:44:32 +000011346
glennrp03812ae2010-12-24 01:31:34 +000011347 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11348 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011349
glennrp03812ae2010-12-24 01:31:34 +000011350 if (LocaleNCompare(value+i,"chrm",4) == 0)
11351 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011352
glennrpa0ed0092011-04-18 16:36:29 +000011353 if (LocaleNCompare(value+i,"date",4) == 0)
11354 mng_info->ping_exclude_date=MagickTrue;
11355
glennrp03812ae2010-12-24 01:31:34 +000011356 if (LocaleNCompare(value+i,"exif",4) == 0)
11357 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011358
glennrp03812ae2010-12-24 01:31:34 +000011359 if (LocaleNCompare(value+i,"gama",4) == 0)
11360 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011361
glennrp03812ae2010-12-24 01:31:34 +000011362 if (LocaleNCompare(value+i,"iccp",4) == 0)
11363 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011364
glennrp03812ae2010-12-24 01:31:34 +000011365 /*
11366 if (LocaleNCompare(value+i,"itxt",4) == 0)
11367 mng_info->ping_exclude_iTXt=MagickTrue;
11368 */
glennrp2cc891a2010-12-24 13:44:32 +000011369
glennrp03812ae2010-12-24 01:31:34 +000011370 if (LocaleNCompare(value+i,"gama",4) == 0)
11371 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011372
glennrp03812ae2010-12-24 01:31:34 +000011373 if (LocaleNCompare(value+i,"offs",4) == 0)
11374 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011375
glennrp03812ae2010-12-24 01:31:34 +000011376 if (LocaleNCompare(value+i,"phys",4) == 0)
11377 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011378
glennrpa1e3b7b2010-12-24 16:37:33 +000011379 if (LocaleNCompare(value+i,"srgb",4) == 0)
11380 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011381
glennrp03812ae2010-12-24 01:31:34 +000011382 if (LocaleNCompare(value+i,"text",4) == 0)
11383 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011384
glennrpa1e3b7b2010-12-24 16:37:33 +000011385 if (LocaleNCompare(value+i,"trns",4) == 0)
11386 mng_info->ping_exclude_tRNS=MagickTrue;
11387
glennrp03812ae2010-12-24 01:31:34 +000011388 if (LocaleNCompare(value+i,"vpag",4) == 0)
11389 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011390
glennrp03812ae2010-12-24 01:31:34 +000011391 if (LocaleNCompare(value+i,"zccp",4) == 0)
11392 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011393
glennrp03812ae2010-12-24 01:31:34 +000011394 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11395 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011396
glennrp03812ae2010-12-24 01:31:34 +000011397 }
glennrpce91ed52010-12-23 22:37:49 +000011398 }
glennrp26f37912010-12-23 16:22:42 +000011399 }
11400
glennrp5c7cf4e2010-12-24 00:30:00 +000011401 for (source=0; source<1; source++)
11402 {
11403 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011404 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011405 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011406
11407 if (value == NULL)
11408 value=GetImageArtifact(image,"png:include-chunks");
11409 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011410 else
glennrpacba0042010-12-24 14:27:26 +000011411 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011412 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011413
glennrpacba0042010-12-24 14:27:26 +000011414 if (value == NULL)
11415 value=GetImageOption(image_info,"png:include-chunks");
11416 }
11417
glennrp03812ae2010-12-24 01:31:34 +000011418 if (value != NULL)
11419 {
11420 size_t
11421 last;
glennrp26f37912010-12-23 16:22:42 +000011422
glennrp03812ae2010-12-24 01:31:34 +000011423 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011424
glennrp03812ae2010-12-24 01:31:34 +000011425 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011426 {
11427 if (source == 0)
11428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11429 " png:include-chunk=%s found in image artifacts.\n", value);
11430 else
11431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11432 " png:include-chunk=%s found in image properties.\n", value);
11433 }
glennrp03812ae2010-12-24 01:31:34 +000011434
11435 last=strlen(value);
11436
11437 for (i=0; i<(int) last; i+=5)
11438 {
11439 if (LocaleNCompare(value+i,"all",3) == 0)
11440 {
11441 mng_info->ping_exclude_bKGD=MagickFalse;
11442 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011443 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011444 mng_info->ping_exclude_EXIF=MagickFalse;
11445 mng_info->ping_exclude_gAMA=MagickFalse;
11446 mng_info->ping_exclude_iCCP=MagickFalse;
11447 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11448 mng_info->ping_exclude_oFFs=MagickFalse;
11449 mng_info->ping_exclude_pHYs=MagickFalse;
11450 mng_info->ping_exclude_sRGB=MagickFalse;
11451 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011452 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011453 mng_info->ping_exclude_vpAg=MagickFalse;
11454 mng_info->ping_exclude_zCCP=MagickFalse;
11455 mng_info->ping_exclude_zTXt=MagickFalse;
11456 i--;
11457 }
glennrp2cc891a2010-12-24 13:44:32 +000011458
glennrp03812ae2010-12-24 01:31:34 +000011459 if (LocaleNCompare(value+i,"none",4) == 0)
11460 {
11461 mng_info->ping_exclude_bKGD=MagickTrue;
11462 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011463 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011464 mng_info->ping_exclude_EXIF=MagickTrue;
11465 mng_info->ping_exclude_gAMA=MagickTrue;
11466 mng_info->ping_exclude_iCCP=MagickTrue;
11467 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11468 mng_info->ping_exclude_oFFs=MagickTrue;
11469 mng_info->ping_exclude_pHYs=MagickTrue;
11470 mng_info->ping_exclude_sRGB=MagickTrue;
11471 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011472 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011473 mng_info->ping_exclude_vpAg=MagickTrue;
11474 mng_info->ping_exclude_zCCP=MagickTrue;
11475 mng_info->ping_exclude_zTXt=MagickTrue;
11476 }
glennrp2cc891a2010-12-24 13:44:32 +000011477
glennrp03812ae2010-12-24 01:31:34 +000011478 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11479 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011480
glennrp03812ae2010-12-24 01:31:34 +000011481 if (LocaleNCompare(value+i,"chrm",4) == 0)
11482 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011483
glennrpa0ed0092011-04-18 16:36:29 +000011484 if (LocaleNCompare(value+i,"date",4) == 0)
11485 mng_info->ping_exclude_date=MagickFalse;
11486
glennrp03812ae2010-12-24 01:31:34 +000011487 if (LocaleNCompare(value+i,"exif",4) == 0)
11488 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011489
glennrp03812ae2010-12-24 01:31:34 +000011490 if (LocaleNCompare(value+i,"gama",4) == 0)
11491 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011492
glennrp03812ae2010-12-24 01:31:34 +000011493 if (LocaleNCompare(value+i,"iccp",4) == 0)
11494 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011495
glennrp03812ae2010-12-24 01:31:34 +000011496 /*
11497 if (LocaleNCompare(value+i,"itxt",4) == 0)
11498 mng_info->ping_exclude_iTXt=MagickFalse;
11499 */
glennrp2cc891a2010-12-24 13:44:32 +000011500
glennrp03812ae2010-12-24 01:31:34 +000011501 if (LocaleNCompare(value+i,"gama",4) == 0)
11502 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011503
glennrp03812ae2010-12-24 01:31:34 +000011504 if (LocaleNCompare(value+i,"offs",4) == 0)
11505 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011506
glennrp03812ae2010-12-24 01:31:34 +000011507 if (LocaleNCompare(value+i,"phys",4) == 0)
11508 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011509
glennrpa1e3b7b2010-12-24 16:37:33 +000011510 if (LocaleNCompare(value+i,"srgb",4) == 0)
11511 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011512
glennrp03812ae2010-12-24 01:31:34 +000011513 if (LocaleNCompare(value+i,"text",4) == 0)
11514 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011515
glennrpa1e3b7b2010-12-24 16:37:33 +000011516 if (LocaleNCompare(value+i,"trns",4) == 0)
11517 mng_info->ping_exclude_tRNS=MagickFalse;
11518
glennrp03812ae2010-12-24 01:31:34 +000011519 if (LocaleNCompare(value+i,"vpag",4) == 0)
11520 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011521
glennrp03812ae2010-12-24 01:31:34 +000011522 if (LocaleNCompare(value+i,"zccp",4) == 0)
11523 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011524
glennrp03812ae2010-12-24 01:31:34 +000011525 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11526 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011527
glennrp03812ae2010-12-24 01:31:34 +000011528 }
glennrpce91ed52010-12-23 22:37:49 +000011529 }
glennrp26f37912010-12-23 16:22:42 +000011530 }
11531
glennrp03812ae2010-12-24 01:31:34 +000011532 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011533 {
11534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011535 " Chunks to be excluded from the output png:");
glennrp26f37912010-12-23 16:22:42 +000011536 if (mng_info->ping_exclude_bKGD != MagickFalse)
11537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11538 " bKGD");
11539 if (mng_info->ping_exclude_cHRM != MagickFalse)
11540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11541 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011542 if (mng_info->ping_exclude_date != MagickFalse)
11543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11544 " date");
glennrp26f37912010-12-23 16:22:42 +000011545 if (mng_info->ping_exclude_EXIF != MagickFalse)
11546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11547 " EXIF");
11548 if (mng_info->ping_exclude_gAMA != MagickFalse)
11549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11550 " gAMA");
11551 if (mng_info->ping_exclude_iCCP != MagickFalse)
11552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11553 " iCCP");
11554/*
11555 if (mng_info->ping_exclude_iTXt != MagickFalse)
11556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11557 " iTXt");
11558*/
11559 if (mng_info->ping_exclude_oFFs != MagickFalse)
11560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11561 " oFFs");
11562 if (mng_info->ping_exclude_pHYs != MagickFalse)
11563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11564 " pHYs");
11565 if (mng_info->ping_exclude_sRGB != MagickFalse)
11566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11567 " sRGB");
11568 if (mng_info->ping_exclude_tEXt != MagickFalse)
11569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11570 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011571 if (mng_info->ping_exclude_tRNS != MagickFalse)
11572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11573 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011574 if (mng_info->ping_exclude_vpAg != MagickFalse)
11575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11576 " vpAg");
11577 if (mng_info->ping_exclude_zCCP != MagickFalse)
11578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11579 " zCCP");
11580 if (mng_info->ping_exclude_zTXt != MagickFalse)
11581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11582 " zTXt");
11583 }
11584
glennrpb9cfe272010-12-21 15:08:06 +000011585 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011586
cristy018f07f2011-09-04 21:15:19 +000011587 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011588
11589 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011590
cristy3ed852e2009-09-05 21:47:34 +000011591 if (logging != MagickFalse)
11592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011593
cristy3ed852e2009-09-05 21:47:34 +000011594 return(status);
11595}
11596
11597#if defined(JNG_SUPPORTED)
11598
11599/* Write one JNG image */
11600static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +000011601 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011602{
11603 Image
11604 *jpeg_image;
11605
11606 ImageInfo
11607 *jpeg_image_info;
11608
11609 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011610 logging,
cristy3ed852e2009-09-05 21:47:34 +000011611 status;
11612
11613 size_t
11614 length;
11615
11616 unsigned char
11617 *blob,
11618 chunk[80],
11619 *p;
11620
11621 unsigned int
11622 jng_alpha_compression_method,
11623 jng_alpha_sample_depth,
11624 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011625 transparent;
11626
cristybb503372010-05-27 20:51:26 +000011627 size_t
cristy3ed852e2009-09-05 21:47:34 +000011628 jng_quality;
11629
11630 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011631 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011632
11633 blob=(unsigned char *) NULL;
11634 jpeg_image=(Image *) NULL;
11635 jpeg_image_info=(ImageInfo *) NULL;
11636
11637 status=MagickTrue;
11638 transparent=image_info->type==GrayscaleMatteType ||
11639 image_info->type==TrueColorMatteType;
11640 jng_color_type=10;
11641 jng_alpha_sample_depth=0;
11642 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11643 jng_alpha_compression_method=0;
11644
11645 if (image->matte != MagickFalse)
11646 {
11647 /* if any pixels are transparent */
11648 transparent=MagickTrue;
11649 if (image_info->compression==JPEGCompression)
11650 jng_alpha_compression_method=8;
11651 }
11652
11653 if (transparent)
11654 {
cristybd5a96c2011-08-21 00:04:26 +000011655 ChannelType
11656 channel_mask;
11657
cristy3ed852e2009-09-05 21:47:34 +000011658 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011659
cristy3ed852e2009-09-05 21:47:34 +000011660 /* Create JPEG blob, image, and image_info */
11661 if (logging != MagickFalse)
11662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +000011663 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011664
cristy3ed852e2009-09-05 21:47:34 +000011665 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011666
cristy3ed852e2009-09-05 21:47:34 +000011667 if (jpeg_image_info == (ImageInfo *) NULL)
11668 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011669
cristy3ed852e2009-09-05 21:47:34 +000011670 if (logging != MagickFalse)
11671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11672 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011673
cristyc82a27b2011-10-21 01:07:16 +000011674 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011675
cristy3ed852e2009-09-05 21:47:34 +000011676 if (jpeg_image == (Image *) NULL)
11677 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011678
cristy3ed852e2009-09-05 21:47:34 +000011679 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristybd5a96c2011-08-21 00:04:26 +000011680 channel_mask=SetPixelChannelMask(jpeg_image,AlphaChannel);
cristye941a752011-10-15 01:52:48 +000011681 status=SeparateImage(jpeg_image,exception);
cristye2a912b2011-12-05 20:02:07 +000011682 (void) SetPixelChannelMapMask(jpeg_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +000011683 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011684
cristy3ed852e2009-09-05 21:47:34 +000011685 if (jng_quality >= 1000)
11686 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000011687
cristy3ed852e2009-09-05 21:47:34 +000011688 else
11689 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000011690
cristy3ed852e2009-09-05 21:47:34 +000011691 jpeg_image_info->type=GrayscaleType;
cristy018f07f2011-09-04 21:15:19 +000011692 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000011693 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011694 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011695 "%s",jpeg_image->filename);
11696 }
11697
11698 /* To do: check bit depth of PNG alpha channel */
11699
11700 /* Check if image is grayscale. */
11701 if (image_info->type != TrueColorMatteType && image_info->type !=
cristyc82a27b2011-10-21 01:07:16 +000011702 TrueColorType && ImageIsGray(image,exception))
cristy3ed852e2009-09-05 21:47:34 +000011703 jng_color_type-=2;
11704
11705 if (transparent)
11706 {
11707 if (jng_alpha_compression_method==0)
11708 {
11709 const char
11710 *value;
11711
cristy4c08aed2011-07-01 19:47:50 +000011712 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011713 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000011714 exception);
cristy3ed852e2009-09-05 21:47:34 +000011715 if (logging != MagickFalse)
11716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11717 " Creating PNG blob.");
11718 length=0;
11719
11720 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11721 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11722 jpeg_image_info->interlace=NoInterlace;
11723
11724 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyc82a27b2011-10-21 01:07:16 +000011725 exception);
cristy3ed852e2009-09-05 21:47:34 +000011726
11727 /* Retrieve sample depth used */
cristyd15e6592011-10-15 00:13:06 +000011728 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
cristy3ed852e2009-09-05 21:47:34 +000011729 if (value != (char *) NULL)
11730 jng_alpha_sample_depth= (unsigned int) value[0];
11731 }
11732 else
11733 {
cristy4c08aed2011-07-01 19:47:50 +000011734 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011735
11736 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000011737 exception);
cristy3ed852e2009-09-05 21:47:34 +000011738
11739 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11740 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11741 jpeg_image_info->interlace=NoInterlace;
11742 if (logging != MagickFalse)
11743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11744 " Creating blob.");
11745 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyc82a27b2011-10-21 01:07:16 +000011746 exception);
cristy3ed852e2009-09-05 21:47:34 +000011747 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011748
cristy3ed852e2009-09-05 21:47:34 +000011749 if (logging != MagickFalse)
11750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011751 " Successfully read jpeg_image into a blob, length=%.20g.",
11752 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011753
11754 }
11755 /* Destroy JPEG image and image_info */
11756 jpeg_image=DestroyImage(jpeg_image);
11757 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11758 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11759 }
11760
11761 /* Write JHDR chunk */
11762 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11763 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011764 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011765 PNGLong(chunk+4,(png_uint_32) image->columns);
11766 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011767 chunk[12]=jng_color_type;
11768 chunk[13]=8; /* sample depth */
11769 chunk[14]=8; /*jng_image_compression_method */
11770 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11771 chunk[16]=jng_alpha_sample_depth;
11772 chunk[17]=jng_alpha_compression_method;
11773 chunk[18]=0; /*jng_alpha_filter_method */
11774 chunk[19]=0; /*jng_alpha_interlace_method */
11775 (void) WriteBlob(image,20,chunk);
11776 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11777 if (logging != MagickFalse)
11778 {
11779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011780 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011781
cristy3ed852e2009-09-05 21:47:34 +000011782 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011783 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011784
cristy3ed852e2009-09-05 21:47:34 +000011785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11786 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011787
cristy3ed852e2009-09-05 21:47:34 +000011788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11789 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011790
cristy3ed852e2009-09-05 21:47:34 +000011791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11792 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011793
cristy3ed852e2009-09-05 21:47:34 +000011794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11795 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011796
cristy3ed852e2009-09-05 21:47:34 +000011797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11798 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011799
cristy3ed852e2009-09-05 21:47:34 +000011800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11801 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011802
cristy3ed852e2009-09-05 21:47:34 +000011803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11804 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011805
cristy3ed852e2009-09-05 21:47:34 +000011806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11807 " JNG alpha interlace:%5d",0);
11808 }
11809
glennrp0fe50b42010-11-16 03:52:51 +000011810 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011811 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011812
11813 /*
11814 Write leading ancillary chunks
11815 */
11816
11817 if (transparent)
11818 {
11819 /*
11820 Write JNG bKGD chunk
11821 */
11822
11823 unsigned char
11824 blue,
11825 green,
11826 red;
11827
cristybb503372010-05-27 20:51:26 +000011828 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011829 num_bytes;
11830
11831 if (jng_color_type == 8 || jng_color_type == 12)
11832 num_bytes=6L;
11833 else
11834 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011835 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011836 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011837 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011838 red=ScaleQuantumToChar(image->background_color.red);
11839 green=ScaleQuantumToChar(image->background_color.green);
11840 blue=ScaleQuantumToChar(image->background_color.blue);
11841 *(chunk+4)=0;
11842 *(chunk+5)=red;
11843 *(chunk+6)=0;
11844 *(chunk+7)=green;
11845 *(chunk+8)=0;
11846 *(chunk+9)=blue;
11847 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11848 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11849 }
11850
11851 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11852 {
11853 /*
11854 Write JNG sRGB chunk
11855 */
11856 (void) WriteBlobMSBULong(image,1L);
11857 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011858 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011859
cristy3ed852e2009-09-05 21:47:34 +000011860 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011861 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011862 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011863 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011864
cristy3ed852e2009-09-05 21:47:34 +000011865 else
glennrpe610a072010-08-05 17:08:46 +000011866 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011867 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011868 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011869
cristy3ed852e2009-09-05 21:47:34 +000011870 (void) WriteBlob(image,5,chunk);
11871 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11872 }
11873 else
11874 {
11875 if (image->gamma != 0.0)
11876 {
11877 /*
11878 Write JNG gAMA chunk
11879 */
11880 (void) WriteBlobMSBULong(image,4L);
11881 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011882 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011883 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011884 (void) WriteBlob(image,8,chunk);
11885 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11886 }
glennrp0fe50b42010-11-16 03:52:51 +000011887
cristy3ed852e2009-09-05 21:47:34 +000011888 if ((mng_info->equal_chrms == MagickFalse) &&
11889 (image->chromaticity.red_primary.x != 0.0))
11890 {
11891 PrimaryInfo
11892 primary;
11893
11894 /*
11895 Write JNG cHRM chunk
11896 */
11897 (void) WriteBlobMSBULong(image,32L);
11898 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011899 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011900 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011901 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11902 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011903 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011904 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11905 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011906 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011907 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11908 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011909 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011910 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11911 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011912 (void) WriteBlob(image,36,chunk);
11913 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11914 }
11915 }
glennrp0fe50b42010-11-16 03:52:51 +000011916
cristy2a11bef2011-10-28 18:33:11 +000011917 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000011918 {
11919 /*
11920 Write JNG pHYs chunk
11921 */
11922 (void) WriteBlobMSBULong(image,9L);
11923 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011924 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011925 if (image->units == PixelsPerInchResolution)
11926 {
cristy35ef8242010-06-03 16:24:13 +000011927 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011928 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011929
cristy35ef8242010-06-03 16:24:13 +000011930 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011931 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011932
cristy3ed852e2009-09-05 21:47:34 +000011933 chunk[12]=1;
11934 }
glennrp0fe50b42010-11-16 03:52:51 +000011935
cristy3ed852e2009-09-05 21:47:34 +000011936 else
11937 {
11938 if (image->units == PixelsPerCentimeterResolution)
11939 {
cristy35ef8242010-06-03 16:24:13 +000011940 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011941 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011942
cristy35ef8242010-06-03 16:24:13 +000011943 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011944 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011945
cristy3ed852e2009-09-05 21:47:34 +000011946 chunk[12]=1;
11947 }
glennrp0fe50b42010-11-16 03:52:51 +000011948
cristy3ed852e2009-09-05 21:47:34 +000011949 else
11950 {
cristy2a11bef2011-10-28 18:33:11 +000011951 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
11952 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011953 chunk[12]=0;
11954 }
11955 }
11956 (void) WriteBlob(image,13,chunk);
11957 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11958 }
11959
11960 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11961 {
11962 /*
11963 Write JNG oFFs chunk
11964 */
11965 (void) WriteBlobMSBULong(image,9L);
11966 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011967 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011968 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11969 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011970 chunk[12]=0;
11971 (void) WriteBlob(image,13,chunk);
11972 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11973 }
11974 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11975 {
11976 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11977 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011978 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011979 PNGLong(chunk+4,(png_uint_32) image->page.width);
11980 PNGLong(chunk+8,(png_uint_32) image->page.height);
11981 chunk[12]=0; /* unit = pixels */
11982 (void) WriteBlob(image,13,chunk);
11983 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11984 }
11985
11986
11987 if (transparent)
11988 {
11989 if (jng_alpha_compression_method==0)
11990 {
cristybb503372010-05-27 20:51:26 +000011991 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011992 i;
11993
cristybb503372010-05-27 20:51:26 +000011994 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011995 len;
11996
11997 /* Write IDAT chunk header */
11998 if (logging != MagickFalse)
11999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012000 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000012001 length);
cristy3ed852e2009-09-05 21:47:34 +000012002
12003 /* Copy IDAT chunks */
12004 len=0;
12005 p=blob+8;
cristybb503372010-05-27 20:51:26 +000012006 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000012007 {
12008 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12009 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012010
cristy3ed852e2009-09-05 21:47:34 +000012011 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12012 {
12013 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000012014 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000012015 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000012016 (void) WriteBlob(image,(size_t) len+4,p);
12017 (void) WriteBlobMSBULong(image,
12018 crc32(0,p,(uInt) len+4));
12019 }
glennrp0fe50b42010-11-16 03:52:51 +000012020
cristy3ed852e2009-09-05 21:47:34 +000012021 else
12022 {
12023 if (logging != MagickFalse)
12024 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012025 " Skipping %c%c%c%c chunk, length=%.20g.",
12026 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012027 }
12028 p+=(8+len);
12029 }
12030 }
12031 else
12032 {
12033 /* Write JDAA chunk header */
12034 if (logging != MagickFalse)
12035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012036 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012037 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012038 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012039 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012040 /* Write JDAT chunk(s) data */
12041 (void) WriteBlob(image,4,chunk);
12042 (void) WriteBlob(image,length,blob);
12043 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12044 (uInt) length));
12045 }
12046 blob=(unsigned char *) RelinquishMagickMemory(blob);
12047 }
12048
12049 /* Encode image as a JPEG blob */
12050 if (logging != MagickFalse)
12051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12052 " Creating jpeg_image_info.");
12053 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12054 if (jpeg_image_info == (ImageInfo *) NULL)
12055 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12056
12057 if (logging != MagickFalse)
12058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12059 " Creating jpeg_image.");
12060
cristyc82a27b2011-10-21 01:07:16 +000012061 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012062 if (jpeg_image == (Image *) NULL)
12063 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12064 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12065
12066 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012067 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012068 jpeg_image->filename);
12069
12070 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000012071 exception);
cristy3ed852e2009-09-05 21:47:34 +000012072
12073 if (logging != MagickFalse)
12074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012075 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12076 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012077
12078 if (jng_color_type == 8 || jng_color_type == 12)
12079 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012080
cristy3ed852e2009-09-05 21:47:34 +000012081 jpeg_image_info->quality=jng_quality % 1000;
12082 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12083 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012084
cristy3ed852e2009-09-05 21:47:34 +000012085 if (logging != MagickFalse)
12086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12087 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012088
cristyc82a27b2011-10-21 01:07:16 +000012089 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
glennrp0fe50b42010-11-16 03:52:51 +000012090
cristy3ed852e2009-09-05 21:47:34 +000012091 if (logging != MagickFalse)
12092 {
12093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012094 " Successfully read jpeg_image into a blob, length=%.20g.",
12095 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012096
12097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012098 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012099 }
glennrp0fe50b42010-11-16 03:52:51 +000012100
cristy3ed852e2009-09-05 21:47:34 +000012101 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012102 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012103 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012104 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012105 (void) WriteBlob(image,4,chunk);
12106 (void) WriteBlob(image,length,blob);
12107 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12108
12109 jpeg_image=DestroyImage(jpeg_image);
12110 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12111 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12112 blob=(unsigned char *) RelinquishMagickMemory(blob);
12113
12114 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012115 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012116
12117 /* Write IEND chunk */
12118 (void) WriteBlobMSBULong(image,0L);
12119 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012120 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012121 (void) WriteBlob(image,4,chunk);
12122 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12123
12124 if (logging != MagickFalse)
12125 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12126 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012127
cristy3ed852e2009-09-05 21:47:34 +000012128 return(status);
12129}
12130
12131
12132/*
12133%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12134% %
12135% %
12136% %
12137% W r i t e J N G I m a g e %
12138% %
12139% %
12140% %
12141%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12142%
12143% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12144%
12145% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12146%
12147% The format of the WriteJNGImage method is:
12148%
cristy1e178e72011-08-28 19:44:34 +000012149% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12150% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012151%
12152% A description of each parameter follows:
12153%
12154% o image_info: the image info.
12155%
12156% o image: The image.
12157%
cristy1e178e72011-08-28 19:44:34 +000012158% o exception: return any errors or warnings in this structure.
12159%
cristy3ed852e2009-09-05 21:47:34 +000012160%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12161*/
cristy1e178e72011-08-28 19:44:34 +000012162static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12163 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012164{
12165 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012166 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012167 logging,
cristy3ed852e2009-09-05 21:47:34 +000012168 status;
12169
12170 MngInfo
12171 *mng_info;
12172
cristy3ed852e2009-09-05 21:47:34 +000012173 /*
12174 Open image file.
12175 */
12176 assert(image_info != (const ImageInfo *) NULL);
12177 assert(image_info->signature == MagickSignature);
12178 assert(image != (Image *) NULL);
12179 assert(image->signature == MagickSignature);
12180 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012181 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristyc82a27b2011-10-21 01:07:16 +000012182 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012183 if (status == MagickFalse)
12184 return(status);
12185
12186 /*
12187 Allocate a MngInfo structure.
12188 */
12189 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012190 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012191 if (mng_info == (MngInfo *) NULL)
12192 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12193 /*
12194 Initialize members of the MngInfo structure.
12195 */
12196 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12197 mng_info->image=image;
12198 have_mng_structure=MagickTrue;
12199
12200 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12201
cristy018f07f2011-09-04 21:15:19 +000012202 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012203 (void) CloseBlob(image);
12204
12205 (void) CatchImageException(image);
12206 MngInfoFreeStruct(mng_info,&have_mng_structure);
12207 if (logging != MagickFalse)
12208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12209 return(status);
12210}
12211#endif
12212
cristy1e178e72011-08-28 19:44:34 +000012213static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12214 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012215{
12216 const char
12217 *option;
12218
12219 Image
12220 *next_image;
12221
12222 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012223 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012224 status;
12225
glennrp03812ae2010-12-24 01:31:34 +000012226 volatile MagickBooleanType
12227 logging;
12228
cristy3ed852e2009-09-05 21:47:34 +000012229 MngInfo
12230 *mng_info;
12231
12232 int
cristy3ed852e2009-09-05 21:47:34 +000012233 image_count,
12234 need_iterations,
12235 need_matte;
12236
12237 volatile int
12238#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12239 defined(PNG_MNG_FEATURES_SUPPORTED)
12240 need_local_plte,
12241#endif
12242 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012243 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012244 use_global_plte;
12245
cristybb503372010-05-27 20:51:26 +000012246 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012247 i;
12248
12249 unsigned char
12250 chunk[800];
12251
12252 volatile unsigned int
12253 write_jng,
12254 write_mng;
12255
cristybb503372010-05-27 20:51:26 +000012256 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012257 scene;
12258
cristybb503372010-05-27 20:51:26 +000012259 size_t
cristy3ed852e2009-09-05 21:47:34 +000012260 final_delay=0,
12261 initial_delay;
12262
glennrpd5045b42010-03-24 12:40:35 +000012263#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012264 if (image_info->verbose)
12265 printf("Your PNG library (libpng-%s) is rather old.\n",
12266 PNG_LIBPNG_VER_STRING);
12267#endif
12268
12269 /*
12270 Open image file.
12271 */
12272 assert(image_info != (const ImageInfo *) NULL);
12273 assert(image_info->signature == MagickSignature);
12274 assert(image != (Image *) NULL);
12275 assert(image->signature == MagickSignature);
12276 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012277 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristyc82a27b2011-10-21 01:07:16 +000012278 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012279 if (status == MagickFalse)
12280 return(status);
12281
12282 /*
12283 Allocate a MngInfo structure.
12284 */
12285 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012286 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012287 if (mng_info == (MngInfo *) NULL)
12288 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12289 /*
12290 Initialize members of the MngInfo structure.
12291 */
12292 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12293 mng_info->image=image;
12294 have_mng_structure=MagickTrue;
12295 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12296
12297 /*
12298 * See if user has requested a specific PNG subformat to be used
12299 * for all of the PNGs in the MNG being written, e.g.,
12300 *
12301 * convert *.png png8:animation.mng
12302 *
12303 * To do: check -define png:bit_depth and png:color_type as well,
12304 * or perhaps use mng:bit_depth and mng:color_type instead for
12305 * global settings.
12306 */
12307
12308 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12309 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12310 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12311
12312 write_jng=MagickFalse;
12313 if (image_info->compression == JPEGCompression)
12314 write_jng=MagickTrue;
12315
12316 mng_info->adjoin=image_info->adjoin &&
12317 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12318
cristy3ed852e2009-09-05 21:47:34 +000012319 if (logging != MagickFalse)
12320 {
12321 /* Log some info about the input */
12322 Image
12323 *p;
12324
12325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12326 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012327
cristy3ed852e2009-09-05 21:47:34 +000012328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012329 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012330
cristy3ed852e2009-09-05 21:47:34 +000012331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12332 " Type: %d",image_info->type);
12333
12334 scene=0;
12335 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12336 {
12337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012338 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012339
cristy3ed852e2009-09-05 21:47:34 +000012340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012341 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012342
cristy3ed852e2009-09-05 21:47:34 +000012343 if (p->matte)
12344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12345 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012346
cristy3ed852e2009-09-05 21:47:34 +000012347 else
12348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12349 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012350
cristy3ed852e2009-09-05 21:47:34 +000012351 if (p->storage_class == PseudoClass)
12352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12353 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012354
cristy3ed852e2009-09-05 21:47:34 +000012355 else
12356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12357 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012358
cristy3ed852e2009-09-05 21:47:34 +000012359 if (p->colors)
12360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012361 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012362
cristy3ed852e2009-09-05 21:47:34 +000012363 else
12364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12365 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012366
cristy3ed852e2009-09-05 21:47:34 +000012367 if (mng_info->adjoin == MagickFalse)
12368 break;
12369 }
12370 }
12371
cristy3ed852e2009-09-05 21:47:34 +000012372 use_global_plte=MagickFalse;
12373 all_images_are_gray=MagickFalse;
12374#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12375 need_local_plte=MagickTrue;
12376#endif
12377 need_defi=MagickFalse;
12378 need_matte=MagickFalse;
12379 mng_info->framing_mode=1;
12380 mng_info->old_framing_mode=1;
12381
12382 if (write_mng)
12383 if (image_info->page != (char *) NULL)
12384 {
12385 /*
12386 Determine image bounding box.
12387 */
12388 SetGeometry(image,&mng_info->page);
12389 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12390 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12391 }
12392 if (write_mng)
12393 {
12394 unsigned int
12395 need_geom;
12396
12397 unsigned short
12398 red,
12399 green,
12400 blue;
12401
12402 mng_info->page=image->page;
12403 need_geom=MagickTrue;
12404 if (mng_info->page.width || mng_info->page.height)
12405 need_geom=MagickFalse;
12406 /*
12407 Check all the scenes.
12408 */
12409 initial_delay=image->delay;
12410 need_iterations=MagickFalse;
12411 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12412 mng_info->equal_physs=MagickTrue,
12413 mng_info->equal_gammas=MagickTrue;
12414 mng_info->equal_srgbs=MagickTrue;
12415 mng_info->equal_backgrounds=MagickTrue;
12416 image_count=0;
12417#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12418 defined(PNG_MNG_FEATURES_SUPPORTED)
12419 all_images_are_gray=MagickTrue;
12420 mng_info->equal_palettes=MagickFalse;
12421 need_local_plte=MagickFalse;
12422#endif
12423 for (next_image=image; next_image != (Image *) NULL; )
12424 {
12425 if (need_geom)
12426 {
12427 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12428 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012429
cristy3ed852e2009-09-05 21:47:34 +000012430 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12431 mng_info->page.height=next_image->rows+next_image->page.y;
12432 }
glennrp0fe50b42010-11-16 03:52:51 +000012433
cristy3ed852e2009-09-05 21:47:34 +000012434 if (next_image->page.x || next_image->page.y)
12435 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012436
cristy3ed852e2009-09-05 21:47:34 +000012437 if (next_image->matte)
12438 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012439
cristy3ed852e2009-09-05 21:47:34 +000012440 if ((int) next_image->dispose >= BackgroundDispose)
12441 if (next_image->matte || next_image->page.x || next_image->page.y ||
12442 ((next_image->columns < mng_info->page.width) &&
12443 (next_image->rows < mng_info->page.height)))
12444 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012445
cristy3ed852e2009-09-05 21:47:34 +000012446 if (next_image->iterations)
12447 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012448
cristy3ed852e2009-09-05 21:47:34 +000012449 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012450
cristy3ed852e2009-09-05 21:47:34 +000012451 if (final_delay != initial_delay || final_delay > 1UL*
12452 next_image->ticks_per_second)
12453 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012454
cristy3ed852e2009-09-05 21:47:34 +000012455#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12456 defined(PNG_MNG_FEATURES_SUPPORTED)
12457 /*
12458 check for global palette possibility.
12459 */
12460 if (image->matte != MagickFalse)
12461 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012462
cristy3ed852e2009-09-05 21:47:34 +000012463 if (need_local_plte == 0)
12464 {
cristyc82a27b2011-10-21 01:07:16 +000012465 if (ImageIsGray(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012466 all_images_are_gray=MagickFalse;
12467 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12468 if (use_global_plte == 0)
12469 use_global_plte=mng_info->equal_palettes;
12470 need_local_plte=!mng_info->equal_palettes;
12471 }
12472#endif
12473 if (GetNextImageInList(next_image) != (Image *) NULL)
12474 {
12475 if (next_image->background_color.red !=
12476 next_image->next->background_color.red ||
12477 next_image->background_color.green !=
12478 next_image->next->background_color.green ||
12479 next_image->background_color.blue !=
12480 next_image->next->background_color.blue)
12481 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012482
cristy3ed852e2009-09-05 21:47:34 +000012483 if (next_image->gamma != next_image->next->gamma)
12484 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012485
cristy3ed852e2009-09-05 21:47:34 +000012486 if (next_image->rendering_intent !=
12487 next_image->next->rendering_intent)
12488 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012489
cristy3ed852e2009-09-05 21:47:34 +000012490 if ((next_image->units != next_image->next->units) ||
cristy2a11bef2011-10-28 18:33:11 +000012491 (next_image->resolution.x != next_image->next->resolution.x) ||
12492 (next_image->resolution.y != next_image->next->resolution.y))
cristy3ed852e2009-09-05 21:47:34 +000012493 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012494
cristy3ed852e2009-09-05 21:47:34 +000012495 if (mng_info->equal_chrms)
12496 {
12497 if (next_image->chromaticity.red_primary.x !=
12498 next_image->next->chromaticity.red_primary.x ||
12499 next_image->chromaticity.red_primary.y !=
12500 next_image->next->chromaticity.red_primary.y ||
12501 next_image->chromaticity.green_primary.x !=
12502 next_image->next->chromaticity.green_primary.x ||
12503 next_image->chromaticity.green_primary.y !=
12504 next_image->next->chromaticity.green_primary.y ||
12505 next_image->chromaticity.blue_primary.x !=
12506 next_image->next->chromaticity.blue_primary.x ||
12507 next_image->chromaticity.blue_primary.y !=
12508 next_image->next->chromaticity.blue_primary.y ||
12509 next_image->chromaticity.white_point.x !=
12510 next_image->next->chromaticity.white_point.x ||
12511 next_image->chromaticity.white_point.y !=
12512 next_image->next->chromaticity.white_point.y)
12513 mng_info->equal_chrms=MagickFalse;
12514 }
12515 }
12516 image_count++;
12517 next_image=GetNextImageInList(next_image);
12518 }
12519 if (image_count < 2)
12520 {
12521 mng_info->equal_backgrounds=MagickFalse;
12522 mng_info->equal_chrms=MagickFalse;
12523 mng_info->equal_gammas=MagickFalse;
12524 mng_info->equal_srgbs=MagickFalse;
12525 mng_info->equal_physs=MagickFalse;
12526 use_global_plte=MagickFalse;
12527#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12528 need_local_plte=MagickTrue;
12529#endif
12530 need_iterations=MagickFalse;
12531 }
glennrp0fe50b42010-11-16 03:52:51 +000012532
cristy3ed852e2009-09-05 21:47:34 +000012533 if (mng_info->need_fram == MagickFalse)
12534 {
12535 /*
12536 Only certain framing rates 100/n are exactly representable without
12537 the FRAM chunk but we'll allow some slop in VLC files
12538 */
12539 if (final_delay == 0)
12540 {
12541 if (need_iterations != MagickFalse)
12542 {
12543 /*
12544 It's probably a GIF with loop; don't run it *too* fast.
12545 */
glennrp02617122010-07-28 13:07:35 +000012546 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012547 {
12548 final_delay=10;
cristyc82a27b2011-10-21 01:07:16 +000012549 (void) ThrowMagickException(exception,GetMagickModule(),
12550 CoderWarning,
glennrpd908de42010-07-28 13:28:27 +000012551 "input has zero delay between all frames; assuming",
12552 " 10 cs `%s'","");
12553 }
cristy3ed852e2009-09-05 21:47:34 +000012554 }
12555 else
12556 mng_info->ticks_per_second=0;
12557 }
12558 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012559 mng_info->ticks_per_second=(png_uint_32)
12560 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012561 if (final_delay > 50)
12562 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012563
cristy3ed852e2009-09-05 21:47:34 +000012564 if (final_delay > 75)
12565 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012566
cristy3ed852e2009-09-05 21:47:34 +000012567 if (final_delay > 125)
12568 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012569
cristy3ed852e2009-09-05 21:47:34 +000012570 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12571 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12572 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12573 1UL*image->ticks_per_second))
12574 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12575 }
glennrp0fe50b42010-11-16 03:52:51 +000012576
cristy3ed852e2009-09-05 21:47:34 +000012577 if (mng_info->need_fram != MagickFalse)
12578 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12579 /*
12580 If pseudocolor, we should also check to see if all the
12581 palettes are identical and write a global PLTE if they are.
12582 ../glennrp Feb 99.
12583 */
12584 /*
12585 Write the MNG version 1.0 signature and MHDR chunk.
12586 */
12587 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12588 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12589 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012590 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012591 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12592 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012593 PNGLong(chunk+12,mng_info->ticks_per_second);
12594 PNGLong(chunk+16,0L); /* layer count=unknown */
12595 PNGLong(chunk+20,0L); /* frame count=unknown */
12596 PNGLong(chunk+24,0L); /* play time=unknown */
12597 if (write_jng)
12598 {
12599 if (need_matte)
12600 {
12601 if (need_defi || mng_info->need_fram || use_global_plte)
12602 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012603
cristy3ed852e2009-09-05 21:47:34 +000012604 else
12605 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12606 }
glennrp0fe50b42010-11-16 03:52:51 +000012607
cristy3ed852e2009-09-05 21:47:34 +000012608 else
12609 {
12610 if (need_defi || mng_info->need_fram || use_global_plte)
12611 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012612
cristy3ed852e2009-09-05 21:47:34 +000012613 else
12614 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12615 }
12616 }
glennrp0fe50b42010-11-16 03:52:51 +000012617
cristy3ed852e2009-09-05 21:47:34 +000012618 else
12619 {
12620 if (need_matte)
12621 {
12622 if (need_defi || mng_info->need_fram || use_global_plte)
12623 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012624
cristy3ed852e2009-09-05 21:47:34 +000012625 else
12626 PNGLong(chunk+28,9L); /* simplicity=VLC */
12627 }
glennrp0fe50b42010-11-16 03:52:51 +000012628
cristy3ed852e2009-09-05 21:47:34 +000012629 else
12630 {
12631 if (need_defi || mng_info->need_fram || use_global_plte)
12632 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012633
cristy3ed852e2009-09-05 21:47:34 +000012634 else
12635 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12636 }
12637 }
12638 (void) WriteBlob(image,32,chunk);
12639 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12640 option=GetImageOption(image_info,"mng:need-cacheoff");
12641 if (option != (const char *) NULL)
12642 {
12643 size_t
12644 length;
12645
12646 /*
12647 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12648 */
12649 PNGType(chunk,mng_nEED);
12650 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012651 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012652 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012653 length+=4;
12654 (void) WriteBlob(image,length,chunk);
12655 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12656 }
12657 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12658 (GetNextImageInList(image) != (Image *) NULL) &&
12659 (image->iterations != 1))
12660 {
12661 /*
12662 Write MNG TERM chunk
12663 */
12664 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12665 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012666 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012667 chunk[4]=3; /* repeat animation */
12668 chunk[5]=0; /* show last frame when done */
12669 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12670 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012671
cristy3ed852e2009-09-05 21:47:34 +000012672 if (image->iterations == 0)
12673 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012674
cristy3ed852e2009-09-05 21:47:34 +000012675 else
12676 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012677
cristy3ed852e2009-09-05 21:47:34 +000012678 if (logging != MagickFalse)
12679 {
12680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012681 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12682 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012683
cristy3ed852e2009-09-05 21:47:34 +000012684 if (image->iterations == 0)
12685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012686 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012687
cristy3ed852e2009-09-05 21:47:34 +000012688 else
12689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012690 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012691 }
12692 (void) WriteBlob(image,14,chunk);
12693 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12694 }
12695 /*
12696 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12697 */
12698 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12699 mng_info->equal_srgbs)
12700 {
12701 /*
12702 Write MNG sRGB chunk
12703 */
12704 (void) WriteBlobMSBULong(image,1L);
12705 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012706 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012707
cristy3ed852e2009-09-05 21:47:34 +000012708 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012709 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012710 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012711 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012712
cristy3ed852e2009-09-05 21:47:34 +000012713 else
glennrpe610a072010-08-05 17:08:46 +000012714 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012715 Magick_RenderingIntent_to_PNG_RenderingIntent(
12716 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012717
cristy3ed852e2009-09-05 21:47:34 +000012718 (void) WriteBlob(image,5,chunk);
12719 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12720 mng_info->have_write_global_srgb=MagickTrue;
12721 }
glennrp0fe50b42010-11-16 03:52:51 +000012722
cristy3ed852e2009-09-05 21:47:34 +000012723 else
12724 {
12725 if (image->gamma && mng_info->equal_gammas)
12726 {
12727 /*
12728 Write MNG gAMA chunk
12729 */
12730 (void) WriteBlobMSBULong(image,4L);
12731 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012732 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012733 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012734 (void) WriteBlob(image,8,chunk);
12735 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12736 mng_info->have_write_global_gama=MagickTrue;
12737 }
12738 if (mng_info->equal_chrms)
12739 {
12740 PrimaryInfo
12741 primary;
12742
12743 /*
12744 Write MNG cHRM chunk
12745 */
12746 (void) WriteBlobMSBULong(image,32L);
12747 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012748 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012749 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012750 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12751 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012752 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012753 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12754 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012755 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012756 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12757 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012758 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012759 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12760 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012761 (void) WriteBlob(image,36,chunk);
12762 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12763 mng_info->have_write_global_chrm=MagickTrue;
12764 }
12765 }
cristy2a11bef2011-10-28 18:33:11 +000012766 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012767 {
12768 /*
12769 Write MNG pHYs chunk
12770 */
12771 (void) WriteBlobMSBULong(image,9L);
12772 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012773 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012774
cristy3ed852e2009-09-05 21:47:34 +000012775 if (image->units == PixelsPerInchResolution)
12776 {
cristy35ef8242010-06-03 16:24:13 +000012777 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012778 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012779
cristy35ef8242010-06-03 16:24:13 +000012780 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012781 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012782
cristy3ed852e2009-09-05 21:47:34 +000012783 chunk[12]=1;
12784 }
glennrp0fe50b42010-11-16 03:52:51 +000012785
cristy3ed852e2009-09-05 21:47:34 +000012786 else
12787 {
12788 if (image->units == PixelsPerCentimeterResolution)
12789 {
cristy35ef8242010-06-03 16:24:13 +000012790 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012791 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012792
cristy35ef8242010-06-03 16:24:13 +000012793 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012794 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012795
cristy3ed852e2009-09-05 21:47:34 +000012796 chunk[12]=1;
12797 }
glennrp0fe50b42010-11-16 03:52:51 +000012798
cristy3ed852e2009-09-05 21:47:34 +000012799 else
12800 {
cristy2a11bef2011-10-28 18:33:11 +000012801 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12802 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012803 chunk[12]=0;
12804 }
12805 }
12806 (void) WriteBlob(image,13,chunk);
12807 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12808 }
12809 /*
12810 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12811 or does not cover the entire frame.
12812 */
12813 if (write_mng && (image->matte || image->page.x > 0 ||
12814 image->page.y > 0 || (image->page.width &&
12815 (image->page.width+image->page.x < mng_info->page.width))
12816 || (image->page.height && (image->page.height+image->page.y
12817 < mng_info->page.height))))
12818 {
12819 (void) WriteBlobMSBULong(image,6L);
12820 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012821 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012822 red=ScaleQuantumToShort(image->background_color.red);
12823 green=ScaleQuantumToShort(image->background_color.green);
12824 blue=ScaleQuantumToShort(image->background_color.blue);
12825 PNGShort(chunk+4,red);
12826 PNGShort(chunk+6,green);
12827 PNGShort(chunk+8,blue);
12828 (void) WriteBlob(image,10,chunk);
12829 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12830 if (mng_info->equal_backgrounds)
12831 {
12832 (void) WriteBlobMSBULong(image,6L);
12833 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012834 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012835 (void) WriteBlob(image,10,chunk);
12836 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12837 }
12838 }
12839
12840#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12841 if ((need_local_plte == MagickFalse) &&
12842 (image->storage_class == PseudoClass) &&
12843 (all_images_are_gray == MagickFalse))
12844 {
cristybb503372010-05-27 20:51:26 +000012845 size_t
cristy3ed852e2009-09-05 21:47:34 +000012846 data_length;
12847
12848 /*
12849 Write MNG PLTE chunk
12850 */
12851 data_length=3*image->colors;
12852 (void) WriteBlobMSBULong(image,data_length);
12853 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012854 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012855
cristybb503372010-05-27 20:51:26 +000012856 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012857 {
cristy5f07f702011-09-26 17:29:10 +000012858 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12859 image->colormap[i].red) & 0xff);
12860 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12861 image->colormap[i].green) & 0xff);
12862 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12863 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000012864 }
glennrp0fe50b42010-11-16 03:52:51 +000012865
cristy3ed852e2009-09-05 21:47:34 +000012866 (void) WriteBlob(image,data_length+4,chunk);
12867 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12868 mng_info->have_write_global_plte=MagickTrue;
12869 }
12870#endif
12871 }
12872 scene=0;
12873 mng_info->delay=0;
12874#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12875 defined(PNG_MNG_FEATURES_SUPPORTED)
12876 mng_info->equal_palettes=MagickFalse;
12877#endif
12878 do
12879 {
12880 if (mng_info->adjoin)
12881 {
12882#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12883 defined(PNG_MNG_FEATURES_SUPPORTED)
12884 /*
12885 If we aren't using a global palette for the entire MNG, check to
12886 see if we can use one for two or more consecutive images.
12887 */
12888 if (need_local_plte && use_global_plte && !all_images_are_gray)
12889 {
12890 if (mng_info->IsPalette)
12891 {
12892 /*
12893 When equal_palettes is true, this image has the same palette
12894 as the previous PseudoClass image
12895 */
12896 mng_info->have_write_global_plte=mng_info->equal_palettes;
12897 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12898 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12899 {
12900 /*
12901 Write MNG PLTE chunk
12902 */
cristybb503372010-05-27 20:51:26 +000012903 size_t
cristy3ed852e2009-09-05 21:47:34 +000012904 data_length;
12905
12906 data_length=3*image->colors;
12907 (void) WriteBlobMSBULong(image,data_length);
12908 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012909 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012910
cristybb503372010-05-27 20:51:26 +000012911 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012912 {
12913 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12914 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12915 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12916 }
glennrp0fe50b42010-11-16 03:52:51 +000012917
cristy3ed852e2009-09-05 21:47:34 +000012918 (void) WriteBlob(image,data_length+4,chunk);
12919 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12920 (uInt) (data_length+4)));
12921 mng_info->have_write_global_plte=MagickTrue;
12922 }
12923 }
12924 else
12925 mng_info->have_write_global_plte=MagickFalse;
12926 }
12927#endif
12928 if (need_defi)
12929 {
cristybb503372010-05-27 20:51:26 +000012930 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012931 previous_x,
12932 previous_y;
12933
12934 if (scene)
12935 {
12936 previous_x=mng_info->page.x;
12937 previous_y=mng_info->page.y;
12938 }
12939 else
12940 {
12941 previous_x=0;
12942 previous_y=0;
12943 }
12944 mng_info->page=image->page;
12945 if ((mng_info->page.x != previous_x) ||
12946 (mng_info->page.y != previous_y))
12947 {
12948 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12949 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012950 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012951 chunk[4]=0; /* object 0 MSB */
12952 chunk[5]=0; /* object 0 LSB */
12953 chunk[6]=0; /* visible */
12954 chunk[7]=0; /* abstract */
12955 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12956 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12957 (void) WriteBlob(image,16,chunk);
12958 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12959 }
12960 }
12961 }
12962
12963 mng_info->write_mng=write_mng;
12964
12965 if ((int) image->dispose >= 3)
12966 mng_info->framing_mode=3;
12967
12968 if (mng_info->need_fram && mng_info->adjoin &&
12969 ((image->delay != mng_info->delay) ||
12970 (mng_info->framing_mode != mng_info->old_framing_mode)))
12971 {
12972 if (image->delay == mng_info->delay)
12973 {
12974 /*
12975 Write a MNG FRAM chunk with the new framing mode.
12976 */
12977 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12978 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012979 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012980 chunk[4]=(unsigned char) mng_info->framing_mode;
12981 (void) WriteBlob(image,5,chunk);
12982 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12983 }
12984 else
12985 {
12986 /*
12987 Write a MNG FRAM chunk with the delay.
12988 */
12989 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12990 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012991 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012992 chunk[4]=(unsigned char) mng_info->framing_mode;
12993 chunk[5]=0; /* frame name separator (no name) */
12994 chunk[6]=2; /* flag for changing default delay */
12995 chunk[7]=0; /* flag for changing frame timeout */
12996 chunk[8]=0; /* flag for changing frame clipping */
12997 chunk[9]=0; /* flag for changing frame sync_id */
12998 PNGLong(chunk+10,(png_uint_32)
12999 ((mng_info->ticks_per_second*
13000 image->delay)/MagickMax(image->ticks_per_second,1)));
13001 (void) WriteBlob(image,14,chunk);
13002 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000013003 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000013004 }
13005 mng_info->old_framing_mode=mng_info->framing_mode;
13006 }
13007
13008#if defined(JNG_SUPPORTED)
13009 if (image_info->compression == JPEGCompression)
13010 {
13011 ImageInfo
13012 *write_info;
13013
13014 if (logging != MagickFalse)
13015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13016 " Writing JNG object.");
13017 /* To do: specify the desired alpha compression method. */
13018 write_info=CloneImageInfo(image_info);
13019 write_info->compression=UndefinedCompression;
cristy018f07f2011-09-04 21:15:19 +000013020 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013021 write_info=DestroyImageInfo(write_info);
13022 }
13023 else
13024#endif
13025 {
13026 if (logging != MagickFalse)
13027 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13028 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013029
glennrpb9cfe272010-12-21 15:08:06 +000013030 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013031 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013032
13033 /* We don't want any ancillary chunks written */
13034 mng_info->ping_exclude_bKGD=MagickTrue;
13035 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013036 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013037 mng_info->ping_exclude_EXIF=MagickTrue;
13038 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013039 mng_info->ping_exclude_iCCP=MagickTrue;
13040 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13041 mng_info->ping_exclude_oFFs=MagickTrue;
13042 mng_info->ping_exclude_pHYs=MagickTrue;
13043 mng_info->ping_exclude_sRGB=MagickTrue;
13044 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013045 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013046 mng_info->ping_exclude_vpAg=MagickTrue;
13047 mng_info->ping_exclude_zCCP=MagickTrue;
13048 mng_info->ping_exclude_zTXt=MagickTrue;
13049
cristy018f07f2011-09-04 21:15:19 +000013050 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013051 }
13052
13053 if (status == MagickFalse)
13054 {
13055 MngInfoFreeStruct(mng_info,&have_mng_structure);
13056 (void) CloseBlob(image);
13057 return(MagickFalse);
13058 }
13059 (void) CatchImageException(image);
13060 if (GetNextImageInList(image) == (Image *) NULL)
13061 break;
13062 image=SyncNextImageInList(image);
13063 status=SetImageProgress(image,SaveImagesTag,scene++,
13064 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013065
cristy3ed852e2009-09-05 21:47:34 +000013066 if (status == MagickFalse)
13067 break;
glennrp0fe50b42010-11-16 03:52:51 +000013068
cristy3ed852e2009-09-05 21:47:34 +000013069 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013070
cristy3ed852e2009-09-05 21:47:34 +000013071 if (write_mng)
13072 {
13073 while (GetPreviousImageInList(image) != (Image *) NULL)
13074 image=GetPreviousImageInList(image);
13075 /*
13076 Write the MEND chunk.
13077 */
13078 (void) WriteBlobMSBULong(image,0x00000000L);
13079 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013080 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013081 (void) WriteBlob(image,4,chunk);
13082 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13083 }
13084 /*
13085 Relinquish resources.
13086 */
13087 (void) CloseBlob(image);
13088 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013089
cristy3ed852e2009-09-05 21:47:34 +000013090 if (logging != MagickFalse)
13091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013092
cristy3ed852e2009-09-05 21:47:34 +000013093 return(MagickTrue);
13094}
glennrpd5045b42010-03-24 12:40:35 +000013095#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013096
cristy3ed852e2009-09-05 21:47:34 +000013097static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13098{
glennrp3bd393f2011-12-21 18:54:53 +000013099 (void) image;
cristy3ed852e2009-09-05 21:47:34 +000013100 printf("Your PNG library is too old: You have libpng-%s\n",
13101 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013102
cristy3ed852e2009-09-05 21:47:34 +000013103 ThrowBinaryException(CoderError,"PNG library is too old",
13104 image_info->filename);
13105}
glennrp39992b42010-11-14 00:03:43 +000013106
cristy3ed852e2009-09-05 21:47:34 +000013107static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13108{
13109 return(WritePNGImage(image_info,image));
13110}
glennrpd5045b42010-03-24 12:40:35 +000013111#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013112#endif