blob: e601a4fbb965f98dc4fa3a866f11dd10bcde7ca1 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP N N GGGG %
7% P P NN N G %
8% PPPP N N N G GG %
9% P N NN G G %
10% P N N GGG %
11% %
12% %
13% Read/Write Portable Network Graphics Image Format %
14% %
15% Software Design %
16% John Cristy %
17% Glenn Randers-Pehrson %
18% November 1997 %
19% %
20% %
cristy1454be72011-12-19 01:52:48 +000021% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000022% dedicated to making software imaging solutions freely available. %
23% %
24% You may not use this file except in compliance with the License. You may %
25% obtain a copy of the License at %
26% %
27% http://www.imagemagick.org/script/license.php %
28% %
29% Unless required by applicable law or agreed to in writing, software %
30% distributed under the License is distributed on an "AS IS" BASIS, %
31% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32% See the License for the specific language governing permissions and %
33% limitations under the License. %
34% %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38*/
39
40
41/*
42 Include declarations.
43*/
cristy4c08aed2011-07-01 19:47:50 +000044#include "MagickCore/studio.h"
45#include "MagickCore/artifact.h"
46#include "MagickCore/attribute.h"
47#include "MagickCore/blob.h"
48#include "MagickCore/blob-private.h"
49#include "MagickCore/cache.h"
50#include "MagickCore/color.h"
51#include "MagickCore/color-private.h"
52#include "MagickCore/colormap.h"
53#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000054#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000055#include "MagickCore/constitute.h"
56#include "MagickCore/enhance.h"
57#include "MagickCore/exception.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/geometry.h"
60#include "MagickCore/histogram.h"
61#include "MagickCore/image.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/layer.h"
64#include "MagickCore/list.h"
65#include "MagickCore/log.h"
66#include "MagickCore/MagickCore.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/module.h"
69#include "MagickCore/monitor.h"
70#include "MagickCore/monitor-private.h"
71#include "MagickCore/option.h"
72#include "MagickCore/pixel.h"
73#include "MagickCore/pixel-accessor.h"
74#include "MagickCore/profile.h"
75#include "MagickCore/property.h"
76#include "MagickCore/quantum-private.h"
77#include "MagickCore/resource_.h"
78#include "MagickCore/semaphore.h"
79#include "MagickCore/quantum-private.h"
80#include "MagickCore/static.h"
81#include "MagickCore/statistic.h"
82#include "MagickCore/string_.h"
83#include "MagickCore/string-private.h"
84#include "MagickCore/transform.h"
85#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000086#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp286a6352009-11-09 02:58:50 +000087
glennrp7ef138c2009-11-10 13:50:20 +000088/* Suppress libpng pedantic warnings that were added in
89 * libpng-1.2.41 and libpng-1.4.0. If you are working on
glennrpfaa852b2010-03-30 12:17:00 +000090 * migration to libpng-1.5, remove these defines and then
glennrp7ef138c2009-11-10 13:50:20 +000091 * fix any code that generates warnings.
92 */
glennrp991e92a2010-01-28 03:09:00 +000093/* #define PNG_DEPRECATED Use of this function is deprecated */
glennrpfaa852b2010-03-30 12:17:00 +000094/* #define PNG_USE_RESULT The result of this function must be checked */
95/* #define PNG_NORETURN This function does not return */
96/* #define PNG_ALLOCATED The result of the function is new memory */
glennrp8371ecc2011-02-19 19:15:38 +000097/* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
glennrp5b927342011-04-14 14:31:48 +000098
99/* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
cristy75cfe702011-04-14 14:27:17 +0000100#define PNG_PTR_NORETURN
glennrp286a6352009-11-09 02:58:50 +0000101
cristy3ed852e2009-09-05 21:47:34 +0000102#include "png.h"
103#include "zlib.h"
104
105/* ImageMagick differences */
106#define first_scene scene
107
glennrpd5045b42010-03-24 12:40:35 +0000108#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000109/*
110 Optional declarations. Define or undefine them as you like.
111*/
112/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
113
114/*
115 Features under construction. Define these to work on them.
116*/
117#undef MNG_OBJECT_BUFFERS
118#undef MNG_BASI_SUPPORTED
119#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
120#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
cristy3ed852e2009-09-05 21:47:34 +0000121#if defined(MAGICKCORE_JPEG_DELEGATE)
122# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
123#endif
124#if !defined(RGBColorMatchExact)
125#define IsPNGColorEqual(color,target) \
glennrp8e045c82011-04-27 16:40:27 +0000126 (((color).red == (target).red) && \
127 ((color).green == (target).green) && \
128 ((color).blue == (target).blue))
cristy3ed852e2009-09-05 21:47:34 +0000129#endif
130
glennrp8e58efd2011-05-20 12:16:29 +0000131/* Macros for left-bit-replication to ensure that pixels
glennrp1a2061f2011-10-18 12:30:45 +0000132 * and PixelInfos all have the same image->depth, and for use
glennrp8e58efd2011-05-20 12:16:29 +0000133 * in PNG8 quantization.
134 */
135
136
137/* LBR01: Replicate top bit */
138
glennrp05001c32011-08-06 13:04:16 +0000139#define LBR01PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000140 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
141 0 : QuantumRange);
142
glennrp91d99252011-06-25 14:30:13 +0000143#define LBR01PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000144 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
145 0 : QuantumRange);
146
glennrp91d99252011-06-25 14:30:13 +0000147#define LBR01PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000148 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
149 0 : QuantumRange);
150
cristy4c08aed2011-07-01 19:47:50 +0000151#define LBR01PacketAlpha(pixelpacket) \
152 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000153 0 : QuantumRange);
154
glennrp91d99252011-06-25 14:30:13 +0000155#define LBR01PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000156 { \
glennrp05001c32011-08-06 13:04:16 +0000157 LBR01PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000158 LBR01PacketGreen((pixelpacket)); \
159 LBR01PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000160 }
glennrp8e58efd2011-05-20 12:16:29 +0000161
glennrp91d99252011-06-25 14:30:13 +0000162#define LBR01PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000163 { \
glennrp91d99252011-06-25 14:30:13 +0000164 LBR01PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000165 LBR01PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000166 }
glennrp8e58efd2011-05-20 12:16:29 +0000167
cristyef618312011-06-25 12:26:44 +0000168#define LBR01PixelRed(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000169 (ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000170 0 : QuantumRange);
171
glennrp54cf7972011-08-06 14:28:09 +0000172#define LBR01PixelGreen(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000173 (ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000174 0 : QuantumRange);
175
glennrp54cf7972011-08-06 14:28:09 +0000176#define LBR01PixelBlue(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000177 (ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000178 0 : QuantumRange);
179
glennrp54cf7972011-08-06 14:28:09 +0000180#define LBR01PixelAlpha(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000181 (ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000182 0 : QuantumRange);
183
glennrp54cf7972011-08-06 14:28:09 +0000184#define LBR01PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000185 { \
cristyef618312011-06-25 12:26:44 +0000186 LBR01PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000187 LBR01PixelGreen((pixel)); \
188 LBR01PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000189 }
glennrp8e58efd2011-05-20 12:16:29 +0000190
glennrp54cf7972011-08-06 14:28:09 +0000191#define LBR01PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000192 { \
glennrp54cf7972011-08-06 14:28:09 +0000193 LBR01PixelRGB((pixel)); \
194 LBR01PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000195 }
glennrp8e58efd2011-05-20 12:16:29 +0000196
197/* LBR02: Replicate top 2 bits */
198
glennrp05001c32011-08-06 13:04:16 +0000199#define LBR02PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000200 { \
201 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
202 (pixelpacket).red=ScaleCharToQuantum( \
203 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
204 }
glennrp91d99252011-06-25 14:30:13 +0000205#define LBR02PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000206 { \
207 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
208 (pixelpacket).green=ScaleCharToQuantum( \
209 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
210 }
glennrp91d99252011-06-25 14:30:13 +0000211#define LBR02PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000212 { \
213 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
214 (pixelpacket).blue=ScaleCharToQuantum( \
215 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
216 }
cristy4c08aed2011-07-01 19:47:50 +0000217#define LBR02PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000218 { \
cristy4c08aed2011-07-01 19:47:50 +0000219 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
220 (pixelpacket).alpha=ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000221 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
222 }
223
glennrp91d99252011-06-25 14:30:13 +0000224#define LBR02PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000225 { \
glennrp05001c32011-08-06 13:04:16 +0000226 LBR02PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000227 LBR02PacketGreen((pixelpacket)); \
228 LBR02PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000229 }
glennrp8e58efd2011-05-20 12:16:29 +0000230
glennrp91d99252011-06-25 14:30:13 +0000231#define LBR02PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000232 { \
glennrp91d99252011-06-25 14:30:13 +0000233 LBR02PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000234 LBR02PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000235 }
glennrp8e58efd2011-05-20 12:16:29 +0000236
cristyef618312011-06-25 12:26:44 +0000237#define LBR02PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000238 { \
cristy4c08aed2011-07-01 19:47:50 +0000239 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000240 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000241 SetPixelRed(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000242 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
243 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000244 }
glennrp54cf7972011-08-06 14:28:09 +0000245#define LBR02PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000246 { \
cristy4c08aed2011-07-01 19:47:50 +0000247 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000248 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000249 SetPixelGreen(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000250 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
251 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000252 }
glennrp54cf7972011-08-06 14:28:09 +0000253#define LBR02PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000254 { \
255 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000256 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
257 SetPixelBlue(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000258 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
259 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000260 }
glennrp54cf7972011-08-06 14:28:09 +0000261#define LBR02PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000262 { \
263 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000264 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
265 SetPixelAlpha(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000266 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
267 (pixel) ); \
glennrp8e58efd2011-05-20 12:16:29 +0000268 }
269
glennrp54cf7972011-08-06 14:28:09 +0000270#define LBR02PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000271 { \
cristyef618312011-06-25 12:26:44 +0000272 LBR02PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000273 LBR02PixelGreen((pixel)); \
274 LBR02PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000275 }
glennrp8e58efd2011-05-20 12:16:29 +0000276
glennrp54cf7972011-08-06 14:28:09 +0000277#define LBR02PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000278 { \
glennrp54cf7972011-08-06 14:28:09 +0000279 LBR02PixelRGB((pixel)); \
280 LBR02PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000281 }
glennrp8e58efd2011-05-20 12:16:29 +0000282
283/* LBR03: Replicate top 3 bits (only used with opaque pixels during
284 PNG8 quantization) */
285
glennrp05001c32011-08-06 13:04:16 +0000286#define LBR03PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000287 { \
288 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
289 (pixelpacket).red=ScaleCharToQuantum( \
290 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
291 }
glennrp91d99252011-06-25 14:30:13 +0000292#define LBR03PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000293 { \
294 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
295 (pixelpacket).green=ScaleCharToQuantum( \
296 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
297 }
glennrp91d99252011-06-25 14:30:13 +0000298#define LBR03PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000299 { \
300 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
301 (pixelpacket).blue=ScaleCharToQuantum( \
302 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
303 }
304
glennrp91d99252011-06-25 14:30:13 +0000305#define LBR03PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000306 { \
glennrp05001c32011-08-06 13:04:16 +0000307 LBR03PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000308 LBR03PacketGreen((pixelpacket)); \
309 LBR03PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000310 }
glennrp8e58efd2011-05-20 12:16:29 +0000311
cristyef618312011-06-25 12:26:44 +0000312#define LBR03PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000313 { \
cristy4c08aed2011-07-01 19:47:50 +0000314 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000315 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000316 SetPixelRed(image, ScaleCharToQuantum( \
317 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000318 }
cristy4c08aed2011-07-01 19:47:50 +0000319#define LBR03Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000320 { \
cristy4c08aed2011-07-01 19:47:50 +0000321 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000322 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000323 SetPixelGreen(image, ScaleCharToQuantum( \
324 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000325 }
cristy4c08aed2011-07-01 19:47:50 +0000326#define LBR03Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000327 { \
cristy4c08aed2011-07-01 19:47:50 +0000328 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000329 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000330 SetPixelBlue(image, ScaleCharToQuantum( \
331 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000332 }
333
cristy4c08aed2011-07-01 19:47:50 +0000334#define LBR03RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000335 { \
cristyef618312011-06-25 12:26:44 +0000336 LBR03PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000337 LBR03Green((pixel)); \
338 LBR03Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000339 }
glennrp8e58efd2011-05-20 12:16:29 +0000340
341/* LBR04: Replicate top 4 bits */
342
glennrp05001c32011-08-06 13:04:16 +0000343#define LBR04PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000344 { \
345 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
346 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
347 }
glennrp91d99252011-06-25 14:30:13 +0000348#define LBR04PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000349 { \
350 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
351 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
352 }
glennrp91d99252011-06-25 14:30:13 +0000353#define LBR04PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000354 { \
355 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
356 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
357 }
cristy4c08aed2011-07-01 19:47:50 +0000358#define LBR04PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000359 { \
cristy4c08aed2011-07-01 19:47:50 +0000360 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
361 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
glennrp8e58efd2011-05-20 12:16:29 +0000362 }
363
glennrp91d99252011-06-25 14:30:13 +0000364#define LBR04PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000365 { \
glennrp05001c32011-08-06 13:04:16 +0000366 LBR04PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000367 LBR04PacketGreen((pixelpacket)); \
368 LBR04PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000369 }
glennrp8e58efd2011-05-20 12:16:29 +0000370
glennrp91d99252011-06-25 14:30:13 +0000371#define LBR04PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000372 { \
glennrp91d99252011-06-25 14:30:13 +0000373 LBR04PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000374 LBR04PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000375 }
glennrp8e58efd2011-05-20 12:16:29 +0000376
cristyef618312011-06-25 12:26:44 +0000377#define LBR04PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000378 { \
cristy4c08aed2011-07-01 19:47:50 +0000379 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000380 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000381 SetPixelRed(image,\
382 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000383 }
glennrp54cf7972011-08-06 14:28:09 +0000384#define LBR04PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000385 { \
cristy4c08aed2011-07-01 19:47:50 +0000386 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000387 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000388 SetPixelGreen(image,\
389 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000390 }
glennrp54cf7972011-08-06 14:28:09 +0000391#define LBR04PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000392 { \
393 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000394 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
395 SetPixelBlue(image,\
396 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000397 }
glennrp54cf7972011-08-06 14:28:09 +0000398#define LBR04PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000399 { \
400 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000401 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
402 SetPixelAlpha(image,\
403 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000404 }
405
glennrp54cf7972011-08-06 14:28:09 +0000406#define LBR04PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000407 { \
cristyef618312011-06-25 12:26:44 +0000408 LBR04PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000409 LBR04PixelGreen((pixel)); \
410 LBR04PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000411 }
glennrp8e58efd2011-05-20 12:16:29 +0000412
glennrp54cf7972011-08-06 14:28:09 +0000413#define LBR04PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000414 { \
glennrp54cf7972011-08-06 14:28:09 +0000415 LBR04PixelRGB((pixel)); \
416 LBR04PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000417 }
glennrp8e58efd2011-05-20 12:16:29 +0000418
419
420/* LBR08: Replicate top 8 bits */
421
glennrp05001c32011-08-06 13:04:16 +0000422#define LBR08PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000423 { \
424 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
425 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
426 }
glennrp91d99252011-06-25 14:30:13 +0000427#define LBR08PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000428 { \
429 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
430 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
431 }
glennrp91d99252011-06-25 14:30:13 +0000432#define LBR08PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000433 { \
434 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
435 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
436 }
cristy4c08aed2011-07-01 19:47:50 +0000437#define LBR08PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000438 { \
cristy4c08aed2011-07-01 19:47:50 +0000439 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
440 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000441 }
442
glennrp91d99252011-06-25 14:30:13 +0000443#define LBR08PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000444 { \
glennrp05001c32011-08-06 13:04:16 +0000445 LBR08PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000446 LBR08PacketGreen((pixelpacket)); \
447 LBR08PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000448 }
glennrp8e58efd2011-05-20 12:16:29 +0000449
glennrp91d99252011-06-25 14:30:13 +0000450#define LBR08PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000451 { \
glennrp91d99252011-06-25 14:30:13 +0000452 LBR08PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000453 LBR08PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000454 }
glennrp8e58efd2011-05-20 12:16:29 +0000455
cristyef618312011-06-25 12:26:44 +0000456#define LBR08PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000457 { \
458 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000459 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
460 SetPixelRed(image,\
461 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000462 }
glennrp54cf7972011-08-06 14:28:09 +0000463#define LBR08PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000464 { \
465 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000466 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
467 SetPixelGreen(image,\
468 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000469 }
glennrp54cf7972011-08-06 14:28:09 +0000470#define LBR08PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000471 { \
472 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000473 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
474 SetPixelBlue(image,\
475 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000476 }
glennrp54cf7972011-08-06 14:28:09 +0000477#define LBR08PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000478 { \
479 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000480 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
481 SetPixelAlpha(image,\
482 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000483 }
484
glennrp54cf7972011-08-06 14:28:09 +0000485#define LBR08PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000486 { \
cristyef618312011-06-25 12:26:44 +0000487 LBR08PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000488 LBR08PixelGreen((pixel)); \
489 LBR08PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000490 }
glennrp8e58efd2011-05-20 12:16:29 +0000491
glennrp54cf7972011-08-06 14:28:09 +0000492#define LBR08PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000493 { \
glennrp54cf7972011-08-06 14:28:09 +0000494 LBR08PixelRGB((pixel)); \
495 LBR08PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000496 }
glennrp8e58efd2011-05-20 12:16:29 +0000497
498
499/* LBR16: Replicate top 16 bits */
500
glennrp05001c32011-08-06 13:04:16 +0000501#define LBR16PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000502 { \
503 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
504 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
505 }
glennrp91d99252011-06-25 14:30:13 +0000506#define LBR16PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000507 { \
508 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
509 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
510 }
glennrp91d99252011-06-25 14:30:13 +0000511#define LBR16PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000512 { \
513 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
514 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
515 }
cristy4c08aed2011-07-01 19:47:50 +0000516#define LBR16PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000517 { \
cristy4c08aed2011-07-01 19:47:50 +0000518 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
519 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000520 }
521
glennrp91d99252011-06-25 14:30:13 +0000522#define LBR16PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000523 { \
glennrp05001c32011-08-06 13:04:16 +0000524 LBR16PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000525 LBR16PacketGreen((pixelpacket)); \
526 LBR16PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000527 }
glennrp8e58efd2011-05-20 12:16:29 +0000528
glennrp91d99252011-06-25 14:30:13 +0000529#define LBR16PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000530 { \
glennrp91d99252011-06-25 14:30:13 +0000531 LBR16PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000532 LBR16PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000533 }
glennrp8e58efd2011-05-20 12:16:29 +0000534
cristyef618312011-06-25 12:26:44 +0000535#define LBR16PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000536 { \
537 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000538 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
539 SetPixelRed(image,\
540 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000541 }
glennrp54cf7972011-08-06 14:28:09 +0000542#define LBR16PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000543 { \
544 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000545 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
546 SetPixelGreen(image,\
547 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000548 }
glennrp54cf7972011-08-06 14:28:09 +0000549#define LBR16PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000550 { \
551 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000552 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
553 SetPixelBlue(image,\
554 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000555 }
glennrp54cf7972011-08-06 14:28:09 +0000556#define LBR16PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000557 { \
558 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000559 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
560 SetPixelAlpha(image,\
561 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000562 }
563
glennrp54cf7972011-08-06 14:28:09 +0000564#define LBR16PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000565 { \
cristyef618312011-06-25 12:26:44 +0000566 LBR16PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000567 LBR16PixelGreen((pixel)); \
568 LBR16PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000569 }
glennrp8e58efd2011-05-20 12:16:29 +0000570
glennrp54cf7972011-08-06 14:28:09 +0000571#define LBR16PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000572 { \
glennrp54cf7972011-08-06 14:28:09 +0000573 LBR16PixelRGB((pixel)); \
574 LBR16PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000575 }
glennrp8e58efd2011-05-20 12:16:29 +0000576
cristy3ed852e2009-09-05 21:47:34 +0000577/*
578 Establish thread safety.
579 setjmp/longjmp is claimed to be safe on these platforms:
580 setjmp/longjmp is alleged to be unsafe on these platforms:
581*/
582#ifndef SETJMP_IS_THREAD_SAFE
583#define PNG_SETJMP_NOT_THREAD_SAFE
584#endif
585
586#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
587static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000588 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000589#endif
590
591/*
592 This temporary until I set up malloc'ed object attributes array.
593 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
594 waste more memory.
595*/
596#define MNG_MAX_OBJECTS 256
597
598/*
599 If this not defined, spec is interpreted strictly. If it is
600 defined, an attempt will be made to recover from some errors,
601 including
602 o global PLTE too short
603*/
604#undef MNG_LOOSE
605
606/*
607 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
608 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
609 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
610 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
611 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
612 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
613 will be enabled by default in libpng-1.2.0.
614*/
cristy3ed852e2009-09-05 21:47:34 +0000615#ifdef PNG_MNG_FEATURES_SUPPORTED
616# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
617# define PNG_READ_EMPTY_PLTE_SUPPORTED
618# endif
619# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
620# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
621# endif
622#endif
623
624/*
cristybb503372010-05-27 20:51:26 +0000625 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000626 This macro is only defined in libpng-1.0.3 and later.
627 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
628*/
629#ifndef PNG_UINT_31_MAX
630#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
631#endif
632
633/*
634 Constant strings for known chunk types. If you need to add a chunk,
635 add a string holding the name here. To make the code more
636 portable, we use ASCII numbers like this, not characters.
637*/
638
glennrp85dcf872011-12-07 02:51:47 +0000639static png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
640static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
641static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
642static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
643static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
644static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
645static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
646static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
647static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
648static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
649static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
650static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
651static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
652static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
653static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
654static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
655static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
656static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
657static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
658static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
659static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
660static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
661static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
662static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
663static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
664static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
665static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
666static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
667static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
668static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
669static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
670static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
671static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
672static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000673
674#if defined(JNG_SUPPORTED)
glennrp85dcf872011-12-07 02:51:47 +0000675static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
676static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
677static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
678static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
679static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
680static png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000681#endif
682
683/*
684Other known chunks that are not yet supported by ImageMagick:
glennrp85dcf872011-12-07 02:51:47 +0000685static png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
686static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
687static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
688static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
689static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
690static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
691static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
692static png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000693*/
694
695typedef struct _MngBox
696{
cristy8182b072010-05-30 20:10:53 +0000697 long
cristy3ed852e2009-09-05 21:47:34 +0000698 left,
699 right,
700 top,
701 bottom;
702} MngBox;
703
704typedef struct _MngPair
705{
cristy8182b072010-05-30 20:10:53 +0000706 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000707 a,
708 b;
709} MngPair;
710
711#ifdef MNG_OBJECT_BUFFERS
712typedef struct _MngBuffer
713{
714
cristybb503372010-05-27 20:51:26 +0000715 size_t
cristy3ed852e2009-09-05 21:47:34 +0000716 height,
717 width;
718
719 Image
720 *image;
721
722 png_color
723 plte[256];
724
725 int
726 reference_count;
727
728 unsigned char
729 alpha_sample_depth,
730 compression_method,
731 color_type,
732 concrete,
733 filter_method,
734 frozen,
735 image_type,
736 interlace_method,
737 pixel_sample_depth,
738 plte_length,
739 sample_depth,
740 viewable;
741} MngBuffer;
742#endif
743
744typedef struct _MngInfo
745{
746
747#ifdef MNG_OBJECT_BUFFERS
748 MngBuffer
749 *ob[MNG_MAX_OBJECTS];
750#endif
751
752 Image *
753 image;
754
755 RectangleInfo
756 page;
757
758 int
759 adjoin,
760#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
761 bytes_in_read_buffer,
762 found_empty_plte,
763#endif
764 equal_backgrounds,
765 equal_chrms,
766 equal_gammas,
767#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
768 defined(PNG_MNG_FEATURES_SUPPORTED)
769 equal_palettes,
770#endif
771 equal_physs,
772 equal_srgbs,
773 framing_mode,
774 have_global_bkgd,
775 have_global_chrm,
776 have_global_gama,
777 have_global_phys,
778 have_global_sbit,
779 have_global_srgb,
780 have_saved_bkgd_index,
781 have_write_global_chrm,
782 have_write_global_gama,
783 have_write_global_plte,
784 have_write_global_srgb,
785 need_fram,
786 object_id,
787 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000788 saved_bkgd_index;
789
790 int
791 new_number_colors;
792
cristybb503372010-05-27 20:51:26 +0000793 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000794 image_found,
795 loop_count[256],
796 loop_iteration[256],
797 scenes_found,
798 x_off[MNG_MAX_OBJECTS],
799 y_off[MNG_MAX_OBJECTS];
800
801 MngBox
802 clip,
803 frame,
804 image_box,
805 object_clip[MNG_MAX_OBJECTS];
806
807 unsigned char
808 /* These flags could be combined into one byte */
809 exists[MNG_MAX_OBJECTS],
810 frozen[MNG_MAX_OBJECTS],
811 loop_active[256],
812 invisible[MNG_MAX_OBJECTS],
813 viewable[MNG_MAX_OBJECTS];
814
815 MagickOffsetType
816 loop_jump[256];
817
818 png_colorp
819 global_plte;
820
821 png_color_8
822 global_sbit;
823
824 png_byte
825#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
826 read_buffer[8],
827#endif
828 global_trns[256];
829
830 float
831 global_gamma;
832
833 ChromaticityInfo
834 global_chrm;
835
836 RenderingIntent
837 global_srgb_intent;
838
cristy35ef8242010-06-03 16:24:13 +0000839 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000840 delay,
841 global_plte_length,
842 global_trns_length,
843 global_x_pixels_per_unit,
844 global_y_pixels_per_unit,
845 mng_width,
846 mng_height,
847 ticks_per_second;
848
glennrpb9cfe272010-12-21 15:08:06 +0000849 MagickBooleanType
850 need_blob;
851
cristy3ed852e2009-09-05 21:47:34 +0000852 unsigned int
853 IsPalette,
854 global_phys_unit_type,
855 basi_warning,
856 clon_warning,
857 dhdr_warning,
858 jhdr_warning,
859 magn_warning,
860 past_warning,
861 phyg_warning,
862 phys_warning,
863 sbit_warning,
864 show_warning,
865 mng_type,
866 write_mng,
867 write_png_colortype,
868 write_png_depth,
glennrp18682582011-06-30 18:11:47 +0000869 write_png_compression_level,
870 write_png_compression_strategy,
871 write_png_compression_filter,
cristy3ed852e2009-09-05 21:47:34 +0000872 write_png8,
873 write_png24,
874 write_png32;
875
876#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000877 size_t
cristy3ed852e2009-09-05 21:47:34 +0000878 basi_width,
879 basi_height;
880
881 unsigned int
882 basi_depth,
883 basi_color_type,
884 basi_compression_method,
885 basi_filter_type,
886 basi_interlace_method,
887 basi_red,
888 basi_green,
889 basi_blue,
890 basi_alpha,
891 basi_viewable;
892#endif
893
894 png_uint_16
895 magn_first,
896 magn_last,
897 magn_mb,
898 magn_ml,
899 magn_mr,
900 magn_mt,
901 magn_mx,
902 magn_my,
903 magn_methx,
904 magn_methy;
905
cristy101ab702011-10-13 13:06:32 +0000906 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000907 mng_global_bkgd;
908
glennrp26f37912010-12-23 16:22:42 +0000909 /* Added at version 6.6.6-7 */
910 MagickBooleanType
911 ping_exclude_bKGD,
912 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000913 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000914 ping_exclude_EXIF,
915 ping_exclude_gAMA,
916 ping_exclude_iCCP,
917 /* ping_exclude_iTXt, */
918 ping_exclude_oFFs,
919 ping_exclude_pHYs,
920 ping_exclude_sRGB,
921 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000922 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000923 ping_exclude_vpAg,
924 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000925 ping_exclude_zTXt,
926 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000927
cristy3ed852e2009-09-05 21:47:34 +0000928} MngInfo;
929#endif /* VER */
930
931/*
932 Forward declarations.
933*/
934static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000935 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000936
cristy3ed852e2009-09-05 21:47:34 +0000937static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000938 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000939
cristy3ed852e2009-09-05 21:47:34 +0000940#if defined(JNG_SUPPORTED)
941static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000942 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000943#endif
944
glennrp0c3e06b2010-11-19 13:45:02 +0000945#if PNG_LIBPNG_VER > 10011
946
glennrpfd05d622011-02-25 04:10:33 +0000947
glennrp0c3e06b2010-11-19 13:45:02 +0000948#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
949static MagickBooleanType
cristyc82a27b2011-10-21 01:07:16 +0000950LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
glennrp0c3e06b2010-11-19 13:45:02 +0000951{
glennrp67b9c1a2011-04-22 18:47:36 +0000952 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
953 *
954 * This is true if the high byte and the next highest byte of
955 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000956 * are equal to each other. We check this by seeing if the samples
957 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000958 *
959 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000960 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000961 */
962
glennrp3faa9a32011-04-23 14:00:25 +0000963#define QuantumToCharToQuantumEqQuantum(quantum) \
964 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
965
glennrp0c3e06b2010-11-19 13:45:02 +0000966 MagickBooleanType
967 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000968
glennrp03e11f62011-04-22 13:30:16 +0000969 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000970 {
971
cristy4c08aed2011-07-01 19:47:50 +0000972 const Quantum
glennrp0c3e06b2010-11-19 13:45:02 +0000973 *p;
974
975 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000976 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
977 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
978 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
979 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000980
981 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
982 {
983 int indx;
984
985 for (indx=0; indx < (ssize_t) image->colors; indx++)
986 {
glennrp3faa9a32011-04-23 14:00:25 +0000987 ok_to_reduce=(
988 QuantumToCharToQuantumEqQuantum(
989 image->colormap[indx].red) &&
990 QuantumToCharToQuantumEqQuantum(
991 image->colormap[indx].green) &&
992 QuantumToCharToQuantumEqQuantum(
993 image->colormap[indx].blue)) ?
994 MagickTrue : MagickFalse;
995
glennrp0c3e06b2010-11-19 13:45:02 +0000996 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000997 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000998 }
999 }
1000
1001 if ((ok_to_reduce != MagickFalse) &&
1002 (image->storage_class != PseudoClass))
1003 {
1004 ssize_t
1005 y;
1006
1007 register ssize_t
1008 x;
1009
1010 for (y=0; y < (ssize_t) image->rows; y++)
1011 {
cristyc82a27b2011-10-21 01:07:16 +00001012 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0c3e06b2010-11-19 13:45:02 +00001013
cristy4c08aed2011-07-01 19:47:50 +00001014 if (p == (const Quantum *) NULL)
glennrp0c3e06b2010-11-19 13:45:02 +00001015 {
1016 ok_to_reduce = MagickFalse;
1017 break;
1018 }
1019
1020 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1021 {
glennrp3faa9a32011-04-23 14:00:25 +00001022 ok_to_reduce=
cristy4c08aed2011-07-01 19:47:50 +00001023 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1024 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1025 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
glennrp3faa9a32011-04-23 14:00:25 +00001026 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001027
1028 if (ok_to_reduce == MagickFalse)
1029 break;
1030
cristyed231572011-07-14 02:18:59 +00001031 p+=GetPixelChannels(image);
glennrp0c3e06b2010-11-19 13:45:02 +00001032 }
glennrp8640fb52010-11-23 15:48:26 +00001033 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +00001034 break;
1035 }
1036 }
1037
1038 if (ok_to_reduce != MagickFalse)
1039 {
glennrp0c3e06b2010-11-19 13:45:02 +00001040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001041 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +00001042 }
glennrpa6a06632011-01-19 15:15:34 +00001043 else
1044 {
1045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001046 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +00001047 }
glennrp0c3e06b2010-11-19 13:45:02 +00001048 }
1049
1050 return ok_to_reduce;
1051}
1052#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1053
glennrpe610a072010-08-05 17:08:46 +00001054static int
glennrpcf002022011-01-30 02:38:15 +00001055Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +00001056{
glennrpe610a072010-08-05 17:08:46 +00001057 switch (intent)
1058 {
1059 case PerceptualIntent:
1060 return 0;
glennrp0fe50b42010-11-16 03:52:51 +00001061
glennrpe610a072010-08-05 17:08:46 +00001062 case RelativeIntent:
1063 return 1;
glennrp0fe50b42010-11-16 03:52:51 +00001064
glennrpe610a072010-08-05 17:08:46 +00001065 case SaturationIntent:
1066 return 2;
glennrp0fe50b42010-11-16 03:52:51 +00001067
glennrpe610a072010-08-05 17:08:46 +00001068 case AbsoluteIntent:
1069 return 3;
glennrp0fe50b42010-11-16 03:52:51 +00001070
glennrpe610a072010-08-05 17:08:46 +00001071 default:
1072 return -1;
1073 }
1074}
1075
1076static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +00001077Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +00001078{
glennrpcf002022011-01-30 02:38:15 +00001079 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001080 {
1081 case 0:
1082 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001083
glennrpe610a072010-08-05 17:08:46 +00001084 case 1:
1085 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001086
glennrpe610a072010-08-05 17:08:46 +00001087 case 2:
1088 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001089
glennrpe610a072010-08-05 17:08:46 +00001090 case 3:
1091 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001092
glennrpe610a072010-08-05 17:08:46 +00001093 default:
1094 return UndefinedIntent;
1095 }
1096}
1097
cristybb503372010-05-27 20:51:26 +00001098static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001099{
1100 if (x > y)
1101 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001102
cristy3ed852e2009-09-05 21:47:34 +00001103 return(y);
1104}
glennrp0c3e06b2010-11-19 13:45:02 +00001105
cristybb503372010-05-27 20:51:26 +00001106static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001107{
1108 if (x < y)
1109 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001110
cristy3ed852e2009-09-05 21:47:34 +00001111 return(y);
1112}
glennrp0c3e06b2010-11-19 13:45:02 +00001113
cristy3ed852e2009-09-05 21:47:34 +00001114
1115/*
1116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1117% %
1118% %
1119% %
1120% I m a g e I s G r a y %
1121% %
1122% %
1123% %
1124%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1125% %
cristy4c08aed2011-07-01 19:47:50 +00001126% Like IsImageGray except does not change DirectClass to PseudoClass %
cristy3ed852e2009-09-05 21:47:34 +00001127% %
1128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1129*/
cristyc82a27b2011-10-21 01:07:16 +00001130static MagickBooleanType ImageIsGray(Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001131{
cristy4c08aed2011-07-01 19:47:50 +00001132 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001133 *p;
1134
cristybb503372010-05-27 20:51:26 +00001135 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001136 i,
1137 x,
1138 y;
1139
1140 assert(image != (Image *) NULL);
1141 assert(image->signature == MagickSignature);
1142 if (image->debug != MagickFalse)
1143 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1144
1145 if (image->storage_class == PseudoClass)
1146 {
cristybb503372010-05-27 20:51:26 +00001147 for (i=0; i < (ssize_t) image->colors; i++)
cristy101ab702011-10-13 13:06:32 +00001148 if (IsPixelInfoGray(image->colormap+i) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001149 return(MagickFalse);
1150 return(MagickTrue);
1151 }
cristybb503372010-05-27 20:51:26 +00001152 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001153 {
cristyc82a27b2011-10-21 01:07:16 +00001154 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001155 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001156 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +00001157 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001158 {
cristy4c08aed2011-07-01 19:47:50 +00001159 if (IsPixelGray(image,p) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001160 return(MagickFalse);
cristyed231572011-07-14 02:18:59 +00001161 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001162 }
1163 }
1164 return(MagickTrue);
1165}
glennrpd5045b42010-03-24 12:40:35 +00001166#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001167#endif /* MAGICKCORE_PNG_DELEGATE */
1168
1169/*
1170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1171% %
1172% %
1173% %
1174% I s M N G %
1175% %
1176% %
1177% %
1178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1179%
1180% IsMNG() returns MagickTrue if the image format type, identified by the
1181% magick string, is MNG.
1182%
1183% The format of the IsMNG method is:
1184%
1185% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1186%
1187% A description of each parameter follows:
1188%
1189% o magick: compare image format pattern against these bytes.
1190%
1191% o length: Specifies the length of the magick string.
1192%
1193%
1194*/
1195static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1196{
1197 if (length < 8)
1198 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001199
cristy3ed852e2009-09-05 21:47:34 +00001200 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1201 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001202
cristy3ed852e2009-09-05 21:47:34 +00001203 return(MagickFalse);
1204}
1205
1206/*
1207%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1208% %
1209% %
1210% %
1211% I s J N G %
1212% %
1213% %
1214% %
1215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1216%
1217% IsJNG() returns MagickTrue if the image format type, identified by the
1218% magick string, is JNG.
1219%
1220% The format of the IsJNG method is:
1221%
1222% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1223%
1224% A description of each parameter follows:
1225%
1226% o magick: compare image format pattern against these bytes.
1227%
1228% o length: Specifies the length of the magick string.
1229%
1230%
1231*/
1232static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1233{
1234 if (length < 8)
1235 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001236
cristy3ed852e2009-09-05 21:47:34 +00001237 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1238 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001239
cristy3ed852e2009-09-05 21:47:34 +00001240 return(MagickFalse);
1241}
1242
1243/*
1244%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245% %
1246% %
1247% %
1248% I s P N G %
1249% %
1250% %
1251% %
1252%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1253%
1254% IsPNG() returns MagickTrue if the image format type, identified by the
1255% magick string, is PNG.
1256%
1257% The format of the IsPNG method is:
1258%
1259% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1260%
1261% A description of each parameter follows:
1262%
1263% o magick: compare image format pattern against these bytes.
1264%
1265% o length: Specifies the length of the magick string.
1266%
1267*/
1268static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1269{
1270 if (length < 8)
1271 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001272
cristy3ed852e2009-09-05 21:47:34 +00001273 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1274 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001275
cristy3ed852e2009-09-05 21:47:34 +00001276 return(MagickFalse);
1277}
1278
1279#if defined(MAGICKCORE_PNG_DELEGATE)
1280#if defined(__cplusplus) || defined(c_plusplus)
1281extern "C" {
1282#endif
1283
glennrpd5045b42010-03-24 12:40:35 +00001284#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001285static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001286{
1287 unsigned char
1288 buffer[4];
1289
1290 assert(image != (Image *) NULL);
1291 assert(image->signature == MagickSignature);
1292 buffer[0]=(unsigned char) (value >> 24);
1293 buffer[1]=(unsigned char) (value >> 16);
1294 buffer[2]=(unsigned char) (value >> 8);
1295 buffer[3]=(unsigned char) value;
1296 return((size_t) WriteBlob(image,4,buffer));
1297}
1298
1299static void PNGLong(png_bytep p,png_uint_32 value)
1300{
1301 *p++=(png_byte) ((value >> 24) & 0xff);
1302 *p++=(png_byte) ((value >> 16) & 0xff);
1303 *p++=(png_byte) ((value >> 8) & 0xff);
1304 *p++=(png_byte) (value & 0xff);
1305}
1306
glennrpa521b2f2010-10-29 04:11:03 +00001307#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001308static void PNGsLong(png_bytep p,png_int_32 value)
1309{
1310 *p++=(png_byte) ((value >> 24) & 0xff);
1311 *p++=(png_byte) ((value >> 16) & 0xff);
1312 *p++=(png_byte) ((value >> 8) & 0xff);
1313 *p++=(png_byte) (value & 0xff);
1314}
glennrpa521b2f2010-10-29 04:11:03 +00001315#endif
cristy3ed852e2009-09-05 21:47:34 +00001316
1317static void PNGShort(png_bytep p,png_uint_16 value)
1318{
1319 *p++=(png_byte) ((value >> 8) & 0xff);
1320 *p++=(png_byte) (value & 0xff);
1321}
1322
1323static void PNGType(png_bytep p,png_bytep type)
1324{
1325 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1326}
1327
glennrp03812ae2010-12-24 01:31:34 +00001328static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1329 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001330{
1331 if (logging != MagickFalse)
1332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001333 " Writing %c%c%c%c chunk, length: %.20g",
1334 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001335}
glennrpd5045b42010-03-24 12:40:35 +00001336#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001337
1338#if defined(__cplusplus) || defined(c_plusplus)
1339}
1340#endif
1341
glennrpd5045b42010-03-24 12:40:35 +00001342#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001343/*
1344%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1345% %
1346% %
1347% %
1348% R e a d P N G I m a g e %
1349% %
1350% %
1351% %
1352%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1353%
1354% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1355% Multiple-image Network Graphics (MNG) image file and returns it. It
1356% allocates the memory necessary for the new Image structure and returns a
1357% pointer to the new image or set of images.
1358%
1359% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1360%
1361% The format of the ReadPNGImage method is:
1362%
1363% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1364%
1365% A description of each parameter follows:
1366%
1367% o image_info: the image info.
1368%
1369% o exception: return any errors or warnings in this structure.
1370%
1371% To do, more or less in chronological order (as of version 5.5.2,
1372% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1373%
1374% Get 16-bit cheap transparency working.
1375%
1376% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1377%
1378% Preserve all unknown and not-yet-handled known chunks found in input
1379% PNG file and copy them into output PNG files according to the PNG
1380% copying rules.
1381%
1382% (At this point, PNG encoding should be in full MNG compliance)
1383%
1384% Provide options for choice of background to use when the MNG BACK
1385% chunk is not present or is not mandatory (i.e., leave transparent,
1386% user specified, MNG BACK, PNG bKGD)
1387%
1388% Implement LOOP/ENDL [done, but could do discretionary loops more
1389% efficiently by linking in the duplicate frames.].
1390%
1391% Decode and act on the MHDR simplicity profile (offer option to reject
1392% files or attempt to process them anyway when the profile isn't LC or VLC).
1393%
1394% Upgrade to full MNG without Delta-PNG.
1395%
1396% o BACK [done a while ago except for background image ID]
1397% o MOVE [done 15 May 1999]
1398% o CLIP [done 15 May 1999]
1399% o DISC [done 19 May 1999]
1400% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1401% o SEEK [partially done 19 May 1999 (discard function only)]
1402% o SHOW
1403% o PAST
1404% o BASI
1405% o MNG-level tEXt/iTXt/zTXt
1406% o pHYg
1407% o pHYs
1408% o sBIT
1409% o bKGD
1410% o iTXt (wait for libpng implementation).
1411%
1412% Use the scene signature to discover when an identical scene is
1413% being reused, and just point to the original image->exception instead
1414% of storing another set of pixels. This not specific to MNG
1415% but could be applied generally.
1416%
1417% Upgrade to full MNG with Delta-PNG.
1418%
1419% JNG tEXt/iTXt/zTXt
1420%
1421% We will not attempt to read files containing the CgBI chunk.
1422% They are really Xcode files meant for display on the iPhone.
1423% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001424% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001425% since irretrievable loss of color data has occurred due to the
1426% use of premultiplied alpha.
1427*/
1428
1429#if defined(__cplusplus) || defined(c_plusplus)
1430extern "C" {
1431#endif
1432
1433/*
1434 This the function that does the actual reading of data. It is
1435 the same as the one supplied in libpng, except that it receives the
1436 datastream from the ReadBlob() function instead of standard input.
1437*/
1438static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1439{
1440 Image
1441 *image;
1442
1443 image=(Image *) png_get_io_ptr(png_ptr);
1444 if (length)
1445 {
1446 png_size_t
1447 check;
1448
1449 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1450 if (check != length)
1451 {
1452 char
1453 msg[MaxTextExtent];
1454
cristy3b6fd2e2011-05-20 12:53:50 +00001455 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001456 "Expected %.20g bytes; found %.20g bytes",(double) length,
1457 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001458 png_warning(png_ptr,msg);
1459 png_error(png_ptr,"Read Exception");
1460 }
1461 }
1462}
1463
1464#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1465 !defined(PNG_MNG_FEATURES_SUPPORTED)
1466/* We use mng_get_data() instead of png_get_data() if we have a libpng
1467 * older than libpng-1.0.3a, which was the first to allow the empty
1468 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1469 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1470 * encountered after an empty PLTE, so we have to look ahead for bKGD
1471 * chunks and remove them from the datastream that is passed to libpng,
1472 * and store their contents for later use.
1473 */
1474static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1475{
1476 MngInfo
1477 *mng_info;
1478
1479 Image
1480 *image;
1481
1482 png_size_t
1483 check;
1484
cristybb503372010-05-27 20:51:26 +00001485 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001486 i;
1487
1488 i=0;
1489 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1490 image=(Image *) mng_info->image;
1491 while (mng_info->bytes_in_read_buffer && length)
1492 {
1493 data[i]=mng_info->read_buffer[i];
1494 mng_info->bytes_in_read_buffer--;
1495 length--;
1496 i++;
1497 }
1498 if (length)
1499 {
1500 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001501
cristy3ed852e2009-09-05 21:47:34 +00001502 if (check != length)
1503 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001504
cristy3ed852e2009-09-05 21:47:34 +00001505 if (length == 4)
1506 {
1507 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1508 (data[3] == 0))
1509 {
1510 check=(png_size_t) ReadBlob(image,(size_t) length,
1511 (char *) mng_info->read_buffer);
1512 mng_info->read_buffer[4]=0;
1513 mng_info->bytes_in_read_buffer=4;
1514 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1515 mng_info->found_empty_plte=MagickTrue;
1516 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1517 {
1518 mng_info->found_empty_plte=MagickFalse;
1519 mng_info->have_saved_bkgd_index=MagickFalse;
1520 }
1521 }
glennrp0fe50b42010-11-16 03:52:51 +00001522
cristy3ed852e2009-09-05 21:47:34 +00001523 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1524 (data[3] == 1))
1525 {
1526 check=(png_size_t) ReadBlob(image,(size_t) length,
1527 (char *) mng_info->read_buffer);
1528 mng_info->read_buffer[4]=0;
1529 mng_info->bytes_in_read_buffer=4;
1530 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1531 if (mng_info->found_empty_plte)
1532 {
1533 /*
1534 Skip the bKGD data byte and CRC.
1535 */
1536 check=(png_size_t)
1537 ReadBlob(image,5,(char *) mng_info->read_buffer);
1538 check=(png_size_t) ReadBlob(image,(size_t) length,
1539 (char *) mng_info->read_buffer);
1540 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1541 mng_info->have_saved_bkgd_index=MagickTrue;
1542 mng_info->bytes_in_read_buffer=0;
1543 }
1544 }
1545 }
1546 }
1547}
1548#endif
1549
1550static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1551{
1552 Image
1553 *image;
1554
1555 image=(Image *) png_get_io_ptr(png_ptr);
1556 if (length)
1557 {
1558 png_size_t
1559 check;
1560
cristybb503372010-05-27 20:51:26 +00001561 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001562
cristy3ed852e2009-09-05 21:47:34 +00001563 if (check != length)
1564 png_error(png_ptr,"WriteBlob Failed");
1565 }
1566}
1567
1568static void png_flush_data(png_structp png_ptr)
1569{
1570 (void) png_ptr;
1571}
1572
1573#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1574static int PalettesAreEqual(Image *a,Image *b)
1575{
cristybb503372010-05-27 20:51:26 +00001576 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001577 i;
1578
1579 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1580 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001581
cristy3ed852e2009-09-05 21:47:34 +00001582 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1583 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001584
cristy3ed852e2009-09-05 21:47:34 +00001585 if (a->colors != b->colors)
1586 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001587
cristybb503372010-05-27 20:51:26 +00001588 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001589 {
1590 if ((a->colormap[i].red != b->colormap[i].red) ||
1591 (a->colormap[i].green != b->colormap[i].green) ||
1592 (a->colormap[i].blue != b->colormap[i].blue))
1593 return((int) MagickFalse);
1594 }
glennrp0fe50b42010-11-16 03:52:51 +00001595
cristy3ed852e2009-09-05 21:47:34 +00001596 return((int) MagickTrue);
1597}
1598#endif
1599
1600static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1601{
1602 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1603 mng_info->exists[i] && !mng_info->frozen[i])
1604 {
1605#ifdef MNG_OBJECT_BUFFERS
1606 if (mng_info->ob[i] != (MngBuffer *) NULL)
1607 {
1608 if (mng_info->ob[i]->reference_count > 0)
1609 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001610
cristy3ed852e2009-09-05 21:47:34 +00001611 if (mng_info->ob[i]->reference_count == 0)
1612 {
1613 if (mng_info->ob[i]->image != (Image *) NULL)
1614 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001615
cristy3ed852e2009-09-05 21:47:34 +00001616 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1617 }
1618 }
1619 mng_info->ob[i]=(MngBuffer *) NULL;
1620#endif
1621 mng_info->exists[i]=MagickFalse;
1622 mng_info->invisible[i]=MagickFalse;
1623 mng_info->viewable[i]=MagickFalse;
1624 mng_info->frozen[i]=MagickFalse;
1625 mng_info->x_off[i]=0;
1626 mng_info->y_off[i]=0;
1627 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001628 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001629 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001630 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001631 }
1632}
1633
glennrp21f0e622011-01-07 16:20:57 +00001634static void MngInfoFreeStruct(MngInfo *mng_info,
1635 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001636{
glennrp21f0e622011-01-07 16:20:57 +00001637 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001638 {
cristybb503372010-05-27 20:51:26 +00001639 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001640 i;
1641
1642 for (i=1; i < MNG_MAX_OBJECTS; i++)
1643 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001644
cristy3ed852e2009-09-05 21:47:34 +00001645 if (mng_info->global_plte != (png_colorp) NULL)
1646 mng_info->global_plte=(png_colorp)
1647 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001648
cristy3ed852e2009-09-05 21:47:34 +00001649 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1650 *have_mng_structure=MagickFalse;
1651 }
1652}
1653
1654static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1655{
1656 MngBox
1657 box;
1658
1659 box=box1;
1660 if (box.left < box2.left)
1661 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001662
cristy3ed852e2009-09-05 21:47:34 +00001663 if (box.top < box2.top)
1664 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001665
cristy3ed852e2009-09-05 21:47:34 +00001666 if (box.right > box2.right)
1667 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001668
cristy3ed852e2009-09-05 21:47:34 +00001669 if (box.bottom > box2.bottom)
1670 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001671
cristy3ed852e2009-09-05 21:47:34 +00001672 return box;
1673}
1674
1675static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1676{
1677 MngBox
1678 box;
1679
1680 /*
1681 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1682 */
cristybb503372010-05-27 20:51:26 +00001683 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1684 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1685 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1686 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001687 if (delta_type != 0)
1688 {
1689 box.left+=previous_box.left;
1690 box.right+=previous_box.right;
1691 box.top+=previous_box.top;
1692 box.bottom+=previous_box.bottom;
1693 }
glennrp0fe50b42010-11-16 03:52:51 +00001694
cristy3ed852e2009-09-05 21:47:34 +00001695 return(box);
1696}
1697
1698static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1699 unsigned char *p)
1700{
1701 MngPair
1702 pair;
1703 /*
cristybb503372010-05-27 20:51:26 +00001704 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001705 */
cristy8182b072010-05-30 20:10:53 +00001706 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1707 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001708
cristy3ed852e2009-09-05 21:47:34 +00001709 if (delta_type != 0)
1710 {
1711 pair.a+=previous_pair.a;
1712 pair.b+=previous_pair.b;
1713 }
glennrp0fe50b42010-11-16 03:52:51 +00001714
cristy3ed852e2009-09-05 21:47:34 +00001715 return(pair);
1716}
1717
cristy8182b072010-05-30 20:10:53 +00001718static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001719{
cristy8182b072010-05-30 20:10:53 +00001720 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001721}
1722
cristyc82a27b2011-10-21 01:07:16 +00001723typedef struct _PNGErrorInfo
cristy3ed852e2009-09-05 21:47:34 +00001724{
1725 Image
1726 *image;
1727
cristyc82a27b2011-10-21 01:07:16 +00001728 ExceptionInfo
1729 *exception;
1730} PNGErrorInfo;
glennrp0fe50b42010-11-16 03:52:51 +00001731
cristyc82a27b2011-10-21 01:07:16 +00001732static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1733{
1734 ExceptionInfo
1735 *exception;
glennrp0fe50b42010-11-16 03:52:51 +00001736
cristyc82a27b2011-10-21 01:07:16 +00001737 Image
1738 *image;
1739
1740 PNGErrorInfo
1741 *error_info;
1742
1743 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1744 image=error_info->image;
1745 exception=error_info->exception;
1746
1747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1748 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1749
1750 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1751 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001752
glennrpe4017e32011-01-08 17:16:09 +00001753#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001754 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1755 * are building with libpng-1.4.x and can be ignored.
1756 */
cristy3ed852e2009-09-05 21:47:34 +00001757 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001758#else
1759 png_longjmp(ping,1);
1760#endif
cristy3ed852e2009-09-05 21:47:34 +00001761}
1762
glennrpcf002022011-01-30 02:38:15 +00001763static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001764{
cristyc82a27b2011-10-21 01:07:16 +00001765 ExceptionInfo
1766 *exception;
1767
cristy3ed852e2009-09-05 21:47:34 +00001768 Image
1769 *image;
1770
cristyc82a27b2011-10-21 01:07:16 +00001771 PNGErrorInfo
1772 *error_info;
1773
cristy3ed852e2009-09-05 21:47:34 +00001774 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1775 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001776
cristyc82a27b2011-10-21 01:07:16 +00001777 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1778 image=error_info->image;
1779 exception=error_info->exception;
1780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1781 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001782
cristyc82a27b2011-10-21 01:07:16 +00001783 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
cristy3ed852e2009-09-05 21:47:34 +00001784 message,"`%s'",image->filename);
1785}
1786
1787#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001788static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001789{
1790#if (PNG_LIBPNG_VER < 10011)
1791 png_voidp
1792 ret;
1793
cristydf0d90e2011-12-12 01:03:55 +00001794 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001795 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001796
cristy3ed852e2009-09-05 21:47:34 +00001797 if (ret == NULL)
1798 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001799
cristy3ed852e2009-09-05 21:47:34 +00001800 return(ret);
1801#else
cristydf0d90e2011-12-12 01:03:55 +00001802 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001803 return((png_voidp) AcquireMagickMemory((size_t) size));
1804#endif
1805}
1806
1807/*
1808 Free a pointer. It is removed from the list at the same time.
1809*/
glennrpcf002022011-01-30 02:38:15 +00001810static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001811{
glennrp3bd393f2011-12-21 18:54:53 +00001812 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001813 ptr=RelinquishMagickMemory(ptr);
1814 return((png_free_ptr) NULL);
1815}
1816#endif
1817
1818#if defined(__cplusplus) || defined(c_plusplus)
1819}
1820#endif
1821
1822static int
glennrpcf002022011-01-30 02:38:15 +00001823Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristyd15e6592011-10-15 00:13:06 +00001824 png_textp text,int ii,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001825{
cristybb503372010-05-27 20:51:26 +00001826 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001827 i;
1828
1829 register unsigned char
1830 *dp;
1831
1832 register png_charp
1833 sp;
1834
1835 png_uint_32
1836 length,
1837 nibbles;
1838
1839 StringInfo
1840 *profile;
1841
glennrp0c3e06b2010-11-19 13:45:02 +00001842 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001843 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1844 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1845 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1846 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1847 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1848 13,14,15};
1849
1850 sp=text[ii].text+1;
1851 /* look for newline */
1852 while (*sp != '\n')
1853 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001854
cristy3ed852e2009-09-05 21:47:34 +00001855 /* look for length */
1856 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1857 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001858
cristyf2f27272009-12-17 14:48:46 +00001859 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001860
glennrp97f90e22011-02-22 05:47:58 +00001861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1862 " length: %lu",(unsigned long) length);
1863
cristy3ed852e2009-09-05 21:47:34 +00001864 while (*sp != ' ' && *sp != '\n')
1865 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001866
cristy3ed852e2009-09-05 21:47:34 +00001867 /* allocate space */
1868 if (length == 0)
1869 {
cristyc82a27b2011-10-21 01:07:16 +00001870 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001871 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1872 return(MagickFalse);
1873 }
glennrp0fe50b42010-11-16 03:52:51 +00001874
cristy8723e4b2011-09-01 13:11:19 +00001875 profile=BlobToStringInfo((const void *) NULL,length);
glennrp0fe50b42010-11-16 03:52:51 +00001876
cristy3ed852e2009-09-05 21:47:34 +00001877 if (profile == (StringInfo *) NULL)
1878 {
cristyc82a27b2011-10-21 01:07:16 +00001879 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001880 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1881 "unable to copy profile");
1882 return(MagickFalse);
1883 }
glennrp0fe50b42010-11-16 03:52:51 +00001884
cristy3ed852e2009-09-05 21:47:34 +00001885 /* copy profile, skipping white space and column 1 "=" signs */
1886 dp=GetStringInfoDatum(profile);
1887 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001888
cristybb503372010-05-27 20:51:26 +00001889 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001890 {
1891 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1892 {
1893 if (*sp == '\0')
1894 {
cristyc82a27b2011-10-21 01:07:16 +00001895 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001896 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1897 profile=DestroyStringInfo(profile);
1898 return(MagickFalse);
1899 }
1900 sp++;
1901 }
glennrp0fe50b42010-11-16 03:52:51 +00001902
cristy3ed852e2009-09-05 21:47:34 +00001903 if (i%2 == 0)
1904 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001905
cristy3ed852e2009-09-05 21:47:34 +00001906 else
1907 (*dp++)+=unhex[(int) *sp++];
1908 }
1909 /*
1910 We have already read "Raw profile type.
1911 */
cristyd15e6592011-10-15 00:13:06 +00001912 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001913 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001914
cristy3ed852e2009-09-05 21:47:34 +00001915 if (image_info->verbose)
1916 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001917
cristy3ed852e2009-09-05 21:47:34 +00001918 return MagickTrue;
1919}
1920
1921#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1922static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1923{
1924 Image
1925 *image;
1926
1927
1928 /* The unknown chunk structure contains the chunk data:
1929 png_byte name[5];
1930 png_byte *data;
1931 png_size_t size;
1932
1933 Note that libpng has already taken care of the CRC handling.
1934 */
1935
1936
1937 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1938 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1939 return(0); /* Did not recognize */
1940
1941 /* recognized vpAg */
1942
1943 if (chunk->size != 9)
1944 return(-1); /* Error return */
1945
1946 if (chunk->data[8] != 0)
1947 return(0); /* ImageMagick requires pixel units */
1948
1949 image=(Image *) png_get_user_chunk_ptr(ping);
1950
cristybb503372010-05-27 20:51:26 +00001951 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001952 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001953
cristybb503372010-05-27 20:51:26 +00001954 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001955 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1956
1957 /* Return one of the following: */
1958 /* return(-n); chunk had an error */
1959 /* return(0); did not recognize */
1960 /* return(n); success */
1961
1962 return(1);
1963
1964}
1965#endif
1966
1967/*
1968%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1969% %
1970% %
1971% %
1972% R e a d O n e P N G I m a g e %
1973% %
1974% %
1975% %
1976%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1977%
1978% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1979% (minus the 8-byte signature) and returns it. It allocates the memory
1980% necessary for the new Image structure and returns a pointer to the new
1981% image.
1982%
1983% The format of the ReadOnePNGImage method is:
1984%
1985% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1986% ExceptionInfo *exception)
1987%
1988% A description of each parameter follows:
1989%
1990% o mng_info: Specifies a pointer to a MngInfo structure.
1991%
1992% o image_info: the image info.
1993%
1994% o exception: return any errors or warnings in this structure.
1995%
1996*/
1997static Image *ReadOnePNGImage(MngInfo *mng_info,
1998 const ImageInfo *image_info, ExceptionInfo *exception)
1999{
2000 /* Read one PNG image */
2001
glennrpcc95c3f2011-04-18 16:46:48 +00002002 /* To do: Read the tIME chunk into the date:modify property */
2003 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2004
cristy3ed852e2009-09-05 21:47:34 +00002005 Image
2006 *image;
2007
2008 int
glennrp4eb39312011-03-30 21:34:55 +00002009 intent,
glennrpcb395ac2011-03-30 19:50:23 +00002010 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00002011 num_text,
glennrp4eb39312011-03-30 21:34:55 +00002012 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00002013 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00002014 pass,
2015 ping_bit_depth,
2016 ping_color_type,
2017 ping_interlace_method,
2018 ping_compression_method,
2019 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00002020 ping_num_trans,
2021 unit_type;
2022
2023 double
2024 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002025
2026 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002027 logging,
cristy3ed852e2009-09-05 21:47:34 +00002028 status;
2029
cristyc82a27b2011-10-21 01:07:16 +00002030 PixelInfo
2031 transparent_color;
2032
2033 PNGErrorInfo
2034 error_info;
2035
glennrpfaa852b2010-03-30 12:17:00 +00002036 png_bytep
2037 ping_trans_alpha;
2038
2039 png_color_16p
2040 ping_background,
2041 ping_trans_color;
2042
cristy3ed852e2009-09-05 21:47:34 +00002043 png_info
2044 *end_info,
2045 *ping_info;
2046
2047 png_struct
2048 *ping;
2049
2050 png_textp
2051 text;
2052
glennrpfaa852b2010-03-30 12:17:00 +00002053 png_uint_32
2054 ping_height,
2055 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002056 x_resolution,
2057 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002058
cristy3ed852e2009-09-05 21:47:34 +00002059 QuantumInfo
2060 *quantum_info;
2061
2062 unsigned char
glennrpcf002022011-01-30 02:38:15 +00002063 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002064
cristybb503372010-05-27 20:51:26 +00002065 ssize_t
cristy756ae432011-11-19 02:18:25 +00002066 ping_rowbytes,
cristy3ed852e2009-09-05 21:47:34 +00002067 y;
2068
2069 register unsigned char
2070 *p;
2071
cristybb503372010-05-27 20:51:26 +00002072 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002073 i,
2074 x;
2075
cristy4c08aed2011-07-01 19:47:50 +00002076 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002077 *q;
2078
2079 size_t
glennrp39992b42010-11-14 00:03:43 +00002080 length,
cristy3ed852e2009-09-05 21:47:34 +00002081 row_offset;
2082
cristyeb3b22a2011-03-31 20:16:11 +00002083 ssize_t
2084 j;
2085
cristy3ed852e2009-09-05 21:47:34 +00002086#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2087 png_byte unused_chunks[]=
2088 {
2089 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2090 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2091 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2092 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2093 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2094 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2095 };
2096#endif
2097
2098 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002099 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002100
2101#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002102 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002103#endif
2104
glennrp25c1e2b2010-03-25 01:39:56 +00002105#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002106 if (image_info->verbose)
2107 printf("Your PNG library (libpng-%s) is rather old.\n",
2108 PNG_LIBPNG_VER_STRING);
2109#endif
2110
glennrp61b4c952009-11-10 20:40:41 +00002111#if (PNG_LIBPNG_VER >= 10400)
2112# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2113 if (image_info->verbose)
2114 {
2115 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2116 PNG_LIBPNG_VER_STRING);
2117 printf("Please update it.\n");
2118 }
2119# endif
2120#endif
2121
2122
cristyed552522009-10-16 14:04:35 +00002123 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002124 image=mng_info->image;
2125
glennrpa6a06632011-01-19 15:15:34 +00002126 if (logging != MagickFalse)
2127 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2128 " image->matte=%d",(int) image->matte);
2129
glennrp0e319732011-01-25 21:53:13 +00002130 /* Set to an out-of-range color unless tRNS chunk is present */
2131 transparent_color.red=65537;
2132 transparent_color.green=65537;
2133 transparent_color.blue=65537;
cristy4c08aed2011-07-01 19:47:50 +00002134 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002135
glennrpcb395ac2011-03-30 19:50:23 +00002136 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002137 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002138 num_raw_profiles = 0;
2139
cristy3ed852e2009-09-05 21:47:34 +00002140 /*
2141 Allocate the PNG structures
2142 */
2143#ifdef PNG_USER_MEM_SUPPORTED
cristyc82a27b2011-10-21 01:07:16 +00002144 error_info.image=image;
2145 error_info.exception=exception;
2146 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002147 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2148 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002149#else
cristyc82a27b2011-10-21 01:07:16 +00002150 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002151 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002152#endif
2153 if (ping == (png_struct *) NULL)
2154 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002155
cristy3ed852e2009-09-05 21:47:34 +00002156 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002157
cristy3ed852e2009-09-05 21:47:34 +00002158 if (ping_info == (png_info *) NULL)
2159 {
2160 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2161 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2162 }
glennrp0fe50b42010-11-16 03:52:51 +00002163
cristy3ed852e2009-09-05 21:47:34 +00002164 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002165
cristy3ed852e2009-09-05 21:47:34 +00002166 if (end_info == (png_info *) NULL)
2167 {
2168 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2169 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2170 }
glennrp0fe50b42010-11-16 03:52:51 +00002171
glennrpcf002022011-01-30 02:38:15 +00002172 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002173
glennrpfaa852b2010-03-30 12:17:00 +00002174 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002175 {
2176 /*
2177 PNG image is corrupt.
2178 */
2179 png_destroy_read_struct(&ping,&ping_info,&end_info);
2180#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002181 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002182#endif
2183 if (logging != MagickFalse)
2184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2185 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002186
cristy3ed852e2009-09-05 21:47:34 +00002187 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002188 {
cristyc82a27b2011-10-21 01:07:16 +00002189 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002190 image->columns=0;
2191 }
glennrp0fe50b42010-11-16 03:52:51 +00002192
cristy3ed852e2009-09-05 21:47:34 +00002193 return(GetFirstImageInList(image));
2194 }
2195 /*
2196 Prepare PNG for reading.
2197 */
glennrpfaa852b2010-03-30 12:17:00 +00002198
cristy3ed852e2009-09-05 21:47:34 +00002199 mng_info->image_found++;
2200 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002201
cristy3ed852e2009-09-05 21:47:34 +00002202 if (LocaleCompare(image_info->magick,"MNG") == 0)
2203 {
2204#if defined(PNG_MNG_FEATURES_SUPPORTED)
2205 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2206 png_set_read_fn(ping,image,png_get_data);
2207#else
2208#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2209 png_permit_empty_plte(ping,MagickTrue);
2210 png_set_read_fn(ping,image,png_get_data);
2211#else
2212 mng_info->image=image;
2213 mng_info->bytes_in_read_buffer=0;
2214 mng_info->found_empty_plte=MagickFalse;
2215 mng_info->have_saved_bkgd_index=MagickFalse;
2216 png_set_read_fn(ping,mng_info,mng_get_data);
2217#endif
2218#endif
2219 }
glennrp0fe50b42010-11-16 03:52:51 +00002220
cristy3ed852e2009-09-05 21:47:34 +00002221 else
2222 png_set_read_fn(ping,image,png_get_data);
2223
2224#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2225 /* Ignore unused chunks and all unknown chunks except for vpAg */
2226 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2227 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2228 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2229 (int)sizeof(unused_chunks)/5);
2230 /* Callback for other unknown chunks */
2231 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2232#endif
2233
glennrp991e92a2010-01-28 03:09:00 +00002234#if (PNG_LIBPNG_VER < 10400)
2235# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2236 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002237 /* Disable thread-unsafe features of pnggccrd */
2238 if (png_access_version_number() >= 10200)
2239 {
2240 png_uint_32 mmx_disable_mask=0;
2241 png_uint_32 asm_flags;
2242
2243 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2244 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2245 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2246 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2247 asm_flags=png_get_asm_flags(ping);
2248 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2249 }
glennrp991e92a2010-01-28 03:09:00 +00002250# endif
cristy3ed852e2009-09-05 21:47:34 +00002251#endif
2252
2253 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002254
2255 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2256 &ping_bit_depth,&ping_color_type,
2257 &ping_interlace_method,&ping_compression_method,
2258 &ping_filter_method);
2259
2260 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2261 &ping_trans_color);
2262
2263 (void) png_get_bKGD(ping, ping_info, &ping_background);
2264
2265 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002266 {
glennrpfaa852b2010-03-30 12:17:00 +00002267 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2268 {
2269 png_set_packing(ping);
2270 ping_bit_depth = 8;
2271 }
cristy3ed852e2009-09-05 21:47:34 +00002272 }
glennrpfaa852b2010-03-30 12:17:00 +00002273
2274 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002275 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002276 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00002277 if (logging != MagickFalse)
2278 {
2279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002280 " PNG width: %.20g, height: %.20g",
2281 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002282
cristy3ed852e2009-09-05 21:47:34 +00002283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2284 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002285 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002286
cristy3ed852e2009-09-05 21:47:34 +00002287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2288 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002289 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002290
cristy3ed852e2009-09-05 21:47:34 +00002291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2292 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002293 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002294 }
2295
glennrpfaa852b2010-03-30 12:17:00 +00002296#ifdef PNG_READ_iCCP_SUPPORTED
2297 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002298 {
2299 int
2300 compression;
2301
glennrpe4017e32011-01-08 17:16:09 +00002302#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002303 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002304 info;
2305#else
2306 png_bytep
2307 info;
2308#endif
2309
2310 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002311 name;
2312
2313 png_uint_32
2314 profile_length;
2315
2316 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2317 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002318
cristy3ed852e2009-09-05 21:47:34 +00002319 if (profile_length != 0)
2320 {
2321 StringInfo
2322 *profile;
2323
2324 if (logging != MagickFalse)
2325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2326 " Reading PNG iCCP chunk.");
cristye8f8f382011-09-01 13:32:37 +00002327 profile=BlobToStringInfo(info,profile_length);
2328 if (profile == (StringInfo *) NULL)
2329 {
cristyc82a27b2011-10-21 01:07:16 +00002330 (void) ThrowMagickException(exception,GetMagickModule(),
cristye8f8f382011-09-01 13:32:37 +00002331 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2332 "unable to copy profile");
cristyad5b0852011-09-08 02:01:21 +00002333 return((Image *) NULL);
cristye8f8f382011-09-01 13:32:37 +00002334 }
cristy3ed852e2009-09-05 21:47:34 +00002335 SetStringInfoDatum(profile,(const unsigned char *) info);
cristyd15e6592011-10-15 00:13:06 +00002336 (void) SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00002337 profile=DestroyStringInfo(profile);
2338 }
2339 }
2340#endif
2341#if defined(PNG_READ_sRGB_SUPPORTED)
2342 {
cristy3ed852e2009-09-05 21:47:34 +00002343 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00002344 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2345 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00002346
cristy3ed852e2009-09-05 21:47:34 +00002347 if (png_get_sRGB(ping,ping_info,&intent))
2348 {
glennrpcf002022011-01-30 02:38:15 +00002349 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2350 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002351
cristy3ed852e2009-09-05 21:47:34 +00002352 if (logging != MagickFalse)
2353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002354 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002355 }
2356 }
2357#endif
2358 {
glennrpfaa852b2010-03-30 12:17:00 +00002359 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2360 if (mng_info->have_global_gama)
2361 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002362
cristy3ed852e2009-09-05 21:47:34 +00002363 if (png_get_gAMA(ping,ping_info,&file_gamma))
2364 {
2365 image->gamma=(float) file_gamma;
2366 if (logging != MagickFalse)
2367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2368 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2369 }
2370 }
glennrpfaa852b2010-03-30 12:17:00 +00002371 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2372 {
2373 if (mng_info->have_global_chrm != MagickFalse)
2374 {
2375 (void) png_set_cHRM(ping,ping_info,
2376 mng_info->global_chrm.white_point.x,
2377 mng_info->global_chrm.white_point.y,
2378 mng_info->global_chrm.red_primary.x,
2379 mng_info->global_chrm.red_primary.y,
2380 mng_info->global_chrm.green_primary.x,
2381 mng_info->global_chrm.green_primary.y,
2382 mng_info->global_chrm.blue_primary.x,
2383 mng_info->global_chrm.blue_primary.y);
2384 }
2385 }
glennrp0fe50b42010-11-16 03:52:51 +00002386
glennrpfaa852b2010-03-30 12:17:00 +00002387 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002388 {
2389 (void) png_get_cHRM(ping,ping_info,
2390 &image->chromaticity.white_point.x,
2391 &image->chromaticity.white_point.y,
2392 &image->chromaticity.red_primary.x,
2393 &image->chromaticity.red_primary.y,
2394 &image->chromaticity.green_primary.x,
2395 &image->chromaticity.green_primary.y,
2396 &image->chromaticity.blue_primary.x,
2397 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002398
cristy3ed852e2009-09-05 21:47:34 +00002399 if (logging != MagickFalse)
2400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2401 " Reading PNG cHRM chunk.");
2402 }
glennrp0fe50b42010-11-16 03:52:51 +00002403
glennrpe610a072010-08-05 17:08:46 +00002404 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002405 {
glennrpe610a072010-08-05 17:08:46 +00002406 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002407 Magick_RenderingIntent_to_PNG_RenderingIntent
2408 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00002409 png_set_gAMA(ping,ping_info,0.45455f);
2410 png_set_cHRM(ping,ping_info,
2411 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2412 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002413 }
cristy3ed852e2009-09-05 21:47:34 +00002414#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002415 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002416 {
cristy905ef802011-02-23 00:29:18 +00002417 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2418 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002419
cristy3ed852e2009-09-05 21:47:34 +00002420 if (logging != MagickFalse)
2421 if (image->page.x || image->page.y)
2422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002423 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2424 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002425 }
2426#endif
2427#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002428 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2429 {
2430 if (mng_info->have_global_phys)
2431 {
2432 png_set_pHYs(ping,ping_info,
2433 mng_info->global_x_pixels_per_unit,
2434 mng_info->global_y_pixels_per_unit,
2435 mng_info->global_phys_unit_type);
2436 }
2437 }
2438
2439 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002440 {
cristy3ed852e2009-09-05 21:47:34 +00002441 /*
2442 Set image resolution.
2443 */
2444 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002445 &unit_type);
cristy2a11bef2011-10-28 18:33:11 +00002446 image->resolution.x=(double) x_resolution;
2447 image->resolution.y=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002448
cristy3ed852e2009-09-05 21:47:34 +00002449 if (unit_type == PNG_RESOLUTION_METER)
2450 {
2451 image->units=PixelsPerCentimeterResolution;
cristy2a11bef2011-10-28 18:33:11 +00002452 image->resolution.x=(double) x_resolution/100.0;
2453 image->resolution.y=(double) y_resolution/100.0;
cristy3ed852e2009-09-05 21:47:34 +00002454 }
glennrp0fe50b42010-11-16 03:52:51 +00002455
cristy3ed852e2009-09-05 21:47:34 +00002456 if (logging != MagickFalse)
2457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002458 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2459 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002460 }
cristy3ed852e2009-09-05 21:47:34 +00002461#endif
glennrp823b55c2011-03-14 18:46:46 +00002462
glennrpfaa852b2010-03-30 12:17:00 +00002463 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002464 {
2465 int
2466 number_colors;
2467
2468 png_colorp
2469 palette;
2470
2471 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002472
cristy3ed852e2009-09-05 21:47:34 +00002473 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002474 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002475 {
2476 if (mng_info->global_plte_length)
2477 {
2478 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2479 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002480
glennrpfaa852b2010-03-30 12:17:00 +00002481 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002482 if (mng_info->global_trns_length)
2483 {
2484 if (mng_info->global_trns_length >
2485 mng_info->global_plte_length)
cristyc82a27b2011-10-21 01:07:16 +00002486 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00002487 GetMagickModule(),CoderError,
2488 "global tRNS has more entries than global PLTE",
2489 "`%s'",image_info->filename);
2490 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2491 (int) mng_info->global_trns_length,NULL);
2492 }
glennrpbfd9e612011-04-22 14:02:20 +00002493#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002494 if (
2495#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2496 mng_info->have_saved_bkgd_index ||
2497#endif
glennrpfaa852b2010-03-30 12:17:00 +00002498 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002499 {
2500 png_color_16
2501 background;
2502
2503#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2504 if (mng_info->have_saved_bkgd_index)
2505 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002506#endif
glennrpfaa852b2010-03-30 12:17:00 +00002507 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2508 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002509
cristy3ed852e2009-09-05 21:47:34 +00002510 background.red=(png_uint_16)
2511 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002512
cristy3ed852e2009-09-05 21:47:34 +00002513 background.green=(png_uint_16)
2514 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002515
cristy3ed852e2009-09-05 21:47:34 +00002516 background.blue=(png_uint_16)
2517 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002518
glennrpc6c391a2011-04-27 02:23:56 +00002519 background.gray=(png_uint_16)
2520 mng_info->global_plte[background.index].green;
2521
cristy3ed852e2009-09-05 21:47:34 +00002522 png_set_bKGD(ping,ping_info,&background);
2523 }
2524#endif
2525 }
2526 else
cristyc82a27b2011-10-21 01:07:16 +00002527 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00002528 CoderError,"No global PLTE in file","`%s'",
2529 image_info->filename);
2530 }
2531 }
2532
glennrpbfd9e612011-04-22 14:02:20 +00002533#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002534 if (mng_info->have_global_bkgd &&
2535 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002536 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002537
glennrpfaa852b2010-03-30 12:17:00 +00002538 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002539 {
glennrpbfd9e612011-04-22 14:02:20 +00002540 unsigned int
2541 bkgd_scale;
2542
cristy3ed852e2009-09-05 21:47:34 +00002543 /*
2544 Set image background color.
2545 */
2546 if (logging != MagickFalse)
2547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2548 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002549
glennrpbfd9e612011-04-22 14:02:20 +00002550 /* Scale background components to 16-bit, then scale
2551 * to quantum depth
2552 */
2553 if (logging != MagickFalse)
2554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2555 " raw ping_background=(%d,%d,%d).",ping_background->red,
2556 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002557
glennrpbfd9e612011-04-22 14:02:20 +00002558 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002559
glennrpbfd9e612011-04-22 14:02:20 +00002560 if (ping_bit_depth == 1)
2561 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002562
glennrpbfd9e612011-04-22 14:02:20 +00002563 else if (ping_bit_depth == 2)
2564 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002565
glennrpbfd9e612011-04-22 14:02:20 +00002566 else if (ping_bit_depth == 4)
2567 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002568
glennrpbfd9e612011-04-22 14:02:20 +00002569 if (ping_bit_depth <= 8)
2570 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002571
glennrpbfd9e612011-04-22 14:02:20 +00002572 ping_background->red *= bkgd_scale;
2573 ping_background->green *= bkgd_scale;
2574 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002575
glennrpbfd9e612011-04-22 14:02:20 +00002576 if (logging != MagickFalse)
2577 {
glennrp2cbb4482010-06-02 04:37:24 +00002578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2579 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002580
glennrp2cbb4482010-06-02 04:37:24 +00002581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2582 " ping_background=(%d,%d,%d).",ping_background->red,
2583 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002584 }
glennrp2cbb4482010-06-02 04:37:24 +00002585
glennrpbfd9e612011-04-22 14:02:20 +00002586 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002587 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002588
glennrpbfd9e612011-04-22 14:02:20 +00002589 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002590 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002591
glennrpbfd9e612011-04-22 14:02:20 +00002592 image->background_color.blue=
2593 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002594
cristy4c08aed2011-07-01 19:47:50 +00002595 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002596
glennrpbfd9e612011-04-22 14:02:20 +00002597 if (logging != MagickFalse)
2598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2599 " image->background_color=(%.20g,%.20g,%.20g).",
2600 (double) image->background_color.red,
2601 (double) image->background_color.green,
2602 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002603 }
glennrpbfd9e612011-04-22 14:02:20 +00002604#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002605
glennrpfaa852b2010-03-30 12:17:00 +00002606 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002607 {
2608 /*
glennrpa6a06632011-01-19 15:15:34 +00002609 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002610 */
2611 int
2612 max_sample;
2613
cristy35ef8242010-06-03 16:24:13 +00002614 size_t
2615 one=1;
2616
cristy3ed852e2009-09-05 21:47:34 +00002617 if (logging != MagickFalse)
2618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2619 " Reading PNG tRNS chunk.");
2620
cristyf9cca6a2010-06-04 23:49:28 +00002621 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002622
glennrpfaa852b2010-03-30 12:17:00 +00002623 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2624 (int)ping_trans_color->gray > max_sample) ||
2625 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2626 ((int)ping_trans_color->red > max_sample ||
2627 (int)ping_trans_color->green > max_sample ||
2628 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002629 {
2630 if (logging != MagickFalse)
2631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2632 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002633 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002634 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002635 image->matte=MagickFalse;
2636 }
2637 else
2638 {
glennrpa6a06632011-01-19 15:15:34 +00002639 int
2640 scale_to_short;
2641
2642 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2643
2644 /* Scale transparent_color to short */
2645 transparent_color.red= scale_to_short*ping_trans_color->red;
2646 transparent_color.green= scale_to_short*ping_trans_color->green;
2647 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy4c08aed2011-07-01 19:47:50 +00002648 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002649
glennrpfaa852b2010-03-30 12:17:00 +00002650 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002651 {
glennrp0f111982010-07-07 20:18:33 +00002652 if (logging != MagickFalse)
2653 {
2654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2655 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002656
glennrp0f111982010-07-07 20:18:33 +00002657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy101ab702011-10-13 13:06:32 +00002658 " scaled graylevel is %.20g.",transparent_color.alpha);
glennrp0f111982010-07-07 20:18:33 +00002659 }
cristy4c08aed2011-07-01 19:47:50 +00002660 transparent_color.red=transparent_color.alpha;
2661 transparent_color.green=transparent_color.alpha;
2662 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002663 }
2664 }
2665 }
2666#if defined(PNG_READ_sBIT_SUPPORTED)
2667 if (mng_info->have_global_sbit)
2668 {
glennrpfaa852b2010-03-30 12:17:00 +00002669 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002670 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2671 }
2672#endif
2673 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002674
cristy3ed852e2009-09-05 21:47:34 +00002675 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002676
2677 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2678
cristy3ed852e2009-09-05 21:47:34 +00002679 /*
2680 Initialize image structure.
2681 */
2682 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002683 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002684 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002685 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002686 if (mng_info->mng_type == 0)
2687 {
glennrpfaa852b2010-03-30 12:17:00 +00002688 mng_info->mng_width=ping_width;
2689 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002690 mng_info->frame=mng_info->image_box;
2691 mng_info->clip=mng_info->image_box;
2692 }
glennrp0fe50b42010-11-16 03:52:51 +00002693
cristy3ed852e2009-09-05 21:47:34 +00002694 else
2695 {
2696 image->page.y=mng_info->y_off[mng_info->object_id];
2697 }
glennrp0fe50b42010-11-16 03:52:51 +00002698
cristy3ed852e2009-09-05 21:47:34 +00002699 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002700 image->columns=ping_width;
2701 image->rows=ping_height;
cristy897f2bf2012-01-05 02:03:44 +00002702 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2703 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2704 image->colorspace=GRAYColorspace;
glennrpfaa852b2010-03-30 12:17:00 +00002705 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002706 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002707 {
cristybefe4d22010-06-07 01:18:58 +00002708 size_t
2709 one;
2710
cristy3ed852e2009-09-05 21:47:34 +00002711 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002712 one=1;
2713 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002714#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2715 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002716 image->colors=256;
2717#else
2718 if (image->colors > 65536L)
2719 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002720#endif
glennrpfaa852b2010-03-30 12:17:00 +00002721 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002722 {
2723 int
2724 number_colors;
2725
2726 png_colorp
2727 palette;
2728
2729 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002730 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002731
cristy3ed852e2009-09-05 21:47:34 +00002732 if (logging != MagickFalse)
2733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2734 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2735 }
2736 }
2737
2738 if (image->storage_class == PseudoClass)
2739 {
2740 /*
2741 Initialize image colormap.
2742 */
cristy018f07f2011-09-04 21:15:19 +00002743 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002744 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002745
glennrpfaa852b2010-03-30 12:17:00 +00002746 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002747 {
2748 int
2749 number_colors;
2750
2751 png_colorp
2752 palette;
2753
2754 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002755
glennrp6af6cf12011-04-22 13:05:16 +00002756 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002757 {
2758 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2759 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2760 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2761 }
glennrp6af6cf12011-04-22 13:05:16 +00002762
glennrp67b9c1a2011-04-22 18:47:36 +00002763 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002764 {
2765 image->colormap[i].red=0;
2766 image->colormap[i].green=0;
2767 image->colormap[i].blue=0;
2768 }
cristy3ed852e2009-09-05 21:47:34 +00002769 }
glennrp0fe50b42010-11-16 03:52:51 +00002770
cristy3ed852e2009-09-05 21:47:34 +00002771 else
2772 {
cristybb503372010-05-27 20:51:26 +00002773 size_t
cristy3ed852e2009-09-05 21:47:34 +00002774 scale;
2775
glennrpfaa852b2010-03-30 12:17:00 +00002776 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002777
cristy3ed852e2009-09-05 21:47:34 +00002778 if (scale < 1)
2779 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002780
cristybb503372010-05-27 20:51:26 +00002781 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002782 {
2783 image->colormap[i].red=(Quantum) (i*scale);
2784 image->colormap[i].green=(Quantum) (i*scale);
2785 image->colormap[i].blue=(Quantum) (i*scale);
2786 }
2787 }
2788 }
glennrp147bc912011-03-30 18:47:21 +00002789
glennrpcb395ac2011-03-30 19:50:23 +00002790 /* Set some properties for reporting by "identify" */
2791 {
glennrp147bc912011-03-30 18:47:21 +00002792 char
2793 msg[MaxTextExtent];
2794
2795 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2796 ping_interlace_method in value */
2797
cristy3b6fd2e2011-05-20 12:53:50 +00002798 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002799 "%d, %d",(int) ping_width, (int) ping_height);
cristy5d6fc9c2011-12-27 03:10:42 +00002800 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002801
cristy3b6fd2e2011-05-20 12:53:50 +00002802 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
cristy5d6fc9c2011-12-27 03:10:42 +00002803 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002804
cristy3b6fd2e2011-05-20 12:53:50 +00002805 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
cristy5d6fc9c2011-12-27 03:10:42 +00002806 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002807
cristy3b6fd2e2011-05-20 12:53:50 +00002808 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002809 (int) ping_interlace_method);
cristy5d6fc9c2011-12-27 03:10:42 +00002810 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
glennrpcb395ac2011-03-30 19:50:23 +00002811 }
glennrp147bc912011-03-30 18:47:21 +00002812
cristy3ed852e2009-09-05 21:47:34 +00002813 /*
2814 Read image scanlines.
2815 */
2816 if (image->delay != 0)
2817 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002818
glennrp0ca69b12010-07-26 01:57:52 +00002819 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002820 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2821 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002822 {
2823 if (logging != MagickFalse)
2824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002825 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002826 mng_info->scenes_found-1);
2827 png_destroy_read_struct(&ping,&ping_info,&end_info);
2828#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002829 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002830#endif
2831 if (logging != MagickFalse)
2832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2833 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002834
cristy3ed852e2009-09-05 21:47:34 +00002835 return(image);
2836 }
glennrp0fe50b42010-11-16 03:52:51 +00002837
cristy3ed852e2009-09-05 21:47:34 +00002838 if (logging != MagickFalse)
2839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2840 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002841
cristy3ed852e2009-09-05 21:47:34 +00002842 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002843 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2844 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002845
cristy3ed852e2009-09-05 21:47:34 +00002846 else
glennrpcf002022011-01-30 02:38:15 +00002847 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2848 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002849
glennrpcf002022011-01-30 02:38:15 +00002850 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002851 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002852
cristy3ed852e2009-09-05 21:47:34 +00002853 if (logging != MagickFalse)
2854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2855 " Converting PNG pixels to pixel packets");
2856 /*
2857 Convert PNG pixels to pixel packets.
2858 */
glennrpfaa852b2010-03-30 12:17:00 +00002859 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002860 {
2861 /*
2862 PNG image is corrupt.
2863 */
2864 png_destroy_read_struct(&ping,&ping_info,&end_info);
2865#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002866 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002867#endif
2868 if (quantum_info != (QuantumInfo *) NULL)
2869 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002870
glennrpcf002022011-01-30 02:38:15 +00002871 if (ping_pixels != (unsigned char *) NULL)
2872 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002873
cristy3ed852e2009-09-05 21:47:34 +00002874 if (logging != MagickFalse)
2875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2876 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002877
cristy3ed852e2009-09-05 21:47:34 +00002878 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002879 {
cristyc82a27b2011-10-21 01:07:16 +00002880 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002881 image->columns=0;
2882 }
glennrp0fe50b42010-11-16 03:52:51 +00002883
cristy3ed852e2009-09-05 21:47:34 +00002884 return(GetFirstImageInList(image));
2885 }
glennrp0fe50b42010-11-16 03:52:51 +00002886
cristyed552522009-10-16 14:04:35 +00002887 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002888
cristyed552522009-10-16 14:04:35 +00002889 if (quantum_info == (QuantumInfo *) NULL)
2890 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002891
glennrpc8cbc5d2011-01-01 00:12:34 +00002892 {
2893
2894 MagickBooleanType
2895 found_transparent_pixel;
2896
2897 found_transparent_pixel=MagickFalse;
2898
cristy3ed852e2009-09-05 21:47:34 +00002899 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002900 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002901 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002902 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002903 /*
2904 Convert image to DirectClass pixel packets.
2905 */
glennrp67b9c1a2011-04-22 18:47:36 +00002906#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2907 int
2908 depth;
2909
2910 depth=(ssize_t) ping_bit_depth;
2911#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002912 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2913 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2914 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2915 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002916
glennrpc8cbc5d2011-01-01 00:12:34 +00002917 for (y=0; y < (ssize_t) image->rows; y++)
2918 {
2919 if (num_passes > 1)
2920 row_offset=ping_rowbytes*y;
2921
2922 else
2923 row_offset=0;
2924
glennrpcf002022011-01-30 02:38:15 +00002925 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002926 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2927
cristyacd2ed22011-08-30 01:44:23 +00002928 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00002929 break;
2930
glennrpc8cbc5d2011-01-01 00:12:34 +00002931 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2932 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002933 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002934
2935 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2936 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002937 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002938
2939 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2940 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002941 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002942
2943 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2944 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002945 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002946
2947 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2948 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002949 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002950
glennrpc8cbc5d2011-01-01 00:12:34 +00002951 if (found_transparent_pixel == MagickFalse)
2952 {
2953 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002954 if (y== 0 && logging != MagickFalse)
2955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2956 " Looking for cheap transparent pixel");
2957
glennrpc8cbc5d2011-01-01 00:12:34 +00002958 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2959 {
glennrp5aa37f62011-01-02 03:07:57 +00002960 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2961 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy4c08aed2011-07-01 19:47:50 +00002962 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00002963 {
glennrpa6a06632011-01-19 15:15:34 +00002964 if (logging != MagickFalse)
2965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2966 " ...got one.");
2967
glennrpc8cbc5d2011-01-01 00:12:34 +00002968 found_transparent_pixel = MagickTrue;
2969 break;
2970 }
glennrp4f25bd02011-01-01 18:51:28 +00002971 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2972 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp847370c2011-07-05 17:37:15 +00002973 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2974 transparent_color.red &&
2975 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2976 transparent_color.green &&
2977 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2978 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002979 {
glennrpa6a06632011-01-19 15:15:34 +00002980 if (logging != MagickFalse)
2981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2982 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002983 found_transparent_pixel = MagickTrue;
2984 break;
2985 }
cristyed231572011-07-14 02:18:59 +00002986 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00002987 }
2988 }
2989
2990 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2991 {
2992 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2993 image->rows);
2994
2995 if (status == MagickFalse)
2996 break;
2997 }
2998 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2999 break;
3000 }
3001
3002 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3003 {
3004 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00003005 if (status == MagickFalse)
3006 break;
3007 }
cristy3ed852e2009-09-05 21:47:34 +00003008 }
cristy3ed852e2009-09-05 21:47:34 +00003009 }
glennrp0fe50b42010-11-16 03:52:51 +00003010
cristy3ed852e2009-09-05 21:47:34 +00003011 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00003012
cristy3ed852e2009-09-05 21:47:34 +00003013 for (pass=0; pass < num_passes; pass++)
3014 {
3015 Quantum
3016 *quantum_scanline;
3017
3018 register Quantum
3019 *r;
3020
3021 /*
3022 Convert grayscale image to PseudoClass pixel packets.
3023 */
glennrpc17d96f2011-06-27 01:20:11 +00003024 if (logging != MagickFalse)
3025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3026 " Converting grayscale pixels to pixel packets");
cristy4c08aed2011-07-01 19:47:50 +00003027
glennrpfaa852b2010-03-30 12:17:00 +00003028 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00003029 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00003030
cristy3ed852e2009-09-05 21:47:34 +00003031 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3032 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003033
cristy3ed852e2009-09-05 21:47:34 +00003034 if (quantum_scanline == (Quantum *) NULL)
3035 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003036
cristybb503372010-05-27 20:51:26 +00003037 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003038 {
3039 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003040 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003041
cristy3ed852e2009-09-05 21:47:34 +00003042 else
3043 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003044
glennrpcf002022011-01-30 02:38:15 +00003045 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00003046 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003047
cristyacd2ed22011-08-30 01:44:23 +00003048 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003049 break;
glennrp0fe50b42010-11-16 03:52:51 +00003050
glennrpcf002022011-01-30 02:38:15 +00003051 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003052 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003053
glennrpfaa852b2010-03-30 12:17:00 +00003054 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003055 {
3056 case 1:
3057 {
cristybb503372010-05-27 20:51:26 +00003058 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003059 bit;
3060
cristybb503372010-05-27 20:51:26 +00003061 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003062 {
3063 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003064 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003065 p++;
3066 }
glennrp0fe50b42010-11-16 03:52:51 +00003067
cristy3ed852e2009-09-05 21:47:34 +00003068 if ((image->columns % 8) != 0)
3069 {
cristybb503372010-05-27 20:51:26 +00003070 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003071 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003072 }
glennrp0fe50b42010-11-16 03:52:51 +00003073
cristy3ed852e2009-09-05 21:47:34 +00003074 break;
3075 }
glennrp47b9dd52010-11-24 18:12:06 +00003076
cristy3ed852e2009-09-05 21:47:34 +00003077 case 2:
3078 {
cristybb503372010-05-27 20:51:26 +00003079 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003080 {
glennrpa18d5bc2011-04-23 14:51:34 +00003081 *r++=(*p >> 6) & 0x03;
3082 *r++=(*p >> 4) & 0x03;
3083 *r++=(*p >> 2) & 0x03;
3084 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003085 }
glennrp0fe50b42010-11-16 03:52:51 +00003086
cristy3ed852e2009-09-05 21:47:34 +00003087 if ((image->columns % 4) != 0)
3088 {
cristybb503372010-05-27 20:51:26 +00003089 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003090 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003091 }
glennrp0fe50b42010-11-16 03:52:51 +00003092
cristy3ed852e2009-09-05 21:47:34 +00003093 break;
3094 }
glennrp47b9dd52010-11-24 18:12:06 +00003095
cristy3ed852e2009-09-05 21:47:34 +00003096 case 4:
3097 {
cristybb503372010-05-27 20:51:26 +00003098 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003099 {
glennrpa18d5bc2011-04-23 14:51:34 +00003100 *r++=(*p >> 4) & 0x0f;
3101 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003102 }
glennrp0fe50b42010-11-16 03:52:51 +00003103
cristy3ed852e2009-09-05 21:47:34 +00003104 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003105 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003106
cristy3ed852e2009-09-05 21:47:34 +00003107 break;
3108 }
glennrp47b9dd52010-11-24 18:12:06 +00003109
cristy3ed852e2009-09-05 21:47:34 +00003110 case 8:
3111 {
glennrpfaa852b2010-03-30 12:17:00 +00003112 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003113 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003114 {
glennrpa18d5bc2011-04-23 14:51:34 +00003115 *r++=*p++;
cristy4c08aed2011-07-01 19:47:50 +00003116 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3117 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003118 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003119 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003120 }
glennrp0fe50b42010-11-16 03:52:51 +00003121
cristy3ed852e2009-09-05 21:47:34 +00003122 else
cristybb503372010-05-27 20:51:26 +00003123 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003124 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003125
cristy3ed852e2009-09-05 21:47:34 +00003126 break;
3127 }
glennrp47b9dd52010-11-24 18:12:06 +00003128
cristy3ed852e2009-09-05 21:47:34 +00003129 case 16:
3130 {
cristybb503372010-05-27 20:51:26 +00003131 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003132 {
glennrpc17d96f2011-06-27 01:20:11 +00003133#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003134 size_t
3135 quantum;
3136
3137 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003138 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003139
3140 else
glennrpc17d96f2011-06-27 01:20:11 +00003141 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003142
glennrp58f77c72011-04-23 14:09:09 +00003143 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003144 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003145 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003146
3147 if (ping_color_type == 4)
3148 {
glennrpc17d96f2011-06-27 01:20:11 +00003149 if (image->colors > 256)
3150 quantum=((*p++) << 8);
3151 else
3152 quantum=0;
3153
glennrp58f77c72011-04-23 14:09:09 +00003154 quantum|=(*p++);
cristy4c08aed2011-07-01 19:47:50 +00003155 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3156 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003157 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003158 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003159 }
glennrp58f77c72011-04-23 14:09:09 +00003160
3161#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3162 *r++=(*p++);
3163 p++; /* strip low byte */
3164
3165 if (ping_color_type == 4)
3166 {
cristy4c08aed2011-07-01 19:47:50 +00003167 SetPixelAlpha(image,*p++,q);
3168 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003169 found_transparent_pixel = MagickTrue;
3170 p++;
cristyed231572011-07-14 02:18:59 +00003171 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003172 }
cristy3ed852e2009-09-05 21:47:34 +00003173#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003174 }
glennrp47b9dd52010-11-24 18:12:06 +00003175
cristy3ed852e2009-09-05 21:47:34 +00003176 break;
3177 }
glennrp47b9dd52010-11-24 18:12:06 +00003178
cristy3ed852e2009-09-05 21:47:34 +00003179 default:
3180 break;
3181 }
glennrp3faa9a32011-04-23 14:00:25 +00003182
cristy3ed852e2009-09-05 21:47:34 +00003183 /*
3184 Transfer image scanline.
3185 */
3186 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003187
cristy4c08aed2011-07-01 19:47:50 +00003188 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3189
cristyacd2ed22011-08-30 01:44:23 +00003190 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00003191 break;
cristybb503372010-05-27 20:51:26 +00003192 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00003193 {
3194 SetPixelIndex(image,*r++,q);
cristyed231572011-07-14 02:18:59 +00003195 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00003196 }
glennrp0fe50b42010-11-16 03:52:51 +00003197
cristy3ed852e2009-09-05 21:47:34 +00003198 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3199 break;
glennrp0fe50b42010-11-16 03:52:51 +00003200
cristy7a287bf2010-02-14 02:18:09 +00003201 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3202 {
cristycee97112010-05-28 00:44:52 +00003203 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003204 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003205
cristy7a287bf2010-02-14 02:18:09 +00003206 if (status == MagickFalse)
3207 break;
3208 }
cristy3ed852e2009-09-05 21:47:34 +00003209 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003210
cristy7a287bf2010-02-14 02:18:09 +00003211 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003212 {
3213 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003214
cristy3ed852e2009-09-05 21:47:34 +00003215 if (status == MagickFalse)
3216 break;
3217 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003218
cristy3ed852e2009-09-05 21:47:34 +00003219 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3220 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003221
3222 image->matte=found_transparent_pixel;
3223
3224 if (logging != MagickFalse)
3225 {
3226 if (found_transparent_pixel != MagickFalse)
3227 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3228 " Found transparent pixel");
3229 else
glennrp5aa37f62011-01-02 03:07:57 +00003230 {
3231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3232 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003233
glennrp5aa37f62011-01-02 03:07:57 +00003234 ping_color_type&=0x03;
3235 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003236 }
3237 }
3238
cristyb32b90a2009-09-07 21:45:48 +00003239 if (quantum_info != (QuantumInfo *) NULL)
3240 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00003241
cristy5c6f7892010-05-05 22:53:29 +00003242 if (image->storage_class == PseudoClass)
3243 {
cristyaeb2cbc2010-05-07 13:28:58 +00003244 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003245 matte;
3246
3247 matte=image->matte;
3248 image->matte=MagickFalse;
cristyea1a8aa2011-10-20 13:24:06 +00003249 (void) SyncImage(image,exception);
cristyaeb2cbc2010-05-07 13:28:58 +00003250 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003251 }
glennrp47b9dd52010-11-24 18:12:06 +00003252
glennrp4eb39312011-03-30 21:34:55 +00003253 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003254
3255 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003256 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003257 {
3258 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003259 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003260 image->colors=2;
cristyea1a8aa2011-10-20 13:24:06 +00003261 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003262#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003263 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003264#endif
3265 if (logging != MagickFalse)
3266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3267 " exit ReadOnePNGImage() early.");
3268 return(image);
3269 }
glennrp47b9dd52010-11-24 18:12:06 +00003270
glennrpfaa852b2010-03-30 12:17:00 +00003271 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003272 {
3273 ClassType
3274 storage_class;
3275
3276 /*
3277 Image has a transparent background.
3278 */
3279 storage_class=image->storage_class;
3280 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003281
glennrp3c218112010-11-27 15:31:26 +00003282/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003283
glennrp0fe50b42010-11-16 03:52:51 +00003284 if (storage_class == PseudoClass)
3285 {
3286 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003287 {
glennrp0fe50b42010-11-16 03:52:51 +00003288 for (x=0; x < ping_num_trans; x++)
3289 {
cristy6b7677c2012-01-01 20:59:57 +00003290 image->colormap[x].matte=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00003291 image->colormap[x].alpha =
3292 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003293 }
glennrpc11cf6a2010-03-20 16:46:19 +00003294 }
glennrp47b9dd52010-11-24 18:12:06 +00003295
glennrp0fe50b42010-11-16 03:52:51 +00003296 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3297 {
3298 for (x=0; x < (int) image->colors; x++)
3299 {
3300 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy4c08aed2011-07-01 19:47:50 +00003301 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003302 {
cristy6b7677c2012-01-01 20:59:57 +00003303 image->colormap[x].matte=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00003304 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003305 }
3306 }
3307 }
cristyea1a8aa2011-10-20 13:24:06 +00003308 (void) SyncImage(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003309 }
glennrp47b9dd52010-11-24 18:12:06 +00003310
glennrpa6a06632011-01-19 15:15:34 +00003311#if 1 /* Should have already been done above, but glennrp problem P10
3312 * needs this.
3313 */
glennrp0fe50b42010-11-16 03:52:51 +00003314 else
3315 {
3316 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003317 {
glennrp0fe50b42010-11-16 03:52:51 +00003318 image->storage_class=storage_class;
3319 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3320
cristyacd2ed22011-08-30 01:44:23 +00003321 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003322 break;
3323
glennrp0fe50b42010-11-16 03:52:51 +00003324
glennrpa6a06632011-01-19 15:15:34 +00003325 /* Caution: on a Q8 build, this does not distinguish between
3326 * 16-bit colors that differ only in the low byte
3327 */
glennrp0fe50b42010-11-16 03:52:51 +00003328 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3329 {
glennrp847370c2011-07-05 17:37:15 +00003330 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3331 transparent_color.red &&
3332 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3333 transparent_color.green &&
3334 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3335 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003336 {
cristy4c08aed2011-07-01 19:47:50 +00003337 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003338 }
glennrp0fe50b42010-11-16 03:52:51 +00003339
glennrp67b9c1a2011-04-22 18:47:36 +00003340#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003341 else
glennrp4f25bd02011-01-01 18:51:28 +00003342 {
cristy4c08aed2011-07-01 19:47:50 +00003343 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003344 }
glennrpa6a06632011-01-19 15:15:34 +00003345#endif
glennrp0fe50b42010-11-16 03:52:51 +00003346
cristyed231572011-07-14 02:18:59 +00003347 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003348 }
3349
3350 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3351 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003352 }
glennrp0fe50b42010-11-16 03:52:51 +00003353 }
glennrpa6a06632011-01-19 15:15:34 +00003354#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003355
cristy3ed852e2009-09-05 21:47:34 +00003356 image->storage_class=DirectClass;
3357 }
glennrp3c218112010-11-27 15:31:26 +00003358
cristyb40fc462010-08-08 00:49:49 +00003359 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3360 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3361 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00003362
cristyeb3b22a2011-03-31 20:16:11 +00003363 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003364 {
3365 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003366 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3367 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003368 else
glennrpa0ed0092011-04-18 16:36:29 +00003369 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3370 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003371
glennrp4eb39312011-03-30 21:34:55 +00003372 if (status != MagickFalse)
3373 for (i=0; i < (ssize_t) num_text; i++)
3374 {
3375 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003376
glennrp4eb39312011-03-30 21:34:55 +00003377 if (logging != MagickFalse)
3378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3379 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003380
glennrp4eb39312011-03-30 21:34:55 +00003381 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003382 {
cristyd15e6592011-10-15 00:13:06 +00003383 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i,
3384 exception);
glennrp4eb39312011-03-30 21:34:55 +00003385 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003386 }
glennrp0fe50b42010-11-16 03:52:51 +00003387
glennrp4eb39312011-03-30 21:34:55 +00003388 else
3389 {
3390 char
3391 *value;
3392
3393 length=text[i].text_length;
3394 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3395 sizeof(*value));
3396 if (value == (char *) NULL)
3397 {
cristyc82a27b2011-10-21 01:07:16 +00003398 (void) ThrowMagickException(exception,GetMagickModule(),
glennrp4eb39312011-03-30 21:34:55 +00003399 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3400 image->filename);
3401 break;
3402 }
3403 *value='\0';
3404 (void) ConcatenateMagickString(value,text[i].text,length+2);
3405
3406 /* Don't save "density" or "units" property if we have a pHYs
3407 * chunk
3408 */
3409 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3410 (LocaleCompare(text[i].key,"density") != 0 &&
3411 LocaleCompare(text[i].key,"units") != 0))
cristyd15e6592011-10-15 00:13:06 +00003412 (void) SetImageProperty(image,text[i].key,value,exception);
glennrp4eb39312011-03-30 21:34:55 +00003413
3414 if (logging != MagickFalse)
3415 {
3416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3417 " length: %lu",(unsigned long) length);
3418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3419 " Keyword: %s",text[i].key);
3420 }
3421
3422 value=DestroyString(value);
3423 }
3424 }
3425 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003426 }
glennrp3c218112010-11-27 15:31:26 +00003427
cristy3ed852e2009-09-05 21:47:34 +00003428#ifdef MNG_OBJECT_BUFFERS
3429 /*
3430 Store the object if necessary.
3431 */
3432 if (object_id && !mng_info->frozen[object_id])
3433 {
3434 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3435 {
3436 /*
3437 create a new object buffer.
3438 */
3439 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003440 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003441
cristy3ed852e2009-09-05 21:47:34 +00003442 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3443 {
3444 mng_info->ob[object_id]->image=(Image *) NULL;
3445 mng_info->ob[object_id]->reference_count=1;
3446 }
3447 }
glennrp47b9dd52010-11-24 18:12:06 +00003448
cristy3ed852e2009-09-05 21:47:34 +00003449 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3450 mng_info->ob[object_id]->frozen)
3451 {
3452 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
cristyc82a27b2011-10-21 01:07:16 +00003453 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003454 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3455 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003456
cristy3ed852e2009-09-05 21:47:34 +00003457 if (mng_info->ob[object_id]->frozen)
cristyc82a27b2011-10-21 01:07:16 +00003458 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003459 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3460 "`%s'",image->filename);
3461 }
glennrp0fe50b42010-11-16 03:52:51 +00003462
cristy3ed852e2009-09-05 21:47:34 +00003463 else
3464 {
cristy3ed852e2009-09-05 21:47:34 +00003465
3466 if (mng_info->ob[object_id]->image != (Image *) NULL)
3467 mng_info->ob[object_id]->image=DestroyImage
3468 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003469
cristy3ed852e2009-09-05 21:47:34 +00003470 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
cristyc82a27b2011-10-21 01:07:16 +00003471 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003472
cristy3ed852e2009-09-05 21:47:34 +00003473 if (mng_info->ob[object_id]->image != (Image *) NULL)
3474 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003475
cristy3ed852e2009-09-05 21:47:34 +00003476 else
cristyc82a27b2011-10-21 01:07:16 +00003477 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003478 ResourceLimitError,"Cloning image for object buffer failed",
3479 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003480
glennrpfaa852b2010-03-30 12:17:00 +00003481 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003482 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003483
glennrpfaa852b2010-03-30 12:17:00 +00003484 mng_info->ob[object_id]->width=ping_width;
3485 mng_info->ob[object_id]->height=ping_height;
3486 mng_info->ob[object_id]->color_type=ping_color_type;
3487 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3488 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3489 mng_info->ob[object_id]->compression_method=
3490 ping_compression_method;
3491 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003492
glennrpfaa852b2010-03-30 12:17:00 +00003493 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003494 {
3495 int
3496 number_colors;
3497
3498 png_colorp
3499 plte;
3500
3501 /*
3502 Copy the PLTE to the object buffer.
3503 */
3504 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3505 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003506
cristy3ed852e2009-09-05 21:47:34 +00003507 for (i=0; i < number_colors; i++)
3508 {
3509 mng_info->ob[object_id]->plte[i]=plte[i];
3510 }
3511 }
glennrp47b9dd52010-11-24 18:12:06 +00003512
cristy3ed852e2009-09-05 21:47:34 +00003513 else
3514 mng_info->ob[object_id]->plte_length=0;
3515 }
3516 }
3517#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003518
3519 /* Set image->matte to MagickTrue if the input colortype supports
3520 * alpha or if a valid tRNS chunk is present, no matter whether there
3521 * is actual transparency present.
3522 */
3523 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3524 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3525 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3526 MagickTrue : MagickFalse;
3527
glennrpcb395ac2011-03-30 19:50:23 +00003528 /* Set more properties for identify to retrieve */
3529 {
3530 char
3531 msg[MaxTextExtent];
3532
glennrp4eb39312011-03-30 21:34:55 +00003533 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003534 {
3535 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003536 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003537 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
cristy5d6fc9c2011-12-27 03:10:42 +00003538 (void) SetImageProperty(image,"png:text ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003539 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003540 }
3541
3542 if (num_raw_profiles != 0)
3543 {
cristy3b6fd2e2011-05-20 12:53:50 +00003544 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003545 "%d were found", num_raw_profiles);
cristy5d6fc9c2011-12-27 03:10:42 +00003546 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003547 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003548 }
3549
glennrpcb395ac2011-03-30 19:50:23 +00003550 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003551 {
cristy3b6fd2e2011-05-20 12:53:50 +00003552 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003553 "chunk was found (see Chromaticity, above)");
cristy5d6fc9c2011-12-27 03:10:42 +00003554 (void) SetImageProperty(image,"png:cHRM ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003555 exception);
glennrp59612252011-03-30 21:45:21 +00003556 }
glennrpcb395ac2011-03-30 19:50:23 +00003557
3558 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003559 {
cristy3b6fd2e2011-05-20 12:53:50 +00003560 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003561 "chunk was found (see Background color, above)");
cristy5d6fc9c2011-12-27 03:10:42 +00003562 (void) SetImageProperty(image,"png:bKGD ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003563 exception);
glennrp59612252011-03-30 21:45:21 +00003564 }
3565
cristy3b6fd2e2011-05-20 12:53:50 +00003566 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003567 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003568
3569 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy5d6fc9c2011-12-27 03:10:42 +00003570 (void) SetImageProperty(image,"png:iCCP ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003571 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003572
glennrpcb395ac2011-03-30 19:50:23 +00003573 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy5d6fc9c2011-12-27 03:10:42 +00003574 (void) SetImageProperty(image,"png:tRNS ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003575 exception);
glennrp4eb39312011-03-30 21:34:55 +00003576
3577#if defined(PNG_sRGB_SUPPORTED)
3578 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3579 {
cristy3b6fd2e2011-05-20 12:53:50 +00003580 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003581 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003582 (int) intent);
cristy5d6fc9c2011-12-27 03:10:42 +00003583 (void) SetImageProperty(image,"png:sRGB ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003584 exception);
glennrp4eb39312011-03-30 21:34:55 +00003585 }
3586#endif
3587
3588 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3589 {
cristy3b6fd2e2011-05-20 12:53:50 +00003590 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003591 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003592 file_gamma);
cristy5d6fc9c2011-12-27 03:10:42 +00003593 (void) SetImageProperty(image,"png:gAMA ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003594 exception);
glennrp4eb39312011-03-30 21:34:55 +00003595 }
3596
3597#if defined(PNG_pHYs_SUPPORTED)
3598 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3599 {
cristy3b6fd2e2011-05-20 12:53:50 +00003600 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003601 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003602 (double) x_resolution,(double) y_resolution, unit_type);
cristy5d6fc9c2011-12-27 03:10:42 +00003603 (void) SetImageProperty(image,"png:pHYs ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003604 exception);
glennrp4eb39312011-03-30 21:34:55 +00003605 }
3606#endif
3607
3608#if defined(PNG_oFFs_SUPPORTED)
3609 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3610 {
cristy3b6fd2e2011-05-20 12:53:50 +00003611 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003612 (double) image->page.x,(double) image->page.y);
cristy5d6fc9c2011-12-27 03:10:42 +00003613 (void) SetImageProperty(image,"png:oFFs ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003614 exception);
glennrp4eb39312011-03-30 21:34:55 +00003615 }
3616#endif
3617
glennrp07523c72011-03-31 18:12:10 +00003618 if ((image->page.width != 0 && image->page.width != image->columns) ||
3619 (image->page.height != 0 && image->page.height != image->rows))
3620 {
cristy3b6fd2e2011-05-20 12:53:50 +00003621 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003622 "width=%.20g, height=%.20g",
3623 (double) image->page.width,(double) image->page.height);
cristy5d6fc9c2011-12-27 03:10:42 +00003624 (void) SetImageProperty(image,"png:vpAg ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003625 exception);
glennrp07523c72011-03-31 18:12:10 +00003626 }
glennrpcb395ac2011-03-30 19:50:23 +00003627 }
3628
cristy3ed852e2009-09-05 21:47:34 +00003629 /*
3630 Relinquish resources.
3631 */
3632 png_destroy_read_struct(&ping,&ping_info,&end_info);
3633
glennrpcf002022011-01-30 02:38:15 +00003634 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003635#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003636 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003637#endif
3638
3639 if (logging != MagickFalse)
3640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3641 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003642
cristy3ed852e2009-09-05 21:47:34 +00003643 return(image);
3644
3645/* end of reading one PNG image */
3646}
3647
3648static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3649{
3650 Image
3651 *image,
3652 *previous;
3653
3654 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003655 have_mng_structure,
3656 logging,
cristy3ed852e2009-09-05 21:47:34 +00003657 status;
3658
3659 MngInfo
3660 *mng_info;
3661
3662 char
3663 magic_number[MaxTextExtent];
3664
cristy3ed852e2009-09-05 21:47:34 +00003665 ssize_t
3666 count;
3667
3668 /*
3669 Open image file.
3670 */
3671 assert(image_info != (const ImageInfo *) NULL);
3672 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003673
cristy3ed852e2009-09-05 21:47:34 +00003674 if (image_info->debug != MagickFalse)
3675 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3676 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003677
cristy3ed852e2009-09-05 21:47:34 +00003678 assert(exception != (ExceptionInfo *) NULL);
3679 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003680 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy9950d572011-10-01 18:22:35 +00003681 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003682 mng_info=(MngInfo *) NULL;
3683 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003684
cristy3ed852e2009-09-05 21:47:34 +00003685 if (status == MagickFalse)
3686 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003687
cristy3ed852e2009-09-05 21:47:34 +00003688 /*
3689 Verify PNG signature.
3690 */
3691 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003692
glennrpdde35db2011-02-21 12:06:32 +00003693 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003694 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003695
cristy3ed852e2009-09-05 21:47:34 +00003696 /*
3697 Allocate a MngInfo structure.
3698 */
3699 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003700 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003701
cristy3ed852e2009-09-05 21:47:34 +00003702 if (mng_info == (MngInfo *) NULL)
3703 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003704
cristy3ed852e2009-09-05 21:47:34 +00003705 /*
3706 Initialize members of the MngInfo structure.
3707 */
3708 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3709 mng_info->image=image;
3710 have_mng_structure=MagickTrue;
3711
3712 previous=image;
3713 image=ReadOnePNGImage(mng_info,image_info,exception);
3714 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003715
cristy3ed852e2009-09-05 21:47:34 +00003716 if (image == (Image *) NULL)
3717 {
3718 if (previous != (Image *) NULL)
3719 {
3720 if (previous->signature != MagickSignature)
3721 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003722
cristy3ed852e2009-09-05 21:47:34 +00003723 (void) CloseBlob(previous);
3724 (void) DestroyImageList(previous);
3725 }
glennrp0fe50b42010-11-16 03:52:51 +00003726
cristy3ed852e2009-09-05 21:47:34 +00003727 if (logging != MagickFalse)
3728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3729 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003730
cristy3ed852e2009-09-05 21:47:34 +00003731 return((Image *) NULL);
3732 }
glennrp47b9dd52010-11-24 18:12:06 +00003733
cristy3ed852e2009-09-05 21:47:34 +00003734 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003735
cristy3ed852e2009-09-05 21:47:34 +00003736 if ((image->columns == 0) || (image->rows == 0))
3737 {
3738 if (logging != MagickFalse)
3739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3740 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003741
cristy3ed852e2009-09-05 21:47:34 +00003742 ThrowReaderException(CorruptImageError,"CorruptImage");
3743 }
glennrp47b9dd52010-11-24 18:12:06 +00003744
cristy3ed852e2009-09-05 21:47:34 +00003745 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3746 {
cristy018f07f2011-09-04 21:15:19 +00003747 (void) SetImageType(image,TrueColorType,exception);
cristy3ed852e2009-09-05 21:47:34 +00003748 image->matte=MagickFalse;
3749 }
glennrp0fe50b42010-11-16 03:52:51 +00003750
cristy3ed852e2009-09-05 21:47:34 +00003751 if (LocaleCompare(image_info->magick,"PNG32") == 0)
cristy018f07f2011-09-04 21:15:19 +00003752 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003753
cristy3ed852e2009-09-05 21:47:34 +00003754 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3756 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3757 (double) image->page.width,(double) image->page.height,
3758 (double) image->page.x,(double) image->page.y);
3759
3760 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003762
cristy3ed852e2009-09-05 21:47:34 +00003763 return(image);
3764}
3765
3766
3767
3768#if defined(JNG_SUPPORTED)
3769/*
3770%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3771% %
3772% %
3773% %
3774% R e a d O n e J N G I m a g e %
3775% %
3776% %
3777% %
3778%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3779%
3780% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3781% (minus the 8-byte signature) and returns it. It allocates the memory
3782% necessary for the new Image structure and returns a pointer to the new
3783% image.
3784%
3785% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3786%
3787% The format of the ReadOneJNGImage method is:
3788%
3789% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3790% ExceptionInfo *exception)
3791%
3792% A description of each parameter follows:
3793%
3794% o mng_info: Specifies a pointer to a MngInfo structure.
3795%
3796% o image_info: the image info.
3797%
3798% o exception: return any errors or warnings in this structure.
3799%
3800*/
3801static Image *ReadOneJNGImage(MngInfo *mng_info,
3802 const ImageInfo *image_info, ExceptionInfo *exception)
3803{
3804 Image
3805 *alpha_image,
3806 *color_image,
3807 *image,
3808 *jng_image;
3809
3810 ImageInfo
3811 *alpha_image_info,
3812 *color_image_info;
3813
cristy4383ec82011-01-05 15:42:32 +00003814 MagickBooleanType
3815 logging;
3816
cristybb503372010-05-27 20:51:26 +00003817 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003818 y;
3819
3820 MagickBooleanType
3821 status;
3822
3823 png_uint_32
3824 jng_height,
3825 jng_width;
3826
3827 png_byte
3828 jng_color_type,
3829 jng_image_sample_depth,
3830 jng_image_compression_method,
3831 jng_image_interlace_method,
3832 jng_alpha_sample_depth,
3833 jng_alpha_compression_method,
3834 jng_alpha_filter_method,
3835 jng_alpha_interlace_method;
3836
cristy4c08aed2011-07-01 19:47:50 +00003837 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003838 *s;
3839
cristybb503372010-05-27 20:51:26 +00003840 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003841 i,
3842 x;
3843
cristy4c08aed2011-07-01 19:47:50 +00003844 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003845 *q;
3846
3847 register unsigned char
3848 *p;
3849
3850 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003851 read_JSEP,
3852 reading_idat,
3853 skip_to_iend;
3854
cristybb503372010-05-27 20:51:26 +00003855 size_t
cristy3ed852e2009-09-05 21:47:34 +00003856 length;
3857
3858 jng_alpha_compression_method=0;
3859 jng_alpha_sample_depth=8;
3860 jng_color_type=0;
3861 jng_height=0;
3862 jng_width=0;
3863 alpha_image=(Image *) NULL;
3864 color_image=(Image *) NULL;
3865 alpha_image_info=(ImageInfo *) NULL;
3866 color_image_info=(ImageInfo *) NULL;
3867
3868 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003869 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003870
3871 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003872
cristy4c08aed2011-07-01 19:47:50 +00003873 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003874 {
3875 /*
3876 Allocate next image structure.
3877 */
3878 if (logging != MagickFalse)
3879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3880 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003881
cristy9950d572011-10-01 18:22:35 +00003882 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003883
cristy3ed852e2009-09-05 21:47:34 +00003884 if (GetNextImageInList(image) == (Image *) NULL)
3885 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003886
cristy3ed852e2009-09-05 21:47:34 +00003887 image=SyncNextImageInList(image);
3888 }
3889 mng_info->image=image;
3890
3891 /*
3892 Signature bytes have already been read.
3893 */
3894
3895 read_JSEP=MagickFalse;
3896 reading_idat=MagickFalse;
3897 skip_to_iend=MagickFalse;
3898 for (;;)
3899 {
3900 char
3901 type[MaxTextExtent];
3902
3903 unsigned char
3904 *chunk;
3905
3906 unsigned int
3907 count;
3908
3909 /*
3910 Read a new JNG chunk.
3911 */
3912 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3913 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003914
cristy3ed852e2009-09-05 21:47:34 +00003915 if (status == MagickFalse)
3916 break;
glennrp0fe50b42010-11-16 03:52:51 +00003917
cristy3ed852e2009-09-05 21:47:34 +00003918 type[0]='\0';
3919 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3920 length=ReadBlobMSBLong(image);
3921 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3922
3923 if (logging != MagickFalse)
3924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003925 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3926 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003927
3928 if (length > PNG_UINT_31_MAX || count == 0)
3929 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003930
cristy3ed852e2009-09-05 21:47:34 +00003931 p=NULL;
3932 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003933
cristy3ed852e2009-09-05 21:47:34 +00003934 if (length)
3935 {
3936 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003937
cristy3ed852e2009-09-05 21:47:34 +00003938 if (chunk == (unsigned char *) NULL)
3939 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003940
cristybb503372010-05-27 20:51:26 +00003941 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003942 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003943
cristy3ed852e2009-09-05 21:47:34 +00003944 p=chunk;
3945 }
glennrp47b9dd52010-11-24 18:12:06 +00003946
cristy3ed852e2009-09-05 21:47:34 +00003947 (void) ReadBlobMSBLong(image); /* read crc word */
3948
3949 if (skip_to_iend)
3950 {
3951 if (length)
3952 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003953
cristy3ed852e2009-09-05 21:47:34 +00003954 continue;
3955 }
3956
3957 if (memcmp(type,mng_JHDR,4) == 0)
3958 {
3959 if (length == 16)
3960 {
cristybb503372010-05-27 20:51:26 +00003961 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003962 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003963 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003964 (p[6] << 8) | p[7]);
3965 jng_color_type=p[8];
3966 jng_image_sample_depth=p[9];
3967 jng_image_compression_method=p[10];
3968 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003969
cristy3ed852e2009-09-05 21:47:34 +00003970 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3971 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003972
cristy3ed852e2009-09-05 21:47:34 +00003973 jng_alpha_sample_depth=p[12];
3974 jng_alpha_compression_method=p[13];
3975 jng_alpha_filter_method=p[14];
3976 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003977
cristy3ed852e2009-09-05 21:47:34 +00003978 if (logging != MagickFalse)
3979 {
3980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003981 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003982
cristy3ed852e2009-09-05 21:47:34 +00003983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003984 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003985
cristy3ed852e2009-09-05 21:47:34 +00003986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3987 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003988
cristy3ed852e2009-09-05 21:47:34 +00003989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3990 " jng_image_sample_depth: %3d",
3991 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003992
cristy3ed852e2009-09-05 21:47:34 +00003993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3994 " jng_image_compression_method:%3d",
3995 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003996
cristy3ed852e2009-09-05 21:47:34 +00003997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3998 " jng_image_interlace_method: %3d",
3999 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00004000
cristy3ed852e2009-09-05 21:47:34 +00004001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4002 " jng_alpha_sample_depth: %3d",
4003 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00004004
cristy3ed852e2009-09-05 21:47:34 +00004005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4006 " jng_alpha_compression_method:%3d",
4007 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004008
cristy3ed852e2009-09-05 21:47:34 +00004009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4010 " jng_alpha_filter_method: %3d",
4011 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00004012
cristy3ed852e2009-09-05 21:47:34 +00004013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4014 " jng_alpha_interlace_method: %3d",
4015 jng_alpha_interlace_method);
4016 }
4017 }
glennrp47b9dd52010-11-24 18:12:06 +00004018
cristy3ed852e2009-09-05 21:47:34 +00004019 if (length)
4020 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004021
cristy3ed852e2009-09-05 21:47:34 +00004022 continue;
4023 }
4024
4025
4026 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4027 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4028 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4029 {
4030 /*
4031 o create color_image
4032 o open color_blob, attached to color_image
4033 o if (color type has alpha)
4034 open alpha_blob, attached to alpha_image
4035 */
4036
cristy73bd4a52010-10-05 11:24:23 +00004037 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004038
cristy3ed852e2009-09-05 21:47:34 +00004039 if (color_image_info == (ImageInfo *) NULL)
4040 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004041
cristy3ed852e2009-09-05 21:47:34 +00004042 GetImageInfo(color_image_info);
cristy9950d572011-10-01 18:22:35 +00004043 color_image=AcquireImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004044
cristy3ed852e2009-09-05 21:47:34 +00004045 if (color_image == (Image *) NULL)
4046 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4047
4048 if (logging != MagickFalse)
4049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4050 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004051
cristy3ed852e2009-09-05 21:47:34 +00004052 (void) AcquireUniqueFilename(color_image->filename);
4053 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4054 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004055
cristy3ed852e2009-09-05 21:47:34 +00004056 if (status == MagickFalse)
4057 return((Image *) NULL);
4058
4059 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4060 {
4061 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004062 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004063
cristy3ed852e2009-09-05 21:47:34 +00004064 if (alpha_image_info == (ImageInfo *) NULL)
4065 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004066
cristy3ed852e2009-09-05 21:47:34 +00004067 GetImageInfo(alpha_image_info);
cristy9950d572011-10-01 18:22:35 +00004068 alpha_image=AcquireImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004069
cristy3ed852e2009-09-05 21:47:34 +00004070 if (alpha_image == (Image *) NULL)
4071 {
4072 alpha_image=DestroyImage(alpha_image);
4073 ThrowReaderException(ResourceLimitError,
4074 "MemoryAllocationFailed");
4075 }
glennrp0fe50b42010-11-16 03:52:51 +00004076
cristy3ed852e2009-09-05 21:47:34 +00004077 if (logging != MagickFalse)
4078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4079 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004080
cristy3ed852e2009-09-05 21:47:34 +00004081 (void) AcquireUniqueFilename(alpha_image->filename);
4082 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4083 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004084
cristy3ed852e2009-09-05 21:47:34 +00004085 if (status == MagickFalse)
4086 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004087
cristy3ed852e2009-09-05 21:47:34 +00004088 if (jng_alpha_compression_method == 0)
4089 {
4090 unsigned char
4091 data[18];
4092
4093 if (logging != MagickFalse)
4094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4095 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004096
cristy3ed852e2009-09-05 21:47:34 +00004097 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4098 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004099
cristy3ed852e2009-09-05 21:47:34 +00004100 (void) WriteBlobMSBULong(alpha_image,13L);
4101 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004102 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004103 PNGLong(data+4,jng_width);
4104 PNGLong(data+8,jng_height);
4105 data[12]=jng_alpha_sample_depth;
4106 data[13]=0; /* color_type gray */
4107 data[14]=0; /* compression method 0 */
4108 data[15]=0; /* filter_method 0 */
4109 data[16]=0; /* interlace_method 0 */
4110 (void) WriteBlob(alpha_image,17,data);
4111 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4112 }
4113 }
4114 reading_idat=MagickTrue;
4115 }
4116
4117 if (memcmp(type,mng_JDAT,4) == 0)
4118 {
glennrp47b9dd52010-11-24 18:12:06 +00004119 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004120
4121 if (logging != MagickFalse)
4122 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4123 " Copying JDAT chunk data to color_blob.");
4124
4125 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004126
cristy3ed852e2009-09-05 21:47:34 +00004127 if (length)
4128 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004129
cristy3ed852e2009-09-05 21:47:34 +00004130 continue;
4131 }
4132
4133 if (memcmp(type,mng_IDAT,4) == 0)
4134 {
4135 png_byte
4136 data[5];
4137
glennrp47b9dd52010-11-24 18:12:06 +00004138 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004139
4140 if (image_info->ping == MagickFalse)
4141 {
4142 if (logging != MagickFalse)
4143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4144 " Copying IDAT chunk data to alpha_blob.");
4145
cristybb503372010-05-27 20:51:26 +00004146 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004147 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004148 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004149 (void) WriteBlob(alpha_image,4,data);
4150 (void) WriteBlob(alpha_image,length,chunk);
4151 (void) WriteBlobMSBULong(alpha_image,
4152 crc32(crc32(0,data,4),chunk,(uInt) length));
4153 }
glennrp0fe50b42010-11-16 03:52:51 +00004154
cristy3ed852e2009-09-05 21:47:34 +00004155 if (length)
4156 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004157
cristy3ed852e2009-09-05 21:47:34 +00004158 continue;
4159 }
4160
4161 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4162 {
glennrp47b9dd52010-11-24 18:12:06 +00004163 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004164
4165 if (image_info->ping == MagickFalse)
4166 {
4167 if (logging != MagickFalse)
4168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4169 " Copying JDAA chunk data to alpha_blob.");
4170
4171 (void) WriteBlob(alpha_image,length,chunk);
4172 }
glennrp0fe50b42010-11-16 03:52:51 +00004173
cristy3ed852e2009-09-05 21:47:34 +00004174 if (length)
4175 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004176
cristy3ed852e2009-09-05 21:47:34 +00004177 continue;
4178 }
4179
4180 if (memcmp(type,mng_JSEP,4) == 0)
4181 {
4182 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004183
cristy3ed852e2009-09-05 21:47:34 +00004184 if (length)
4185 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004186
cristy3ed852e2009-09-05 21:47:34 +00004187 continue;
4188 }
4189
4190 if (memcmp(type,mng_bKGD,4) == 0)
4191 {
4192 if (length == 2)
4193 {
4194 image->background_color.red=ScaleCharToQuantum(p[1]);
4195 image->background_color.green=image->background_color.red;
4196 image->background_color.blue=image->background_color.red;
4197 }
glennrp0fe50b42010-11-16 03:52:51 +00004198
cristy3ed852e2009-09-05 21:47:34 +00004199 if (length == 6)
4200 {
4201 image->background_color.red=ScaleCharToQuantum(p[1]);
4202 image->background_color.green=ScaleCharToQuantum(p[3]);
4203 image->background_color.blue=ScaleCharToQuantum(p[5]);
4204 }
glennrp0fe50b42010-11-16 03:52:51 +00004205
cristy3ed852e2009-09-05 21:47:34 +00004206 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4207 continue;
4208 }
4209
4210 if (memcmp(type,mng_gAMA,4) == 0)
4211 {
4212 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004213 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004214
cristy3ed852e2009-09-05 21:47:34 +00004215 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4216 continue;
4217 }
4218
4219 if (memcmp(type,mng_cHRM,4) == 0)
4220 {
4221 if (length == 32)
4222 {
cristy8182b072010-05-30 20:10:53 +00004223 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4224 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4225 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4226 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4227 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4228 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4229 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4230 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004231 }
glennrp47b9dd52010-11-24 18:12:06 +00004232
cristy3ed852e2009-09-05 21:47:34 +00004233 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4234 continue;
4235 }
4236
4237 if (memcmp(type,mng_sRGB,4) == 0)
4238 {
4239 if (length == 1)
4240 {
glennrpe610a072010-08-05 17:08:46 +00004241 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004242 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004243 image->gamma=0.45455f;
4244 image->chromaticity.red_primary.x=0.6400f;
4245 image->chromaticity.red_primary.y=0.3300f;
4246 image->chromaticity.green_primary.x=0.3000f;
4247 image->chromaticity.green_primary.y=0.6000f;
4248 image->chromaticity.blue_primary.x=0.1500f;
4249 image->chromaticity.blue_primary.y=0.0600f;
4250 image->chromaticity.white_point.x=0.3127f;
4251 image->chromaticity.white_point.y=0.3290f;
4252 }
glennrp47b9dd52010-11-24 18:12:06 +00004253
cristy3ed852e2009-09-05 21:47:34 +00004254 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4255 continue;
4256 }
4257
4258 if (memcmp(type,mng_oFFs,4) == 0)
4259 {
4260 if (length > 8)
4261 {
glennrp5eae7602011-02-22 15:21:32 +00004262 image->page.x=(ssize_t) mng_get_long(p);
4263 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004264
cristy3ed852e2009-09-05 21:47:34 +00004265 if ((int) p[8] != 0)
4266 {
4267 image->page.x/=10000;
4268 image->page.y/=10000;
4269 }
4270 }
glennrp47b9dd52010-11-24 18:12:06 +00004271
cristy3ed852e2009-09-05 21:47:34 +00004272 if (length)
4273 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004274
cristy3ed852e2009-09-05 21:47:34 +00004275 continue;
4276 }
4277
4278 if (memcmp(type,mng_pHYs,4) == 0)
4279 {
4280 if (length > 8)
4281 {
cristy2a11bef2011-10-28 18:33:11 +00004282 image->resolution.x=(double) mng_get_long(p);
4283 image->resolution.y=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004284 if ((int) p[8] == PNG_RESOLUTION_METER)
4285 {
4286 image->units=PixelsPerCentimeterResolution;
cristy2a11bef2011-10-28 18:33:11 +00004287 image->resolution.x=image->resolution.x/100.0f;
4288 image->resolution.y=image->resolution.y/100.0f;
cristy3ed852e2009-09-05 21:47:34 +00004289 }
4290 }
glennrp0fe50b42010-11-16 03:52:51 +00004291
cristy3ed852e2009-09-05 21:47:34 +00004292 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4293 continue;
4294 }
4295
4296#if 0
4297 if (memcmp(type,mng_iCCP,4) == 0)
4298 {
glennrpfd05d622011-02-25 04:10:33 +00004299 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004300 if (length)
4301 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004302
cristy3ed852e2009-09-05 21:47:34 +00004303 continue;
4304 }
4305#endif
4306
4307 if (length)
4308 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4309
4310 if (memcmp(type,mng_IEND,4))
4311 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004312
cristy3ed852e2009-09-05 21:47:34 +00004313 break;
4314 }
4315
4316
4317 /* IEND found */
4318
4319 /*
4320 Finish up reading image data:
4321
4322 o read main image from color_blob.
4323
4324 o close color_blob.
4325
4326 o if (color_type has alpha)
4327 if alpha_encoding is PNG
4328 read secondary image from alpha_blob via ReadPNG
4329 if alpha_encoding is JPEG
4330 read secondary image from alpha_blob via ReadJPEG
4331
4332 o close alpha_blob.
4333
4334 o copy intensity of secondary image into
cristy4c08aed2011-07-01 19:47:50 +00004335 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004336
4337 o destroy the secondary image.
4338 */
4339
4340 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004341
cristy3ed852e2009-09-05 21:47:34 +00004342 if (logging != MagickFalse)
4343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4344 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004345
cristy3b6fd2e2011-05-20 12:53:50 +00004346 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004347 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004348
cristy3ed852e2009-09-05 21:47:34 +00004349 color_image_info->ping=MagickFalse; /* To do: avoid this */
4350 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004351
cristy3ed852e2009-09-05 21:47:34 +00004352 if (jng_image == (Image *) NULL)
4353 return((Image *) NULL);
4354
4355 (void) RelinquishUniqueFileResource(color_image->filename);
4356 color_image=DestroyImage(color_image);
4357 color_image_info=DestroyImageInfo(color_image_info);
4358
4359 if (jng_image == (Image *) NULL)
4360 return((Image *) NULL);
4361
4362 if (logging != MagickFalse)
4363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4364 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004365
cristy3ed852e2009-09-05 21:47:34 +00004366 image->rows=jng_height;
4367 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004368
cristybb503372010-05-27 20:51:26 +00004369 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004370 {
cristyc82a27b2011-10-21 01:07:16 +00004371 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00004372 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004373 for (x=(ssize_t) image->columns; x != 0; x--)
4374 {
4375 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4376 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4377 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004378 q+=GetPixelChannels(image);
4379 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004380 }
glennrp47b9dd52010-11-24 18:12:06 +00004381
cristy3ed852e2009-09-05 21:47:34 +00004382 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4383 break;
4384 }
glennrp0fe50b42010-11-16 03:52:51 +00004385
cristy3ed852e2009-09-05 21:47:34 +00004386 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004387
cristy3ed852e2009-09-05 21:47:34 +00004388 if (image_info->ping == MagickFalse)
4389 {
4390 if (jng_color_type >= 12)
4391 {
4392 if (jng_alpha_compression_method == 0)
4393 {
4394 png_byte
4395 data[5];
4396 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4397 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004398 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004399 (void) WriteBlob(alpha_image,4,data);
4400 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4401 }
glennrp0fe50b42010-11-16 03:52:51 +00004402
cristy3ed852e2009-09-05 21:47:34 +00004403 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004404
cristy3ed852e2009-09-05 21:47:34 +00004405 if (logging != MagickFalse)
4406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00004407 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004408
cristy3b6fd2e2011-05-20 12:53:50 +00004409 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004410 "%s",alpha_image->filename);
4411
4412 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004413
cristy3ed852e2009-09-05 21:47:34 +00004414 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004415 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004416 {
4417 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristyc82a27b2011-10-21 01:07:16 +00004418 exception);
cristy3ed852e2009-09-05 21:47:34 +00004419 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004420
cristy3ed852e2009-09-05 21:47:34 +00004421 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00004422 for (x=(ssize_t) image->columns; x != 0; x--)
4423 {
4424 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004425 q+=GetPixelChannels(image);
4426 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004427 }
glennrp0fe50b42010-11-16 03:52:51 +00004428
cristy3ed852e2009-09-05 21:47:34 +00004429 else
cristy4c08aed2011-07-01 19:47:50 +00004430 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004431 {
cristy4c08aed2011-07-01 19:47:50 +00004432 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4433 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004434 image->matte=MagickTrue;
cristyed231572011-07-14 02:18:59 +00004435 q+=GetPixelChannels(image);
4436 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004437 }
glennrp0fe50b42010-11-16 03:52:51 +00004438
cristy3ed852e2009-09-05 21:47:34 +00004439 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4440 break;
4441 }
4442 (void) RelinquishUniqueFileResource(alpha_image->filename);
4443 alpha_image=DestroyImage(alpha_image);
4444 alpha_image_info=DestroyImageInfo(alpha_image_info);
4445 if (jng_image != (Image *) NULL)
4446 jng_image=DestroyImage(jng_image);
4447 }
4448 }
4449
glennrp47b9dd52010-11-24 18:12:06 +00004450 /* Read the JNG image. */
4451
cristy3ed852e2009-09-05 21:47:34 +00004452 if (mng_info->mng_type == 0)
4453 {
4454 mng_info->mng_width=jng_width;
4455 mng_info->mng_height=jng_height;
4456 }
glennrp0fe50b42010-11-16 03:52:51 +00004457
cristy3ed852e2009-09-05 21:47:34 +00004458 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004459 {
4460 image->page.width=jng_width;
4461 image->page.height=jng_height;
4462 }
4463
cristy3ed852e2009-09-05 21:47:34 +00004464 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004465 {
4466 image->page.x=mng_info->x_off[mng_info->object_id];
4467 image->page.y=mng_info->y_off[mng_info->object_id];
4468 }
4469
cristy3ed852e2009-09-05 21:47:34 +00004470 else
glennrp0fe50b42010-11-16 03:52:51 +00004471 {
4472 image->page.y=mng_info->y_off[mng_info->object_id];
4473 }
4474
cristy3ed852e2009-09-05 21:47:34 +00004475 mng_info->image_found++;
4476 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4477 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004478
cristy3ed852e2009-09-05 21:47:34 +00004479 if (logging != MagickFalse)
4480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4481 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004482
cristy3ed852e2009-09-05 21:47:34 +00004483 return(image);
4484}
4485
4486/*
4487%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4488% %
4489% %
4490% %
4491% R e a d J N G I m a g e %
4492% %
4493% %
4494% %
4495%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4496%
4497% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4498% (including the 8-byte signature) and returns it. It allocates the memory
4499% necessary for the new Image structure and returns a pointer to the new
4500% image.
4501%
4502% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4503%
4504% The format of the ReadJNGImage method is:
4505%
4506% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4507% *exception)
4508%
4509% A description of each parameter follows:
4510%
4511% o image_info: the image info.
4512%
4513% o exception: return any errors or warnings in this structure.
4514%
4515*/
4516
4517static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4518{
4519 Image
4520 *image,
4521 *previous;
4522
4523 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004524 have_mng_structure,
4525 logging,
cristy3ed852e2009-09-05 21:47:34 +00004526 status;
4527
4528 MngInfo
4529 *mng_info;
4530
4531 char
4532 magic_number[MaxTextExtent];
4533
cristy3ed852e2009-09-05 21:47:34 +00004534 size_t
4535 count;
4536
4537 /*
4538 Open image file.
4539 */
4540 assert(image_info != (const ImageInfo *) NULL);
4541 assert(image_info->signature == MagickSignature);
4542 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4543 assert(exception != (ExceptionInfo *) NULL);
4544 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004545 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy9950d572011-10-01 18:22:35 +00004546 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004547 mng_info=(MngInfo *) NULL;
4548 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004549
cristy3ed852e2009-09-05 21:47:34 +00004550 if (status == MagickFalse)
4551 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004552
cristy3ed852e2009-09-05 21:47:34 +00004553 if (LocaleCompare(image_info->magick,"JNG") != 0)
4554 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004555
glennrp47b9dd52010-11-24 18:12:06 +00004556 /* Verify JNG signature. */
4557
cristy3ed852e2009-09-05 21:47:34 +00004558 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004559
glennrp3b8763e2011-02-21 12:08:18 +00004560 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004561 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004562
glennrp47b9dd52010-11-24 18:12:06 +00004563 /* Allocate a MngInfo structure. */
4564
cristy3ed852e2009-09-05 21:47:34 +00004565 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004566 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004567
cristy3ed852e2009-09-05 21:47:34 +00004568 if (mng_info == (MngInfo *) NULL)
4569 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004570
glennrp47b9dd52010-11-24 18:12:06 +00004571 /* Initialize members of the MngInfo structure. */
4572
cristy3ed852e2009-09-05 21:47:34 +00004573 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4574 have_mng_structure=MagickTrue;
4575
4576 mng_info->image=image;
4577 previous=image;
4578 image=ReadOneJNGImage(mng_info,image_info,exception);
4579 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004580
cristy3ed852e2009-09-05 21:47:34 +00004581 if (image == (Image *) NULL)
4582 {
4583 if (IsImageObject(previous) != MagickFalse)
4584 {
4585 (void) CloseBlob(previous);
4586 (void) DestroyImageList(previous);
4587 }
glennrp0fe50b42010-11-16 03:52:51 +00004588
cristy3ed852e2009-09-05 21:47:34 +00004589 if (logging != MagickFalse)
4590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4591 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004592
cristy3ed852e2009-09-05 21:47:34 +00004593 return((Image *) NULL);
4594 }
4595 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004596
cristy3ed852e2009-09-05 21:47:34 +00004597 if (image->columns == 0 || image->rows == 0)
4598 {
4599 if (logging != MagickFalse)
4600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4601 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004602
cristy3ed852e2009-09-05 21:47:34 +00004603 ThrowReaderException(CorruptImageError,"CorruptImage");
4604 }
glennrp0fe50b42010-11-16 03:52:51 +00004605
cristy3ed852e2009-09-05 21:47:34 +00004606 if (logging != MagickFalse)
4607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004608
cristy3ed852e2009-09-05 21:47:34 +00004609 return(image);
4610}
4611#endif
4612
4613static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4614{
4615 char
4616 page_geometry[MaxTextExtent];
4617
4618 Image
4619 *image,
4620 *previous;
4621
cristy4383ec82011-01-05 15:42:32 +00004622 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004623 logging,
4624 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004625
cristy3ed852e2009-09-05 21:47:34 +00004626 volatile int
4627 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004628 object_id,
4629 term_chunk_found,
4630 skip_to_iend;
4631
cristybb503372010-05-27 20:51:26 +00004632 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004633 image_count=0;
4634
4635 MagickBooleanType
4636 status;
4637
4638 MagickOffsetType
4639 offset;
4640
4641 MngInfo
4642 *mng_info;
4643
4644 MngBox
4645 default_fb,
4646 fb,
4647 previous_fb;
4648
4649#if defined(MNG_INSERT_LAYERS)
cristy101ab702011-10-13 13:06:32 +00004650 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004651 mng_background_color;
4652#endif
4653
4654 register unsigned char
4655 *p;
4656
cristybb503372010-05-27 20:51:26 +00004657 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004658 i;
4659
4660 size_t
4661 count;
4662
cristybb503372010-05-27 20:51:26 +00004663 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004664 loop_level;
4665
4666 volatile short
4667 skipping_loop;
4668
4669#if defined(MNG_INSERT_LAYERS)
4670 unsigned int
4671 mandatory_back=0;
4672#endif
4673
4674 volatile unsigned int
4675#ifdef MNG_OBJECT_BUFFERS
4676 mng_background_object=0,
4677#endif
4678 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4679
cristybb503372010-05-27 20:51:26 +00004680 size_t
cristy3ed852e2009-09-05 21:47:34 +00004681 default_frame_timeout,
4682 frame_timeout,
4683#if defined(MNG_INSERT_LAYERS)
4684 image_height,
4685 image_width,
4686#endif
4687 length;
4688
glennrp38ea0832010-06-02 18:50:28 +00004689 /* These delays are all measured in image ticks_per_second,
4690 * not in MNG ticks_per_second
4691 */
cristybb503372010-05-27 20:51:26 +00004692 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004693 default_frame_delay,
4694 final_delay,
4695 final_image_delay,
4696 frame_delay,
4697#if defined(MNG_INSERT_LAYERS)
4698 insert_layers,
4699#endif
4700 mng_iterations=1,
4701 simplicity=0,
4702 subframe_height=0,
4703 subframe_width=0;
4704
4705 previous_fb.top=0;
4706 previous_fb.bottom=0;
4707 previous_fb.left=0;
4708 previous_fb.right=0;
4709 default_fb.top=0;
4710 default_fb.bottom=0;
4711 default_fb.left=0;
4712 default_fb.right=0;
4713
glennrp47b9dd52010-11-24 18:12:06 +00004714 /* Open image file. */
4715
cristy3ed852e2009-09-05 21:47:34 +00004716 assert(image_info != (const ImageInfo *) NULL);
4717 assert(image_info->signature == MagickSignature);
4718 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4719 assert(exception != (ExceptionInfo *) NULL);
4720 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004721 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy9950d572011-10-01 18:22:35 +00004722 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004723 mng_info=(MngInfo *) NULL;
4724 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004725
cristy3ed852e2009-09-05 21:47:34 +00004726 if (status == MagickFalse)
4727 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004728
cristy3ed852e2009-09-05 21:47:34 +00004729 first_mng_object=MagickFalse;
4730 skipping_loop=(-1);
4731 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004732
4733 /* Allocate a MngInfo structure. */
4734
cristy73bd4a52010-10-05 11:24:23 +00004735 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004736
cristy3ed852e2009-09-05 21:47:34 +00004737 if (mng_info == (MngInfo *) NULL)
4738 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004739
glennrp47b9dd52010-11-24 18:12:06 +00004740 /* Initialize members of the MngInfo structure. */
4741
cristy3ed852e2009-09-05 21:47:34 +00004742 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4743 mng_info->image=image;
4744 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004745
4746 if (LocaleCompare(image_info->magick,"MNG") == 0)
4747 {
4748 char
4749 magic_number[MaxTextExtent];
4750
glennrp47b9dd52010-11-24 18:12:06 +00004751 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004752 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4753 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4754 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004755
4756 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004757 for (i=0; i < MNG_MAX_OBJECTS; i++)
4758 {
cristybb503372010-05-27 20:51:26 +00004759 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4760 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004761 }
4762 mng_info->exists[0]=MagickTrue;
4763 }
glennrp47b9dd52010-11-24 18:12:06 +00004764
cristy3ed852e2009-09-05 21:47:34 +00004765 first_mng_object=MagickTrue;
4766 mng_type=0;
4767#if defined(MNG_INSERT_LAYERS)
4768 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4769#endif
4770 default_frame_delay=0;
4771 default_frame_timeout=0;
4772 frame_delay=0;
4773 final_delay=1;
4774 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4775 object_id=0;
4776 skip_to_iend=MagickFalse;
4777 term_chunk_found=MagickFalse;
4778 mng_info->framing_mode=1;
4779#if defined(MNG_INSERT_LAYERS)
4780 mandatory_back=MagickFalse;
4781#endif
4782#if defined(MNG_INSERT_LAYERS)
4783 mng_background_color=image->background_color;
4784#endif
4785 default_fb=mng_info->frame;
4786 previous_fb=mng_info->frame;
4787 do
4788 {
4789 char
4790 type[MaxTextExtent];
4791
4792 if (LocaleCompare(image_info->magick,"MNG") == 0)
4793 {
4794 unsigned char
4795 *chunk;
4796
4797 /*
4798 Read a new chunk.
4799 */
4800 type[0]='\0';
4801 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4802 length=ReadBlobMSBLong(image);
4803 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4804
4805 if (logging != MagickFalse)
4806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004807 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4808 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004809
4810 if (length > PNG_UINT_31_MAX)
4811 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004812
cristy3ed852e2009-09-05 21:47:34 +00004813 if (count == 0)
4814 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004815
cristy3ed852e2009-09-05 21:47:34 +00004816 p=NULL;
4817 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004818
cristy3ed852e2009-09-05 21:47:34 +00004819 if (length)
4820 {
4821 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004822
cristy3ed852e2009-09-05 21:47:34 +00004823 if (chunk == (unsigned char *) NULL)
4824 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004825
cristybb503372010-05-27 20:51:26 +00004826 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004827 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004828
cristy3ed852e2009-09-05 21:47:34 +00004829 p=chunk;
4830 }
glennrp0fe50b42010-11-16 03:52:51 +00004831
cristy3ed852e2009-09-05 21:47:34 +00004832 (void) ReadBlobMSBLong(image); /* read crc word */
4833
4834#if !defined(JNG_SUPPORTED)
4835 if (memcmp(type,mng_JHDR,4) == 0)
4836 {
4837 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004838
cristy3ed852e2009-09-05 21:47:34 +00004839 if (mng_info->jhdr_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00004840 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004841 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004842
cristy3ed852e2009-09-05 21:47:34 +00004843 mng_info->jhdr_warning++;
4844 }
4845#endif
4846 if (memcmp(type,mng_DHDR,4) == 0)
4847 {
4848 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004849
cristy3ed852e2009-09-05 21:47:34 +00004850 if (mng_info->dhdr_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00004851 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004852 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004853
cristy3ed852e2009-09-05 21:47:34 +00004854 mng_info->dhdr_warning++;
4855 }
4856 if (memcmp(type,mng_MEND,4) == 0)
4857 break;
glennrp47b9dd52010-11-24 18:12:06 +00004858
cristy3ed852e2009-09-05 21:47:34 +00004859 if (skip_to_iend)
4860 {
4861 if (memcmp(type,mng_IEND,4) == 0)
4862 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004863
cristy3ed852e2009-09-05 21:47:34 +00004864 if (length)
4865 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004866
cristy3ed852e2009-09-05 21:47:34 +00004867 if (logging != MagickFalse)
4868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4869 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004870
cristy3ed852e2009-09-05 21:47:34 +00004871 continue;
4872 }
glennrp0fe50b42010-11-16 03:52:51 +00004873
cristy3ed852e2009-09-05 21:47:34 +00004874 if (memcmp(type,mng_MHDR,4) == 0)
4875 {
cristybb503372010-05-27 20:51:26 +00004876 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004877 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004878
cristybb503372010-05-27 20:51:26 +00004879 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004880 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004881
cristy3ed852e2009-09-05 21:47:34 +00004882 if (logging != MagickFalse)
4883 {
4884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004885 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004887 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004888 }
glennrp0fe50b42010-11-16 03:52:51 +00004889
cristy3ed852e2009-09-05 21:47:34 +00004890 p+=8;
cristy8182b072010-05-30 20:10:53 +00004891 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004892
cristy3ed852e2009-09-05 21:47:34 +00004893 if (mng_info->ticks_per_second == 0)
4894 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004895
cristy3ed852e2009-09-05 21:47:34 +00004896 else
4897 default_frame_delay=1UL*image->ticks_per_second/
4898 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004899
cristy3ed852e2009-09-05 21:47:34 +00004900 frame_delay=default_frame_delay;
4901 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004902
cristy3ed852e2009-09-05 21:47:34 +00004903 if (length > 16)
4904 {
4905 p+=16;
cristy8182b072010-05-30 20:10:53 +00004906 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004907 }
glennrp0fe50b42010-11-16 03:52:51 +00004908
cristy3ed852e2009-09-05 21:47:34 +00004909 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004910
cristy3ed852e2009-09-05 21:47:34 +00004911 if ((simplicity != 0) && ((simplicity | 11) == 11))
4912 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004913
cristy3ed852e2009-09-05 21:47:34 +00004914 if ((simplicity != 0) && ((simplicity | 9) == 9))
4915 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004916
cristy3ed852e2009-09-05 21:47:34 +00004917#if defined(MNG_INSERT_LAYERS)
4918 if (mng_type != 3)
4919 insert_layers=MagickTrue;
4920#endif
cristy4c08aed2011-07-01 19:47:50 +00004921 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004922 {
glennrp47b9dd52010-11-24 18:12:06 +00004923 /* Allocate next image structure. */
cristy9950d572011-10-01 18:22:35 +00004924 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004925
cristy3ed852e2009-09-05 21:47:34 +00004926 if (GetNextImageInList(image) == (Image *) NULL)
4927 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004928
cristy3ed852e2009-09-05 21:47:34 +00004929 image=SyncNextImageInList(image);
4930 mng_info->image=image;
4931 }
4932
4933 if ((mng_info->mng_width > 65535L) ||
4934 (mng_info->mng_height > 65535L))
4935 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004936
cristy3b6fd2e2011-05-20 12:53:50 +00004937 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004938 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004939 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004940
cristy3ed852e2009-09-05 21:47:34 +00004941 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004942 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004943 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004944 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004945 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004946
cristy3ed852e2009-09-05 21:47:34 +00004947 for (i=0; i < MNG_MAX_OBJECTS; i++)
4948 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004949
cristy3ed852e2009-09-05 21:47:34 +00004950 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4951 continue;
4952 }
4953
4954 if (memcmp(type,mng_TERM,4) == 0)
4955 {
4956 int
4957 repeat=0;
4958
4959
4960 if (length)
4961 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004962
cristy3ed852e2009-09-05 21:47:34 +00004963 if (repeat == 3)
4964 {
cristy8182b072010-05-30 20:10:53 +00004965 final_delay=(png_uint_32) mng_get_long(&p[2]);
4966 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004967
cristy3ed852e2009-09-05 21:47:34 +00004968 if (mng_iterations == PNG_UINT_31_MAX)
4969 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004970
cristy3ed852e2009-09-05 21:47:34 +00004971 image->iterations=mng_iterations;
4972 term_chunk_found=MagickTrue;
4973 }
glennrp0fe50b42010-11-16 03:52:51 +00004974
cristy3ed852e2009-09-05 21:47:34 +00004975 if (logging != MagickFalse)
4976 {
4977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4978 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004979
cristy3ed852e2009-09-05 21:47:34 +00004980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004981 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004982
cristy3ed852e2009-09-05 21:47:34 +00004983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004984 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004985 }
glennrp0fe50b42010-11-16 03:52:51 +00004986
cristy3ed852e2009-09-05 21:47:34 +00004987 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4988 continue;
4989 }
4990 if (memcmp(type,mng_DEFI,4) == 0)
4991 {
4992 if (mng_type == 3)
cristyc82a27b2011-10-21 01:07:16 +00004993 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004994 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4995 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004996
cristy3ed852e2009-09-05 21:47:34 +00004997 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004998
cristy3ed852e2009-09-05 21:47:34 +00004999 if (mng_type == 2 && object_id != 0)
cristyc82a27b2011-10-21 01:07:16 +00005000 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005001 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5002 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005003
cristy3ed852e2009-09-05 21:47:34 +00005004 if (object_id > MNG_MAX_OBJECTS)
5005 {
5006 /*
5007 Instead ofsuing a warning we should allocate a larger
5008 MngInfo structure and continue.
5009 */
cristyc82a27b2011-10-21 01:07:16 +00005010 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005011 CoderError,"object id too large","`%s'",image->filename);
5012 object_id=MNG_MAX_OBJECTS;
5013 }
glennrp0fe50b42010-11-16 03:52:51 +00005014
cristy3ed852e2009-09-05 21:47:34 +00005015 if (mng_info->exists[object_id])
5016 if (mng_info->frozen[object_id])
5017 {
5018 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
cristyc82a27b2011-10-21 01:07:16 +00005019 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005020 GetMagickModule(),CoderError,
5021 "DEFI cannot redefine a frozen MNG object","`%s'",
5022 image->filename);
5023 continue;
5024 }
glennrp0fe50b42010-11-16 03:52:51 +00005025
cristy3ed852e2009-09-05 21:47:34 +00005026 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005027
cristy3ed852e2009-09-05 21:47:34 +00005028 if (length > 2)
5029 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00005030
cristy3ed852e2009-09-05 21:47:34 +00005031 /*
5032 Extract object offset info.
5033 */
5034 if (length > 11)
5035 {
glennrp0fe50b42010-11-16 03:52:51 +00005036 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5037 (p[5] << 16) | (p[6] << 8) | p[7]);
5038
5039 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5040 (p[9] << 16) | (p[10] << 8) | p[11]);
5041
cristy3ed852e2009-09-05 21:47:34 +00005042 if (logging != MagickFalse)
5043 {
5044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005045 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005046 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005047
cristy3ed852e2009-09-05 21:47:34 +00005048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005049 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005050 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005051 }
5052 }
glennrp0fe50b42010-11-16 03:52:51 +00005053
cristy3ed852e2009-09-05 21:47:34 +00005054 /*
5055 Extract object clipping info.
5056 */
5057 if (length > 27)
5058 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5059 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005060
cristy3ed852e2009-09-05 21:47:34 +00005061 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5062 continue;
5063 }
5064 if (memcmp(type,mng_bKGD,4) == 0)
5065 {
5066 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005067
cristy3ed852e2009-09-05 21:47:34 +00005068 if (length > 5)
5069 {
5070 mng_info->mng_global_bkgd.red=
5071 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005072
cristy3ed852e2009-09-05 21:47:34 +00005073 mng_info->mng_global_bkgd.green=
5074 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005075
cristy3ed852e2009-09-05 21:47:34 +00005076 mng_info->mng_global_bkgd.blue=
5077 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005078
cristy3ed852e2009-09-05 21:47:34 +00005079 mng_info->have_global_bkgd=MagickTrue;
5080 }
glennrp0fe50b42010-11-16 03:52:51 +00005081
cristy3ed852e2009-09-05 21:47:34 +00005082 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5083 continue;
5084 }
5085 if (memcmp(type,mng_BACK,4) == 0)
5086 {
5087#if defined(MNG_INSERT_LAYERS)
5088 if (length > 6)
5089 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005090
cristy3ed852e2009-09-05 21:47:34 +00005091 else
5092 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005093
cristy3ed852e2009-09-05 21:47:34 +00005094 if (mandatory_back && length > 5)
5095 {
5096 mng_background_color.red=
5097 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005098
cristy3ed852e2009-09-05 21:47:34 +00005099 mng_background_color.green=
5100 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005101
cristy3ed852e2009-09-05 21:47:34 +00005102 mng_background_color.blue=
5103 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005104
cristy4c08aed2011-07-01 19:47:50 +00005105 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005106 }
glennrp0fe50b42010-11-16 03:52:51 +00005107
cristy3ed852e2009-09-05 21:47:34 +00005108#ifdef MNG_OBJECT_BUFFERS
5109 if (length > 8)
5110 mng_background_object=(p[7] << 8) | p[8];
5111#endif
5112#endif
5113 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5114 continue;
5115 }
glennrp47b9dd52010-11-24 18:12:06 +00005116
cristy3ed852e2009-09-05 21:47:34 +00005117 if (memcmp(type,mng_PLTE,4) == 0)
5118 {
glennrp47b9dd52010-11-24 18:12:06 +00005119 /* Read global PLTE. */
5120
cristy3ed852e2009-09-05 21:47:34 +00005121 if (length && (length < 769))
5122 {
5123 if (mng_info->global_plte == (png_colorp) NULL)
5124 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5125 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005126
cristybb503372010-05-27 20:51:26 +00005127 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005128 {
5129 mng_info->global_plte[i].red=p[3*i];
5130 mng_info->global_plte[i].green=p[3*i+1];
5131 mng_info->global_plte[i].blue=p[3*i+2];
5132 }
glennrp0fe50b42010-11-16 03:52:51 +00005133
cristy35ef8242010-06-03 16:24:13 +00005134 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005135 }
5136#ifdef MNG_LOOSE
5137 for ( ; i < 256; i++)
5138 {
5139 mng_info->global_plte[i].red=i;
5140 mng_info->global_plte[i].green=i;
5141 mng_info->global_plte[i].blue=i;
5142 }
glennrp0fe50b42010-11-16 03:52:51 +00005143
cristy3ed852e2009-09-05 21:47:34 +00005144 if (length)
5145 mng_info->global_plte_length=256;
5146#endif
5147 else
5148 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005149
cristy3ed852e2009-09-05 21:47:34 +00005150 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5151 continue;
5152 }
glennrp47b9dd52010-11-24 18:12:06 +00005153
cristy3ed852e2009-09-05 21:47:34 +00005154 if (memcmp(type,mng_tRNS,4) == 0)
5155 {
5156 /* read global tRNS */
5157
5158 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005159 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005160 mng_info->global_trns[i]=p[i];
5161
5162#ifdef MNG_LOOSE
5163 for ( ; i < 256; i++)
5164 mng_info->global_trns[i]=255;
5165#endif
cristy12560f32010-06-03 16:51:08 +00005166 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005167 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5168 continue;
5169 }
5170 if (memcmp(type,mng_gAMA,4) == 0)
5171 {
5172 if (length == 4)
5173 {
cristybb503372010-05-27 20:51:26 +00005174 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005175 igamma;
5176
cristy8182b072010-05-30 20:10:53 +00005177 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005178 mng_info->global_gamma=((float) igamma)*0.00001;
5179 mng_info->have_global_gama=MagickTrue;
5180 }
glennrp0fe50b42010-11-16 03:52:51 +00005181
cristy3ed852e2009-09-05 21:47:34 +00005182 else
5183 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005184
cristy3ed852e2009-09-05 21:47:34 +00005185 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5186 continue;
5187 }
5188
5189 if (memcmp(type,mng_cHRM,4) == 0)
5190 {
glennrp47b9dd52010-11-24 18:12:06 +00005191 /* Read global cHRM */
5192
cristy3ed852e2009-09-05 21:47:34 +00005193 if (length == 32)
5194 {
cristy8182b072010-05-30 20:10:53 +00005195 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5196 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5197 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005198 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005199 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005200 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005201 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005202 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005203 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005204 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005205 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005206 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005207 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005208 mng_info->have_global_chrm=MagickTrue;
5209 }
5210 else
5211 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005212
cristy3ed852e2009-09-05 21:47:34 +00005213 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5214 continue;
5215 }
glennrp47b9dd52010-11-24 18:12:06 +00005216
cristy3ed852e2009-09-05 21:47:34 +00005217 if (memcmp(type,mng_sRGB,4) == 0)
5218 {
5219 /*
5220 Read global sRGB.
5221 */
5222 if (length)
5223 {
glennrpe610a072010-08-05 17:08:46 +00005224 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005225 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005226 mng_info->have_global_srgb=MagickTrue;
5227 }
5228 else
5229 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005230
cristy3ed852e2009-09-05 21:47:34 +00005231 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5232 continue;
5233 }
glennrp47b9dd52010-11-24 18:12:06 +00005234
cristy3ed852e2009-09-05 21:47:34 +00005235 if (memcmp(type,mng_iCCP,4) == 0)
5236 {
glennrpfd05d622011-02-25 04:10:33 +00005237 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005238
5239 /*
5240 Read global iCCP.
5241 */
5242 if (length)
5243 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005244
cristy3ed852e2009-09-05 21:47:34 +00005245 continue;
5246 }
glennrp47b9dd52010-11-24 18:12:06 +00005247
cristy3ed852e2009-09-05 21:47:34 +00005248 if (memcmp(type,mng_FRAM,4) == 0)
5249 {
5250 if (mng_type == 3)
cristyc82a27b2011-10-21 01:07:16 +00005251 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005252 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5253 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005254
cristy3ed852e2009-09-05 21:47:34 +00005255 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5256 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005257
cristy3ed852e2009-09-05 21:47:34 +00005258 frame_delay=default_frame_delay;
5259 frame_timeout=default_frame_timeout;
5260 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005261
cristy3ed852e2009-09-05 21:47:34 +00005262 if (length)
5263 if (p[0])
5264 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005265
cristy3ed852e2009-09-05 21:47:34 +00005266 if (logging != MagickFalse)
5267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5268 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005269
cristy3ed852e2009-09-05 21:47:34 +00005270 if (length > 6)
5271 {
glennrp47b9dd52010-11-24 18:12:06 +00005272 /* Note the delay and frame clipping boundaries. */
5273
cristy3ed852e2009-09-05 21:47:34 +00005274 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005275
cristybb503372010-05-27 20:51:26 +00005276 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005277 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005278
cristy3ed852e2009-09-05 21:47:34 +00005279 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005280
cristybb503372010-05-27 20:51:26 +00005281 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005282 {
5283 int
5284 change_delay,
5285 change_timeout,
5286 change_clipping;
5287
5288 change_delay=(*p++);
5289 change_timeout=(*p++);
5290 change_clipping=(*p++);
5291 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005292
cristy3ed852e2009-09-05 21:47:34 +00005293 if (change_delay)
5294 {
cristy8182b072010-05-30 20:10:53 +00005295 frame_delay=1UL*image->ticks_per_second*
5296 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005297
cristy8182b072010-05-30 20:10:53 +00005298 if (mng_info->ticks_per_second != 0)
5299 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005300
glennrpbb010dd2010-06-01 13:07:15 +00005301 else
5302 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005303
cristy3ed852e2009-09-05 21:47:34 +00005304 if (change_delay == 2)
5305 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005306
cristy3ed852e2009-09-05 21:47:34 +00005307 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005308
cristy3ed852e2009-09-05 21:47:34 +00005309 if (logging != MagickFalse)
5310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005311 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005312 }
glennrp47b9dd52010-11-24 18:12:06 +00005313
cristy3ed852e2009-09-05 21:47:34 +00005314 if (change_timeout)
5315 {
glennrpbb010dd2010-06-01 13:07:15 +00005316 frame_timeout=1UL*image->ticks_per_second*
5317 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005318
glennrpbb010dd2010-06-01 13:07:15 +00005319 if (mng_info->ticks_per_second != 0)
5320 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005321
glennrpbb010dd2010-06-01 13:07:15 +00005322 else
5323 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005324
cristy3ed852e2009-09-05 21:47:34 +00005325 if (change_delay == 2)
5326 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005327
cristy3ed852e2009-09-05 21:47:34 +00005328 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005329
cristy3ed852e2009-09-05 21:47:34 +00005330 if (logging != MagickFalse)
5331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005332 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005333 }
glennrp47b9dd52010-11-24 18:12:06 +00005334
cristy3ed852e2009-09-05 21:47:34 +00005335 if (change_clipping)
5336 {
5337 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5338 p+=17;
5339 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005340
cristy3ed852e2009-09-05 21:47:34 +00005341 if (logging != MagickFalse)
5342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005343 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005344 (double) fb.left,(double) fb.right,(double) fb.top,
5345 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005346
cristy3ed852e2009-09-05 21:47:34 +00005347 if (change_clipping == 2)
5348 default_fb=fb;
5349 }
5350 }
5351 }
5352 mng_info->clip=fb;
5353 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005354
cristybb503372010-05-27 20:51:26 +00005355 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005356 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005357
cristybb503372010-05-27 20:51:26 +00005358 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005359 -mng_info->clip.top);
5360 /*
5361 Insert a background layer behind the frame if framing_mode is 4.
5362 */
5363#if defined(MNG_INSERT_LAYERS)
5364 if (logging != MagickFalse)
5365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005366 " subframe_width=%.20g, subframe_height=%.20g",(double)
5367 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005368
cristy3ed852e2009-09-05 21:47:34 +00005369 if (insert_layers && (mng_info->framing_mode == 4) &&
5370 (subframe_width) && (subframe_height))
5371 {
glennrp47b9dd52010-11-24 18:12:06 +00005372 /* Allocate next image structure. */
cristy4c08aed2011-07-01 19:47:50 +00005373 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005374 {
cristy9950d572011-10-01 18:22:35 +00005375 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005376
cristy3ed852e2009-09-05 21:47:34 +00005377 if (GetNextImageInList(image) == (Image *) NULL)
5378 {
5379 image=DestroyImageList(image);
5380 MngInfoFreeStruct(mng_info,&have_mng_structure);
5381 return((Image *) NULL);
5382 }
glennrp47b9dd52010-11-24 18:12:06 +00005383
cristy3ed852e2009-09-05 21:47:34 +00005384 image=SyncNextImageInList(image);
5385 }
glennrp0fe50b42010-11-16 03:52:51 +00005386
cristy3ed852e2009-09-05 21:47:34 +00005387 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005388
cristy3ed852e2009-09-05 21:47:34 +00005389 if (term_chunk_found)
5390 {
5391 image->start_loop=MagickTrue;
5392 image->iterations=mng_iterations;
5393 term_chunk_found=MagickFalse;
5394 }
glennrp0fe50b42010-11-16 03:52:51 +00005395
cristy3ed852e2009-09-05 21:47:34 +00005396 else
5397 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005398
cristy3ed852e2009-09-05 21:47:34 +00005399 image->columns=subframe_width;
5400 image->rows=subframe_height;
5401 image->page.width=subframe_width;
5402 image->page.height=subframe_height;
5403 image->page.x=mng_info->clip.left;
5404 image->page.y=mng_info->clip.top;
5405 image->background_color=mng_background_color;
5406 image->matte=MagickFalse;
5407 image->delay=0;
cristyea1a8aa2011-10-20 13:24:06 +00005408 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005409
cristy3ed852e2009-09-05 21:47:34 +00005410 if (logging != MagickFalse)
5411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005412 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005413 (double) mng_info->clip.left,(double) mng_info->clip.right,
5414 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005415 }
5416#endif
5417 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5418 continue;
5419 }
5420 if (memcmp(type,mng_CLIP,4) == 0)
5421 {
5422 unsigned int
5423 first_object,
5424 last_object;
5425
5426 /*
5427 Read CLIP.
5428 */
5429 first_object=(p[0] << 8) | p[1];
5430 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005431
cristy3ed852e2009-09-05 21:47:34 +00005432 for (i=(int) first_object; i <= (int) last_object; i++)
5433 {
5434 if (mng_info->exists[i] && !mng_info->frozen[i])
5435 {
5436 MngBox
5437 box;
5438
5439 box=mng_info->object_clip[i];
5440 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5441 }
5442 }
glennrp47b9dd52010-11-24 18:12:06 +00005443
cristy3ed852e2009-09-05 21:47:34 +00005444 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5445 continue;
5446 }
5447 if (memcmp(type,mng_SAVE,4) == 0)
5448 {
5449 for (i=1; i < MNG_MAX_OBJECTS; i++)
5450 if (mng_info->exists[i])
5451 {
5452 mng_info->frozen[i]=MagickTrue;
5453#ifdef MNG_OBJECT_BUFFERS
5454 if (mng_info->ob[i] != (MngBuffer *) NULL)
5455 mng_info->ob[i]->frozen=MagickTrue;
5456#endif
5457 }
glennrp0fe50b42010-11-16 03:52:51 +00005458
cristy3ed852e2009-09-05 21:47:34 +00005459 if (length)
5460 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005461
cristy3ed852e2009-09-05 21:47:34 +00005462 continue;
5463 }
5464
5465 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5466 {
glennrp47b9dd52010-11-24 18:12:06 +00005467 /* Read DISC or SEEK. */
5468
cristy3ed852e2009-09-05 21:47:34 +00005469 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5470 {
5471 for (i=1; i < MNG_MAX_OBJECTS; i++)
5472 MngInfoDiscardObject(mng_info,i);
5473 }
glennrp0fe50b42010-11-16 03:52:51 +00005474
cristy3ed852e2009-09-05 21:47:34 +00005475 else
5476 {
cristybb503372010-05-27 20:51:26 +00005477 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005478 j;
5479
cristybb503372010-05-27 20:51:26 +00005480 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005481 {
5482 i=p[j] << 8 | p[j+1];
5483 MngInfoDiscardObject(mng_info,i);
5484 }
5485 }
glennrp0fe50b42010-11-16 03:52:51 +00005486
cristy3ed852e2009-09-05 21:47:34 +00005487 if (length)
5488 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005489
cristy3ed852e2009-09-05 21:47:34 +00005490 continue;
5491 }
glennrp47b9dd52010-11-24 18:12:06 +00005492
cristy3ed852e2009-09-05 21:47:34 +00005493 if (memcmp(type,mng_MOVE,4) == 0)
5494 {
cristybb503372010-05-27 20:51:26 +00005495 size_t
cristy3ed852e2009-09-05 21:47:34 +00005496 first_object,
5497 last_object;
5498
glennrp47b9dd52010-11-24 18:12:06 +00005499 /* read MOVE */
5500
cristy3ed852e2009-09-05 21:47:34 +00005501 first_object=(p[0] << 8) | p[1];
5502 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005503 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005504 {
5505 if (mng_info->exists[i] && !mng_info->frozen[i])
5506 {
5507 MngPair
5508 new_pair;
5509
5510 MngPair
5511 old_pair;
5512
5513 old_pair.a=mng_info->x_off[i];
5514 old_pair.b=mng_info->y_off[i];
5515 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5516 mng_info->x_off[i]=new_pair.a;
5517 mng_info->y_off[i]=new_pair.b;
5518 }
5519 }
glennrp47b9dd52010-11-24 18:12:06 +00005520
cristy3ed852e2009-09-05 21:47:34 +00005521 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5522 continue;
5523 }
5524
5525 if (memcmp(type,mng_LOOP,4) == 0)
5526 {
cristybb503372010-05-27 20:51:26 +00005527 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005528 loop_level=chunk[0];
5529 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005530
5531 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005532 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005533
cristy3ed852e2009-09-05 21:47:34 +00005534 if (logging != MagickFalse)
5535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005536 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5537 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005538
cristy3ed852e2009-09-05 21:47:34 +00005539 if (loop_iters == 0)
5540 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005541
cristy3ed852e2009-09-05 21:47:34 +00005542 else
5543 {
5544 mng_info->loop_jump[loop_level]=TellBlob(image);
5545 mng_info->loop_count[loop_level]=loop_iters;
5546 }
glennrp0fe50b42010-11-16 03:52:51 +00005547
cristy3ed852e2009-09-05 21:47:34 +00005548 mng_info->loop_iteration[loop_level]=0;
5549 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5550 continue;
5551 }
glennrp47b9dd52010-11-24 18:12:06 +00005552
cristy3ed852e2009-09-05 21:47:34 +00005553 if (memcmp(type,mng_ENDL,4) == 0)
5554 {
5555 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005556
cristy3ed852e2009-09-05 21:47:34 +00005557 if (skipping_loop > 0)
5558 {
5559 if (skipping_loop == loop_level)
5560 {
5561 /*
5562 Found end of zero-iteration loop.
5563 */
5564 skipping_loop=(-1);
5565 mng_info->loop_active[loop_level]=0;
5566 }
5567 }
glennrp47b9dd52010-11-24 18:12:06 +00005568
cristy3ed852e2009-09-05 21:47:34 +00005569 else
5570 {
5571 if (mng_info->loop_active[loop_level] == 1)
5572 {
5573 mng_info->loop_count[loop_level]--;
5574 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005575
cristy3ed852e2009-09-05 21:47:34 +00005576 if (logging != MagickFalse)
5577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005578 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005579 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005580 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005581
cristy3ed852e2009-09-05 21:47:34 +00005582 if (mng_info->loop_count[loop_level] != 0)
5583 {
5584 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5585 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005586
cristy3ed852e2009-09-05 21:47:34 +00005587 if (offset < 0)
5588 ThrowReaderException(CorruptImageError,
5589 "ImproperImageHeader");
5590 }
glennrp47b9dd52010-11-24 18:12:06 +00005591
cristy3ed852e2009-09-05 21:47:34 +00005592 else
5593 {
5594 short
5595 last_level;
5596
5597 /*
5598 Finished loop.
5599 */
5600 mng_info->loop_active[loop_level]=0;
5601 last_level=(-1);
5602 for (i=0; i < loop_level; i++)
5603 if (mng_info->loop_active[i] == 1)
5604 last_level=(short) i;
5605 loop_level=last_level;
5606 }
5607 }
5608 }
glennrp47b9dd52010-11-24 18:12:06 +00005609
cristy3ed852e2009-09-05 21:47:34 +00005610 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5611 continue;
5612 }
glennrp47b9dd52010-11-24 18:12:06 +00005613
cristy3ed852e2009-09-05 21:47:34 +00005614 if (memcmp(type,mng_CLON,4) == 0)
5615 {
5616 if (mng_info->clon_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005617 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005618 CoderError,"CLON is not implemented yet","`%s'",
5619 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005620
cristy3ed852e2009-09-05 21:47:34 +00005621 mng_info->clon_warning++;
5622 }
glennrp47b9dd52010-11-24 18:12:06 +00005623
cristy3ed852e2009-09-05 21:47:34 +00005624 if (memcmp(type,mng_MAGN,4) == 0)
5625 {
5626 png_uint_16
5627 magn_first,
5628 magn_last,
5629 magn_mb,
5630 magn_ml,
5631 magn_mr,
5632 magn_mt,
5633 magn_mx,
5634 magn_my,
5635 magn_methx,
5636 magn_methy;
5637
5638 if (length > 1)
5639 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005640
cristy3ed852e2009-09-05 21:47:34 +00005641 else
5642 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005643
cristy3ed852e2009-09-05 21:47:34 +00005644 if (length > 3)
5645 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005646
cristy3ed852e2009-09-05 21:47:34 +00005647 else
5648 magn_last=magn_first;
5649#ifndef MNG_OBJECT_BUFFERS
5650 if (magn_first || magn_last)
5651 if (mng_info->magn_warning == 0)
5652 {
cristyc82a27b2011-10-21 01:07:16 +00005653 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005654 GetMagickModule(),CoderError,
5655 "MAGN is not implemented yet for nonzero objects",
5656 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005657
cristy3ed852e2009-09-05 21:47:34 +00005658 mng_info->magn_warning++;
5659 }
5660#endif
5661 if (length > 4)
5662 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005663
cristy3ed852e2009-09-05 21:47:34 +00005664 else
5665 magn_methx=0;
5666
5667 if (length > 6)
5668 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005669
cristy3ed852e2009-09-05 21:47:34 +00005670 else
5671 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005672
cristy3ed852e2009-09-05 21:47:34 +00005673 if (magn_mx == 0)
5674 magn_mx=1;
5675
5676 if (length > 8)
5677 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005678
cristy3ed852e2009-09-05 21:47:34 +00005679 else
5680 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005681
cristy3ed852e2009-09-05 21:47:34 +00005682 if (magn_my == 0)
5683 magn_my=1;
5684
5685 if (length > 10)
5686 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005687
cristy3ed852e2009-09-05 21:47:34 +00005688 else
5689 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005690
cristy3ed852e2009-09-05 21:47:34 +00005691 if (magn_ml == 0)
5692 magn_ml=1;
5693
5694 if (length > 12)
5695 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005696
cristy3ed852e2009-09-05 21:47:34 +00005697 else
5698 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005699
cristy3ed852e2009-09-05 21:47:34 +00005700 if (magn_mr == 0)
5701 magn_mr=1;
5702
5703 if (length > 14)
5704 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005705
cristy3ed852e2009-09-05 21:47:34 +00005706 else
5707 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005708
cristy3ed852e2009-09-05 21:47:34 +00005709 if (magn_mt == 0)
5710 magn_mt=1;
5711
5712 if (length > 16)
5713 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005714
cristy3ed852e2009-09-05 21:47:34 +00005715 else
5716 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005717
cristy3ed852e2009-09-05 21:47:34 +00005718 if (magn_mb == 0)
5719 magn_mb=1;
5720
5721 if (length > 17)
5722 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005723
cristy3ed852e2009-09-05 21:47:34 +00005724 else
5725 magn_methy=magn_methx;
5726
glennrp47b9dd52010-11-24 18:12:06 +00005727
cristy3ed852e2009-09-05 21:47:34 +00005728 if (magn_methx > 5 || magn_methy > 5)
5729 if (mng_info->magn_warning == 0)
5730 {
cristyc82a27b2011-10-21 01:07:16 +00005731 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005732 GetMagickModule(),CoderError,
5733 "Unknown MAGN method in MNG datastream","`%s'",
5734 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005735
cristy3ed852e2009-09-05 21:47:34 +00005736 mng_info->magn_warning++;
5737 }
5738#ifdef MNG_OBJECT_BUFFERS
5739 /* Magnify existing objects in the range magn_first to magn_last */
5740#endif
5741 if (magn_first == 0 || magn_last == 0)
5742 {
5743 /* Save the magnification factors for object 0 */
5744 mng_info->magn_mb=magn_mb;
5745 mng_info->magn_ml=magn_ml;
5746 mng_info->magn_mr=magn_mr;
5747 mng_info->magn_mt=magn_mt;
5748 mng_info->magn_mx=magn_mx;
5749 mng_info->magn_my=magn_my;
5750 mng_info->magn_methx=magn_methx;
5751 mng_info->magn_methy=magn_methy;
5752 }
5753 }
glennrp47b9dd52010-11-24 18:12:06 +00005754
cristy3ed852e2009-09-05 21:47:34 +00005755 if (memcmp(type,mng_PAST,4) == 0)
5756 {
5757 if (mng_info->past_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005758 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005759 CoderError,"PAST is not implemented yet","`%s'",
5760 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005761
cristy3ed852e2009-09-05 21:47:34 +00005762 mng_info->past_warning++;
5763 }
glennrp47b9dd52010-11-24 18:12:06 +00005764
cristy3ed852e2009-09-05 21:47:34 +00005765 if (memcmp(type,mng_SHOW,4) == 0)
5766 {
5767 if (mng_info->show_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005768 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005769 CoderError,"SHOW is not implemented yet","`%s'",
5770 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005771
cristy3ed852e2009-09-05 21:47:34 +00005772 mng_info->show_warning++;
5773 }
glennrp47b9dd52010-11-24 18:12:06 +00005774
cristy3ed852e2009-09-05 21:47:34 +00005775 if (memcmp(type,mng_sBIT,4) == 0)
5776 {
5777 if (length < 4)
5778 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005779
cristy3ed852e2009-09-05 21:47:34 +00005780 else
5781 {
5782 mng_info->global_sbit.gray=p[0];
5783 mng_info->global_sbit.red=p[0];
5784 mng_info->global_sbit.green=p[1];
5785 mng_info->global_sbit.blue=p[2];
5786 mng_info->global_sbit.alpha=p[3];
5787 mng_info->have_global_sbit=MagickTrue;
5788 }
5789 }
5790 if (memcmp(type,mng_pHYs,4) == 0)
5791 {
5792 if (length > 8)
5793 {
5794 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005795 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005796 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005797 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005798 mng_info->global_phys_unit_type=p[8];
5799 mng_info->have_global_phys=MagickTrue;
5800 }
glennrp47b9dd52010-11-24 18:12:06 +00005801
cristy3ed852e2009-09-05 21:47:34 +00005802 else
5803 mng_info->have_global_phys=MagickFalse;
5804 }
5805 if (memcmp(type,mng_pHYg,4) == 0)
5806 {
5807 if (mng_info->phyg_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005808 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005809 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005810
cristy3ed852e2009-09-05 21:47:34 +00005811 mng_info->phyg_warning++;
5812 }
5813 if (memcmp(type,mng_BASI,4) == 0)
5814 {
5815 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005816
cristy3ed852e2009-09-05 21:47:34 +00005817 if (mng_info->basi_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005818 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005819 CoderError,"BASI is not implemented yet","`%s'",
5820 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005821
cristy3ed852e2009-09-05 21:47:34 +00005822 mng_info->basi_warning++;
5823#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005824 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005825 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005826 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005827 (p[6] << 8) | p[7]);
5828 basi_color_type=p[8];
5829 basi_compression_method=p[9];
5830 basi_filter_type=p[10];
5831 basi_interlace_method=p[11];
5832 if (length > 11)
5833 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005834
cristy3ed852e2009-09-05 21:47:34 +00005835 else
5836 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005837
cristy3ed852e2009-09-05 21:47:34 +00005838 if (length > 13)
5839 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005840
cristy3ed852e2009-09-05 21:47:34 +00005841 else
5842 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005843
cristy3ed852e2009-09-05 21:47:34 +00005844 if (length > 15)
5845 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005846
cristy3ed852e2009-09-05 21:47:34 +00005847 else
5848 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005849
cristy3ed852e2009-09-05 21:47:34 +00005850 if (length > 17)
5851 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005852
cristy3ed852e2009-09-05 21:47:34 +00005853 else
5854 {
5855 if (basi_sample_depth == 16)
5856 basi_alpha=65535L;
5857 else
5858 basi_alpha=255;
5859 }
glennrp47b9dd52010-11-24 18:12:06 +00005860
cristy3ed852e2009-09-05 21:47:34 +00005861 if (length > 19)
5862 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005863
cristy3ed852e2009-09-05 21:47:34 +00005864 else
5865 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005866
cristy3ed852e2009-09-05 21:47:34 +00005867#endif
5868 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5869 continue;
5870 }
glennrp47b9dd52010-11-24 18:12:06 +00005871
cristy3ed852e2009-09-05 21:47:34 +00005872 if (memcmp(type,mng_IHDR,4)
5873#if defined(JNG_SUPPORTED)
5874 && memcmp(type,mng_JHDR,4)
5875#endif
5876 )
5877 {
5878 /* Not an IHDR or JHDR chunk */
5879 if (length)
5880 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005881
cristy3ed852e2009-09-05 21:47:34 +00005882 continue;
5883 }
5884/* Process IHDR */
5885 if (logging != MagickFalse)
5886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5887 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005888
cristy3ed852e2009-09-05 21:47:34 +00005889 mng_info->exists[object_id]=MagickTrue;
5890 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005891
cristy3ed852e2009-09-05 21:47:34 +00005892 if (mng_info->invisible[object_id])
5893 {
5894 if (logging != MagickFalse)
5895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5896 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005897
cristy3ed852e2009-09-05 21:47:34 +00005898 skip_to_iend=MagickTrue;
5899 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5900 continue;
5901 }
5902#if defined(MNG_INSERT_LAYERS)
5903 if (length < 8)
5904 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005905
cristy8182b072010-05-30 20:10:53 +00005906 image_width=(size_t) mng_get_long(p);
5907 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005908#endif
5909 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5910
5911 /*
5912 Insert a transparent background layer behind the entire animation
5913 if it is not full screen.
5914 */
5915#if defined(MNG_INSERT_LAYERS)
5916 if (insert_layers && mng_type && first_mng_object)
5917 {
5918 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5919 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005920 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005921 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005922 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005923 {
cristy4c08aed2011-07-01 19:47:50 +00005924 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005925 {
5926 /*
5927 Allocate next image structure.
5928 */
cristy9950d572011-10-01 18:22:35 +00005929 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005930
cristy3ed852e2009-09-05 21:47:34 +00005931 if (GetNextImageInList(image) == (Image *) NULL)
5932 {
5933 image=DestroyImageList(image);
5934 MngInfoFreeStruct(mng_info,&have_mng_structure);
5935 return((Image *) NULL);
5936 }
glennrp47b9dd52010-11-24 18:12:06 +00005937
cristy3ed852e2009-09-05 21:47:34 +00005938 image=SyncNextImageInList(image);
5939 }
5940 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005941
cristy3ed852e2009-09-05 21:47:34 +00005942 if (term_chunk_found)
5943 {
5944 image->start_loop=MagickTrue;
5945 image->iterations=mng_iterations;
5946 term_chunk_found=MagickFalse;
5947 }
glennrp47b9dd52010-11-24 18:12:06 +00005948
cristy3ed852e2009-09-05 21:47:34 +00005949 else
5950 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005951
5952 /* Make a background rectangle. */
5953
cristy3ed852e2009-09-05 21:47:34 +00005954 image->delay=0;
5955 image->columns=mng_info->mng_width;
5956 image->rows=mng_info->mng_height;
5957 image->page.width=mng_info->mng_width;
5958 image->page.height=mng_info->mng_height;
5959 image->page.x=0;
5960 image->page.y=0;
5961 image->background_color=mng_background_color;
cristyea1a8aa2011-10-20 13:24:06 +00005962 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005963 if (logging != MagickFalse)
5964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005965 " Inserted transparent background layer, W=%.20g, H=%.20g",
5966 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005967 }
5968 }
5969 /*
5970 Insert a background layer behind the upcoming image if
5971 framing_mode is 3, and we haven't already inserted one.
5972 */
5973 if (insert_layers && (mng_info->framing_mode == 3) &&
5974 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5975 (simplicity & 0x08)))
5976 {
cristy4c08aed2011-07-01 19:47:50 +00005977 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005978 {
5979 /*
5980 Allocate next image structure.
5981 */
cristy9950d572011-10-01 18:22:35 +00005982 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005983
cristy3ed852e2009-09-05 21:47:34 +00005984 if (GetNextImageInList(image) == (Image *) NULL)
5985 {
5986 image=DestroyImageList(image);
5987 MngInfoFreeStruct(mng_info,&have_mng_structure);
5988 return((Image *) NULL);
5989 }
glennrp47b9dd52010-11-24 18:12:06 +00005990
cristy3ed852e2009-09-05 21:47:34 +00005991 image=SyncNextImageInList(image);
5992 }
glennrp0fe50b42010-11-16 03:52:51 +00005993
cristy3ed852e2009-09-05 21:47:34 +00005994 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005995
cristy3ed852e2009-09-05 21:47:34 +00005996 if (term_chunk_found)
5997 {
5998 image->start_loop=MagickTrue;
5999 image->iterations=mng_iterations;
6000 term_chunk_found=MagickFalse;
6001 }
glennrp0fe50b42010-11-16 03:52:51 +00006002
cristy3ed852e2009-09-05 21:47:34 +00006003 else
6004 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006005
cristy3ed852e2009-09-05 21:47:34 +00006006 image->delay=0;
6007 image->columns=subframe_width;
6008 image->rows=subframe_height;
6009 image->page.width=subframe_width;
6010 image->page.height=subframe_height;
6011 image->page.x=mng_info->clip.left;
6012 image->page.y=mng_info->clip.top;
6013 image->background_color=mng_background_color;
6014 image->matte=MagickFalse;
cristyea1a8aa2011-10-20 13:24:06 +00006015 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006016
cristy3ed852e2009-09-05 21:47:34 +00006017 if (logging != MagickFalse)
6018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00006019 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00006020 (double) mng_info->clip.left,(double) mng_info->clip.right,
6021 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00006022 }
6023#endif /* MNG_INSERT_LAYERS */
6024 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006025
cristy4c08aed2011-07-01 19:47:50 +00006026 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006027 {
6028 /*
6029 Allocate next image structure.
6030 */
cristy9950d572011-10-01 18:22:35 +00006031 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006032
cristy3ed852e2009-09-05 21:47:34 +00006033 if (GetNextImageInList(image) == (Image *) NULL)
6034 {
6035 image=DestroyImageList(image);
6036 MngInfoFreeStruct(mng_info,&have_mng_structure);
6037 return((Image *) NULL);
6038 }
glennrp47b9dd52010-11-24 18:12:06 +00006039
cristy3ed852e2009-09-05 21:47:34 +00006040 image=SyncNextImageInList(image);
6041 }
6042 mng_info->image=image;
6043 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6044 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006045
cristy3ed852e2009-09-05 21:47:34 +00006046 if (status == MagickFalse)
6047 break;
glennrp0fe50b42010-11-16 03:52:51 +00006048
cristy3ed852e2009-09-05 21:47:34 +00006049 if (term_chunk_found)
6050 {
6051 image->start_loop=MagickTrue;
6052 term_chunk_found=MagickFalse;
6053 }
glennrp0fe50b42010-11-16 03:52:51 +00006054
cristy3ed852e2009-09-05 21:47:34 +00006055 else
6056 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006057
cristy3ed852e2009-09-05 21:47:34 +00006058 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6059 {
6060 image->delay=frame_delay;
6061 frame_delay=default_frame_delay;
6062 }
glennrp0fe50b42010-11-16 03:52:51 +00006063
cristy3ed852e2009-09-05 21:47:34 +00006064 else
6065 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006066
cristy3ed852e2009-09-05 21:47:34 +00006067 image->page.width=mng_info->mng_width;
6068 image->page.height=mng_info->mng_height;
6069 image->page.x=mng_info->x_off[object_id];
6070 image->page.y=mng_info->y_off[object_id];
6071 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006072
cristy3ed852e2009-09-05 21:47:34 +00006073 /*
6074 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6075 */
glennrp47b9dd52010-11-24 18:12:06 +00006076
cristy3ed852e2009-09-05 21:47:34 +00006077 if (logging != MagickFalse)
6078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6079 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6080 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006081
cristybb503372010-05-27 20:51:26 +00006082 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006083
cristy3ed852e2009-09-05 21:47:34 +00006084 if (offset < 0)
6085 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6086 }
6087
6088 previous=image;
6089 mng_info->image=image;
6090 mng_info->mng_type=mng_type;
6091 mng_info->object_id=object_id;
6092
6093 if (memcmp(type,mng_IHDR,4) == 0)
6094 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006095
cristy3ed852e2009-09-05 21:47:34 +00006096#if defined(JNG_SUPPORTED)
6097 else
6098 image=ReadOneJNGImage(mng_info,image_info,exception);
6099#endif
6100
6101 if (image == (Image *) NULL)
6102 {
6103 if (IsImageObject(previous) != MagickFalse)
6104 {
6105 (void) DestroyImageList(previous);
6106 (void) CloseBlob(previous);
6107 }
glennrp47b9dd52010-11-24 18:12:06 +00006108
cristy3ed852e2009-09-05 21:47:34 +00006109 MngInfoFreeStruct(mng_info,&have_mng_structure);
6110 return((Image *) NULL);
6111 }
glennrp0fe50b42010-11-16 03:52:51 +00006112
cristy3ed852e2009-09-05 21:47:34 +00006113 if (image->columns == 0 || image->rows == 0)
6114 {
6115 (void) CloseBlob(image);
6116 image=DestroyImageList(image);
6117 MngInfoFreeStruct(mng_info,&have_mng_structure);
6118 return((Image *) NULL);
6119 }
glennrp0fe50b42010-11-16 03:52:51 +00006120
cristy3ed852e2009-09-05 21:47:34 +00006121 mng_info->image=image;
6122
6123 if (mng_type)
6124 {
6125 MngBox
6126 crop_box;
6127
6128 if (mng_info->magn_methx || mng_info->magn_methy)
6129 {
6130 png_uint_32
6131 magnified_height,
6132 magnified_width;
6133
6134 if (logging != MagickFalse)
6135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6136 " Processing MNG MAGN chunk");
6137
6138 if (mng_info->magn_methx == 1)
6139 {
6140 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006141
cristy3ed852e2009-09-05 21:47:34 +00006142 if (image->columns > 1)
6143 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006144
cristy3ed852e2009-09-05 21:47:34 +00006145 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006146 magnified_width += (png_uint_32)
6147 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006148 }
glennrp47b9dd52010-11-24 18:12:06 +00006149
cristy3ed852e2009-09-05 21:47:34 +00006150 else
6151 {
cristy4e5bc842010-06-09 13:56:01 +00006152 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006153
cristy3ed852e2009-09-05 21:47:34 +00006154 if (image->columns > 1)
6155 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006156
cristy3ed852e2009-09-05 21:47:34 +00006157 if (image->columns > 2)
6158 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006159
cristy3ed852e2009-09-05 21:47:34 +00006160 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006161 magnified_width += (png_uint_32)
6162 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006163 }
glennrp47b9dd52010-11-24 18:12:06 +00006164
cristy3ed852e2009-09-05 21:47:34 +00006165 if (mng_info->magn_methy == 1)
6166 {
6167 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006168
cristy3ed852e2009-09-05 21:47:34 +00006169 if (image->rows > 1)
6170 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006171
cristy3ed852e2009-09-05 21:47:34 +00006172 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006173 magnified_height += (png_uint_32)
6174 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006175 }
glennrp47b9dd52010-11-24 18:12:06 +00006176
cristy3ed852e2009-09-05 21:47:34 +00006177 else
6178 {
cristy4e5bc842010-06-09 13:56:01 +00006179 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006180
cristy3ed852e2009-09-05 21:47:34 +00006181 if (image->rows > 1)
6182 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006183
cristy3ed852e2009-09-05 21:47:34 +00006184 if (image->rows > 2)
6185 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006186
cristy3ed852e2009-09-05 21:47:34 +00006187 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006188 magnified_height += (png_uint_32)
6189 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006190 }
glennrp47b9dd52010-11-24 18:12:06 +00006191
cristy3ed852e2009-09-05 21:47:34 +00006192 if (magnified_height > image->rows ||
6193 magnified_width > image->columns)
6194 {
6195 Image
6196 *large_image;
6197
6198 int
6199 yy;
6200
cristy4c08aed2011-07-01 19:47:50 +00006201 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006202 *next,
6203 *prev;
6204
6205 png_uint_16
6206 magn_methx,
6207 magn_methy;
6208
cristy4c08aed2011-07-01 19:47:50 +00006209 ssize_t
6210 m,
6211 y;
6212
6213 register Quantum
6214 *n,
6215 *q;
6216
6217 register ssize_t
6218 x;
6219
glennrp47b9dd52010-11-24 18:12:06 +00006220 /* Allocate next image structure. */
6221
cristy3ed852e2009-09-05 21:47:34 +00006222 if (logging != MagickFalse)
6223 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6224 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006225
cristy9950d572011-10-01 18:22:35 +00006226 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006227
cristy3ed852e2009-09-05 21:47:34 +00006228 if (GetNextImageInList(image) == (Image *) NULL)
6229 {
6230 image=DestroyImageList(image);
6231 MngInfoFreeStruct(mng_info,&have_mng_structure);
6232 return((Image *) NULL);
6233 }
6234
6235 large_image=SyncNextImageInList(image);
6236
6237 large_image->columns=magnified_width;
6238 large_image->rows=magnified_height;
6239
6240 magn_methx=mng_info->magn_methx;
6241 magn_methy=mng_info->magn_methy;
6242
glennrp3faa9a32011-04-23 14:00:25 +00006243#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006244#define QM unsigned short
6245 if (magn_methx != 1 || magn_methy != 1)
6246 {
6247 /*
6248 Scale pixels to unsigned shorts to prevent
6249 overflow of intermediate values of interpolations
6250 */
cristybb503372010-05-27 20:51:26 +00006251 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006252 {
6253 q=GetAuthenticPixels(image,0,y,image->columns,1,
6254 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006255
cristybb503372010-05-27 20:51:26 +00006256 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006257 {
cristy4c08aed2011-07-01 19:47:50 +00006258 SetPixelRed(image,ScaleQuantumToShort(
6259 GetPixelRed(image,q)),q);
6260 SetPixelGreen(image,ScaleQuantumToShort(
6261 GetPixelGreen(image,q)),q);
6262 SetPixelBlue(image,ScaleQuantumToShort(
6263 GetPixelBlue(image,q)),q);
6264 SetPixelAlpha(image,ScaleQuantumToShort(
6265 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006266 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006267 }
glennrp47b9dd52010-11-24 18:12:06 +00006268
cristy3ed852e2009-09-05 21:47:34 +00006269 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6270 break;
6271 }
6272 }
6273#else
6274#define QM Quantum
6275#endif
6276
6277 if (image->matte != MagickFalse)
cristyea1a8aa2011-10-20 13:24:06 +00006278 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006279
cristy3ed852e2009-09-05 21:47:34 +00006280 else
6281 {
cristy4c08aed2011-07-01 19:47:50 +00006282 large_image->background_color.alpha=OpaqueAlpha;
cristyea1a8aa2011-10-20 13:24:06 +00006283 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006284
cristy3ed852e2009-09-05 21:47:34 +00006285 if (magn_methx == 4)
6286 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006287
cristy3ed852e2009-09-05 21:47:34 +00006288 if (magn_methx == 5)
6289 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006290
cristy3ed852e2009-09-05 21:47:34 +00006291 if (magn_methy == 4)
6292 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006293
cristy3ed852e2009-09-05 21:47:34 +00006294 if (magn_methy == 5)
6295 magn_methy=3;
6296 }
6297
6298 /* magnify the rows into the right side of the large image */
6299
6300 if (logging != MagickFalse)
6301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006302 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006303 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006304 yy=0;
cristy8a20fa02011-12-27 15:54:31 +00006305 length=(size_t) image->columns*GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00006306 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6307 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006308
cristy4c08aed2011-07-01 19:47:50 +00006309 if ((prev == (Quantum *) NULL) ||
6310 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006311 {
6312 image=DestroyImageList(image);
6313 MngInfoFreeStruct(mng_info,&have_mng_structure);
6314 ThrowReaderException(ResourceLimitError,
6315 "MemoryAllocationFailed");
6316 }
glennrp47b9dd52010-11-24 18:12:06 +00006317
cristy3ed852e2009-09-05 21:47:34 +00006318 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6319 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006320
cristybb503372010-05-27 20:51:26 +00006321 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006322 {
6323 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006324 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006325
cristybb503372010-05-27 20:51:26 +00006326 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6327 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006328
cristybb503372010-05-27 20:51:26 +00006329 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6330 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006331
cristybb503372010-05-27 20:51:26 +00006332 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006333 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006334
cristy3ed852e2009-09-05 21:47:34 +00006335 else
cristybb503372010-05-27 20:51:26 +00006336 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006337
cristy3ed852e2009-09-05 21:47:34 +00006338 n=prev;
6339 prev=next;
6340 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006341
cristybb503372010-05-27 20:51:26 +00006342 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006343 {
6344 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6345 exception);
6346 (void) CopyMagickMemory(next,n,length);
6347 }
glennrp47b9dd52010-11-24 18:12:06 +00006348
cristy3ed852e2009-09-05 21:47:34 +00006349 for (i=0; i < m; i++, yy++)
6350 {
cristy4c08aed2011-07-01 19:47:50 +00006351 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006352 *pixels;
6353
cristybb503372010-05-27 20:51:26 +00006354 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006355 pixels=prev;
6356 n=next;
6357 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006358 1,exception);
cristy97707062011-12-27 18:25:00 +00006359 q+=(large_image->columns-image->columns)*
6360 GetPixelChannels(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006361
cristybb503372010-05-27 20:51:26 +00006362 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006363 {
glennrpfd05d622011-02-25 04:10:33 +00006364 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006365 /*
6366 if (image->storage_class == PseudoClass)
6367 {
6368 }
6369 */
6370
6371 if (magn_methy <= 1)
6372 {
glennrpbb4f99d2011-05-22 11:13:17 +00006373 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006374 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
glennrp847370c2011-07-05 17:37:15 +00006375 SetPixelGreen(large_image,GetPixelGreen(image,
6376 pixels),q);
6377 SetPixelBlue(large_image,GetPixelBlue(image,
6378 pixels),q);
6379 SetPixelAlpha(large_image,GetPixelAlpha(image,
6380 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006381 }
glennrp47b9dd52010-11-24 18:12:06 +00006382
cristy3ed852e2009-09-05 21:47:34 +00006383 else if (magn_methy == 2 || magn_methy == 4)
6384 {
6385 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006386 {
glennrp847370c2011-07-05 17:37:15 +00006387 SetPixelRed(large_image,GetPixelRed(image,
6388 pixels),q);
6389 SetPixelGreen(large_image,GetPixelGreen(image,
6390 pixels),q);
6391 SetPixelBlue(large_image,GetPixelBlue(image,
6392 pixels),q);
6393 SetPixelAlpha(large_image,GetPixelAlpha(image,
6394 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006395 }
glennrp47b9dd52010-11-24 18:12:06 +00006396
cristy3ed852e2009-09-05 21:47:34 +00006397 else
6398 {
6399 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006400 SetPixelRed(large_image,((QM) (((ssize_t)
6401 (2*i*(GetPixelRed(image,n)
6402 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006403 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006404 +GetPixelRed(image,pixels)))),q);
6405 SetPixelGreen(large_image,((QM) (((ssize_t)
6406 (2*i*(GetPixelGreen(image,n)
6407 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006408 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006409 +GetPixelGreen(image,pixels)))),q);
6410 SetPixelBlue(large_image,((QM) (((ssize_t)
6411 (2*i*(GetPixelBlue(image,n)
6412 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006413 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006414 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006415
cristy3ed852e2009-09-05 21:47:34 +00006416 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006417 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6418 (2*i*(GetPixelAlpha(image,n)
6419 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006420 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006421 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006422 }
glennrp47b9dd52010-11-24 18:12:06 +00006423
cristy3ed852e2009-09-05 21:47:34 +00006424 if (magn_methy == 4)
6425 {
6426 /* Replicate nearest */
6427 if (i <= ((m+1) << 1))
glennrp847370c2011-07-05 17:37:15 +00006428 SetPixelAlpha(large_image,GetPixelAlpha(image,
6429 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006430 else
glennrp847370c2011-07-05 17:37:15 +00006431 SetPixelAlpha(large_image,GetPixelAlpha(image,
6432 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006433 }
6434 }
glennrp47b9dd52010-11-24 18:12:06 +00006435
cristy3ed852e2009-09-05 21:47:34 +00006436 else /* if (magn_methy == 3 || magn_methy == 5) */
6437 {
6438 /* Replicate nearest */
6439 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006440 {
glennrp847370c2011-07-05 17:37:15 +00006441 SetPixelRed(large_image,GetPixelRed(image,
6442 pixels),q);
6443 SetPixelGreen(large_image,GetPixelGreen(image,
6444 pixels),q);
6445 SetPixelBlue(large_image,GetPixelBlue(image,
6446 pixels),q);
6447 SetPixelAlpha(large_image,GetPixelAlpha(image,
6448 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006449 }
glennrp47b9dd52010-11-24 18:12:06 +00006450
cristy3ed852e2009-09-05 21:47:34 +00006451 else
glennrpbb4f99d2011-05-22 11:13:17 +00006452 {
cristy4c08aed2011-07-01 19:47:50 +00006453 SetPixelRed(large_image,GetPixelRed(image,n),q);
glennrp847370c2011-07-05 17:37:15 +00006454 SetPixelGreen(large_image,GetPixelGreen(image,n),
6455 q);
6456 SetPixelBlue(large_image,GetPixelBlue(image,n),
6457 q);
6458 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6459 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006460 }
glennrp47b9dd52010-11-24 18:12:06 +00006461
cristy3ed852e2009-09-05 21:47:34 +00006462 if (magn_methy == 5)
6463 {
cristy4c08aed2011-07-01 19:47:50 +00006464 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6465 (GetPixelAlpha(image,n)
6466 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006467 +m))/((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006468 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006469 }
6470 }
cristyed231572011-07-14 02:18:59 +00006471 n+=GetPixelChannels(image);
6472 q+=GetPixelChannels(large_image);
6473 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006474 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006475
cristy3ed852e2009-09-05 21:47:34 +00006476 if (SyncAuthenticPixels(large_image,exception) == 0)
6477 break;
glennrp47b9dd52010-11-24 18:12:06 +00006478
cristy3ed852e2009-09-05 21:47:34 +00006479 } /* i */
6480 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006481
cristy4c08aed2011-07-01 19:47:50 +00006482 prev=(Quantum *) RelinquishMagickMemory(prev);
6483 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006484
6485 length=image->columns;
6486
6487 if (logging != MagickFalse)
6488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6489 " Delete original image");
6490
6491 DeleteImageFromList(&image);
6492
6493 image=large_image;
6494
6495 mng_info->image=image;
6496
6497 /* magnify the columns */
6498 if (logging != MagickFalse)
6499 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006500 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006501
cristybb503372010-05-27 20:51:26 +00006502 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006503 {
cristy4c08aed2011-07-01 19:47:50 +00006504 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006505 *pixels;
6506
6507 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristyed231572011-07-14 02:18:59 +00006508 pixels=q+(image->columns-length)*GetPixelChannels(image);
6509 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006510
cristybb503372010-05-27 20:51:26 +00006511 for (x=(ssize_t) (image->columns-length);
6512 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006513 {
cristyed231572011-07-14 02:18:59 +00006514 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006515
cristybb503372010-05-27 20:51:26 +00006516 if (x == (ssize_t) (image->columns-length))
6517 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006518
cristybb503372010-05-27 20:51:26 +00006519 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6520 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006521
cristybb503372010-05-27 20:51:26 +00006522 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6523 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006524
cristybb503372010-05-27 20:51:26 +00006525 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006526 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006527
cristy3ed852e2009-09-05 21:47:34 +00006528 else
cristybb503372010-05-27 20:51:26 +00006529 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006530
cristy3ed852e2009-09-05 21:47:34 +00006531 for (i=0; i < m; i++)
6532 {
6533 if (magn_methx <= 1)
6534 {
6535 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006536 SetPixelRed(image,GetPixelRed(image,pixels),q);
6537 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6538 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6539 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006540 }
glennrp47b9dd52010-11-24 18:12:06 +00006541
cristy3ed852e2009-09-05 21:47:34 +00006542 else if (magn_methx == 2 || magn_methx == 4)
6543 {
6544 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006545 {
cristy4c08aed2011-07-01 19:47:50 +00006546 SetPixelRed(image,GetPixelRed(image,pixels),q);
6547 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6548 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6549 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006550 }
glennrp47b9dd52010-11-24 18:12:06 +00006551
cristyed231572011-07-14 02:18:59 +00006552 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006553 else
6554 {
6555 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006556 SetPixelRed(image,(QM) ((2*i*(
6557 GetPixelRed(image,n)
6558 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006559 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006560 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006561
cristy4c08aed2011-07-01 19:47:50 +00006562 SetPixelGreen(image,(QM) ((2*i*(
6563 GetPixelGreen(image,n)
6564 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006565 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006566 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006567
cristy4c08aed2011-07-01 19:47:50 +00006568 SetPixelBlue(image,(QM) ((2*i*(
6569 GetPixelBlue(image,n)
6570 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006571 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006572 GetPixelBlue(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006573 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006574 SetPixelAlpha(image,(QM) ((2*i*(
6575 GetPixelAlpha(image,n)
6576 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006577 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006578 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006579 }
glennrp47b9dd52010-11-24 18:12:06 +00006580
cristy3ed852e2009-09-05 21:47:34 +00006581 if (magn_methx == 4)
6582 {
6583 /* Replicate nearest */
6584 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006585 {
cristy4c08aed2011-07-01 19:47:50 +00006586 SetPixelAlpha(image,
6587 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006588 }
cristy3ed852e2009-09-05 21:47:34 +00006589 else
glennrpbb4f99d2011-05-22 11:13:17 +00006590 {
cristy4c08aed2011-07-01 19:47:50 +00006591 SetPixelAlpha(image,
6592 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006593 }
cristy3ed852e2009-09-05 21:47:34 +00006594 }
6595 }
glennrp47b9dd52010-11-24 18:12:06 +00006596
cristy3ed852e2009-09-05 21:47:34 +00006597 else /* if (magn_methx == 3 || magn_methx == 5) */
6598 {
6599 /* Replicate nearest */
6600 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006601 {
cristy4c08aed2011-07-01 19:47:50 +00006602 SetPixelRed(image,GetPixelRed(image,pixels),q);
6603 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6604 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6605 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006606 }
glennrp47b9dd52010-11-24 18:12:06 +00006607
cristy3ed852e2009-09-05 21:47:34 +00006608 else
glennrpbb4f99d2011-05-22 11:13:17 +00006609 {
cristy4c08aed2011-07-01 19:47:50 +00006610 SetPixelRed(image,GetPixelRed(image,n),q);
6611 SetPixelGreen(image,GetPixelGreen(image,n),q);
6612 SetPixelBlue(image,GetPixelBlue(image,n),q);
6613 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006614 }
glennrp47b9dd52010-11-24 18:12:06 +00006615
cristy3ed852e2009-09-05 21:47:34 +00006616 if (magn_methx == 5)
6617 {
6618 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006619 SetPixelAlpha(image,
6620 (QM) ((2*i*( GetPixelAlpha(image,n)
6621 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006622 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006623 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006624 }
6625 }
cristyed231572011-07-14 02:18:59 +00006626 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006627 }
cristyed231572011-07-14 02:18:59 +00006628 n+=GetPixelChannels(image);
6629 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006630 }
glennrp47b9dd52010-11-24 18:12:06 +00006631
cristy3ed852e2009-09-05 21:47:34 +00006632 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6633 break;
6634 }
glennrp3faa9a32011-04-23 14:00:25 +00006635#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006636 if (magn_methx != 1 || magn_methy != 1)
6637 {
6638 /*
6639 Rescale pixels to Quantum
6640 */
cristybb503372010-05-27 20:51:26 +00006641 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006642 {
6643 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006644
cristybb503372010-05-27 20:51:26 +00006645 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006646 {
cristy4c08aed2011-07-01 19:47:50 +00006647 SetPixelRed(image,ScaleShortToQuantum(
6648 GetPixelRed(image,q)),q);
6649 SetPixelGreen(image,ScaleShortToQuantum(
6650 GetPixelGreen(image,q)),q);
6651 SetPixelBlue(image,ScaleShortToQuantum(
6652 GetPixelBlue(image,q)),q);
6653 SetPixelAlpha(image,ScaleShortToQuantum(
6654 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006655 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006656 }
glennrp47b9dd52010-11-24 18:12:06 +00006657
cristy3ed852e2009-09-05 21:47:34 +00006658 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6659 break;
6660 }
6661 }
6662#endif
6663 if (logging != MagickFalse)
6664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6665 " Finished MAGN processing");
6666 }
6667 }
6668
6669 /*
6670 Crop_box is with respect to the upper left corner of the MNG.
6671 */
6672 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6673 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6674 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6675 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6676 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6677 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6678 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6679 if ((crop_box.left != (mng_info->image_box.left
6680 +mng_info->x_off[object_id])) ||
6681 (crop_box.right != (mng_info->image_box.right
6682 +mng_info->x_off[object_id])) ||
6683 (crop_box.top != (mng_info->image_box.top
6684 +mng_info->y_off[object_id])) ||
6685 (crop_box.bottom != (mng_info->image_box.bottom
6686 +mng_info->y_off[object_id])))
6687 {
6688 if (logging != MagickFalse)
6689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6690 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006691
cristy3ed852e2009-09-05 21:47:34 +00006692 if ((crop_box.left < crop_box.right) &&
6693 (crop_box.top < crop_box.bottom))
6694 {
6695 Image
6696 *im;
6697
6698 RectangleInfo
6699 crop_info;
6700
6701 /*
6702 Crop_info is with respect to the upper left corner of
6703 the image.
6704 */
6705 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6706 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006707 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6708 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006709 image->page.width=image->columns;
6710 image->page.height=image->rows;
6711 image->page.x=0;
6712 image->page.y=0;
6713 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006714
cristy3ed852e2009-09-05 21:47:34 +00006715 if (im != (Image *) NULL)
6716 {
6717 image->columns=im->columns;
6718 image->rows=im->rows;
6719 im=DestroyImage(im);
6720 image->page.width=image->columns;
6721 image->page.height=image->rows;
6722 image->page.x=crop_box.left;
6723 image->page.y=crop_box.top;
6724 }
6725 }
glennrp47b9dd52010-11-24 18:12:06 +00006726
cristy3ed852e2009-09-05 21:47:34 +00006727 else
6728 {
6729 /*
6730 No pixels in crop area. The MNG spec still requires
6731 a layer, though, so make a single transparent pixel in
6732 the top left corner.
6733 */
6734 image->columns=1;
6735 image->rows=1;
6736 image->colors=2;
cristyea1a8aa2011-10-20 13:24:06 +00006737 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006738 image->page.width=1;
6739 image->page.height=1;
6740 image->page.x=0;
6741 image->page.y=0;
6742 }
6743 }
6744#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6745 image=mng_info->image;
6746#endif
6747 }
6748
glennrp2b013e42010-11-24 16:55:50 +00006749#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6750 /* PNG does not handle depths greater than 16 so reduce it even
cristy897f2bf2012-01-05 02:03:44 +00006751 * if lossy
glennrp2b013e42010-11-24 16:55:50 +00006752 */
6753 if (image->depth > 16)
6754 image->depth=16;
6755#endif
6756
glennrp3faa9a32011-04-23 14:00:25 +00006757#if (MAGICKCORE_QUANTUM_DEPTH > 8)
cristyc82a27b2011-10-21 01:07:16 +00006758 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00006759 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006760#endif
glennrpd6afd542010-11-19 01:53:05 +00006761
cristy3ed852e2009-09-05 21:47:34 +00006762 if (image_info->number_scenes != 0)
6763 {
6764 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006765 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006766 break;
6767 }
glennrpd6afd542010-11-19 01:53:05 +00006768
cristy3ed852e2009-09-05 21:47:34 +00006769 if (logging != MagickFalse)
6770 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6771 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006772
cristy3ed852e2009-09-05 21:47:34 +00006773 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006774
cristy3ed852e2009-09-05 21:47:34 +00006775 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006776
cristy3ed852e2009-09-05 21:47:34 +00006777 if (logging != MagickFalse)
6778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6779 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006780
cristy3ed852e2009-09-05 21:47:34 +00006781#if defined(MNG_INSERT_LAYERS)
6782 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6783 (mng_info->mng_height))
6784 {
6785 /*
6786 Insert a background layer if nothing else was found.
6787 */
6788 if (logging != MagickFalse)
6789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6790 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006791
cristy4c08aed2011-07-01 19:47:50 +00006792 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006793 {
6794 /*
6795 Allocate next image structure.
6796 */
cristy9950d572011-10-01 18:22:35 +00006797 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006798 if (GetNextImageInList(image) == (Image *) NULL)
6799 {
6800 image=DestroyImageList(image);
6801 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006802
cristy3ed852e2009-09-05 21:47:34 +00006803 if (logging != MagickFalse)
6804 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6805 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006806
cristy3ed852e2009-09-05 21:47:34 +00006807 return((Image *) NULL);
6808 }
6809 image=SyncNextImageInList(image);
6810 }
6811 image->columns=mng_info->mng_width;
6812 image->rows=mng_info->mng_height;
6813 image->page.width=mng_info->mng_width;
6814 image->page.height=mng_info->mng_height;
6815 image->page.x=0;
6816 image->page.y=0;
6817 image->background_color=mng_background_color;
6818 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006819
cristy3ed852e2009-09-05 21:47:34 +00006820 if (image_info->ping == MagickFalse)
cristyea1a8aa2011-10-20 13:24:06 +00006821 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006822
cristy3ed852e2009-09-05 21:47:34 +00006823 mng_info->image_found++;
6824 }
6825#endif
6826 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006827
cristy3ed852e2009-09-05 21:47:34 +00006828 if (mng_iterations == 1)
6829 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006830
cristy3ed852e2009-09-05 21:47:34 +00006831 while (GetPreviousImageInList(image) != (Image *) NULL)
6832 {
6833 image_count++;
6834 if (image_count > 10*mng_info->image_found)
6835 {
6836 if (logging != MagickFalse)
6837 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006838
cristyc82a27b2011-10-21 01:07:16 +00006839 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006840 CoderError,"Linked list is corrupted, beginning of list not found",
6841 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006842
cristy3ed852e2009-09-05 21:47:34 +00006843 return((Image *) NULL);
6844 }
glennrp0fe50b42010-11-16 03:52:51 +00006845
cristy3ed852e2009-09-05 21:47:34 +00006846 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006847
cristy3ed852e2009-09-05 21:47:34 +00006848 if (GetNextImageInList(image) == (Image *) NULL)
6849 {
6850 if (logging != MagickFalse)
6851 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006852
cristyc82a27b2011-10-21 01:07:16 +00006853 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006854 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6855 image_info->filename);
6856 }
6857 }
glennrp47b9dd52010-11-24 18:12:06 +00006858
cristy3ed852e2009-09-05 21:47:34 +00006859 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6860 GetNextImageInList(image) ==
6861 (Image *) NULL)
6862 {
6863 if (logging != MagickFalse)
6864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6865 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006866
cristyc82a27b2011-10-21 01:07:16 +00006867 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006868 CoderError,"image->next for first image is NULL but shouldn't be.",
6869 "`%s'",image_info->filename);
6870 }
glennrp47b9dd52010-11-24 18:12:06 +00006871
cristy3ed852e2009-09-05 21:47:34 +00006872 if (mng_info->image_found == 0)
6873 {
6874 if (logging != MagickFalse)
6875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6876 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006877
cristyc82a27b2011-10-21 01:07:16 +00006878 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006879 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006880
cristy3ed852e2009-09-05 21:47:34 +00006881 if (image != (Image *) NULL)
6882 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006883
cristy3ed852e2009-09-05 21:47:34 +00006884 MngInfoFreeStruct(mng_info,&have_mng_structure);
6885 return((Image *) NULL);
6886 }
6887
6888 if (mng_info->ticks_per_second)
6889 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6890 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006891
cristy3ed852e2009-09-05 21:47:34 +00006892 else
6893 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006894
cristy3ed852e2009-09-05 21:47:34 +00006895 /* Find final nonzero image delay */
6896 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006897
cristy3ed852e2009-09-05 21:47:34 +00006898 while (GetNextImageInList(image) != (Image *) NULL)
6899 {
6900 if (image->delay)
6901 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006902
cristy3ed852e2009-09-05 21:47:34 +00006903 image=GetNextImageInList(image);
6904 }
glennrp0fe50b42010-11-16 03:52:51 +00006905
cristy3ed852e2009-09-05 21:47:34 +00006906 if (final_delay < final_image_delay)
6907 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006908
cristy3ed852e2009-09-05 21:47:34 +00006909 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006910
cristy3ed852e2009-09-05 21:47:34 +00006911 if (logging != MagickFalse)
6912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006913 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6914 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006915
cristy3ed852e2009-09-05 21:47:34 +00006916 if (logging != MagickFalse)
6917 {
6918 int
6919 scene;
6920
6921 scene=0;
6922 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006923
cristy3ed852e2009-09-05 21:47:34 +00006924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6925 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006926
cristy3ed852e2009-09-05 21:47:34 +00006927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006928 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006929
cristy3ed852e2009-09-05 21:47:34 +00006930 while (GetNextImageInList(image) != (Image *) NULL)
6931 {
6932 image=GetNextImageInList(image);
6933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006934 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006935 }
6936 }
6937
6938 image=GetFirstImageInList(image);
6939#ifdef MNG_COALESCE_LAYERS
6940 if (insert_layers)
6941 {
6942 Image
6943 *next_image,
6944 *next;
6945
cristybb503372010-05-27 20:51:26 +00006946 size_t
cristy3ed852e2009-09-05 21:47:34 +00006947 scene;
6948
6949 if (logging != MagickFalse)
6950 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006951
cristy3ed852e2009-09-05 21:47:34 +00006952 scene=image->scene;
cristyc82a27b2011-10-21 01:07:16 +00006953 next_image=CoalesceImages(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006954
cristy3ed852e2009-09-05 21:47:34 +00006955 if (next_image == (Image *) NULL)
6956 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006957
cristy3ed852e2009-09-05 21:47:34 +00006958 image=DestroyImageList(image);
6959 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006960
cristy3ed852e2009-09-05 21:47:34 +00006961 for (next=image; next != (Image *) NULL; next=next_image)
6962 {
6963 next->page.width=mng_info->mng_width;
6964 next->page.height=mng_info->mng_height;
6965 next->page.x=0;
6966 next->page.y=0;
6967 next->scene=scene++;
6968 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006969
cristy3ed852e2009-09-05 21:47:34 +00006970 if (next_image == (Image *) NULL)
6971 break;
glennrp47b9dd52010-11-24 18:12:06 +00006972
cristy3ed852e2009-09-05 21:47:34 +00006973 if (next->delay == 0)
6974 {
6975 scene--;
6976 next_image->previous=GetPreviousImageInList(next);
6977 if (GetPreviousImageInList(next) == (Image *) NULL)
6978 image=next_image;
6979 else
6980 next->previous->next=next_image;
6981 next=DestroyImage(next);
6982 }
6983 }
6984 }
6985#endif
6986
6987 while (GetNextImageInList(image) != (Image *) NULL)
6988 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006989
cristy3ed852e2009-09-05 21:47:34 +00006990 image->dispose=BackgroundDispose;
6991
6992 if (logging != MagickFalse)
6993 {
6994 int
6995 scene;
6996
6997 scene=0;
6998 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006999
cristy3ed852e2009-09-05 21:47:34 +00007000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7001 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00007002
cristy3ed852e2009-09-05 21:47:34 +00007003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007004 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7005 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00007006
cristy3ed852e2009-09-05 21:47:34 +00007007 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00007008 {
7009 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007010
cristyf2faecf2010-05-28 19:19:36 +00007011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007012 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7013 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00007014 }
7015 }
glennrp47b9dd52010-11-24 18:12:06 +00007016
cristy3ed852e2009-09-05 21:47:34 +00007017 image=GetFirstImageInList(image);
7018 MngInfoFreeStruct(mng_info,&have_mng_structure);
7019 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00007020
cristy3ed852e2009-09-05 21:47:34 +00007021 if (logging != MagickFalse)
7022 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00007023
cristy3ed852e2009-09-05 21:47:34 +00007024 return(GetFirstImageInList(image));
7025}
glennrp25c1e2b2010-03-25 01:39:56 +00007026#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007027static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7028{
7029 printf("Your PNG library is too old: You have libpng-%s\n",
7030 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00007031
cristy3ed852e2009-09-05 21:47:34 +00007032 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7033 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007034
cristy3ed852e2009-09-05 21:47:34 +00007035 return(Image *) NULL;
7036}
glennrp47b9dd52010-11-24 18:12:06 +00007037
cristy3ed852e2009-09-05 21:47:34 +00007038static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7039{
7040 return(ReadPNGImage(image_info,exception));
7041}
glennrp25c1e2b2010-03-25 01:39:56 +00007042#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007043#endif
7044
7045/*
7046%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7047% %
7048% %
7049% %
7050% R e g i s t e r P N G I m a g e %
7051% %
7052% %
7053% %
7054%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7055%
7056% RegisterPNGImage() adds properties for the PNG image format to
7057% the list of supported formats. The properties include the image format
7058% tag, a method to read and/or write the format, whether the format
7059% supports the saving of more than one frame to the same file or blob,
7060% whether the format supports native in-memory I/O, and a brief
7061% description of the format.
7062%
7063% The format of the RegisterPNGImage method is:
7064%
cristybb503372010-05-27 20:51:26 +00007065% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007066%
7067*/
cristybb503372010-05-27 20:51:26 +00007068ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007069{
7070 char
7071 version[MaxTextExtent];
7072
7073 MagickInfo
7074 *entry;
7075
7076 static const char
7077 *PNGNote=
7078 {
7079 "See http://www.libpng.org/ for details about the PNG format."
7080 },
glennrp47b9dd52010-11-24 18:12:06 +00007081
cristy3ed852e2009-09-05 21:47:34 +00007082 *JNGNote=
7083 {
7084 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7085 "format."
7086 },
glennrp47b9dd52010-11-24 18:12:06 +00007087
cristy3ed852e2009-09-05 21:47:34 +00007088 *MNGNote=
7089 {
7090 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7091 "format."
7092 };
7093
7094 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007095
cristy3ed852e2009-09-05 21:47:34 +00007096#if defined(PNG_LIBPNG_VER_STRING)
7097 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7098 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007099
cristy3ed852e2009-09-05 21:47:34 +00007100 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7101 {
7102 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7103 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7104 MaxTextExtent);
7105 }
7106#endif
glennrp47b9dd52010-11-24 18:12:06 +00007107
cristy3ed852e2009-09-05 21:47:34 +00007108 entry=SetMagickInfo("MNG");
7109 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007110
cristy3ed852e2009-09-05 21:47:34 +00007111#if defined(MAGICKCORE_PNG_DELEGATE)
7112 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7113 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7114#endif
glennrp47b9dd52010-11-24 18:12:06 +00007115
cristy3ed852e2009-09-05 21:47:34 +00007116 entry->magick=(IsImageFormatHandler *) IsMNG;
7117 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007118
cristy3ed852e2009-09-05 21:47:34 +00007119 if (*version != '\0')
7120 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007121
cristy3ed852e2009-09-05 21:47:34 +00007122 entry->module=ConstantString("PNG");
7123 entry->note=ConstantString(MNGNote);
7124 (void) RegisterMagickInfo(entry);
7125
7126 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007127
cristy3ed852e2009-09-05 21:47:34 +00007128#if defined(MAGICKCORE_PNG_DELEGATE)
7129 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7130 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7131#endif
glennrp47b9dd52010-11-24 18:12:06 +00007132
cristy3ed852e2009-09-05 21:47:34 +00007133 entry->magick=(IsImageFormatHandler *) IsPNG;
7134 entry->adjoin=MagickFalse;
7135 entry->description=ConstantString("Portable Network Graphics");
7136 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007137
cristy3ed852e2009-09-05 21:47:34 +00007138 if (*version != '\0')
7139 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007140
cristy3ed852e2009-09-05 21:47:34 +00007141 entry->note=ConstantString(PNGNote);
7142 (void) RegisterMagickInfo(entry);
7143
7144 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007145
cristy3ed852e2009-09-05 21:47:34 +00007146#if defined(MAGICKCORE_PNG_DELEGATE)
7147 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7148 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7149#endif
glennrp47b9dd52010-11-24 18:12:06 +00007150
cristy3ed852e2009-09-05 21:47:34 +00007151 entry->magick=(IsImageFormatHandler *) IsPNG;
7152 entry->adjoin=MagickFalse;
7153 entry->description=ConstantString(
7154 "8-bit indexed with optional binary transparency");
7155 entry->module=ConstantString("PNG");
7156 (void) RegisterMagickInfo(entry);
7157
7158 entry=SetMagickInfo("PNG24");
7159 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007160
cristy3ed852e2009-09-05 21:47:34 +00007161#if defined(ZLIB_VERSION)
7162 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7163 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007164
cristy3ed852e2009-09-05 21:47:34 +00007165 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7166 {
7167 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7168 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7169 }
7170#endif
glennrp47b9dd52010-11-24 18:12:06 +00007171
cristy3ed852e2009-09-05 21:47:34 +00007172 if (*version != '\0')
7173 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007174
cristy3ed852e2009-09-05 21:47:34 +00007175#if defined(MAGICKCORE_PNG_DELEGATE)
7176 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7177 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7178#endif
glennrp47b9dd52010-11-24 18:12:06 +00007179
cristy3ed852e2009-09-05 21:47:34 +00007180 entry->magick=(IsImageFormatHandler *) IsPNG;
7181 entry->adjoin=MagickFalse;
7182 entry->description=ConstantString("opaque 24-bit RGB");
7183 entry->module=ConstantString("PNG");
7184 (void) RegisterMagickInfo(entry);
7185
7186 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007187
cristy3ed852e2009-09-05 21:47:34 +00007188#if defined(MAGICKCORE_PNG_DELEGATE)
7189 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7190 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7191#endif
glennrp47b9dd52010-11-24 18:12:06 +00007192
cristy3ed852e2009-09-05 21:47:34 +00007193 entry->magick=(IsImageFormatHandler *) IsPNG;
7194 entry->adjoin=MagickFalse;
7195 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7196 entry->module=ConstantString("PNG");
7197 (void) RegisterMagickInfo(entry);
7198
7199 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007200
cristy3ed852e2009-09-05 21:47:34 +00007201#if defined(JNG_SUPPORTED)
7202#if defined(MAGICKCORE_PNG_DELEGATE)
7203 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7204 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7205#endif
7206#endif
glennrp47b9dd52010-11-24 18:12:06 +00007207
cristy3ed852e2009-09-05 21:47:34 +00007208 entry->magick=(IsImageFormatHandler *) IsJNG;
7209 entry->adjoin=MagickFalse;
7210 entry->description=ConstantString("JPEG Network Graphics");
7211 entry->module=ConstantString("PNG");
7212 entry->note=ConstantString(JNGNote);
7213 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007214
cristy18b17442009-10-25 18:36:48 +00007215#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007216 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007217#endif
glennrp47b9dd52010-11-24 18:12:06 +00007218
cristy3ed852e2009-09-05 21:47:34 +00007219 return(MagickImageCoderSignature);
7220}
7221
7222/*
7223%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7224% %
7225% %
7226% %
7227% U n r e g i s t e r P N G I m a g e %
7228% %
7229% %
7230% %
7231%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7232%
7233% UnregisterPNGImage() removes format registrations made by the
7234% PNG module from the list of supported formats.
7235%
7236% The format of the UnregisterPNGImage method is:
7237%
7238% UnregisterPNGImage(void)
7239%
7240*/
7241ModuleExport void UnregisterPNGImage(void)
7242{
7243 (void) UnregisterMagickInfo("MNG");
7244 (void) UnregisterMagickInfo("PNG");
7245 (void) UnregisterMagickInfo("PNG8");
7246 (void) UnregisterMagickInfo("PNG24");
7247 (void) UnregisterMagickInfo("PNG32");
7248 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007249
cristy3ed852e2009-09-05 21:47:34 +00007250#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007251 if (ping_semaphore != (SemaphoreInfo *) NULL)
7252 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007253#endif
7254}
7255
7256#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007257#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007258/*
7259%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7260% %
7261% %
7262% %
7263% W r i t e M N G I m a g e %
7264% %
7265% %
7266% %
7267%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7268%
7269% WriteMNGImage() writes an image in the Portable Network Graphics
7270% Group's "Multiple-image Network Graphics" encoded image format.
7271%
7272% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7273%
7274% The format of the WriteMNGImage method is:
7275%
cristy1e178e72011-08-28 19:44:34 +00007276% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7277% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007278%
7279% A description of each parameter follows.
7280%
7281% o image_info: the image info.
7282%
7283% o image: The image.
7284%
cristy1e178e72011-08-28 19:44:34 +00007285% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007286%
7287% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7288% "To do" under ReadPNGImage):
7289%
cristy3ed852e2009-09-05 21:47:34 +00007290% Preserve all unknown and not-yet-handled known chunks found in input
7291% PNG file and copy them into output PNG files according to the PNG
7292% copying rules.
7293%
7294% Write the iCCP chunk at MNG level when (icc profile length > 0)
7295%
7296% Improve selection of color type (use indexed-colour or indexed-colour
7297% with tRNS when 256 or fewer unique RGBA values are present).
7298%
7299% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7300% This will be complicated if we limit ourselves to generating MNG-LC
7301% files. For now we ignore disposal method 3 and simply overlay the next
7302% image on it.
7303%
7304% Check for identical PLTE's or PLTE/tRNS combinations and use a
7305% global MNG PLTE or PLTE/tRNS combination when appropriate.
7306% [mostly done 15 June 1999 but still need to take care of tRNS]
7307%
7308% Check for identical sRGB and replace with a global sRGB (and remove
7309% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7310% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7311% local gAMA/cHRM with local sRGB if appropriate).
7312%
7313% Check for identical sBIT chunks and write global ones.
7314%
7315% Provide option to skip writing the signature tEXt chunks.
7316%
7317% Use signatures to detect identical objects and reuse the first
7318% instance of such objects instead of writing duplicate objects.
7319%
7320% Use a smaller-than-32k value of compression window size when
7321% appropriate.
7322%
7323% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7324% ancillary text chunks and save profiles.
7325%
7326% Provide an option to force LC files (to ensure exact framing rate)
7327% instead of VLC.
7328%
7329% Provide an option to force VLC files instead of LC, even when offsets
7330% are present. This will involve expanding the embedded images with a
7331% transparent region at the top and/or left.
7332*/
7333
cristy3ed852e2009-09-05 21:47:34 +00007334static void
glennrpcf002022011-01-30 02:38:15 +00007335Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007336 png_info *ping_info, unsigned char *profile_type, unsigned char
7337 *profile_description, unsigned char *profile_data, png_uint_32 length)
7338{
cristy3ed852e2009-09-05 21:47:34 +00007339 png_textp
7340 text;
7341
cristybb503372010-05-27 20:51:26 +00007342 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007343 i;
7344
7345 unsigned char
7346 *sp;
7347
7348 png_charp
7349 dp;
7350
7351 png_uint_32
7352 allocated_length,
7353 description_length;
7354
7355 unsigned char
7356 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007357
cristy3ed852e2009-09-05 21:47:34 +00007358 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7359 return;
7360
7361 if (image_info->verbose)
7362 {
glennrp0fe50b42010-11-16 03:52:51 +00007363 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7364 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007365 }
glennrp0fe50b42010-11-16 03:52:51 +00007366
cristy3ed852e2009-09-05 21:47:34 +00007367 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7368 description_length=(png_uint_32) strlen((const char *) profile_description);
7369 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7370 + description_length);
7371 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7372 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7373 text[0].key[0]='\0';
7374 (void) ConcatenateMagickString(text[0].key,
7375 "Raw profile type ",MaxTextExtent);
7376 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7377 sp=profile_data;
7378 dp=text[0].text;
7379 *dp++='\n';
7380 (void) CopyMagickString(dp,(const char *) profile_description,
7381 allocated_length);
7382 dp+=description_length;
7383 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007384 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007385 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007386 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007387
cristybb503372010-05-27 20:51:26 +00007388 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007389 {
7390 if (i%36 == 0)
7391 *dp++='\n';
7392 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7393 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7394 }
glennrp47b9dd52010-11-24 18:12:06 +00007395
cristy3ed852e2009-09-05 21:47:34 +00007396 *dp++='\n';
7397 *dp='\0';
7398 text[0].text_length=(png_size_t) (dp-text[0].text);
7399 text[0].compression=image_info->compression == NoCompression ||
7400 (image_info->compression == UndefinedCompression &&
7401 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007402
cristy3ed852e2009-09-05 21:47:34 +00007403 if (text[0].text_length <= allocated_length)
7404 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007405
cristy3ed852e2009-09-05 21:47:34 +00007406 png_free(ping,text[0].text);
7407 png_free(ping,text[0].key);
7408 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007409}
7410
glennrpcf002022011-01-30 02:38:15 +00007411static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007412 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007413{
7414 char
7415 *name;
7416
7417 const StringInfo
7418 *profile;
7419
7420 unsigned char
7421 *data;
7422
7423 png_uint_32 length;
7424
7425 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007426
7427 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7428 {
cristy3ed852e2009-09-05 21:47:34 +00007429 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007430
cristy3ed852e2009-09-05 21:47:34 +00007431 if (profile != (const StringInfo *) NULL)
7432 {
7433 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007434 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007435
glennrp47b9dd52010-11-24 18:12:06 +00007436 if (LocaleNCompare(name,string,11) == 0)
7437 {
7438 if (logging != MagickFalse)
7439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7440 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007441
glennrpcf002022011-01-30 02:38:15 +00007442 ping_profile=CloneStringInfo(profile);
7443 data=GetStringInfoDatum(ping_profile),
7444 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007445 data[4]=data[3];
7446 data[3]=data[2];
7447 data[2]=data[1];
7448 data[1]=data[0];
7449 (void) WriteBlobMSBULong(image,length-5); /* data length */
7450 (void) WriteBlob(image,length-1,data+1);
7451 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007452 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007453 }
cristy3ed852e2009-09-05 21:47:34 +00007454 }
glennrp47b9dd52010-11-24 18:12:06 +00007455
cristy3ed852e2009-09-05 21:47:34 +00007456 name=GetNextImageProfile(image);
7457 }
glennrp47b9dd52010-11-24 18:12:06 +00007458
cristy3ed852e2009-09-05 21:47:34 +00007459 return(MagickTrue);
7460}
7461
glennrpb9cfe272010-12-21 15:08:06 +00007462
cristy3ed852e2009-09-05 21:47:34 +00007463/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007464static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +00007465 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007466{
7467 Image
7468 *image;
7469
7470 ImageInfo
7471 *image_info;
7472
cristy3ed852e2009-09-05 21:47:34 +00007473 char
7474 s[2];
7475
7476 const char
7477 *name,
7478 *property,
7479 *value;
7480
7481 const StringInfo
7482 *profile;
7483
cristy3ed852e2009-09-05 21:47:34 +00007484 int
cristy3ed852e2009-09-05 21:47:34 +00007485 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007486 pass;
7487
glennrpe9c26dc2010-05-30 01:56:35 +00007488 png_byte
7489 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007490
glennrp39992b42010-11-14 00:03:43 +00007491 png_color
7492 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007493
glennrp5af765f2010-03-30 11:12:18 +00007494 png_color_16
7495 ping_background,
7496 ping_trans_color;
7497
cristy3ed852e2009-09-05 21:47:34 +00007498 png_info
7499 *ping_info;
7500
7501 png_struct
7502 *ping;
7503
glennrp5af765f2010-03-30 11:12:18 +00007504 png_uint_32
7505 ping_height,
7506 ping_width;
7507
cristybb503372010-05-27 20:51:26 +00007508 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007509 y;
7510
7511 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007512 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007513 logging,
glennrp58e01762011-01-07 15:28:54 +00007514 matte,
7515
glennrpda8f3a72011-02-27 23:54:12 +00007516 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007517 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007518 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007519 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007520 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007521 ping_have_bKGD,
7522 ping_have_pHYs,
7523 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007524
7525 ping_exclude_bKGD,
7526 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007527 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007528 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007529 ping_exclude_gAMA,
7530 ping_exclude_iCCP,
7531 /* ping_exclude_iTXt, */
7532 ping_exclude_oFFs,
7533 ping_exclude_pHYs,
7534 ping_exclude_sRGB,
7535 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007536 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007537 ping_exclude_vpAg,
7538 ping_exclude_zCCP, /* hex-encoded iCCP */
7539 ping_exclude_zTXt,
7540
glennrp8d3d6e52011-04-19 04:39:51 +00007541 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007542 ping_need_colortype_warning,
7543
glennrp82b3c532011-03-22 19:20:54 +00007544 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007545 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007546 tried_333,
7547 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007548
7549 QuantumInfo
7550 *quantum_info;
7551
cristyc82a27b2011-10-21 01:07:16 +00007552 PNGErrorInfo
7553 error_info;
7554
cristybb503372010-05-27 20:51:26 +00007555 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007556 i,
7557 x;
7558
7559 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007560 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007561
glennrp5af765f2010-03-30 11:12:18 +00007562 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007563 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007564 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007565 ping_color_type,
7566 ping_interlace_method,
7567 ping_compression_method,
7568 ping_filter_method,
7569 ping_num_trans;
7570
cristybb503372010-05-27 20:51:26 +00007571 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007572 image_depth,
7573 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007574
cristybb503372010-05-27 20:51:26 +00007575 size_t
cristy3ed852e2009-09-05 21:47:34 +00007576 quality,
7577 rowbytes,
7578 save_image_depth;
7579
glennrpdfd70802010-11-14 01:23:35 +00007580 int
glennrpfd05d622011-02-25 04:10:33 +00007581 j,
glennrpf09bded2011-01-08 01:15:59 +00007582 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007583 number_opaque,
7584 number_semitransparent,
7585 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007586 ping_pHYs_unit_type;
7587
7588 png_uint_32
7589 ping_pHYs_x_resolution,
7590 ping_pHYs_y_resolution;
7591
cristy3ed852e2009-09-05 21:47:34 +00007592 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007593 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007594
cristyc82a27b2011-10-21 01:07:16 +00007595 image = CloneImage(IMimage,0,0,MagickFalse,exception);
glennrpb9cfe272010-12-21 15:08:06 +00007596 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007597 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007598 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007599
cristy3ed852e2009-09-05 21:47:34 +00007600#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007601 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007602#endif
7603
glennrp5af765f2010-03-30 11:12:18 +00007604 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007605 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007606 ping_color_type=0,
7607 ping_interlace_method=0,
7608 ping_compression_method=0,
7609 ping_filter_method=0,
7610 ping_num_trans = 0;
7611
7612 ping_background.red = 0;
7613 ping_background.green = 0;
7614 ping_background.blue = 0;
7615 ping_background.gray = 0;
7616 ping_background.index = 0;
7617
7618 ping_trans_color.red=0;
7619 ping_trans_color.green=0;
7620 ping_trans_color.blue=0;
7621 ping_trans_color.gray=0;
7622
glennrpdfd70802010-11-14 01:23:35 +00007623 ping_pHYs_unit_type = 0;
7624 ping_pHYs_x_resolution = 0;
7625 ping_pHYs_y_resolution = 0;
7626
glennrpda8f3a72011-02-27 23:54:12 +00007627 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007628 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007629 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007630 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007631 ping_have_bKGD=MagickFalse;
7632 ping_have_pHYs=MagickFalse;
7633 ping_have_tRNS=MagickFalse;
7634
glennrp0e8ea192010-12-24 18:00:33 +00007635 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7636 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007637 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007638 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007639 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007640 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7641 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7642 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7643 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7644 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7645 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007646 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007647 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7648 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7649 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7650
glennrp8d3d6e52011-04-19 04:39:51 +00007651 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007652 ping_need_colortype_warning = MagickFalse;
7653
cristy0d57eec2011-09-04 22:13:56 +00007654 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7655 * i.e., eliminate the ICC profile and set image->rendering_intent.
7656 * Note that this will not involve any changes to the actual pixels
7657 * but merely passes information to applications that read the resulting
7658 * PNG image.
7659 */
7660 if (ping_exclude_sRGB == MagickFalse)
7661 {
7662 char
7663 *name;
7664
7665 const StringInfo
7666 *profile;
7667
7668 ResetImageProfileIterator(image);
7669 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7670 {
7671 profile=GetImageProfile(image,name);
7672
7673 if (profile != (StringInfo *) NULL)
7674 {
7675 if ((LocaleCompare(name,"ICC") == 0) ||
glennrp29a106e2011-09-06 17:11:42 +00007676 (LocaleCompare(name,"ICM") == 0))
7677 {
glennrpee7b4c02011-10-04 01:21:09 +00007678 int
7679 icheck;
7680
7681 /* 0: not a known sRGB profile
7682 * 1: HP-Microsoft sRGB v2
7683 * 2: ICC sRGB v4 perceptual
7684 * 3: ICC sRGB v2 perceptual no black-compensation
7685 */
7686 png_uint_32
7687 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7688 check_len[4] = {0, 3144, 60960, 3052};
7689
7690 png_uint_32
7691 length,
7692 profile_crc;
7693
cristy0d57eec2011-09-04 22:13:56 +00007694 unsigned char
7695 *data;
7696
glennrp29a106e2011-09-06 17:11:42 +00007697 length=(png_uint_32) GetStringInfoLength(profile);
7698
glennrpee7b4c02011-10-04 01:21:09 +00007699 for (icheck=3; icheck > 0; icheck--)
cristy0d57eec2011-09-04 22:13:56 +00007700 {
glennrpee7b4c02011-10-04 01:21:09 +00007701 if (length == check_len[icheck])
glennrp29a106e2011-09-06 17:11:42 +00007702 {
glennrpee7b4c02011-10-04 01:21:09 +00007703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7704 " Got a %lu-byte ICC profile (potentially sRGB)",
7705 (unsigned long) length);
glennrp29a106e2011-09-06 17:11:42 +00007706
glennrpee7b4c02011-10-04 01:21:09 +00007707 data=GetStringInfoDatum(profile);
7708 profile_crc=crc32(0,data,length);
glennrp29a106e2011-09-06 17:11:42 +00007709
glennrpee7b4c02011-10-04 01:21:09 +00007710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe54cd4b2011-10-04 01:31:35 +00007711 " with crc=%8x",(unsigned int) profile_crc);
glennrpee7b4c02011-10-04 01:21:09 +00007712
7713 if (profile_crc == check_crc[icheck])
7714 {
7715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7716 " It is sRGB.");
7717 if (image->rendering_intent==UndefinedIntent)
7718 image->rendering_intent=PerceptualIntent;
7719 break;
7720 }
glennrp29a106e2011-09-06 17:11:42 +00007721 }
glennrp29a106e2011-09-06 17:11:42 +00007722 }
glennrpee7b4c02011-10-04 01:21:09 +00007723 if (icheck == 0)
glennrp29a106e2011-09-06 17:11:42 +00007724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpee7b4c02011-10-04 01:21:09 +00007725 " Got a %lu-byte ICC profile",
glennrp29a106e2011-09-06 17:11:42 +00007726 (unsigned long) length);
7727 }
cristy0d57eec2011-09-04 22:13:56 +00007728 }
7729 name=GetNextImageProfile(image);
7730 }
7731 }
7732
glennrp8bb3a022010-12-13 20:40:04 +00007733 number_opaque = 0;
7734 number_semitransparent = 0;
7735 number_transparent = 0;
7736
glennrpfd05d622011-02-25 04:10:33 +00007737 if (logging != MagickFalse)
7738 {
7739 if (image->storage_class == UndefinedClass)
7740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7741 " storage_class=UndefinedClass");
7742 if (image->storage_class == DirectClass)
7743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7744 " storage_class=DirectClass");
7745 if (image->storage_class == PseudoClass)
7746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7747 " storage_class=PseudoClass");
7748 }
glennrp28af3712011-04-06 18:07:30 +00007749
glennrp7e65e932011-08-19 02:31:16 +00007750 if (image->storage_class == PseudoClass &&
7751 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7752 (mng_info->write_png_colortype != 0 &&
7753 mng_info->write_png_colortype != 4)))
7754 {
cristyea1a8aa2011-10-20 13:24:06 +00007755 (void) SyncImage(image,exception);
glennrp7e65e932011-08-19 02:31:16 +00007756 image->storage_class = DirectClass;
7757 }
7758
glennrpc6c391a2011-04-27 02:23:56 +00007759 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007760 {
glennrpc6c391a2011-04-27 02:23:56 +00007761 if (image->storage_class != PseudoClass && image->colormap != NULL)
7762 {
7763 /* Free the bogus colormap; it can cause trouble later */
7764 if (logging != MagickFalse)
7765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7766 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00007767 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00007768 image->colormap=NULL;
7769 }
glennrp28af3712011-04-06 18:07:30 +00007770 }
glennrpbb4f99d2011-05-22 11:13:17 +00007771
cristy510d06a2011-07-06 23:43:54 +00007772 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristye941a752011-10-15 01:52:48 +00007773 (void) TransformImageColorspace(image,RGBColorspace,exception);
glennrp0fe50b42010-11-16 03:52:51 +00007774
glennrp3241bd02010-12-12 04:36:28 +00007775 /*
7776 Sometimes we get PseudoClass images whose RGB values don't match
7777 the colors in the colormap. This code syncs the RGB values.
7778 */
7779 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
cristyea1a8aa2011-10-20 13:24:06 +00007780 (void) SyncImage(image,exception);
glennrp3241bd02010-12-12 04:36:28 +00007781
glennrpa6a06632011-01-19 15:15:34 +00007782#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7783 if (image->depth > 8)
7784 {
7785 if (logging != MagickFalse)
7786 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7787 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7788
7789 image->depth=8;
7790 }
7791#endif
7792
glennrp8e58efd2011-05-20 12:16:29 +00007793 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007794 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7795 {
cristy4c08aed2011-07-01 19:47:50 +00007796 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007797 *r;
7798
glennrp8e58efd2011-05-20 12:16:29 +00007799 if (image->depth > 8)
7800 {
7801#if MAGICKCORE_QUANTUM_DEPTH > 16
7802 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007803 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007804
7805 for (y=0; y < (ssize_t) image->rows; y++)
7806 {
cristy8a20fa02011-12-27 15:54:31 +00007807 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007808
cristy4c08aed2011-07-01 19:47:50 +00007809 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007810 break;
7811
7812 for (x=0; x < (ssize_t) image->columns; x++)
7813 {
glennrp54cf7972011-08-06 14:28:09 +00007814 LBR16PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007815 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007816 }
glennrpbb4f99d2011-05-22 11:13:17 +00007817
glennrp8e58efd2011-05-20 12:16:29 +00007818 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7819 break;
7820 }
7821
7822 if (image->storage_class == PseudoClass && image->colormap != NULL)
7823 {
cristy3e08f112011-05-24 13:19:30 +00007824 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007825 {
glennrp91d99252011-06-25 14:30:13 +00007826 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007827 }
7828 }
7829#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7830 }
7831
7832 else if (image->depth > 4)
7833 {
7834#if MAGICKCORE_QUANTUM_DEPTH > 8
7835 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007836 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007837
7838 for (y=0; y < (ssize_t) image->rows; y++)
7839 {
cristyc82a27b2011-10-21 01:07:16 +00007840 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007841
cristy4c08aed2011-07-01 19:47:50 +00007842 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007843 break;
7844
7845 for (x=0; x < (ssize_t) image->columns; x++)
7846 {
glennrp54cf7972011-08-06 14:28:09 +00007847 LBR08PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007848 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007849 }
glennrpbb4f99d2011-05-22 11:13:17 +00007850
glennrp8e58efd2011-05-20 12:16:29 +00007851 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7852 break;
7853 }
7854
7855 if (image->storage_class == PseudoClass && image->colormap != NULL)
7856 {
cristy3e08f112011-05-24 13:19:30 +00007857 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007858 {
glennrp91d99252011-06-25 14:30:13 +00007859 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007860 }
7861 }
7862#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7863 }
7864 else
7865 if (image->depth > 2)
7866 {
7867 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007868 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007869
7870 for (y=0; y < (ssize_t) image->rows; y++)
7871 {
cristy8a20fa02011-12-27 15:54:31 +00007872 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007873
cristy4c08aed2011-07-01 19:47:50 +00007874 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007875 break;
7876
7877 for (x=0; x < (ssize_t) image->columns; x++)
7878 {
glennrp54cf7972011-08-06 14:28:09 +00007879 LBR04PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007880 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007881 }
glennrpbb4f99d2011-05-22 11:13:17 +00007882
glennrp8e58efd2011-05-20 12:16:29 +00007883 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7884 break;
7885 }
7886
7887 if (image->storage_class == PseudoClass && image->colormap != NULL)
7888 {
cristy3e08f112011-05-24 13:19:30 +00007889 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007890 {
glennrp91d99252011-06-25 14:30:13 +00007891 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007892 }
7893 }
7894 }
7895
7896 else if (image->depth > 1)
7897 {
7898 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007899 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007900
7901 for (y=0; y < (ssize_t) image->rows; y++)
7902 {
cristy8a20fa02011-12-27 15:54:31 +00007903 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007904
cristy4c08aed2011-07-01 19:47:50 +00007905 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007906 break;
7907
7908 for (x=0; x < (ssize_t) image->columns; x++)
7909 {
glennrp54cf7972011-08-06 14:28:09 +00007910 LBR02PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007911 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007912 }
glennrpbb4f99d2011-05-22 11:13:17 +00007913
glennrp8e58efd2011-05-20 12:16:29 +00007914 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7915 break;
7916 }
7917
7918 if (image->storage_class == PseudoClass && image->colormap != NULL)
7919 {
cristy3e08f112011-05-24 13:19:30 +00007920 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007921 {
glennrp91d99252011-06-25 14:30:13 +00007922 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007923 }
7924 }
7925 }
7926 else
7927 {
7928 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007929 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007930
7931 for (y=0; y < (ssize_t) image->rows; y++)
7932 {
cristy8a20fa02011-12-27 15:54:31 +00007933 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007934
cristy4c08aed2011-07-01 19:47:50 +00007935 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007936 break;
7937
7938 for (x=0; x < (ssize_t) image->columns; x++)
7939 {
glennrp54cf7972011-08-06 14:28:09 +00007940 LBR01PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007941 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007942 }
glennrpbb4f99d2011-05-22 11:13:17 +00007943
glennrp8e58efd2011-05-20 12:16:29 +00007944 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7945 break;
7946 }
7947
7948 if (image->storage_class == PseudoClass && image->colormap != NULL)
7949 {
cristy3e08f112011-05-24 13:19:30 +00007950 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007951 {
glennrp91d99252011-06-25 14:30:13 +00007952 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007953 }
7954 }
7955 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007956 }
7957
glennrp67b9c1a2011-04-22 18:47:36 +00007958 /* To do: set to next higher multiple of 8 */
7959 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007960 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007961
glennrp2b013e42010-11-24 16:55:50 +00007962#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7963 /* PNG does not handle depths greater than 16 so reduce it even
7964 * if lossy
7965 */
glennrp8e58efd2011-05-20 12:16:29 +00007966 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007967 image->depth=16;
7968#endif
7969
glennrp3faa9a32011-04-23 14:00:25 +00007970#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007971 if (image->depth == 16 && mng_info->write_png_depth != 16)
cristyc82a27b2011-10-21 01:07:16 +00007972 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007973 image->depth = 8;
7974#endif
7975
glennrpc8c2f062011-02-25 19:00:33 +00007976 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007977 * we reduce the transparency to binary and run again, then if there
7978 * 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 +00007979 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7980 * palette. Then (To do) we take care of a final reduction that is only
7981 * needed if there are still 256 colors present and one of them has both
7982 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007983 */
glennrp82b3c532011-03-22 19:20:54 +00007984
glennrp8ca51ad2011-05-12 21:22:32 +00007985 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007986 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007987 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007988
glennrp8ca51ad2011-05-12 21:22:32 +00007989 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007990 {
7991 /* BUILD_PALETTE
7992 *
7993 * Sometimes we get DirectClass images that have 256 colors or fewer.
7994 * This code will build a colormap.
7995 *
7996 * Also, sometimes we get PseudoClass images with an out-of-date
7997 * colormap. This code will replace the colormap with a new one.
7998 * Sometimes we get PseudoClass images that have more than 256 colors.
7999 * This code will delete the colormap and change the image to
8000 * DirectClass.
8001 *
cristy4c08aed2011-07-01 19:47:50 +00008002 * If image->matte is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00008003 * even though it sometimes contains left-over non-opaque values.
8004 *
8005 * Also we gather some information (number of opaque, transparent,
8006 * and semitransparent pixels, and whether the image has any non-gray
8007 * pixels or only black-and-white pixels) that we might need later.
8008 *
8009 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8010 * we need to check for bogus non-opaque values, at least.
8011 */
glennrp3c218112010-11-27 15:31:26 +00008012
glennrpd71e86a2011-02-24 01:28:37 +00008013 int
8014 n;
glennrp3c218112010-11-27 15:31:26 +00008015
cristy101ab702011-10-13 13:06:32 +00008016 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008017 opaque[260],
8018 semitransparent[260],
8019 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00008020
cristy4c08aed2011-07-01 19:47:50 +00008021 register const Quantum
8022 *s;
glennrp8bb3a022010-12-13 20:40:04 +00008023
cristy4c08aed2011-07-01 19:47:50 +00008024 register Quantum
8025 *q,
glennrpfd05d622011-02-25 04:10:33 +00008026 *r;
8027
glennrpd71e86a2011-02-24 01:28:37 +00008028 if (logging != MagickFalse)
8029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8030 " Enter BUILD_PALETTE:");
8031
8032 if (logging != MagickFalse)
8033 {
glennrp03812ae2010-12-24 01:31:34 +00008034 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008035 " image->columns=%.20g",(double) image->columns);
8036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8037 " image->rows=%.20g",(double) image->rows);
8038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8039 " image->matte=%.20g",(double) image->matte);
8040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8041 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008042
glennrpfd05d622011-02-25 04:10:33 +00008043 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008044 {
8045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008046 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008048 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008049
glennrpd71e86a2011-02-24 01:28:37 +00008050 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008051 {
glennrpd71e86a2011-02-24 01:28:37 +00008052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8053 " %d (%d,%d,%d,%d)",
8054 (int) i,
8055 (int) image->colormap[i].red,
8056 (int) image->colormap[i].green,
8057 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008058 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008059 }
glennrp2cc891a2010-12-24 13:44:32 +00008060
glennrpd71e86a2011-02-24 01:28:37 +00008061 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8062 {
8063 if (i > 255)
8064 {
8065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8066 " %d (%d,%d,%d,%d)",
8067 (int) i,
8068 (int) image->colormap[i].red,
8069 (int) image->colormap[i].green,
8070 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008071 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008072 }
8073 }
glennrp03812ae2010-12-24 01:31:34 +00008074 }
glennrp7ddcc222010-12-11 05:01:05 +00008075
glennrpd71e86a2011-02-24 01:28:37 +00008076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8077 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008078
glennrpd71e86a2011-02-24 01:28:37 +00008079 if (image->colors == 0)
cristyc458f912011-12-27 20:26:40 +00008080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8081 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008082
glennrp8d3d6e52011-04-19 04:39:51 +00008083 if (ping_preserve_colormap == MagickFalse)
8084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8085 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008086 }
8087
glennrpd71e86a2011-02-24 01:28:37 +00008088 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008089 number_opaque = 0;
8090 number_semitransparent = 0;
8091 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008092
8093 for (y=0; y < (ssize_t) image->rows; y++)
8094 {
8095 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8096
cristyacd2ed22011-08-30 01:44:23 +00008097 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008098 break;
8099
8100 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008101 {
glennrp4737d522011-04-29 03:33:42 +00008102 if (image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008103 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008104 {
8105 if (number_opaque < 259)
8106 {
8107 if (number_opaque == 0)
8108 {
cristy101ab702011-10-13 13:06:32 +00008109 GetPixelInfoPixel(image, q, opaque);
cristy4c08aed2011-07-01 19:47:50 +00008110 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008111 number_opaque=1;
8112 }
glennrp2cc891a2010-12-24 13:44:32 +00008113
glennrpd71e86a2011-02-24 01:28:37 +00008114 for (i=0; i< (ssize_t) number_opaque; i++)
8115 {
cristy4c08aed2011-07-01 19:47:50 +00008116 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008117 break;
8118 }
glennrp7ddcc222010-12-11 05:01:05 +00008119
cristyc458f912011-12-27 20:26:40 +00008120 if (i == (ssize_t) number_opaque && number_opaque < 259)
glennrpd71e86a2011-02-24 01:28:37 +00008121 {
8122 number_opaque++;
cristy101ab702011-10-13 13:06:32 +00008123 GetPixelInfoPixel(image, q, opaque+i);
cristy4c08aed2011-07-01 19:47:50 +00008124 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008125 }
8126 }
8127 }
cristy4c08aed2011-07-01 19:47:50 +00008128 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008129 {
8130 if (number_transparent < 259)
8131 {
8132 if (number_transparent == 0)
8133 {
cristy101ab702011-10-13 13:06:32 +00008134 GetPixelInfoPixel(image, q, transparent);
cristy4c08aed2011-07-01 19:47:50 +00008135 ping_trans_color.red=(unsigned short)
8136 GetPixelRed(image,q);
8137 ping_trans_color.green=(unsigned short)
8138 GetPixelGreen(image,q);
8139 ping_trans_color.blue=(unsigned short)
8140 GetPixelBlue(image,q);
8141 ping_trans_color.gray=(unsigned short)
8142 GetPixelRed(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008143 number_transparent = 1;
8144 }
8145
8146 for (i=0; i< (ssize_t) number_transparent; i++)
8147 {
cristy4c08aed2011-07-01 19:47:50 +00008148 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008149 break;
8150 }
8151
8152 if (i == (ssize_t) number_transparent &&
8153 number_transparent < 259)
8154 {
8155 number_transparent++;
cristy101ab702011-10-13 13:06:32 +00008156 GetPixelInfoPixel(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008157 }
8158 }
8159 }
8160 else
8161 {
8162 if (number_semitransparent < 259)
8163 {
8164 if (number_semitransparent == 0)
8165 {
cristy101ab702011-10-13 13:06:32 +00008166 GetPixelInfoPixel(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008167 number_semitransparent = 1;
8168 }
8169
8170 for (i=0; i< (ssize_t) number_semitransparent; i++)
8171 {
cristy4c08aed2011-07-01 19:47:50 +00008172 if (IsPixelEquivalent(image,q, semitransparent+i)
8173 && GetPixelAlpha(image,q) ==
8174 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008175 break;
8176 }
8177
8178 if (i == (ssize_t) number_semitransparent &&
8179 number_semitransparent < 259)
8180 {
8181 number_semitransparent++;
cristy101ab702011-10-13 13:06:32 +00008182 GetPixelInfoPixel(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008183 }
8184 }
8185 }
cristyed231572011-07-14 02:18:59 +00008186 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008187 }
8188 }
8189
cristy4054bfb2011-08-29 23:41:39 +00008190 if (mng_info->write_png8 == MagickFalse &&
8191 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008192 {
8193 /* Add the background color to the palette, if it
8194 * isn't already there.
8195 */
glennrpc6c391a2011-04-27 02:23:56 +00008196 if (logging != MagickFalse)
8197 {
8198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8199 " Check colormap for background (%d,%d,%d)",
8200 (int) image->background_color.red,
8201 (int) image->background_color.green,
8202 (int) image->background_color.blue);
8203 }
glennrpd71e86a2011-02-24 01:28:37 +00008204 for (i=0; i<number_opaque; i++)
8205 {
glennrpca7ad3a2011-04-26 04:44:54 +00008206 if (opaque[i].red == image->background_color.red &&
8207 opaque[i].green == image->background_color.green &&
8208 opaque[i].blue == image->background_color.blue)
8209 break;
glennrpd71e86a2011-02-24 01:28:37 +00008210 }
glennrpd71e86a2011-02-24 01:28:37 +00008211 if (number_opaque < 259 && i == number_opaque)
8212 {
glennrp8e045c82011-04-27 16:40:27 +00008213 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008214 ping_background.index = i;
8215 if (logging != MagickFalse)
8216 {
8217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8218 " background_color index is %d",(int) i);
8219 }
8220
glennrpd71e86a2011-02-24 01:28:37 +00008221 }
glennrpa080bc32011-03-11 18:03:44 +00008222 else if (logging != MagickFalse)
8223 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8224 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008225 }
8226
8227 image_colors=number_opaque+number_transparent+number_semitransparent;
8228
glennrpa080bc32011-03-11 18:03:44 +00008229 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8230 {
8231 /* No room for the background color; remove it. */
8232 number_opaque--;
8233 image_colors--;
8234 }
8235
glennrpd71e86a2011-02-24 01:28:37 +00008236 if (logging != MagickFalse)
8237 {
8238 if (image_colors > 256)
8239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8240 " image has more than 256 colors");
8241
8242 else
8243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8244 " image has %d colors",image_colors);
8245 }
8246
glennrp8d3d6e52011-04-19 04:39:51 +00008247 if (ping_preserve_colormap != MagickFalse)
8248 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008249
glennrpfd05d622011-02-25 04:10:33 +00008250 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008251 {
8252 ping_have_color=MagickFalse;
8253 ping_have_non_bw=MagickFalse;
8254
8255 if(image_colors > 256)
8256 {
8257 for (y=0; y < (ssize_t) image->rows; y++)
8258 {
8259 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8260
cristyacd2ed22011-08-30 01:44:23 +00008261 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008262 break;
8263
glennrpe5e6b802011-07-20 14:44:40 +00008264 s=q;
8265 for (x=0; x < (ssize_t) image->columns; x++)
8266 {
8267 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8268 GetPixelRed(image,s) != GetPixelBlue(image,s))
8269 {
8270 ping_have_color=MagickTrue;
8271 ping_have_non_bw=MagickTrue;
8272 break;
8273 }
8274 s+=GetPixelChannels(image);
8275 }
8276
8277 if (ping_have_color != MagickFalse)
8278 break;
8279
glennrpd71e86a2011-02-24 01:28:37 +00008280 /* Worst case is black-and-white; we are looking at every
8281 * pixel twice.
8282 */
8283
glennrpd71e86a2011-02-24 01:28:37 +00008284 if (ping_have_non_bw == MagickFalse)
8285 {
8286 s=q;
8287 for (x=0; x < (ssize_t) image->columns; x++)
8288 {
cristy4c08aed2011-07-01 19:47:50 +00008289 if (GetPixelRed(image,s) != 0 &&
8290 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008291 {
8292 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008293 break;
glennrpd71e86a2011-02-24 01:28:37 +00008294 }
cristyed231572011-07-14 02:18:59 +00008295 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008296 }
glennrpe5e6b802011-07-20 14:44:40 +00008297 }
glennrpd71e86a2011-02-24 01:28:37 +00008298 }
glennrpbb4f99d2011-05-22 11:13:17 +00008299 }
8300 }
glennrpd71e86a2011-02-24 01:28:37 +00008301
8302 if (image_colors < 257)
8303 {
cristy101ab702011-10-13 13:06:32 +00008304 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008305 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008306
glennrpd71e86a2011-02-24 01:28:37 +00008307 /*
8308 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008309 */
8310
glennrpd71e86a2011-02-24 01:28:37 +00008311 if (logging != MagickFalse)
8312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8313 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008314
glennrpd71e86a2011-02-24 01:28:37 +00008315 /* Sort palette, transparent first */;
8316
8317 n = 0;
8318
8319 for (i=0; i<number_transparent; i++)
8320 colormap[n++] = transparent[i];
8321
8322 for (i=0; i<number_semitransparent; i++)
8323 colormap[n++] = semitransparent[i];
8324
8325 for (i=0; i<number_opaque; i++)
8326 colormap[n++] = opaque[i];
8327
glennrpc6c391a2011-04-27 02:23:56 +00008328 ping_background.index +=
8329 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008330
glennrpd71e86a2011-02-24 01:28:37 +00008331 /* image_colors < 257; search the colormap instead of the pixels
8332 * to get ping_have_color and ping_have_non_bw
8333 */
8334 for (i=0; i<n; i++)
8335 {
8336 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008337 {
glennrpd71e86a2011-02-24 01:28:37 +00008338 if (colormap[i].red != colormap[i].green ||
8339 colormap[i].red != colormap[i].blue)
8340 {
8341 ping_have_color=MagickTrue;
8342 ping_have_non_bw=MagickTrue;
8343 break;
8344 }
8345 }
8346
8347 if (ping_have_non_bw == MagickFalse)
8348 {
8349 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008350 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008351 }
glennrp8bb3a022010-12-13 20:40:04 +00008352 }
8353
glennrpd71e86a2011-02-24 01:28:37 +00008354 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8355 (number_transparent == 0 && number_semitransparent == 0)) &&
8356 (((mng_info->write_png_colortype-1) ==
8357 PNG_COLOR_TYPE_PALETTE) ||
8358 (mng_info->write_png_colortype == 0)))
8359 {
glennrp6185c532011-01-14 17:58:40 +00008360 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008361 {
glennrpd71e86a2011-02-24 01:28:37 +00008362 if (n != (ssize_t) image_colors)
8363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8364 " image_colors (%d) and n (%d) don't match",
8365 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008366
glennrpd71e86a2011-02-24 01:28:37 +00008367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8368 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008369 }
glennrp03812ae2010-12-24 01:31:34 +00008370
glennrpd71e86a2011-02-24 01:28:37 +00008371 image->colors = image_colors;
8372
cristy018f07f2011-09-04 21:15:19 +00008373 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008374 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008375 ThrowWriterException(ResourceLimitError,
8376 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008377
8378 for (i=0; i< (ssize_t) image_colors; i++)
8379 image->colormap[i] = colormap[i];
8380
8381 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008382 {
glennrpd71e86a2011-02-24 01:28:37 +00008383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8384 " image->colors=%d (%d)",
8385 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008386
glennrpd71e86a2011-02-24 01:28:37 +00008387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8388 " Update the pixel indexes");
8389 }
glennrp6185c532011-01-14 17:58:40 +00008390
glennrpfd05d622011-02-25 04:10:33 +00008391 /* Sync the pixel indices with the new colormap */
8392
glennrpd71e86a2011-02-24 01:28:37 +00008393 for (y=0; y < (ssize_t) image->rows; y++)
8394 {
cristy97707062011-12-27 18:25:00 +00008395 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp6185c532011-01-14 17:58:40 +00008396
cristyacd2ed22011-08-30 01:44:23 +00008397 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008398 break;
glennrp6185c532011-01-14 17:58:40 +00008399
glennrpd71e86a2011-02-24 01:28:37 +00008400 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008401 {
glennrpd71e86a2011-02-24 01:28:37 +00008402 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008403 {
glennrpd71e86a2011-02-24 01:28:37 +00008404 if ((image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008405 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8406 image->colormap[i].red == GetPixelRed(image,q) &&
8407 image->colormap[i].green == GetPixelGreen(image,q) &&
8408 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008409 {
cristy4c08aed2011-07-01 19:47:50 +00008410 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008411 break;
glennrp6185c532011-01-14 17:58:40 +00008412 }
glennrp6185c532011-01-14 17:58:40 +00008413 }
cristyed231572011-07-14 02:18:59 +00008414 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008415 }
glennrp6185c532011-01-14 17:58:40 +00008416
glennrpd71e86a2011-02-24 01:28:37 +00008417 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8418 break;
8419 }
8420 }
8421 }
8422
8423 if (logging != MagickFalse)
8424 {
8425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8426 " image->colors=%d", (int) image->colors);
8427
8428 if (image->colormap != NULL)
8429 {
8430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008431 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008432
8433 for (i=0; i < (ssize_t) image->colors; i++)
8434 {
cristy72988482011-03-29 16:34:38 +00008435 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008436 {
8437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8438 " %d (%d,%d,%d,%d)",
8439 (int) i,
8440 (int) image->colormap[i].red,
8441 (int) image->colormap[i].green,
8442 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008443 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008444 }
glennrp6185c532011-01-14 17:58:40 +00008445 }
8446 }
glennrp03812ae2010-12-24 01:31:34 +00008447
glennrpd71e86a2011-02-24 01:28:37 +00008448 if (number_transparent < 257)
8449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8450 " number_transparent = %d",
8451 number_transparent);
8452 else
glennrp03812ae2010-12-24 01:31:34 +00008453
glennrpd71e86a2011-02-24 01:28:37 +00008454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8455 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008456
glennrpd71e86a2011-02-24 01:28:37 +00008457 if (number_opaque < 257)
8458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8459 " number_opaque = %d",
8460 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008461
glennrpd71e86a2011-02-24 01:28:37 +00008462 else
8463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8464 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008465
glennrpd71e86a2011-02-24 01:28:37 +00008466 if (number_semitransparent < 257)
8467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8468 " number_semitransparent = %d",
8469 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008470
glennrpd71e86a2011-02-24 01:28:37 +00008471 else
8472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8473 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008474
glennrpd71e86a2011-02-24 01:28:37 +00008475 if (ping_have_non_bw == MagickFalse)
8476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8477 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008478
glennrpd71e86a2011-02-24 01:28:37 +00008479 else if (ping_have_color == MagickFalse)
8480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8481 " All pixels and the background are gray");
8482
8483 else
8484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8485 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008486
glennrp03812ae2010-12-24 01:31:34 +00008487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8488 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008489 }
glennrpfd05d622011-02-25 04:10:33 +00008490
glennrpc8c2f062011-02-25 19:00:33 +00008491 if (mng_info->write_png8 == MagickFalse)
8492 break;
glennrpfd05d622011-02-25 04:10:33 +00008493
glennrpc8c2f062011-02-25 19:00:33 +00008494 /* Make any reductions necessary for the PNG8 format */
8495 if (image_colors <= 256 &&
8496 image_colors != 0 && image->colormap != NULL &&
8497 number_semitransparent == 0 &&
8498 number_transparent <= 1)
8499 break;
8500
8501 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008502 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8503 * transparent color so if more than one is transparent we merge
8504 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008505 */
glennrp130fc452011-08-20 03:43:18 +00008506 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008507 {
8508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8509 " Thresholding the alpha channel to binary");
8510
8511 for (y=0; y < (ssize_t) image->rows; y++)
8512 {
cristy8a20fa02011-12-27 15:54:31 +00008513 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008514
cristy4c08aed2011-07-01 19:47:50 +00008515 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008516 break;
8517
8518 for (x=0; x < (ssize_t) image->columns; x++)
8519 {
glennrpf73547f2011-08-20 04:40:26 +00008520 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008521 {
cristy803640d2011-11-17 02:11:32 +00008522 SetPixelInfoPixel(image,&image->background_color,r);
cristy4c08aed2011-07-01 19:47:50 +00008523 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008524 }
8525 else
cristy4c08aed2011-07-01 19:47:50 +00008526 SetPixelAlpha(image,OpaqueAlpha,r);
cristyed231572011-07-14 02:18:59 +00008527 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008528 }
glennrpbb4f99d2011-05-22 11:13:17 +00008529
glennrpc8c2f062011-02-25 19:00:33 +00008530 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8531 break;
8532
8533 if (image_colors != 0 && image_colors <= 256 &&
8534 image->colormap != NULL)
8535 for (i=0; i<image_colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00008536 image->colormap[i].alpha =
8537 (image->colormap[i].alpha > TransparentAlpha/2 ?
8538 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008539 }
8540 continue;
8541 }
8542
8543 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008544 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8545 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8546 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008547 */
glennrpd3371642011-03-22 19:42:23 +00008548 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8549 {
8550 if (logging != MagickFalse)
8551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8552 " Quantizing the background color to 4-4-4");
8553
8554 tried_444 = MagickTrue;
8555
glennrp91d99252011-06-25 14:30:13 +00008556 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008557
8558 if (logging != MagickFalse)
8559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8560 " Quantizing the pixel colors to 4-4-4");
8561
8562 if (image->colormap == NULL)
8563 {
8564 for (y=0; y < (ssize_t) image->rows; y++)
8565 {
cristy8a20fa02011-12-27 15:54:31 +00008566 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpd3371642011-03-22 19:42:23 +00008567
cristy4c08aed2011-07-01 19:47:50 +00008568 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008569 break;
8570
8571 for (x=0; x < (ssize_t) image->columns; x++)
8572 {
cristy4c08aed2011-07-01 19:47:50 +00008573 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008574 LBR04PixelRGB(r);
cristy8a20fa02011-12-27 15:54:31 +00008575 r+=GetPixelChannels(image);
glennrpd3371642011-03-22 19:42:23 +00008576 }
glennrpbb4f99d2011-05-22 11:13:17 +00008577
glennrpd3371642011-03-22 19:42:23 +00008578 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8579 break;
8580 }
8581 }
8582
8583 else /* Should not reach this; colormap already exists and
8584 must be <= 256 */
8585 {
8586 if (logging != MagickFalse)
8587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8588 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008589
glennrpd3371642011-03-22 19:42:23 +00008590 for (i=0; i<image_colors; i++)
8591 {
glennrp91d99252011-06-25 14:30:13 +00008592 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008593 }
8594 }
8595 continue;
8596 }
8597
glennrp82b3c532011-03-22 19:20:54 +00008598 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8599 {
8600 if (logging != MagickFalse)
8601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8602 " Quantizing the background color to 3-3-3");
8603
8604 tried_333 = MagickTrue;
8605
glennrp91d99252011-06-25 14:30:13 +00008606 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008607
8608 if (logging != MagickFalse)
8609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008610 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008611
8612 if (image->colormap == NULL)
8613 {
8614 for (y=0; y < (ssize_t) image->rows; y++)
8615 {
cristy8a20fa02011-12-27 15:54:31 +00008616 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp82b3c532011-03-22 19:20:54 +00008617
cristy4c08aed2011-07-01 19:47:50 +00008618 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008619 break;
8620
8621 for (x=0; x < (ssize_t) image->columns; x++)
8622 {
cristy4c08aed2011-07-01 19:47:50 +00008623 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8624 LBR03RGB(r);
cristy8a20fa02011-12-27 15:54:31 +00008625 r+=GetPixelChannels(image);
glennrp82b3c532011-03-22 19:20:54 +00008626 }
glennrpbb4f99d2011-05-22 11:13:17 +00008627
glennrp82b3c532011-03-22 19:20:54 +00008628 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8629 break;
8630 }
8631 }
8632
8633 else /* Should not reach this; colormap already exists and
8634 must be <= 256 */
8635 {
8636 if (logging != MagickFalse)
8637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008638 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008639 for (i=0; i<image_colors; i++)
8640 {
glennrp91d99252011-06-25 14:30:13 +00008641 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008642 }
glennrpd3371642011-03-22 19:42:23 +00008643 }
8644 continue;
glennrp82b3c532011-03-22 19:20:54 +00008645 }
glennrpc8c2f062011-02-25 19:00:33 +00008646
glennrp8ca51ad2011-05-12 21:22:32 +00008647 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008648 {
8649 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008651 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008652
glennrp8ca51ad2011-05-12 21:22:32 +00008653 tried_332 = MagickTrue;
8654
glennrp3faa9a32011-04-23 14:00:25 +00008655 /* Red and green were already done so we only quantize the blue
8656 * channel
8657 */
8658
glennrp91d99252011-06-25 14:30:13 +00008659 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008660
glennrpc8c2f062011-02-25 19:00:33 +00008661 if (logging != MagickFalse)
8662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008663 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008664
glennrpc8c2f062011-02-25 19:00:33 +00008665 if (image->colormap == NULL)
8666 {
8667 for (y=0; y < (ssize_t) image->rows; y++)
8668 {
cristy8a20fa02011-12-27 15:54:31 +00008669 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008670
cristy4c08aed2011-07-01 19:47:50 +00008671 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008672 break;
8673
8674 for (x=0; x < (ssize_t) image->columns; x++)
8675 {
cristy4c08aed2011-07-01 19:47:50 +00008676 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008677 LBR02PixelBlue(r);
cristy8a20fa02011-12-27 15:54:31 +00008678 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008679 }
glennrpbb4f99d2011-05-22 11:13:17 +00008680
glennrpc8c2f062011-02-25 19:00:33 +00008681 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8682 break;
8683 }
8684 }
glennrpfd05d622011-02-25 04:10:33 +00008685
glennrpc8c2f062011-02-25 19:00:33 +00008686 else /* Should not reach this; colormap already exists and
8687 must be <= 256 */
8688 {
8689 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008691 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008692 for (i=0; i<image_colors; i++)
8693 {
glennrp91d99252011-06-25 14:30:13 +00008694 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008695 }
8696 }
8697 continue;
8698 }
8699 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008700
8701 if (image_colors == 0 || image_colors > 256)
8702 {
8703 /* Take care of special case with 256 colors + 1 transparent
8704 * color. We don't need to quantize to 2-3-2-1; we only need to
8705 * eliminate one color, so we'll merge the two darkest red
8706 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8707 */
8708 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8709 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8710 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8711 {
8712 image->background_color.red=ScaleCharToQuantum(0x24);
8713 }
glennrpbb4f99d2011-05-22 11:13:17 +00008714
glennrp8ca51ad2011-05-12 21:22:32 +00008715 if (image->colormap == NULL)
8716 {
8717 for (y=0; y < (ssize_t) image->rows; y++)
8718 {
cristy8a20fa02011-12-27 15:54:31 +00008719 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008720
cristy4c08aed2011-07-01 19:47:50 +00008721 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008722 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008723
glennrp8ca51ad2011-05-12 21:22:32 +00008724 for (x=0; x < (ssize_t) image->columns; x++)
8725 {
cristy4c08aed2011-07-01 19:47:50 +00008726 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8727 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8728 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8729 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008730 {
cristy4c08aed2011-07-01 19:47:50 +00008731 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008732 }
cristyed231572011-07-14 02:18:59 +00008733 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008734 }
glennrpbb4f99d2011-05-22 11:13:17 +00008735
glennrp8ca51ad2011-05-12 21:22:32 +00008736 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8737 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008738
glennrp8ca51ad2011-05-12 21:22:32 +00008739 }
8740 }
8741
8742 else
8743 {
8744 for (i=0; i<image_colors; i++)
8745 {
8746 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8747 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8748 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8749 {
8750 image->colormap[i].red=ScaleCharToQuantum(0x24);
8751 }
8752 }
8753 }
8754 }
glennrpd71e86a2011-02-24 01:28:37 +00008755 }
glennrpfd05d622011-02-25 04:10:33 +00008756 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008757
glennrpfd05d622011-02-25 04:10:33 +00008758 /* If we are excluding the tRNS chunk and there is transparency,
8759 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8760 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008761 */
glennrp0e8ea192010-12-24 18:00:33 +00008762 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8763 (number_transparent != 0 || number_semitransparent != 0))
8764 {
glennrpd17915c2011-04-29 14:24:22 +00008765 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008766
8767 if (ping_have_color == MagickFalse)
8768 mng_info->write_png_colortype = 5;
8769
8770 else
8771 mng_info->write_png_colortype = 7;
8772
glennrp8d579662011-02-23 02:05:02 +00008773 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008774 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008775 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008776
glennrp0e8ea192010-12-24 18:00:33 +00008777 }
8778
glennrpfd05d622011-02-25 04:10:33 +00008779 /* See if cheap transparency is possible. It is only possible
8780 * when there is a single transparent color, no semitransparent
8781 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008782 * as the transparent color. We only need this information if
8783 * we are writing a PNG with colortype 0 or 2, and we have not
8784 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008785 */
glennrp5a39f372011-02-25 04:52:16 +00008786 if (number_transparent == 1 &&
8787 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008788 {
8789 ping_have_cheap_transparency = MagickTrue;
8790
8791 if (number_semitransparent != 0)
8792 ping_have_cheap_transparency = MagickFalse;
8793
8794 else if (image_colors == 0 || image_colors > 256 ||
8795 image->colormap == NULL)
8796 {
cristy4c08aed2011-07-01 19:47:50 +00008797 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008798 *q;
8799
glennrpfd05d622011-02-25 04:10:33 +00008800 for (y=0; y < (ssize_t) image->rows; y++)
8801 {
8802 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8803
cristyacd2ed22011-08-30 01:44:23 +00008804 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008805 break;
8806
8807 for (x=0; x < (ssize_t) image->columns; x++)
8808 {
cristy4c08aed2011-07-01 19:47:50 +00008809 if (GetPixelAlpha(image,q) != TransparentAlpha &&
glennrp847370c2011-07-05 17:37:15 +00008810 (unsigned short) GetPixelRed(image,q) ==
8811 ping_trans_color.red &&
8812 (unsigned short) GetPixelGreen(image,q) ==
8813 ping_trans_color.green &&
8814 (unsigned short) GetPixelBlue(image,q) ==
8815 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008816 {
8817 ping_have_cheap_transparency = MagickFalse;
8818 break;
8819 }
8820
cristyed231572011-07-14 02:18:59 +00008821 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008822 }
glennrpbb4f99d2011-05-22 11:13:17 +00008823
glennrpfd05d622011-02-25 04:10:33 +00008824 if (ping_have_cheap_transparency == MagickFalse)
8825 break;
8826 }
8827 }
8828 else
8829 {
glennrp67b9c1a2011-04-22 18:47:36 +00008830 /* Assuming that image->colormap[0] is the one transparent color
8831 * and that all others are opaque.
8832 */
glennrpfd05d622011-02-25 04:10:33 +00008833 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008834 for (i=1; i<image_colors; i++)
8835 if (image->colormap[i].red == image->colormap[0].red &&
8836 image->colormap[i].green == image->colormap[0].green &&
8837 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008838 {
glennrp67b9c1a2011-04-22 18:47:36 +00008839 ping_have_cheap_transparency = MagickFalse;
8840 break;
glennrpfd05d622011-02-25 04:10:33 +00008841 }
8842 }
glennrpbb4f99d2011-05-22 11:13:17 +00008843
glennrpfd05d622011-02-25 04:10:33 +00008844 if (logging != MagickFalse)
8845 {
8846 if (ping_have_cheap_transparency == MagickFalse)
8847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8848 " Cheap transparency is not possible.");
8849
8850 else
8851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8852 " Cheap transparency is possible.");
8853 }
8854 }
8855 else
8856 ping_have_cheap_transparency = MagickFalse;
8857
glennrp8640fb52010-11-23 15:48:26 +00008858 image_depth=image->depth;
8859
glennrp26c990a2010-11-23 02:23:20 +00008860 quantum_info = (QuantumInfo *) NULL;
8861 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008862 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008863 image_matte=image->matte;
8864
glennrp0fe50b42010-11-16 03:52:51 +00008865 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008866 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008867
glennrp52a479c2011-02-26 21:14:38 +00008868 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8869 (image->colors == 0 || image->colormap == NULL))
8870 {
glennrp52a479c2011-02-26 21:14:38 +00008871 image_info=DestroyImageInfo(image_info);
8872 image=DestroyImage(image);
cristyc82a27b2011-10-21 01:07:16 +00008873 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
glennrp15e01552011-03-06 23:29:17 +00008874 "Cannot write PNG8 or color-type 3; colormap is NULL",
8875 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008876#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8877 UnlockSemaphoreInfo(ping_semaphore);
8878#endif
8879 return(MagickFalse);
8880 }
8881
cristy3ed852e2009-09-05 21:47:34 +00008882 /*
8883 Allocate the PNG structures
8884 */
8885#ifdef PNG_USER_MEM_SUPPORTED
cristyc82a27b2011-10-21 01:07:16 +00008886 error_info.image=image;
8887 error_info.exception=exception;
8888 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008889 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8890 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008891
cristy3ed852e2009-09-05 21:47:34 +00008892#else
cristyc82a27b2011-10-21 01:07:16 +00008893 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008894 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008895
cristy3ed852e2009-09-05 21:47:34 +00008896#endif
8897 if (ping == (png_struct *) NULL)
8898 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008899
cristy3ed852e2009-09-05 21:47:34 +00008900 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008901
cristy3ed852e2009-09-05 21:47:34 +00008902 if (ping_info == (png_info *) NULL)
8903 {
8904 png_destroy_write_struct(&ping,(png_info **) NULL);
8905 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8906 }
glennrp0fe50b42010-11-16 03:52:51 +00008907
cristy3ed852e2009-09-05 21:47:34 +00008908 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008909 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008910
glennrp5af765f2010-03-30 11:12:18 +00008911 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008912 {
8913 /*
8914 PNG write failed.
8915 */
8916#ifdef PNG_DEBUG
8917 if (image_info->verbose)
8918 (void) printf("PNG write has failed.\n");
8919#endif
8920 png_destroy_write_struct(&ping,&ping_info);
8921#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008922 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008923#endif
glennrpda8f3a72011-02-27 23:54:12 +00008924 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008925 (void) CloseBlob(image);
8926 image_info=DestroyImageInfo(image_info);
8927 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008928 return(MagickFalse);
8929 }
8930 /*
8931 Prepare PNG for writing.
8932 */
8933#if defined(PNG_MNG_FEATURES_SUPPORTED)
8934 if (mng_info->write_mng)
8935 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008936
cristy3ed852e2009-09-05 21:47:34 +00008937#else
8938# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8939 if (mng_info->write_mng)
8940 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008941
cristy3ed852e2009-09-05 21:47:34 +00008942# endif
8943#endif
glennrp2b013e42010-11-24 16:55:50 +00008944
cristy3ed852e2009-09-05 21:47:34 +00008945 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008946
cristy4e5bc842010-06-09 13:56:01 +00008947 ping_width=(png_uint_32) image->columns;
8948 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008949
cristy3ed852e2009-09-05 21:47:34 +00008950 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8951 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008952
cristy3ed852e2009-09-05 21:47:34 +00008953 if (mng_info->write_png_depth != 0)
8954 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008955
cristy3ed852e2009-09-05 21:47:34 +00008956 /* Adjust requested depth to next higher valid depth if necessary */
8957 if (image_depth > 8)
8958 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008959
cristy3ed852e2009-09-05 21:47:34 +00008960 if ((image_depth > 4) && (image_depth < 8))
8961 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008962
cristy3ed852e2009-09-05 21:47:34 +00008963 if (image_depth == 3)
8964 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008965
cristy3ed852e2009-09-05 21:47:34 +00008966 if (logging != MagickFalse)
8967 {
8968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008969 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008971 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008973 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008975 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008977 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008978 }
glennrp8640fb52010-11-23 15:48:26 +00008979
cristy3ed852e2009-09-05 21:47:34 +00008980 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008981 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008982
glennrp26f37912010-12-23 16:22:42 +00008983
cristy3ed852e2009-09-05 21:47:34 +00008984#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008985 if (ping_exclude_pHYs == MagickFalse)
8986 {
cristy2a11bef2011-10-28 18:33:11 +00008987 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00008988 (!mng_info->write_mng || !mng_info->equal_physs))
8989 {
glennrp0fe50b42010-11-16 03:52:51 +00008990 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8992 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008993
8994 if (image->units == PixelsPerInchResolution)
8995 {
glennrpdfd70802010-11-14 01:23:35 +00008996 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008997 ping_pHYs_x_resolution=
cristy2a11bef2011-10-28 18:33:11 +00008998 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
glennrp823b55c2011-03-14 18:46:46 +00008999 ping_pHYs_y_resolution=
cristy2a11bef2011-10-28 18:33:11 +00009000 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00009001 }
glennrpdfd70802010-11-14 01:23:35 +00009002
cristy3ed852e2009-09-05 21:47:34 +00009003 else if (image->units == PixelsPerCentimeterResolution)
9004 {
glennrpdfd70802010-11-14 01:23:35 +00009005 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
cristy2a11bef2011-10-28 18:33:11 +00009006 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9007 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00009008 }
glennrp991d11d2010-11-12 21:55:28 +00009009
cristy3ed852e2009-09-05 21:47:34 +00009010 else
9011 {
glennrpdfd70802010-11-14 01:23:35 +00009012 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
cristy2a11bef2011-10-28 18:33:11 +00009013 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9014 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00009015 }
glennrp991d11d2010-11-12 21:55:28 +00009016
glennrp823b55c2011-03-14 18:46:46 +00009017 if (logging != MagickFalse)
9018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9019 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9020 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9021 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009022 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009023 }
glennrp26f37912010-12-23 16:22:42 +00009024 }
cristy3ed852e2009-09-05 21:47:34 +00009025#endif
glennrpa521b2f2010-10-29 04:11:03 +00009026
glennrp26f37912010-12-23 16:22:42 +00009027 if (ping_exclude_bKGD == MagickFalse)
9028 {
glennrpa521b2f2010-10-29 04:11:03 +00009029 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009030 {
glennrpa521b2f2010-10-29 04:11:03 +00009031 unsigned int
9032 mask;
cristy3ed852e2009-09-05 21:47:34 +00009033
glennrpa521b2f2010-10-29 04:11:03 +00009034 mask=0xffff;
9035 if (ping_bit_depth == 8)
9036 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009037
glennrpa521b2f2010-10-29 04:11:03 +00009038 if (ping_bit_depth == 4)
9039 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009040
glennrpa521b2f2010-10-29 04:11:03 +00009041 if (ping_bit_depth == 2)
9042 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009043
glennrpa521b2f2010-10-29 04:11:03 +00009044 if (ping_bit_depth == 1)
9045 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009046
glennrpa521b2f2010-10-29 04:11:03 +00009047 ping_background.red=(png_uint_16)
9048 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009049
glennrpa521b2f2010-10-29 04:11:03 +00009050 ping_background.green=(png_uint_16)
9051 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009052
glennrpa521b2f2010-10-29 04:11:03 +00009053 ping_background.blue=(png_uint_16)
9054 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009055
9056 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009057 }
cristy3ed852e2009-09-05 21:47:34 +00009058
glennrp0fe50b42010-11-16 03:52:51 +00009059 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009060 {
9061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9062 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9064 " background_color index is %d",
9065 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009066
9067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9068 " ping_bit_depth=%d",ping_bit_depth);
9069 }
glennrp0fe50b42010-11-16 03:52:51 +00009070
9071 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009072 }
glennrp0fe50b42010-11-16 03:52:51 +00009073
cristy3ed852e2009-09-05 21:47:34 +00009074 /*
9075 Select the color type.
9076 */
9077 matte=image_matte;
9078 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009079
glennrp1273f7b2011-02-24 03:20:30 +00009080 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009081 {
glennrp0fe50b42010-11-16 03:52:51 +00009082
glennrpfd05d622011-02-25 04:10:33 +00009083 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009084 for reducing the sample depth from 8. */
9085
glennrp0fe50b42010-11-16 03:52:51 +00009086 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009087
glennrp8bb3a022010-12-13 20:40:04 +00009088 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009089
9090 /*
9091 Set image palette.
9092 */
9093 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9094
glennrp0fe50b42010-11-16 03:52:51 +00009095 if (logging != MagickFalse)
9096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9097 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009098 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009099
9100 for (i=0; i < (ssize_t) number_colors; i++)
9101 {
9102 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9103 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9104 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9105 if (logging != MagickFalse)
9106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009107#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009108 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009109#else
9110 " %5ld (%5d,%5d,%5d)",
9111#endif
glennrp0fe50b42010-11-16 03:52:51 +00009112 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9113
9114 }
glennrp2b013e42010-11-24 16:55:50 +00009115
glennrp8bb3a022010-12-13 20:40:04 +00009116 ping_have_PLTE=MagickTrue;
9117 image_depth=ping_bit_depth;
9118 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009119
glennrp58e01762011-01-07 15:28:54 +00009120 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009121 {
glennrp0fe50b42010-11-16 03:52:51 +00009122 /*
9123 Identify which colormap entry is transparent.
9124 */
9125 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009126 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009127
glennrp8bb3a022010-12-13 20:40:04 +00009128 for (i=0; i < (ssize_t) number_transparent; i++)
9129 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009130
glennrp0fe50b42010-11-16 03:52:51 +00009131
glennrp2cc891a2010-12-24 13:44:32 +00009132 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009133 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009134
9135 if (ping_num_trans == 0)
9136 ping_have_tRNS=MagickFalse;
9137
glennrp8bb3a022010-12-13 20:40:04 +00009138 else
9139 ping_have_tRNS=MagickTrue;
9140 }
glennrp0fe50b42010-11-16 03:52:51 +00009141
glennrp1273f7b2011-02-24 03:20:30 +00009142 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009143 {
glennrp1273f7b2011-02-24 03:20:30 +00009144 /*
9145 * Identify which colormap entry is the background color.
9146 */
9147
glennrp4f25bd02011-01-01 18:51:28 +00009148 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9149 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9150 break;
glennrp0fe50b42010-11-16 03:52:51 +00009151
glennrp4f25bd02011-01-01 18:51:28 +00009152 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009153
9154 if (logging != MagickFalse)
9155 {
9156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9157 " background_color index is %d",
9158 (int) ping_background.index);
9159 }
glennrp4f25bd02011-01-01 18:51:28 +00009160 }
cristy3ed852e2009-09-05 21:47:34 +00009161 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009162
glennrp7e65e932011-08-19 02:31:16 +00009163 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009164 {
9165 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009166 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009167 }
glennrp0fe50b42010-11-16 03:52:51 +00009168
glennrp7e65e932011-08-19 02:31:16 +00009169 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009170 {
9171 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009172 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009173 }
glennrp0fe50b42010-11-16 03:52:51 +00009174
glennrp8bb3a022010-12-13 20:40:04 +00009175 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009176 {
glennrp5af765f2010-03-30 11:12:18 +00009177 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009178
glennrp8bb3a022010-12-13 20:40:04 +00009179 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009180 {
glennrp5af765f2010-03-30 11:12:18 +00009181 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009182
glennrp5af765f2010-03-30 11:12:18 +00009183 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9184 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009185 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009186
glennrp8bb3a022010-12-13 20:40:04 +00009187 else
9188 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009189
9190 if (logging != MagickFalse)
9191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9192 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009193 }
glennrp0fe50b42010-11-16 03:52:51 +00009194
glennrp7c4c9e62011-03-21 20:23:32 +00009195 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009196 {
9197 if (logging != MagickFalse)
9198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009199 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009200
glennrpd6bf1612010-12-17 17:28:54 +00009201 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009202 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009203
glennrpd6bf1612010-12-17 17:28:54 +00009204 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009205 {
glennrp5af765f2010-03-30 11:12:18 +00009206 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009207 image_matte=MagickFalse;
9208 }
glennrp0fe50b42010-11-16 03:52:51 +00009209
glennrpd6bf1612010-12-17 17:28:54 +00009210 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009211 {
glennrp5af765f2010-03-30 11:12:18 +00009212 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009213 image_matte=MagickTrue;
9214 }
glennrp0fe50b42010-11-16 03:52:51 +00009215
glennrp5aa37f62011-01-02 03:07:57 +00009216 if (image_info->type == PaletteType ||
9217 image_info->type == PaletteMatteType)
9218 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9219
glennrp7c4c9e62011-03-21 20:23:32 +00009220 if (mng_info->write_png_colortype == 0 &&
9221 (image_info->type == UndefinedType ||
9222 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009223 {
glennrp5aa37f62011-01-02 03:07:57 +00009224 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009225 {
glennrp5aa37f62011-01-02 03:07:57 +00009226 if (image_matte == MagickFalse)
9227 {
9228 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9229 image_matte=MagickFalse;
9230 }
glennrp0fe50b42010-11-16 03:52:51 +00009231
glennrp0b206f52011-01-07 04:55:32 +00009232 else
glennrp5aa37f62011-01-02 03:07:57 +00009233 {
9234 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9235 image_matte=MagickTrue;
9236 }
9237 }
9238 else
glennrp8bb3a022010-12-13 20:40:04 +00009239 {
glennrp5aa37f62011-01-02 03:07:57 +00009240 if (image_matte == MagickFalse)
9241 {
9242 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9243 image_matte=MagickFalse;
9244 }
glennrp8bb3a022010-12-13 20:40:04 +00009245
glennrp0b206f52011-01-07 04:55:32 +00009246 else
glennrp5aa37f62011-01-02 03:07:57 +00009247 {
9248 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9249 image_matte=MagickTrue;
9250 }
9251 }
glennrp0fe50b42010-11-16 03:52:51 +00009252 }
glennrp5aa37f62011-01-02 03:07:57 +00009253
cristy3ed852e2009-09-05 21:47:34 +00009254 }
glennrp0fe50b42010-11-16 03:52:51 +00009255
cristy3ed852e2009-09-05 21:47:34 +00009256 if (logging != MagickFalse)
9257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009258 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009259
glennrp5af765f2010-03-30 11:12:18 +00009260 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009261 {
9262 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9263 ping_color_type == PNG_COLOR_TYPE_RGB ||
9264 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9265 ping_bit_depth=8;
9266 }
cristy3ed852e2009-09-05 21:47:34 +00009267
glennrpd6bf1612010-12-17 17:28:54 +00009268 old_bit_depth=ping_bit_depth;
9269
glennrp5af765f2010-03-30 11:12:18 +00009270 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009271 {
glennrp8d579662011-02-23 02:05:02 +00009272 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9273 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009274 }
glennrp8640fb52010-11-23 15:48:26 +00009275
glennrp5af765f2010-03-30 11:12:18 +00009276 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009277 {
cristy35ef8242010-06-03 16:24:13 +00009278 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009279 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009280
9281 if (image->colors == 0)
9282 {
glennrp0fe50b42010-11-16 03:52:51 +00009283 /* DO SOMETHING */
cristyc82a27b2011-10-21 01:07:16 +00009284 (void) ThrowMagickException(exception,
glennrp0f111982010-07-07 20:18:33 +00009285 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009286 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009287 }
9288
cristy35ef8242010-06-03 16:24:13 +00009289 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009290 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009291 }
glennrp2b013e42010-11-24 16:55:50 +00009292
glennrpd6bf1612010-12-17 17:28:54 +00009293 if (logging != MagickFalse)
9294 {
9295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9296 " Number of colors: %.20g",(double) image_colors);
9297
9298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9299 " Tentative PNG bit depth: %d",ping_bit_depth);
9300 }
9301
9302 if (ping_bit_depth < (int) mng_info->write_png_depth)
9303 ping_bit_depth = mng_info->write_png_depth;
9304 }
glennrp2cc891a2010-12-24 13:44:32 +00009305
glennrp5af765f2010-03-30 11:12:18 +00009306 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009307
cristy3ed852e2009-09-05 21:47:34 +00009308 if (logging != MagickFalse)
9309 {
9310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009311 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009312
cristy3ed852e2009-09-05 21:47:34 +00009313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009314 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009315
cristy3ed852e2009-09-05 21:47:34 +00009316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009317 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009318
cristy3ed852e2009-09-05 21:47:34 +00009319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009320
glennrp8640fb52010-11-23 15:48:26 +00009321 " image->depth: %.20g",(double) image->depth);
9322
9323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009324 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009325 }
9326
glennrp58e01762011-01-07 15:28:54 +00009327 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009328 {
glennrp4f25bd02011-01-01 18:51:28 +00009329 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009330 {
glennrp7c4c9e62011-03-21 20:23:32 +00009331 if (mng_info->write_png_colortype == 0)
9332 {
9333 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009334
glennrp7c4c9e62011-03-21 20:23:32 +00009335 if (ping_have_color != MagickFalse)
9336 ping_color_type=PNG_COLOR_TYPE_RGBA;
9337 }
glennrp4f25bd02011-01-01 18:51:28 +00009338
9339 /*
9340 * Determine if there is any transparent color.
9341 */
9342 if (number_transparent + number_semitransparent == 0)
9343 {
9344 /*
9345 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9346 */
glennrpa6a06632011-01-19 15:15:34 +00009347
glennrp4f25bd02011-01-01 18:51:28 +00009348 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009349
9350 if (mng_info->write_png_colortype == 0)
9351 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009352 }
9353
9354 else
9355 {
9356 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009357 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009358
9359 mask=0xffff;
9360
9361 if (ping_bit_depth == 8)
9362 mask=0x00ff;
9363
9364 if (ping_bit_depth == 4)
9365 mask=0x000f;
9366
9367 if (ping_bit_depth == 2)
9368 mask=0x0003;
9369
9370 if (ping_bit_depth == 1)
9371 mask=0x0001;
9372
9373 ping_trans_color.red=(png_uint_16)
9374 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9375
9376 ping_trans_color.green=(png_uint_16)
9377 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9378
9379 ping_trans_color.blue=(png_uint_16)
9380 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9381
9382 ping_trans_color.gray=(png_uint_16)
cristy101ab702011-10-13 13:06:32 +00009383 (ScaleQuantumToShort(GetPixelInfoIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009384 image->colormap)) & mask);
9385
9386 ping_trans_color.index=(png_byte) 0;
9387
9388 ping_have_tRNS=MagickTrue;
9389 }
9390
9391 if (ping_have_tRNS != MagickFalse)
9392 {
9393 /*
glennrpfd05d622011-02-25 04:10:33 +00009394 * Determine if there is one and only one transparent color
9395 * and if so if it is fully transparent.
9396 */
9397 if (ping_have_cheap_transparency == MagickFalse)
9398 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009399 }
9400
9401 if (ping_have_tRNS != MagickFalse)
9402 {
glennrp7c4c9e62011-03-21 20:23:32 +00009403 if (mng_info->write_png_colortype == 0)
9404 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009405
9406 if (image_depth == 8)
9407 {
9408 ping_trans_color.red&=0xff;
9409 ping_trans_color.green&=0xff;
9410 ping_trans_color.blue&=0xff;
9411 ping_trans_color.gray&=0xff;
9412 }
9413 }
9414 }
cristy3ed852e2009-09-05 21:47:34 +00009415 else
9416 {
cristy3ed852e2009-09-05 21:47:34 +00009417 if (image_depth == 8)
9418 {
glennrp5af765f2010-03-30 11:12:18 +00009419 ping_trans_color.red&=0xff;
9420 ping_trans_color.green&=0xff;
9421 ping_trans_color.blue&=0xff;
9422 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009423 }
9424 }
9425 }
glennrp8640fb52010-11-23 15:48:26 +00009426
cristy3ed852e2009-09-05 21:47:34 +00009427 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009428
glennrp2e09f552010-11-14 00:38:48 +00009429 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009430 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009431
glennrp39992b42010-11-14 00:03:43 +00009432 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009433 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009434 ping_have_color == MagickFalse &&
9435 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009436 {
cristy35ef8242010-06-03 16:24:13 +00009437 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009438
cristy3ed852e2009-09-05 21:47:34 +00009439 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009440 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009441
glennrp7c4c9e62011-03-21 20:23:32 +00009442 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009443 {
glennrp5af765f2010-03-30 11:12:18 +00009444 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009445
cristy3ed852e2009-09-05 21:47:34 +00009446 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009447 {
9448 if (logging != MagickFalse)
9449 {
9450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9451 " Scaling ping_trans_color (0)");
9452 }
9453 ping_trans_color.gray*=0x0101;
9454 }
cristy3ed852e2009-09-05 21:47:34 +00009455 }
glennrp0fe50b42010-11-16 03:52:51 +00009456
cristy3ed852e2009-09-05 21:47:34 +00009457 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9458 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009459
glennrp136ee3a2011-04-27 15:47:45 +00009460 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009461 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009462 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009463
cristy3ed852e2009-09-05 21:47:34 +00009464 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009465 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009466
cristy3ed852e2009-09-05 21:47:34 +00009467 else
9468 {
glennrp5af765f2010-03-30 11:12:18 +00009469 ping_bit_depth=8;
9470 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009471 {
9472 if(!mng_info->write_png_depth)
9473 {
glennrp5af765f2010-03-30 11:12:18 +00009474 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009475
cristy35ef8242010-06-03 16:24:13 +00009476 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009477 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009478 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009479 }
9480 }
glennrp2b013e42010-11-24 16:55:50 +00009481
glennrp0fe50b42010-11-16 03:52:51 +00009482 else if (ping_color_type ==
9483 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009484 mng_info->IsPalette)
9485 {
cristy3ed852e2009-09-05 21:47:34 +00009486 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009487
cristy3ed852e2009-09-05 21:47:34 +00009488 int
9489 depth_4_ok=MagickTrue,
9490 depth_2_ok=MagickTrue,
9491 depth_1_ok=MagickTrue;
9492
cristybb503372010-05-27 20:51:26 +00009493 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009494 {
9495 unsigned char
9496 intensity;
9497
9498 intensity=ScaleQuantumToChar(image->colormap[i].red);
9499
9500 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9501 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9502 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9503 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009504 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009505 depth_1_ok=MagickFalse;
9506 }
glennrp2b013e42010-11-24 16:55:50 +00009507
cristy3ed852e2009-09-05 21:47:34 +00009508 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009509 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009510
cristy3ed852e2009-09-05 21:47:34 +00009511 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009512 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009513
cristy3ed852e2009-09-05 21:47:34 +00009514 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009515 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009516 }
9517 }
glennrp2b013e42010-11-24 16:55:50 +00009518
glennrp5af765f2010-03-30 11:12:18 +00009519 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009520 }
glennrp0fe50b42010-11-16 03:52:51 +00009521
cristy3ed852e2009-09-05 21:47:34 +00009522 else
glennrp0fe50b42010-11-16 03:52:51 +00009523
cristy3ed852e2009-09-05 21:47:34 +00009524 if (mng_info->IsPalette)
9525 {
glennrp17a14852010-05-10 03:01:59 +00009526 number_colors=image_colors;
9527
cristy3ed852e2009-09-05 21:47:34 +00009528 if (image_depth <= 8)
9529 {
cristy3ed852e2009-09-05 21:47:34 +00009530 /*
9531 Set image palette.
9532 */
glennrp5af765f2010-03-30 11:12:18 +00009533 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009534
glennrp58e01762011-01-07 15:28:54 +00009535 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009536 {
glennrp9c1eb072010-06-06 22:19:15 +00009537 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009538
glennrp3b51f0e2010-11-27 18:14:08 +00009539 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9541 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009542 }
glennrp0fe50b42010-11-16 03:52:51 +00009543
cristy3ed852e2009-09-05 21:47:34 +00009544 else
9545 {
cristybb503372010-05-27 20:51:26 +00009546 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009547 {
9548 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9549 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9550 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9551 }
glennrp0fe50b42010-11-16 03:52:51 +00009552
glennrp3b51f0e2010-11-27 18:14:08 +00009553 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009555 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009556 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009557
glennrp39992b42010-11-14 00:03:43 +00009558 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009559 }
glennrp0fe50b42010-11-16 03:52:51 +00009560
cristy3ed852e2009-09-05 21:47:34 +00009561 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009562 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009563 {
cristybefe4d22010-06-07 01:18:58 +00009564 size_t
9565 one;
9566
glennrp5af765f2010-03-30 11:12:18 +00009567 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009568 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009569
cristy94b11832011-09-08 19:46:03 +00009570 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009571 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009572 }
glennrp0fe50b42010-11-16 03:52:51 +00009573
glennrp5af765f2010-03-30 11:12:18 +00009574 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009575
glennrp58e01762011-01-07 15:28:54 +00009576 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009577 {
glennrp0fe50b42010-11-16 03:52:51 +00009578 /*
glennrpd6bf1612010-12-17 17:28:54 +00009579 * Set up trans_colors array.
9580 */
glennrp0fe50b42010-11-16 03:52:51 +00009581 assert(number_colors <= 256);
9582
glennrpd6bf1612010-12-17 17:28:54 +00009583 ping_num_trans=(unsigned short) (number_transparent +
9584 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009585
9586 if (ping_num_trans == 0)
9587 ping_have_tRNS=MagickFalse;
9588
glennrpd6bf1612010-12-17 17:28:54 +00009589 else
glennrp0fe50b42010-11-16 03:52:51 +00009590 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009591 if (logging != MagickFalse)
9592 {
9593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9594 " Scaling ping_trans_color (1)");
9595 }
glennrpd6bf1612010-12-17 17:28:54 +00009596 ping_have_tRNS=MagickTrue;
9597
9598 for (i=0; i < ping_num_trans; i++)
9599 {
cristy4c08aed2011-07-01 19:47:50 +00009600 ping_trans_alpha[i]= (png_byte)
9601 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009602 }
glennrp0fe50b42010-11-16 03:52:51 +00009603 }
9604 }
cristy3ed852e2009-09-05 21:47:34 +00009605 }
9606 }
glennrp0fe50b42010-11-16 03:52:51 +00009607
cristy3ed852e2009-09-05 21:47:34 +00009608 else
9609 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009610
cristy3ed852e2009-09-05 21:47:34 +00009611 if (image_depth < 8)
9612 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009613
cristy3ed852e2009-09-05 21:47:34 +00009614 if ((save_image_depth == 16) && (image_depth == 8))
9615 {
glennrp4f25bd02011-01-01 18:51:28 +00009616 if (logging != MagickFalse)
9617 {
9618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9619 " Scaling ping_trans_color from (%d,%d,%d)",
9620 (int) ping_trans_color.red,
9621 (int) ping_trans_color.green,
9622 (int) ping_trans_color.blue);
9623 }
9624
glennrp5af765f2010-03-30 11:12:18 +00009625 ping_trans_color.red*=0x0101;
9626 ping_trans_color.green*=0x0101;
9627 ping_trans_color.blue*=0x0101;
9628 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009629
9630 if (logging != MagickFalse)
9631 {
9632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9633 " to (%d,%d,%d)",
9634 (int) ping_trans_color.red,
9635 (int) ping_trans_color.green,
9636 (int) ping_trans_color.blue);
9637 }
cristy3ed852e2009-09-05 21:47:34 +00009638 }
9639 }
9640
cristy4383ec82011-01-05 15:42:32 +00009641 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9642 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009643
cristy3ed852e2009-09-05 21:47:34 +00009644 /*
9645 Adjust background and transparency samples in sub-8-bit grayscale files.
9646 */
glennrp5af765f2010-03-30 11:12:18 +00009647 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009648 PNG_COLOR_TYPE_GRAY)
9649 {
9650 png_uint_16
9651 maxval;
9652
cristy35ef8242010-06-03 16:24:13 +00009653 size_t
9654 one=1;
9655
cristy22ffd972010-06-03 16:51:47 +00009656 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009657
glennrp4f25bd02011-01-01 18:51:28 +00009658 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009659 {
cristy3ed852e2009-09-05 21:47:34 +00009660
glennrp9f0fa852011-12-15 12:20:50 +00009661 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9662 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9663 &image->background_color))) +.5)));
cristy3ed852e2009-09-05 21:47:34 +00009664
9665 if (logging != MagickFalse)
9666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009667 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9669 " background_color index is %d",
9670 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009671
glennrp991d11d2010-11-12 21:55:28 +00009672 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009673 }
cristy3ed852e2009-09-05 21:47:34 +00009674
glennrp3e3e20f2011-06-09 04:21:43 +00009675 if (logging != MagickFalse)
9676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9677 " Scaling ping_trans_color.gray from %d",
9678 (int)ping_trans_color.gray);
9679
glennrp9be9b1c2011-06-09 12:21:45 +00009680 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009681 ping_trans_color.gray)+.5);
9682
9683 if (logging != MagickFalse)
9684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9685 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009686 }
glennrp17a14852010-05-10 03:01:59 +00009687
glennrp26f37912010-12-23 16:22:42 +00009688 if (ping_exclude_bKGD == MagickFalse)
9689 {
glennrp1273f7b2011-02-24 03:20:30 +00009690 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009691 {
9692 /*
9693 Identify which colormap entry is the background color.
9694 */
9695
glennrp17a14852010-05-10 03:01:59 +00009696 number_colors=image_colors;
9697
glennrpa521b2f2010-10-29 04:11:03 +00009698 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9699 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009700 break;
9701
9702 ping_background.index=(png_byte) i;
9703
glennrp3b51f0e2010-11-27 18:14:08 +00009704 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009705 {
9706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009707 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009708 }
glennrp0fe50b42010-11-16 03:52:51 +00009709
cristy13d07042010-11-21 20:56:18 +00009710 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009711 {
9712 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009713
9714 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009715 {
9716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9717 " background =(%d,%d,%d)",
9718 (int) ping_background.red,
9719 (int) ping_background.green,
9720 (int) ping_background.blue);
9721 }
9722 }
glennrpa521b2f2010-10-29 04:11:03 +00009723
glennrpd6bf1612010-12-17 17:28:54 +00009724 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009725 {
glennrp3b51f0e2010-11-27 18:14:08 +00009726 if (logging != MagickFalse)
9727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9728 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009729 ping_have_bKGD = MagickFalse;
9730 }
glennrp17a14852010-05-10 03:01:59 +00009731 }
glennrp26f37912010-12-23 16:22:42 +00009732 }
glennrp17a14852010-05-10 03:01:59 +00009733
cristy3ed852e2009-09-05 21:47:34 +00009734 if (logging != MagickFalse)
9735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009736 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009737 /*
9738 Initialize compression level and filtering.
9739 */
9740 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009741 {
9742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9743 " Setting up deflate compression");
9744
9745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9746 " Compression buffer size: 32768");
9747 }
9748
cristy3ed852e2009-09-05 21:47:34 +00009749 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009750
cristy3ed852e2009-09-05 21:47:34 +00009751 if (logging != MagickFalse)
9752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9753 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009754
cristy4054bfb2011-08-29 23:41:39 +00009755 png_set_compression_mem_level(ping, 9);
9756
glennrp10d739e2011-06-29 18:00:52 +00009757 /* Untangle the "-quality" setting:
9758
9759 Undefined is 0; the default is used.
9760 Default is 75
9761
9762 10's digit:
9763
9764 0: Use Z_HUFFMAN_ONLY strategy with the
9765 zlib default compression level
9766
9767 1-9: the zlib compression level
9768
9769 1's digit:
9770
9771 0-4: the PNG filter method
9772
9773 5: libpng adaptive filtering if compression level > 5
9774 libpng filter type "none" if compression level <= 5
9775 or if image is grayscale or palette
9776
9777 6: libpng adaptive filtering
9778
9779 7: "LOCO" filtering (intrapixel differing) if writing
9780 a MNG, othewise "none". Did not work in IM-6.7.0-9
9781 and earlier because of a missing "else".
9782
9783 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009784 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009785
9786 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009787 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009788
9789 Note that using the -quality option, not all combinations of
9790 PNG filter type, zlib compression level, and zlib compression
cristy5e6be1e2011-07-16 01:23:39 +00009791 strategy are possible. This will be addressed soon in a
cristy5d6fc9c2011-12-27 03:10:42 +00009792 release that accomodates "-define png:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +00009793
9794 */
9795
cristy3ed852e2009-09-05 21:47:34 +00009796 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9797 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009798
glennrp18682582011-06-30 18:11:47 +00009799 if (quality <= 9)
9800 {
9801 if (mng_info->write_png_compression_strategy == 0)
9802 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9803 }
9804
9805 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009806 {
9807 int
9808 level;
9809
cristybb503372010-05-27 20:51:26 +00009810 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009811
glennrp18682582011-06-30 18:11:47 +00009812 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009813 }
glennrp0fe50b42010-11-16 03:52:51 +00009814
glennrp18682582011-06-30 18:11:47 +00009815 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009816 {
glennrp18682582011-06-30 18:11:47 +00009817 if ((quality %10) == 8 || (quality %10) == 9)
9818 mng_info->write_png_compression_strategy=Z_RLE;
cristy3ed852e2009-09-05 21:47:34 +00009819 }
glennrp0fe50b42010-11-16 03:52:51 +00009820
glennrp18682582011-06-30 18:11:47 +00009821 if (mng_info->write_png_compression_filter == 0)
9822 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9823
cristy3ed852e2009-09-05 21:47:34 +00009824 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009825 {
glennrp18682582011-06-30 18:11:47 +00009826 if (mng_info->write_png_compression_level)
9827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9828 " Compression level: %d",
9829 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009830
glennrp18682582011-06-30 18:11:47 +00009831 if (mng_info->write_png_compression_strategy)
9832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9833 " Compression strategy: %d",
9834 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009835
glennrp18682582011-06-30 18:11:47 +00009836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9837 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009838
cristy4054bfb2011-08-29 23:41:39 +00009839 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +00009840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9841 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +00009842 else if (mng_info->write_png_compression_filter == 0 ||
9843 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +00009844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9845 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009846 else
9847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9848 " Base filter method: %d",
9849 (int) mng_info->write_png_compression_filter-1);
9850 }
glennrp2b013e42010-11-24 16:55:50 +00009851
glennrp18682582011-06-30 18:11:47 +00009852 if (mng_info->write_png_compression_level != 0)
9853 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9854
9855 if (mng_info->write_png_compression_filter == 6)
9856 {
9857 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9858 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9859 (quality < 50))
9860 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9861 else
9862 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9863 }
cristy4054bfb2011-08-29 23:41:39 +00009864 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +00009865 mng_info->write_png_compression_filter == 10)
9866 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9867
9868 else if (mng_info->write_png_compression_filter == 8)
9869 {
9870#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9871 if (mng_info->write_mng)
9872 {
9873 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9874 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9875 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9876 }
9877#endif
cristy4054bfb2011-08-29 23:41:39 +00009878 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +00009879 }
9880
9881 else if (mng_info->write_png_compression_filter == 9)
9882 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9883
9884 else if (mng_info->write_png_compression_filter != 0)
9885 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9886 mng_info->write_png_compression_filter-1);
9887
9888 if (mng_info->write_png_compression_strategy != 0)
9889 png_set_compression_strategy(ping,
9890 mng_info->write_png_compression_strategy-1);
9891
cristy0d57eec2011-09-04 22:13:56 +00009892 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9893 if (ping_exclude_sRGB != MagickFalse ||
9894 (image->rendering_intent == UndefinedIntent))
9895 {
9896 if ((ping_exclude_tEXt == MagickFalse ||
9897 ping_exclude_zTXt == MagickFalse) &&
9898 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009899 {
9900 ResetImageProfileIterator(image);
9901 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009902 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009903 profile=GetImageProfile(image,name);
9904
9905 if (profile != (StringInfo *) NULL)
9906 {
glennrp5af765f2010-03-30 11:12:18 +00009907#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009908 if ((LocaleCompare(name,"ICC") == 0) ||
9909 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009910 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009911
9912 if (ping_exclude_iCCP == MagickFalse)
9913 {
cristy9f027d12011-09-21 01:17:17 +00009914 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009915#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009916 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009917#else
9918 (png_const_bytep) GetStringInfoDatum(profile),
9919#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009920 (png_uint_32) GetStringInfoLength(profile));
9921 }
glennrp26f37912010-12-23 16:22:42 +00009922 }
glennrp0fe50b42010-11-16 03:52:51 +00009923
glennrpc8cbc5d2011-01-01 00:12:34 +00009924 else
cristy3ed852e2009-09-05 21:47:34 +00009925#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009926 if (ping_exclude_zCCP == MagickFalse)
9927 {
glennrpcf002022011-01-30 02:38:15 +00009928 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009929 (unsigned char *) name,(unsigned char *) name,
9930 GetStringInfoDatum(profile),
9931 (png_uint_32) GetStringInfoLength(profile));
9932 }
9933 }
glennrp0b206f52011-01-07 04:55:32 +00009934
glennrpc8cbc5d2011-01-01 00:12:34 +00009935 if (logging != MagickFalse)
9936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9937 " Setting up text chunk with %s profile",name);
9938
9939 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009940 }
cristy0d57eec2011-09-04 22:13:56 +00009941 }
cristy3ed852e2009-09-05 21:47:34 +00009942 }
9943
9944#if defined(PNG_WRITE_sRGB_SUPPORTED)
9945 if ((mng_info->have_write_global_srgb == 0) &&
9946 ((image->rendering_intent != UndefinedIntent) ||
9947 (image->colorspace == sRGBColorspace)))
9948 {
glennrp26f37912010-12-23 16:22:42 +00009949 if (ping_exclude_sRGB == MagickFalse)
9950 {
9951 /*
9952 Note image rendering intent.
9953 */
9954 if (logging != MagickFalse)
9955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9956 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009957
glennrp26f37912010-12-23 16:22:42 +00009958 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009959 Magick_RenderingIntent_to_PNG_RenderingIntent(
9960 image->rendering_intent)));
glennrp26f37912010-12-23 16:22:42 +00009961 }
cristy3ed852e2009-09-05 21:47:34 +00009962 }
glennrp26f37912010-12-23 16:22:42 +00009963
glennrp5af765f2010-03-30 11:12:18 +00009964 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009965#endif
9966 {
glennrp2cc891a2010-12-24 13:44:32 +00009967 if (ping_exclude_gAMA == MagickFalse &&
9968 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009969 (image->gamma < .45 || image->gamma > .46)))
9970 {
cristy3ed852e2009-09-05 21:47:34 +00009971 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9972 {
9973 /*
9974 Note image gamma.
9975 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9976 */
9977 if (logging != MagickFalse)
9978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9979 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009980
cristy3ed852e2009-09-05 21:47:34 +00009981 png_set_gAMA(ping,ping_info,image->gamma);
9982 }
glennrp26f37912010-12-23 16:22:42 +00009983 }
glennrp2b013e42010-11-24 16:55:50 +00009984
glennrp26f37912010-12-23 16:22:42 +00009985 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009986 {
glennrp26f37912010-12-23 16:22:42 +00009987 if ((mng_info->have_write_global_chrm == 0) &&
9988 (image->chromaticity.red_primary.x != 0.0))
9989 {
9990 /*
9991 Note image chromaticity.
9992 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9993 */
9994 PrimaryInfo
9995 bp,
9996 gp,
9997 rp,
9998 wp;
cristy3ed852e2009-09-05 21:47:34 +00009999
glennrp26f37912010-12-23 16:22:42 +000010000 wp=image->chromaticity.white_point;
10001 rp=image->chromaticity.red_primary;
10002 gp=image->chromaticity.green_primary;
10003 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +000010004
glennrp26f37912010-12-23 16:22:42 +000010005 if (logging != MagickFalse)
10006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10007 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010008
glennrp26f37912010-12-23 16:22:42 +000010009 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10010 bp.x,bp.y);
10011 }
10012 }
cristy3ed852e2009-09-05 21:47:34 +000010013 }
glennrpdfd70802010-11-14 01:23:35 +000010014
glennrp5af765f2010-03-30 11:12:18 +000010015 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +000010016
10017 if (mng_info->write_mng)
10018 png_set_sig_bytes(ping,8);
10019
cristy5d6fc9c2011-12-27 03:10:42 +000010020 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
cristy3ed852e2009-09-05 21:47:34 +000010021
glennrpd6bf1612010-12-17 17:28:54 +000010022 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +000010023 {
10024 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +000010025 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010026 {
glennrp5af765f2010-03-30 11:12:18 +000010027 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +000010028
glennrp5af765f2010-03-30 11:12:18 +000010029 if (ping_bit_depth < 8)
10030 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +000010031 }
glennrp0fe50b42010-11-16 03:52:51 +000010032
cristy3ed852e2009-09-05 21:47:34 +000010033 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +000010034 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +000010035 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +000010036 }
10037
glennrp0e8ea192010-12-24 18:00:33 +000010038 if (ping_need_colortype_warning != MagickFalse ||
10039 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +000010040 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +000010041 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +000010042 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +000010043 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +000010044 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +000010045 {
10046 if (logging != MagickFalse)
10047 {
glennrp0e8ea192010-12-24 18:00:33 +000010048 if (ping_need_colortype_warning != MagickFalse)
10049 {
10050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10051 " Image has transparency but tRNS chunk was excluded");
10052 }
10053
cristy3ed852e2009-09-05 21:47:34 +000010054 if (mng_info->write_png_depth)
10055 {
10056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010057 " Defined png:bit-depth=%u, Computed depth=%u",
cristy3ed852e2009-09-05 21:47:34 +000010058 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +000010059 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +000010060 }
glennrp0e8ea192010-12-24 18:00:33 +000010061
cristy3ed852e2009-09-05 21:47:34 +000010062 if (mng_info->write_png_colortype)
10063 {
10064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010065 " Defined png:color-type=%u, Computed color type=%u",
cristy3ed852e2009-09-05 21:47:34 +000010066 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +000010067 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010068 }
10069 }
glennrp0e8ea192010-12-24 18:00:33 +000010070
glennrp3bd2e412010-08-10 13:34:52 +000010071 png_warning(ping,
cristy5d6fc9c2011-12-27 03:10:42 +000010072 "Cannot write image with defined png:bit-depth or png:color-type.");
cristy3ed852e2009-09-05 21:47:34 +000010073 }
10074
glennrp58e01762011-01-07 15:28:54 +000010075 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010076 {
10077 /* Add an opaque matte channel */
10078 image->matte = MagickTrue;
cristye941a752011-10-15 01:52:48 +000010079 (void) SetImageAlpha(image,OpaqueAlpha,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010080
glennrpb4a13412010-05-05 12:47:19 +000010081 if (logging != MagickFalse)
10082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10083 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +000010084 }
10085
glennrp0e319732011-01-25 21:53:13 +000010086 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +000010087 {
glennrp991d11d2010-11-12 21:55:28 +000010088 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +000010089 {
glennrp991d11d2010-11-12 21:55:28 +000010090 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010091 if (logging != MagickFalse)
10092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10093 " Setting ping_have_tRNS=MagickTrue.");
10094 }
glennrpe9c26dc2010-05-30 01:56:35 +000010095 }
10096
cristy3ed852e2009-09-05 21:47:34 +000010097 if (logging != MagickFalse)
10098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10099 " Writing PNG header chunks");
10100
glennrp5af765f2010-03-30 11:12:18 +000010101 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10102 ping_bit_depth,ping_color_type,
10103 ping_interlace_method,ping_compression_method,
10104 ping_filter_method);
10105
glennrp39992b42010-11-14 00:03:43 +000010106 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10107 {
glennrpf09bded2011-01-08 01:15:59 +000010108 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010109
glennrp3b51f0e2010-11-27 18:14:08 +000010110 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +000010111 {
glennrp8640fb52010-11-23 15:48:26 +000010112 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +000010113 {
glennrpd6bf1612010-12-17 17:28:54 +000010114 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +000010115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010116 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10117 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010118 (int) palette[i].red,
10119 (int) palette[i].green,
10120 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +000010121 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010122 (int) ping_trans_alpha[i]);
10123 else
10124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010125 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010126 (int) i,
10127 (int) palette[i].red,
10128 (int) palette[i].green,
10129 (int) palette[i].blue);
10130 }
glennrp39992b42010-11-14 00:03:43 +000010131 }
glennrp39992b42010-11-14 00:03:43 +000010132 }
10133
glennrp26f37912010-12-23 16:22:42 +000010134 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010135 {
glennrp26f37912010-12-23 16:22:42 +000010136 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010137 {
glennrp26f37912010-12-23 16:22:42 +000010138 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010139 if (logging)
10140 {
10141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10142 " Setting up bKGD chunk");
10143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10144 " background color = (%d,%d,%d)",
10145 (int) ping_background.red,
10146 (int) ping_background.green,
10147 (int) ping_background.blue);
10148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10149 " index = %d, gray=%d",
10150 (int) ping_background.index,
10151 (int) ping_background.gray);
10152 }
10153 }
glennrp26f37912010-12-23 16:22:42 +000010154 }
10155
10156 if (ping_exclude_pHYs == MagickFalse)
10157 {
10158 if (ping_have_pHYs != MagickFalse)
10159 {
10160 png_set_pHYs(ping,ping_info,
10161 ping_pHYs_x_resolution,
10162 ping_pHYs_y_resolution,
10163 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010164
10165 if (logging)
10166 {
10167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10168 " Setting up pHYs chunk");
10169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10170 " x_resolution=%lu",
10171 (unsigned long) ping_pHYs_x_resolution);
10172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10173 " y_resolution=%lu",
10174 (unsigned long) ping_pHYs_y_resolution);
10175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10176 " unit_type=%lu",
10177 (unsigned long) ping_pHYs_unit_type);
10178 }
glennrp26f37912010-12-23 16:22:42 +000010179 }
glennrpdfd70802010-11-14 01:23:35 +000010180 }
10181
10182#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010183 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010184 {
glennrp26f37912010-12-23 16:22:42 +000010185 if (image->page.x || image->page.y)
10186 {
10187 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10188 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010189
glennrp26f37912010-12-23 16:22:42 +000010190 if (logging != MagickFalse)
10191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10192 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10193 (int) image->page.x, (int) image->page.y);
10194 }
glennrpdfd70802010-11-14 01:23:35 +000010195 }
10196#endif
10197
glennrpda8f3a72011-02-27 23:54:12 +000010198 if (mng_info->need_blob != MagickFalse)
10199 {
cristyc82a27b2011-10-21 01:07:16 +000010200 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
glennrpda8f3a72011-02-27 23:54:12 +000010201 MagickFalse)
10202 png_error(ping,"WriteBlob Failed");
10203
10204 ping_have_blob=MagickTrue;
10205 }
10206
cristy3ed852e2009-09-05 21:47:34 +000010207 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010208
glennrp39992b42010-11-14 00:03:43 +000010209 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010210 {
glennrp3b51f0e2010-11-27 18:14:08 +000010211 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010212 {
10213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10214 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10215 }
10216
10217 if (ping_color_type == 3)
10218 (void) png_set_tRNS(ping, ping_info,
10219 ping_trans_alpha,
10220 ping_num_trans,
10221 NULL);
10222
10223 else
10224 {
10225 (void) png_set_tRNS(ping, ping_info,
10226 NULL,
10227 0,
10228 &ping_trans_color);
10229
glennrp3b51f0e2010-11-27 18:14:08 +000010230 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010231 {
10232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010233 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010234 (int) ping_trans_color.red,
10235 (int) ping_trans_color.green,
10236 (int) ping_trans_color.blue);
10237 }
10238 }
glennrp991d11d2010-11-12 21:55:28 +000010239 }
10240
cristy3ed852e2009-09-05 21:47:34 +000010241 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010242 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010243
cristy3ed852e2009-09-05 21:47:34 +000010244 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010245
cristy3ed852e2009-09-05 21:47:34 +000010246 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010247 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010248
glennrp26f37912010-12-23 16:22:42 +000010249 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010250 {
glennrp4f25bd02011-01-01 18:51:28 +000010251 if ((image->page.width != 0 && image->page.width != image->columns) ||
10252 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010253 {
10254 unsigned char
10255 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010256
glennrp26f37912010-12-23 16:22:42 +000010257 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10258 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010259 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010260 PNGLong(chunk+4,(png_uint_32) image->page.width);
10261 PNGLong(chunk+8,(png_uint_32) image->page.height);
10262 chunk[12]=0; /* unit = pixels */
10263 (void) WriteBlob(image,13,chunk);
10264 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10265 }
cristy3ed852e2009-09-05 21:47:34 +000010266 }
10267
10268#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010269 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010270#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010271 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010272#undef PNG_HAVE_IDAT
10273#endif
10274
10275 png_set_packing(ping);
10276 /*
10277 Allocate memory.
10278 */
10279 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010280 if (image_depth > 8)
10281 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010282 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010283 {
glennrpb4a13412010-05-05 12:47:19 +000010284 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010285 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010286 break;
glennrp0fe50b42010-11-16 03:52:51 +000010287
glennrpb4a13412010-05-05 12:47:19 +000010288 case PNG_COLOR_TYPE_GRAY_ALPHA:
10289 rowbytes*=2;
10290 break;
glennrp0fe50b42010-11-16 03:52:51 +000010291
glennrpb4a13412010-05-05 12:47:19 +000010292 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010293 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010294 break;
glennrp0fe50b42010-11-16 03:52:51 +000010295
glennrpb4a13412010-05-05 12:47:19 +000010296 default:
10297 break;
cristy3ed852e2009-09-05 21:47:34 +000010298 }
glennrp3b51f0e2010-11-27 18:14:08 +000010299
10300 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010301 {
10302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10303 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010304
glennrpb4a13412010-05-05 12:47:19 +000010305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010306 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010307 }
glennrpcf002022011-01-30 02:38:15 +000010308 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10309 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010310
glennrpcf002022011-01-30 02:38:15 +000010311 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010312 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010313
cristy3ed852e2009-09-05 21:47:34 +000010314 /*
10315 Initialize image scanlines.
10316 */
glennrp5af765f2010-03-30 11:12:18 +000010317 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010318 {
10319 /*
10320 PNG write failed.
10321 */
10322#ifdef PNG_DEBUG
10323 if (image_info->verbose)
10324 (void) printf("PNG write has failed.\n");
10325#endif
10326 png_destroy_write_struct(&ping,&ping_info);
10327 if (quantum_info != (QuantumInfo *) NULL)
10328 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010329 if (ping_pixels != (unsigned char *) NULL)
10330 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010331#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010332 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010333#endif
glennrpda8f3a72011-02-27 23:54:12 +000010334 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010335 (void) CloseBlob(image);
10336 image_info=DestroyImageInfo(image_info);
10337 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010338 return(MagickFalse);
10339 }
cristyed552522009-10-16 14:04:35 +000010340 quantum_info=AcquireQuantumInfo(image_info,image);
10341 if (quantum_info == (QuantumInfo *) NULL)
10342 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010343 quantum_info->format=UndefinedQuantumFormat;
10344 quantum_info->depth=image_depth;
10345 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010346
cristy3ed852e2009-09-05 21:47:34 +000010347 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010348 !mng_info->write_png32) &&
10349 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010350 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010351 image_matte == MagickFalse &&
10352 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010353 {
glennrp8bb3a022010-12-13 20:40:04 +000010354 /* Palette, Bilevel, or Opaque Monochrome */
cristy4c08aed2011-07-01 19:47:50 +000010355 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010356 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010357
cristy3ed852e2009-09-05 21:47:34 +000010358 quantum_info->depth=8;
10359 for (pass=0; pass < num_passes; pass++)
10360 {
10361 /*
10362 Convert PseudoClass image to a PNG monochrome image.
10363 */
cristybb503372010-05-27 20:51:26 +000010364 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010365 {
glennrpd71e86a2011-02-24 01:28:37 +000010366 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10368 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010369
cristyc82a27b2011-10-21 01:07:16 +000010370 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010371
cristy4c08aed2011-07-01 19:47:50 +000010372 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010373 break;
glennrp0fe50b42010-11-16 03:52:51 +000010374
cristy3ed852e2009-09-05 21:47:34 +000010375 if (mng_info->IsPalette)
10376 {
cristy4c08aed2011-07-01 19:47:50 +000010377 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010378 quantum_info,GrayQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010379 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10380 mng_info->write_png_depth &&
10381 mng_info->write_png_depth != old_bit_depth)
10382 {
10383 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010384 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010385 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010386 >> (8-old_bit_depth));
10387 }
10388 }
glennrp0fe50b42010-11-16 03:52:51 +000010389
cristy3ed852e2009-09-05 21:47:34 +000010390 else
10391 {
cristy4c08aed2011-07-01 19:47:50 +000010392 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010393 quantum_info,RedQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010394 }
glennrp0fe50b42010-11-16 03:52:51 +000010395
cristy3ed852e2009-09-05 21:47:34 +000010396 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010397 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010398 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010399 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010400
glennrp3b51f0e2010-11-27 18:14:08 +000010401 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10403 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010404
glennrpcf002022011-01-30 02:38:15 +000010405 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010406 }
10407 if (image->previous == (Image *) NULL)
10408 {
10409 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10410 if (status == MagickFalse)
10411 break;
10412 }
10413 }
10414 }
glennrp0fe50b42010-11-16 03:52:51 +000010415
glennrp8bb3a022010-12-13 20:40:04 +000010416 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010417 {
glennrp0fe50b42010-11-16 03:52:51 +000010418 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010419 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010420 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010421 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010422 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010423 {
cristy4c08aed2011-07-01 19:47:50 +000010424 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010425 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010426
glennrp8bb3a022010-12-13 20:40:04 +000010427 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010428 {
glennrp8bb3a022010-12-13 20:40:04 +000010429
cristybb503372010-05-27 20:51:26 +000010430 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010431 {
cristyc82a27b2011-10-21 01:07:16 +000010432 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010433
cristy4c08aed2011-07-01 19:47:50 +000010434 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010435 break;
glennrp2cc891a2010-12-24 13:44:32 +000010436
glennrp5af765f2010-03-30 11:12:18 +000010437 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010438 {
glennrp8bb3a022010-12-13 20:40:04 +000010439 if (mng_info->IsPalette)
cristy4c08aed2011-07-01 19:47:50 +000010440 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010441 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010442
glennrp8bb3a022010-12-13 20:40:04 +000010443 else
cristy4c08aed2011-07-01 19:47:50 +000010444 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010445 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010446
glennrp3b51f0e2010-11-27 18:14:08 +000010447 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010449 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010450 }
glennrp2cc891a2010-12-24 13:44:32 +000010451
glennrp8bb3a022010-12-13 20:40:04 +000010452 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10453 {
10454 if (logging != MagickFalse && y == 0)
10455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10456 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010457
cristy4c08aed2011-07-01 19:47:50 +000010458 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010459 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010460 }
glennrp2cc891a2010-12-24 13:44:32 +000010461
glennrp3b51f0e2010-11-27 18:14:08 +000010462 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010464 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010465
glennrpcf002022011-01-30 02:38:15 +000010466 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010467 }
glennrp2cc891a2010-12-24 13:44:32 +000010468
glennrp8bb3a022010-12-13 20:40:04 +000010469 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010470 {
glennrp8bb3a022010-12-13 20:40:04 +000010471 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10472 if (status == MagickFalse)
10473 break;
cristy3ed852e2009-09-05 21:47:34 +000010474 }
cristy3ed852e2009-09-05 21:47:34 +000010475 }
10476 }
glennrp8bb3a022010-12-13 20:40:04 +000010477
10478 else
10479 {
cristy4c08aed2011-07-01 19:47:50 +000010480 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010481 *p;
10482
10483 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010484 {
glennrp8bb3a022010-12-13 20:40:04 +000010485 if ((image_depth > 8) || (mng_info->write_png24 ||
10486 mng_info->write_png32 ||
10487 (!mng_info->write_png8 && !mng_info->IsPalette)))
10488 {
10489 for (y=0; y < (ssize_t) image->rows; y++)
10490 {
10491 p=GetVirtualPixels(image,0,y,image->columns,1,
cristyc82a27b2011-10-21 01:07:16 +000010492 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010493
cristy4c08aed2011-07-01 19:47:50 +000010494 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010495 break;
glennrp2cc891a2010-12-24 13:44:32 +000010496
glennrp8bb3a022010-12-13 20:40:04 +000010497 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10498 {
10499 if (image->storage_class == DirectClass)
cristy4c08aed2011-07-01 19:47:50 +000010500 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010501 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010502
glennrp8bb3a022010-12-13 20:40:04 +000010503 else
cristy4c08aed2011-07-01 19:47:50 +000010504 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010505 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010506 }
glennrp2cc891a2010-12-24 13:44:32 +000010507
glennrp8bb3a022010-12-13 20:40:04 +000010508 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10509 {
cristy4c08aed2011-07-01 19:47:50 +000010510 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010511 quantum_info,GrayAlphaQuantum,ping_pixels,
cristyc82a27b2011-10-21 01:07:16 +000010512 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010513
glennrp8bb3a022010-12-13 20:40:04 +000010514 if (logging != MagickFalse && y == 0)
10515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10516 " Writing GRAY_ALPHA PNG pixels (3)");
10517 }
glennrp2cc891a2010-12-24 13:44:32 +000010518
glennrp8bb3a022010-12-13 20:40:04 +000010519 else if (image_matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +000010520 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010521 quantum_info,RGBAQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010522
glennrp8bb3a022010-12-13 20:40:04 +000010523 else
cristy4c08aed2011-07-01 19:47:50 +000010524 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010525 quantum_info,RGBQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010526
glennrp8bb3a022010-12-13 20:40:04 +000010527 if (logging != MagickFalse && y == 0)
10528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10529 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010530
glennrpcf002022011-01-30 02:38:15 +000010531 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010532 }
10533 }
glennrp2cc891a2010-12-24 13:44:32 +000010534
glennrp8bb3a022010-12-13 20:40:04 +000010535 else
10536 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10537 mng_info->write_png32 ||
10538 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10539 {
10540 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10541 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10542 {
10543 if (logging != MagickFalse)
10544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10545 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010546
glennrp8bb3a022010-12-13 20:40:04 +000010547 quantum_info->depth=8;
10548 image_depth=8;
10549 }
glennrp2cc891a2010-12-24 13:44:32 +000010550
glennrp8bb3a022010-12-13 20:40:04 +000010551 for (y=0; y < (ssize_t) image->rows; y++)
10552 {
10553 if (logging != MagickFalse && y == 0)
10554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10555 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010556
cristy97707062011-12-27 18:25:00 +000010557 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000010558
cristy4c08aed2011-07-01 19:47:50 +000010559 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010560 break;
glennrp2cc891a2010-12-24 13:44:32 +000010561
glennrp8bb3a022010-12-13 20:40:04 +000010562 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010563 {
glennrp4bf89732011-03-21 13:48:28 +000010564 quantum_info->depth=image->depth;
10565
cristy4c08aed2011-07-01 19:47:50 +000010566 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010567 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp44757ab2011-03-17 12:57:03 +000010568 }
glennrp2cc891a2010-12-24 13:44:32 +000010569
glennrp8bb3a022010-12-13 20:40:04 +000010570 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10571 {
10572 if (logging != MagickFalse && y == 0)
10573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10574 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010575
cristy4c08aed2011-07-01 19:47:50 +000010576 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010577 quantum_info,GrayAlphaQuantum,ping_pixels,
cristyc82a27b2011-10-21 01:07:16 +000010578 exception);
glennrp8bb3a022010-12-13 20:40:04 +000010579 }
glennrp2cc891a2010-12-24 13:44:32 +000010580
glennrp8bb3a022010-12-13 20:40:04 +000010581 else
glennrp8bb3a022010-12-13 20:40:04 +000010582 {
cristy4c08aed2011-07-01 19:47:50 +000010583 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010584 quantum_info,IndexQuantum,ping_pixels,exception);
glennrp5eae7602011-02-22 15:21:32 +000010585
10586 if (logging != MagickFalse && y <= 2)
10587 {
10588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010589 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010590
10591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10592 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10593 (int)ping_pixels[0],(int)ping_pixels[1]);
10594 }
glennrp8bb3a022010-12-13 20:40:04 +000010595 }
glennrpcf002022011-01-30 02:38:15 +000010596 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010597 }
10598 }
glennrp2cc891a2010-12-24 13:44:32 +000010599
glennrp8bb3a022010-12-13 20:40:04 +000010600 if (image->previous == (Image *) NULL)
10601 {
10602 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10603 if (status == MagickFalse)
10604 break;
10605 }
cristy3ed852e2009-09-05 21:47:34 +000010606 }
glennrp8bb3a022010-12-13 20:40:04 +000010607 }
10608 }
10609
cristyb32b90a2009-09-07 21:45:48 +000010610 if (quantum_info != (QuantumInfo *) NULL)
10611 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010612
10613 if (logging != MagickFalse)
10614 {
10615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010616 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010617
cristy3ed852e2009-09-05 21:47:34 +000010618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010619 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010620
cristy3ed852e2009-09-05 21:47:34 +000010621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010622 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010623
cristy3ed852e2009-09-05 21:47:34 +000010624 if (mng_info->write_png_depth)
10625 {
10626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010627 " Defined png:bit-depth: %d",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010628 }
glennrp0fe50b42010-11-16 03:52:51 +000010629
cristy3ed852e2009-09-05 21:47:34 +000010630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010631 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010632
cristy3ed852e2009-09-05 21:47:34 +000010633 if (mng_info->write_png_colortype)
10634 {
10635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010636 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010637 }
glennrp0fe50b42010-11-16 03:52:51 +000010638
cristy3ed852e2009-09-05 21:47:34 +000010639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010640 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010641
cristy3ed852e2009-09-05 21:47:34 +000010642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010643 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010644 }
10645 /*
glennrpa0ed0092011-04-18 16:36:29 +000010646 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010647 */
glennrp823b55c2011-03-14 18:46:46 +000010648 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010649 {
glennrp26f37912010-12-23 16:22:42 +000010650 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010651 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010652 while (property != (const char *) NULL)
10653 {
10654 png_textp
10655 text;
glennrp2cc891a2010-12-24 13:44:32 +000010656
cristyd15e6592011-10-15 00:13:06 +000010657 value=GetImageProperty(image,property,exception);
glennrpa0ed0092011-04-18 16:36:29 +000010658
10659 /* Don't write any "png:" properties; those are just for "identify" */
10660 if (LocaleNCompare(property,"png:",4) != 0 &&
10661
10662 /* Suppress density and units if we wrote a pHYs chunk */
10663 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010664 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010665 LocaleCompare(property,"units") != 0) &&
10666
10667 /* Suppress the IM-generated Date:create and Date:modify */
10668 (ping_exclude_date == MagickFalse ||
10669 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010670 {
glennrpc70af4a2011-03-07 00:08:23 +000010671 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010672 {
glennrpc70af4a2011-03-07 00:08:23 +000010673 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10674 text[0].key=(char *) property;
10675 text[0].text=(char *) value;
10676 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010677
glennrpc70af4a2011-03-07 00:08:23 +000010678 if (ping_exclude_tEXt != MagickFalse)
10679 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10680
10681 else if (ping_exclude_zTXt != MagickFalse)
10682 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10683
10684 else
glennrp26f37912010-12-23 16:22:42 +000010685 {
glennrpc70af4a2011-03-07 00:08:23 +000010686 text[0].compression=image_info->compression == NoCompression ||
10687 (image_info->compression == UndefinedCompression &&
10688 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10689 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010690 }
glennrp2cc891a2010-12-24 13:44:32 +000010691
glennrpc70af4a2011-03-07 00:08:23 +000010692 if (logging != MagickFalse)
10693 {
10694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10695 " Setting up text chunk");
10696
10697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10698 " keyword: %s",text[0].key);
10699 }
10700
10701 png_set_text(ping,ping_info,text,1);
10702 png_free(ping,text);
10703 }
glennrp26f37912010-12-23 16:22:42 +000010704 }
10705 property=GetNextImageProperty(image);
10706 }
cristy3ed852e2009-09-05 21:47:34 +000010707 }
10708
10709 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010710 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010711
10712 if (logging != MagickFalse)
10713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10714 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010715
cristy3ed852e2009-09-05 21:47:34 +000010716 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010717
cristy3ed852e2009-09-05 21:47:34 +000010718 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10719 {
10720 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010721 (ping_width != mng_info->page.width) ||
10722 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010723 {
10724 unsigned char
10725 chunk[32];
10726
10727 /*
10728 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10729 */
10730 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10731 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010732 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010733 chunk[4]=4;
10734 chunk[5]=0; /* frame name separator (no name) */
10735 chunk[6]=1; /* flag for changing delay, for next frame only */
10736 chunk[7]=0; /* flag for changing frame timeout */
10737 chunk[8]=1; /* flag for changing frame clipping for next frame */
10738 chunk[9]=0; /* flag for changing frame sync_id */
10739 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10740 chunk[14]=0; /* clipping boundaries delta type */
10741 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10742 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010743 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010744 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10745 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010746 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010747 (void) WriteBlob(image,31,chunk);
10748 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10749 mng_info->old_framing_mode=4;
10750 mng_info->framing_mode=1;
10751 }
glennrp0fe50b42010-11-16 03:52:51 +000010752
cristy3ed852e2009-09-05 21:47:34 +000010753 else
10754 mng_info->framing_mode=3;
10755 }
10756 if (mng_info->write_mng && !mng_info->need_fram &&
10757 ((int) image->dispose == 3))
cristyc82a27b2011-10-21 01:07:16 +000010758 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010759 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010760 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010761
cristy3ed852e2009-09-05 21:47:34 +000010762 /*
10763 Free PNG resources.
10764 */
glennrp5af765f2010-03-30 11:12:18 +000010765
cristy3ed852e2009-09-05 21:47:34 +000010766 png_destroy_write_struct(&ping,&ping_info);
10767
glennrpcf002022011-01-30 02:38:15 +000010768 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010769
10770#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010771 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010772#endif
10773
glennrpda8f3a72011-02-27 23:54:12 +000010774 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010775 (void) CloseBlob(image);
10776
10777 image_info=DestroyImageInfo(image_info);
10778 image=DestroyImage(image);
10779
10780 /* Store bit depth actually written */
10781 s[0]=(char) ping_bit_depth;
10782 s[1]='\0';
10783
cristyd15e6592011-10-15 00:13:06 +000010784 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
glennrpb9cfe272010-12-21 15:08:06 +000010785
cristy3ed852e2009-09-05 21:47:34 +000010786 if (logging != MagickFalse)
10787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10788 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010789
cristy3ed852e2009-09-05 21:47:34 +000010790 return(MagickTrue);
10791/* End write one PNG image */
10792}
10793
10794/*
10795%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10796% %
10797% %
10798% %
10799% W r i t e P N G I m a g e %
10800% %
10801% %
10802% %
10803%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10804%
10805% WritePNGImage() writes a Portable Network Graphics (PNG) or
10806% Multiple-image Network Graphics (MNG) image file.
10807%
10808% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10809%
10810% The format of the WritePNGImage method is:
10811%
cristy1e178e72011-08-28 19:44:34 +000010812% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10813% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010814%
10815% A description of each parameter follows:
10816%
10817% o image_info: the image info.
10818%
10819% o image: The image.
10820%
cristy1e178e72011-08-28 19:44:34 +000010821% o exception: return any errors or warnings in this structure.
10822%
cristy3ed852e2009-09-05 21:47:34 +000010823% Returns MagickTrue on success, MagickFalse on failure.
10824%
10825% Communicating with the PNG encoder:
10826%
10827% While the datastream written is always in PNG format and normally would
10828% be given the "png" file extension, this method also writes the following
cristy5d6fc9c2011-12-27 03:10:42 +000010829% pseudo-formats which are subsets of png:
cristy3ed852e2009-09-05 21:47:34 +000010830%
glennrp5a39f372011-02-25 04:52:16 +000010831% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10832% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010833% is present, the tRNS chunk must only have values 0 and 255
10834% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010835% transparent). If other values are present they will be
10836% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010837% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000010838% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10839% of any resulting fully-transparent pixels is changed to
10840% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000010841%
10842% If you want better quantization or dithering of the colors
10843% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010844% PNG encoder. The pixels contain 8-bit indices even if
10845% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010846% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010847% PNG grayscale type might be slightly more efficient. Please
10848% note that writing to the PNG8 format may result in loss
10849% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010850%
10851% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10852% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010853% one of the colors as transparent. The only loss incurred
10854% is reduction of sample depth to 8. If the image has more
10855% than one transparent color, has semitransparent pixels, or
10856% has an opaque pixel with the same RGB components as the
10857% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010858%
10859% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10860% transparency is permitted, i.e., the alpha sample for
10861% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010862% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010863% The only loss in data is the reduction of the sample depth
10864% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010865%
10866% o -define: For more precise control of the PNG output, you can use the
10867% Image options "png:bit-depth" and "png:color-type". These
10868% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010869% from the application programming interfaces. The options
10870% are case-independent and are converted to lowercase before
10871% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010872%
10873% png:color-type can be 0, 2, 3, 4, or 6.
10874%
10875% When png:color-type is 0 (Grayscale), png:bit-depth can
10876% be 1, 2, 4, 8, or 16.
10877%
10878% When png:color-type is 2 (RGB), png:bit-depth can
10879% be 8 or 16.
10880%
10881% When png:color-type is 3 (Indexed), png:bit-depth can
10882% be 1, 2, 4, or 8. This refers to the number of bits
10883% used to store the index. The color samples always have
10884% bit-depth 8 in indexed PNG files.
10885%
10886% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10887% png:bit-depth can be 8 or 16.
10888%
glennrp5a39f372011-02-25 04:52:16 +000010889% If the image cannot be written without loss with the requested bit-depth
10890% and color-type, a PNG file will not be written, and the encoder will
10891% return MagickFalse.
10892%
cristy3ed852e2009-09-05 21:47:34 +000010893% Since image encoders should not be responsible for the "heavy lifting",
10894% the user should make sure that ImageMagick has already reduced the
10895% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010896% transparency prior to attempting to write the image with depth, color,
glennrp54dc0692011-07-01 19:02:13 +000010897% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010898%
cristy3ed852e2009-09-05 21:47:34 +000010899% Note that another definition, "png:bit-depth-written" exists, but it
10900% is not intended for external use. It is only used internally by the
10901% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10902%
10903% It is possible to request that the PNG encoder write previously-formatted
10904% ancillary chunks in the output PNG file, using the "-profile" commandline
10905% option as shown below or by setting the profile via a programming
10906% interface:
10907%
10908% -profile PNG-chunk-x:<file>
10909%
10910% where x is a location flag and <file> is a file containing the chunk
10911% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010912% This encoder will compute the chunk length and CRC, so those must not
10913% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010914%
10915% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10916% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10917% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010918% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010919%
glennrpbb8a7332010-11-13 15:17:35 +000010920% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010921%
glennrp3241bd02010-12-12 04:36:28 +000010922% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010923%
glennrpd6afd542010-11-19 01:53:05 +000010924% o 32-bit depth is reduced to 16.
10925% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10926% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010927% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010928% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010929% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010930% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10931% this can be done without loss and a larger bit depth N was not
cristy5d6fc9c2011-12-27 03:10:42 +000010932% requested via the "-define png:bit-depth=N" option.
glennrpd6afd542010-11-19 01:53:05 +000010933% o If matte channel is present but only one transparent color is
10934% present, RGB+tRNS is written instead of RGBA
10935% o Opaque matte channel is removed (or added, if color-type 4 or 6
10936% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010937%
cristy3ed852e2009-09-05 21:47:34 +000010938%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10939*/
10940static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +000010941 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010942{
10943 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010944 excluding,
10945 logging,
10946 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010947 status;
10948
10949 MngInfo
10950 *mng_info;
10951
10952 const char
10953 *value;
10954
10955 int
glennrp21f0e622011-01-07 16:20:57 +000010956 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010957 source;
10958
cristy3ed852e2009-09-05 21:47:34 +000010959 /*
10960 Open image file.
10961 */
10962 assert(image_info != (const ImageInfo *) NULL);
10963 assert(image_info->signature == MagickSignature);
10964 assert(image != (Image *) NULL);
10965 assert(image->signature == MagickSignature);
10966 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010967 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010968 /*
10969 Allocate a MngInfo structure.
10970 */
10971 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010972 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010973
cristy3ed852e2009-09-05 21:47:34 +000010974 if (mng_info == (MngInfo *) NULL)
10975 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010976
cristy3ed852e2009-09-05 21:47:34 +000010977 /*
10978 Initialize members of the MngInfo structure.
10979 */
10980 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10981 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010982 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010983 have_mng_structure=MagickTrue;
10984
10985 /* See if user has requested a specific PNG subformat */
10986
10987 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10988 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10989 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10990
10991 if (mng_info->write_png8)
10992 {
glennrp9c1eb072010-06-06 22:19:15 +000010993 mng_info->write_png_colortype = /* 3 */ 4;
10994 mng_info->write_png_depth = 8;
10995 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010996 }
10997
10998 if (mng_info->write_png24)
10999 {
glennrp9c1eb072010-06-06 22:19:15 +000011000 mng_info->write_png_colortype = /* 2 */ 3;
11001 mng_info->write_png_depth = 8;
11002 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011003
glennrp9c1eb072010-06-06 22:19:15 +000011004 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011005 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011006
glennrp9c1eb072010-06-06 22:19:15 +000011007 else
cristy018f07f2011-09-04 21:15:19 +000011008 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011009
cristyea1a8aa2011-10-20 13:24:06 +000011010 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011011 }
11012
11013 if (mng_info->write_png32)
11014 {
glennrp9c1eb072010-06-06 22:19:15 +000011015 mng_info->write_png_colortype = /* 6 */ 7;
11016 mng_info->write_png_depth = 8;
11017 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011018
glennrp9c1eb072010-06-06 22:19:15 +000011019 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011020 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011021
glennrp9c1eb072010-06-06 22:19:15 +000011022 else
cristy018f07f2011-09-04 21:15:19 +000011023 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011024
cristyea1a8aa2011-10-20 13:24:06 +000011025 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011026 }
11027
11028 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011029
cristy3ed852e2009-09-05 21:47:34 +000011030 if (value != (char *) NULL)
11031 {
11032 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011033 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011034
cristy3ed852e2009-09-05 21:47:34 +000011035 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011036 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011037
cristy3ed852e2009-09-05 21:47:34 +000011038 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011039 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011040
cristy3ed852e2009-09-05 21:47:34 +000011041 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011042 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011043
cristy3ed852e2009-09-05 21:47:34 +000011044 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011045 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011046
glennrpbb8a7332010-11-13 15:17:35 +000011047 else
cristyc82a27b2011-10-21 01:07:16 +000011048 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011049 GetMagickModule(),CoderWarning,
11050 "ignoring invalid defined png:bit-depth",
11051 "=%s",value);
11052
cristy3ed852e2009-09-05 21:47:34 +000011053 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011055 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011056 }
glennrp0fe50b42010-11-16 03:52:51 +000011057
cristy3ed852e2009-09-05 21:47:34 +000011058 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011059
cristy3ed852e2009-09-05 21:47:34 +000011060 if (value != (char *) NULL)
11061 {
11062 /* We must store colortype+1 because 0 is a valid colortype */
11063 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011064 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011065
cristy3ed852e2009-09-05 21:47:34 +000011066 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011067 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011068
cristy3ed852e2009-09-05 21:47:34 +000011069 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011070 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011071
cristy3ed852e2009-09-05 21:47:34 +000011072 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011073 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011074
cristy3ed852e2009-09-05 21:47:34 +000011075 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011076 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011077
glennrpbb8a7332010-11-13 15:17:35 +000011078 else
cristyc82a27b2011-10-21 01:07:16 +000011079 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011080 GetMagickModule(),CoderWarning,
11081 "ignoring invalid defined png:color-type",
11082 "=%s",value);
11083
cristy3ed852e2009-09-05 21:47:34 +000011084 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011086 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011087 }
11088
glennrp0e8ea192010-12-24 18:00:33 +000011089 /* Check for chunks to be excluded:
11090 *
glennrp0dff56c2011-01-29 19:10:02 +000011091 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011092 * listed in the "unused_chunks" array, above.
11093 *
cristy5d6fc9c2011-12-27 03:10:42 +000011094 * Chunks can be listed for exclusion via a "png:exclude-chunk"
glennrp0e8ea192010-12-24 18:00:33 +000011095 * define (in the image properties or in the image artifacts)
11096 * or via a mng_info member. For convenience, in addition
11097 * to or instead of a comma-separated list of chunks, the
11098 * "exclude-chunk" string can be simply "all" or "none".
11099 *
11100 * The exclude-chunk define takes priority over the mng_info.
11101 *
cristy5d6fc9c2011-12-27 03:10:42 +000011102 * A "png:include-chunk" define takes priority over both the
11103 * mng_info and the "png:exclude-chunk" define. Like the
glennrp0e8ea192010-12-24 18:00:33 +000011104 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011105 * well as a comma-separated list. Chunks that are unknown to
11106 * ImageMagick are always excluded, regardless of their "copy-safe"
11107 * status according to the PNG specification, and even if they
11108 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000011109 *
11110 * Finally, all chunks listed in the "unused_chunks" array are
11111 * automatically excluded, regardless of the other instructions
11112 * or lack thereof.
11113 *
11114 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11115 * will not be written and the gAMA chunk will only be written if it
11116 * is not between .45 and .46, or approximately (1.0/2.2).
11117 *
11118 * If you exclude tRNS and the image has transparency, the colortype
11119 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11120 *
11121 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011122 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011123 */
11124
glennrp26f37912010-12-23 16:22:42 +000011125 mng_info->ping_exclude_bKGD=MagickFalse;
11126 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011127 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011128 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11129 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011130 mng_info->ping_exclude_iCCP=MagickFalse;
11131 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11132 mng_info->ping_exclude_oFFs=MagickFalse;
11133 mng_info->ping_exclude_pHYs=MagickFalse;
11134 mng_info->ping_exclude_sRGB=MagickFalse;
11135 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011136 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011137 mng_info->ping_exclude_vpAg=MagickFalse;
11138 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11139 mng_info->ping_exclude_zTXt=MagickFalse;
11140
glennrp8d3d6e52011-04-19 04:39:51 +000011141 mng_info->ping_preserve_colormap=MagickFalse;
11142
11143 value=GetImageArtifact(image,"png:preserve-colormap");
11144 if (value == NULL)
11145 value=GetImageOption(image_info,"png:preserve-colormap");
11146 if (value != NULL)
11147 mng_info->ping_preserve_colormap=MagickTrue;
11148
glennrp18682582011-06-30 18:11:47 +000011149 /* Thes compression-level, compression-strategy, and compression-filter
11150 * defines take precedence over values from the -quality option.
11151 */
11152 value=GetImageArtifact(image,"png:compression-level");
11153 if (value == NULL)
11154 value=GetImageOption(image_info,"png:compression-level");
11155 if (value != NULL)
11156 {
glennrp18682582011-06-30 18:11:47 +000011157 /* We have to add 1 to everything because 0 is a valid input,
11158 * and we want to use 0 (the default) to mean undefined.
11159 */
11160 if (LocaleCompare(value,"0") == 0)
11161 mng_info->write_png_compression_level = 1;
11162
11163 if (LocaleCompare(value,"1") == 0)
11164 mng_info->write_png_compression_level = 2;
11165
11166 else if (LocaleCompare(value,"2") == 0)
11167 mng_info->write_png_compression_level = 3;
11168
11169 else if (LocaleCompare(value,"3") == 0)
11170 mng_info->write_png_compression_level = 4;
11171
11172 else if (LocaleCompare(value,"4") == 0)
11173 mng_info->write_png_compression_level = 5;
11174
11175 else if (LocaleCompare(value,"5") == 0)
11176 mng_info->write_png_compression_level = 6;
11177
11178 else if (LocaleCompare(value,"6") == 0)
11179 mng_info->write_png_compression_level = 7;
11180
11181 else if (LocaleCompare(value,"7") == 0)
11182 mng_info->write_png_compression_level = 8;
11183
11184 else if (LocaleCompare(value,"8") == 0)
11185 mng_info->write_png_compression_level = 9;
11186
11187 else if (LocaleCompare(value,"9") == 0)
11188 mng_info->write_png_compression_level = 10;
11189
11190 else
cristyc82a27b2011-10-21 01:07:16 +000011191 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011192 GetMagickModule(),CoderWarning,
11193 "ignoring invalid defined png:compression-level",
11194 "=%s",value);
11195 }
11196
11197 value=GetImageArtifact(image,"png:compression-strategy");
11198 if (value == NULL)
11199 value=GetImageOption(image_info,"png:compression-strategy");
11200 if (value != NULL)
11201 {
11202
11203 if (LocaleCompare(value,"0") == 0)
11204 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11205
11206 else if (LocaleCompare(value,"1") == 0)
11207 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11208
11209 else if (LocaleCompare(value,"2") == 0)
11210 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11211
11212 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011213#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011214 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011215#else
11216 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11217#endif
glennrp18682582011-06-30 18:11:47 +000011218
11219 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011220#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011221 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011222#else
11223 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11224#endif
glennrp18682582011-06-30 18:11:47 +000011225
11226 else
cristyc82a27b2011-10-21 01:07:16 +000011227 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011228 GetMagickModule(),CoderWarning,
11229 "ignoring invalid defined png:compression-strategy",
11230 "=%s",value);
11231 }
11232
11233 value=GetImageArtifact(image,"png:compression-filter");
11234 if (value == NULL)
11235 value=GetImageOption(image_info,"png:compression-filter");
11236 if (value != NULL)
11237 {
11238
11239 /* To do: combinations of filters allowed by libpng
11240 * masks 0x08 through 0xf8
11241 *
11242 * Implement this as a comma-separated list of 0,1,2,3,4,5
11243 * where 5 is a special case meaning PNG_ALL_FILTERS.
11244 */
11245
11246 if (LocaleCompare(value,"0") == 0)
11247 mng_info->write_png_compression_filter = 1;
11248
11249 if (LocaleCompare(value,"1") == 0)
11250 mng_info->write_png_compression_filter = 2;
11251
11252 else if (LocaleCompare(value,"2") == 0)
11253 mng_info->write_png_compression_filter = 3;
11254
11255 else if (LocaleCompare(value,"3") == 0)
11256 mng_info->write_png_compression_filter = 4;
11257
11258 else if (LocaleCompare(value,"4") == 0)
11259 mng_info->write_png_compression_filter = 5;
11260
11261 else if (LocaleCompare(value,"5") == 0)
11262 mng_info->write_png_compression_filter = 6;
11263
glennrp18682582011-06-30 18:11:47 +000011264 else
cristyc82a27b2011-10-21 01:07:16 +000011265 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011266 GetMagickModule(),CoderWarning,
11267 "ignoring invalid defined png:compression-filter",
11268 "=%s",value);
11269 }
11270
glennrp03812ae2010-12-24 01:31:34 +000011271 excluding=MagickFalse;
11272
glennrp5c7cf4e2010-12-24 00:30:00 +000011273 for (source=0; source<1; source++)
11274 {
11275 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011276 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011277 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011278
11279 if (value == NULL)
11280 value=GetImageArtifact(image,"png:exclude-chunks");
11281 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011282 else
glennrpacba0042010-12-24 14:27:26 +000011283 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011284 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011285
glennrpacba0042010-12-24 14:27:26 +000011286 if (value == NULL)
11287 value=GetImageOption(image_info,"png:exclude-chunks");
11288 }
11289
glennrp03812ae2010-12-24 01:31:34 +000011290 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011291 {
glennrp03812ae2010-12-24 01:31:34 +000011292
11293 size_t
11294 last;
11295
11296 excluding=MagickTrue;
11297
11298 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011299 {
11300 if (source == 0)
11301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11302 " png:exclude-chunk=%s found in image artifacts.\n", value);
11303 else
11304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11305 " png:exclude-chunk=%s found in image properties.\n", value);
11306 }
glennrp03812ae2010-12-24 01:31:34 +000011307
11308 last=strlen(value);
11309
11310 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011311 {
glennrp03812ae2010-12-24 01:31:34 +000011312
11313 if (LocaleNCompare(value+i,"all",3) == 0)
11314 {
11315 mng_info->ping_exclude_bKGD=MagickTrue;
11316 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011317 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011318 mng_info->ping_exclude_EXIF=MagickTrue;
11319 mng_info->ping_exclude_gAMA=MagickTrue;
11320 mng_info->ping_exclude_iCCP=MagickTrue;
11321 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11322 mng_info->ping_exclude_oFFs=MagickTrue;
11323 mng_info->ping_exclude_pHYs=MagickTrue;
11324 mng_info->ping_exclude_sRGB=MagickTrue;
11325 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011326 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011327 mng_info->ping_exclude_vpAg=MagickTrue;
11328 mng_info->ping_exclude_zCCP=MagickTrue;
11329 mng_info->ping_exclude_zTXt=MagickTrue;
11330 i--;
11331 }
glennrp2cc891a2010-12-24 13:44:32 +000011332
glennrp03812ae2010-12-24 01:31:34 +000011333 if (LocaleNCompare(value+i,"none",4) == 0)
11334 {
11335 mng_info->ping_exclude_bKGD=MagickFalse;
11336 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011337 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011338 mng_info->ping_exclude_EXIF=MagickFalse;
11339 mng_info->ping_exclude_gAMA=MagickFalse;
11340 mng_info->ping_exclude_iCCP=MagickFalse;
11341 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11342 mng_info->ping_exclude_oFFs=MagickFalse;
11343 mng_info->ping_exclude_pHYs=MagickFalse;
11344 mng_info->ping_exclude_sRGB=MagickFalse;
11345 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011346 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011347 mng_info->ping_exclude_vpAg=MagickFalse;
11348 mng_info->ping_exclude_zCCP=MagickFalse;
11349 mng_info->ping_exclude_zTXt=MagickFalse;
11350 }
glennrp2cc891a2010-12-24 13:44:32 +000011351
glennrp03812ae2010-12-24 01:31:34 +000011352 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11353 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011354
glennrp03812ae2010-12-24 01:31:34 +000011355 if (LocaleNCompare(value+i,"chrm",4) == 0)
11356 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011357
glennrpa0ed0092011-04-18 16:36:29 +000011358 if (LocaleNCompare(value+i,"date",4) == 0)
11359 mng_info->ping_exclude_date=MagickTrue;
11360
glennrp03812ae2010-12-24 01:31:34 +000011361 if (LocaleNCompare(value+i,"exif",4) == 0)
11362 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011363
glennrp03812ae2010-12-24 01:31:34 +000011364 if (LocaleNCompare(value+i,"gama",4) == 0)
11365 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011366
glennrp03812ae2010-12-24 01:31:34 +000011367 if (LocaleNCompare(value+i,"iccp",4) == 0)
11368 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011369
glennrp03812ae2010-12-24 01:31:34 +000011370 /*
11371 if (LocaleNCompare(value+i,"itxt",4) == 0)
11372 mng_info->ping_exclude_iTXt=MagickTrue;
11373 */
glennrp2cc891a2010-12-24 13:44:32 +000011374
glennrp03812ae2010-12-24 01:31:34 +000011375 if (LocaleNCompare(value+i,"gama",4) == 0)
11376 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011377
glennrp03812ae2010-12-24 01:31:34 +000011378 if (LocaleNCompare(value+i,"offs",4) == 0)
11379 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011380
glennrp03812ae2010-12-24 01:31:34 +000011381 if (LocaleNCompare(value+i,"phys",4) == 0)
11382 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011383
glennrpa1e3b7b2010-12-24 16:37:33 +000011384 if (LocaleNCompare(value+i,"srgb",4) == 0)
11385 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011386
glennrp03812ae2010-12-24 01:31:34 +000011387 if (LocaleNCompare(value+i,"text",4) == 0)
11388 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011389
glennrpa1e3b7b2010-12-24 16:37:33 +000011390 if (LocaleNCompare(value+i,"trns",4) == 0)
11391 mng_info->ping_exclude_tRNS=MagickTrue;
11392
glennrp03812ae2010-12-24 01:31:34 +000011393 if (LocaleNCompare(value+i,"vpag",4) == 0)
11394 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011395
glennrp03812ae2010-12-24 01:31:34 +000011396 if (LocaleNCompare(value+i,"zccp",4) == 0)
11397 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011398
glennrp03812ae2010-12-24 01:31:34 +000011399 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11400 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011401
glennrp03812ae2010-12-24 01:31:34 +000011402 }
glennrpce91ed52010-12-23 22:37:49 +000011403 }
glennrp26f37912010-12-23 16:22:42 +000011404 }
11405
glennrp5c7cf4e2010-12-24 00:30:00 +000011406 for (source=0; source<1; source++)
11407 {
11408 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011409 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011410 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011411
11412 if (value == NULL)
11413 value=GetImageArtifact(image,"png:include-chunks");
11414 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011415 else
glennrpacba0042010-12-24 14:27:26 +000011416 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011417 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011418
glennrpacba0042010-12-24 14:27:26 +000011419 if (value == NULL)
11420 value=GetImageOption(image_info,"png:include-chunks");
11421 }
11422
glennrp03812ae2010-12-24 01:31:34 +000011423 if (value != NULL)
11424 {
11425 size_t
11426 last;
glennrp26f37912010-12-23 16:22:42 +000011427
glennrp03812ae2010-12-24 01:31:34 +000011428 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011429
glennrp03812ae2010-12-24 01:31:34 +000011430 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011431 {
11432 if (source == 0)
11433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11434 " png:include-chunk=%s found in image artifacts.\n", value);
11435 else
11436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11437 " png:include-chunk=%s found in image properties.\n", value);
11438 }
glennrp03812ae2010-12-24 01:31:34 +000011439
11440 last=strlen(value);
11441
11442 for (i=0; i<(int) last; i+=5)
11443 {
11444 if (LocaleNCompare(value+i,"all",3) == 0)
11445 {
11446 mng_info->ping_exclude_bKGD=MagickFalse;
11447 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011448 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011449 mng_info->ping_exclude_EXIF=MagickFalse;
11450 mng_info->ping_exclude_gAMA=MagickFalse;
11451 mng_info->ping_exclude_iCCP=MagickFalse;
11452 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11453 mng_info->ping_exclude_oFFs=MagickFalse;
11454 mng_info->ping_exclude_pHYs=MagickFalse;
11455 mng_info->ping_exclude_sRGB=MagickFalse;
11456 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011457 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011458 mng_info->ping_exclude_vpAg=MagickFalse;
11459 mng_info->ping_exclude_zCCP=MagickFalse;
11460 mng_info->ping_exclude_zTXt=MagickFalse;
11461 i--;
11462 }
glennrp2cc891a2010-12-24 13:44:32 +000011463
glennrp03812ae2010-12-24 01:31:34 +000011464 if (LocaleNCompare(value+i,"none",4) == 0)
11465 {
11466 mng_info->ping_exclude_bKGD=MagickTrue;
11467 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011468 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011469 mng_info->ping_exclude_EXIF=MagickTrue;
11470 mng_info->ping_exclude_gAMA=MagickTrue;
11471 mng_info->ping_exclude_iCCP=MagickTrue;
11472 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11473 mng_info->ping_exclude_oFFs=MagickTrue;
11474 mng_info->ping_exclude_pHYs=MagickTrue;
11475 mng_info->ping_exclude_sRGB=MagickTrue;
11476 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011477 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011478 mng_info->ping_exclude_vpAg=MagickTrue;
11479 mng_info->ping_exclude_zCCP=MagickTrue;
11480 mng_info->ping_exclude_zTXt=MagickTrue;
11481 }
glennrp2cc891a2010-12-24 13:44:32 +000011482
glennrp03812ae2010-12-24 01:31:34 +000011483 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11484 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011485
glennrp03812ae2010-12-24 01:31:34 +000011486 if (LocaleNCompare(value+i,"chrm",4) == 0)
11487 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011488
glennrpa0ed0092011-04-18 16:36:29 +000011489 if (LocaleNCompare(value+i,"date",4) == 0)
11490 mng_info->ping_exclude_date=MagickFalse;
11491
glennrp03812ae2010-12-24 01:31:34 +000011492 if (LocaleNCompare(value+i,"exif",4) == 0)
11493 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011494
glennrp03812ae2010-12-24 01:31:34 +000011495 if (LocaleNCompare(value+i,"gama",4) == 0)
11496 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011497
glennrp03812ae2010-12-24 01:31:34 +000011498 if (LocaleNCompare(value+i,"iccp",4) == 0)
11499 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011500
glennrp03812ae2010-12-24 01:31:34 +000011501 /*
11502 if (LocaleNCompare(value+i,"itxt",4) == 0)
11503 mng_info->ping_exclude_iTXt=MagickFalse;
11504 */
glennrp2cc891a2010-12-24 13:44:32 +000011505
glennrp03812ae2010-12-24 01:31:34 +000011506 if (LocaleNCompare(value+i,"gama",4) == 0)
11507 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011508
glennrp03812ae2010-12-24 01:31:34 +000011509 if (LocaleNCompare(value+i,"offs",4) == 0)
11510 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011511
glennrp03812ae2010-12-24 01:31:34 +000011512 if (LocaleNCompare(value+i,"phys",4) == 0)
11513 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011514
glennrpa1e3b7b2010-12-24 16:37:33 +000011515 if (LocaleNCompare(value+i,"srgb",4) == 0)
11516 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011517
glennrp03812ae2010-12-24 01:31:34 +000011518 if (LocaleNCompare(value+i,"text",4) == 0)
11519 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011520
glennrpa1e3b7b2010-12-24 16:37:33 +000011521 if (LocaleNCompare(value+i,"trns",4) == 0)
11522 mng_info->ping_exclude_tRNS=MagickFalse;
11523
glennrp03812ae2010-12-24 01:31:34 +000011524 if (LocaleNCompare(value+i,"vpag",4) == 0)
11525 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011526
glennrp03812ae2010-12-24 01:31:34 +000011527 if (LocaleNCompare(value+i,"zccp",4) == 0)
11528 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011529
glennrp03812ae2010-12-24 01:31:34 +000011530 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11531 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011532
glennrp03812ae2010-12-24 01:31:34 +000011533 }
glennrpce91ed52010-12-23 22:37:49 +000011534 }
glennrp26f37912010-12-23 16:22:42 +000011535 }
11536
glennrp03812ae2010-12-24 01:31:34 +000011537 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011538 {
11539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011540 " Chunks to be excluded from the output png:");
glennrp26f37912010-12-23 16:22:42 +000011541 if (mng_info->ping_exclude_bKGD != MagickFalse)
11542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11543 " bKGD");
11544 if (mng_info->ping_exclude_cHRM != MagickFalse)
11545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11546 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011547 if (mng_info->ping_exclude_date != MagickFalse)
11548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11549 " date");
glennrp26f37912010-12-23 16:22:42 +000011550 if (mng_info->ping_exclude_EXIF != MagickFalse)
11551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11552 " EXIF");
11553 if (mng_info->ping_exclude_gAMA != MagickFalse)
11554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11555 " gAMA");
11556 if (mng_info->ping_exclude_iCCP != MagickFalse)
11557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11558 " iCCP");
11559/*
11560 if (mng_info->ping_exclude_iTXt != MagickFalse)
11561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11562 " iTXt");
11563*/
11564 if (mng_info->ping_exclude_oFFs != MagickFalse)
11565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11566 " oFFs");
11567 if (mng_info->ping_exclude_pHYs != MagickFalse)
11568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11569 " pHYs");
11570 if (mng_info->ping_exclude_sRGB != MagickFalse)
11571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11572 " sRGB");
11573 if (mng_info->ping_exclude_tEXt != MagickFalse)
11574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11575 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011576 if (mng_info->ping_exclude_tRNS != MagickFalse)
11577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11578 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011579 if (mng_info->ping_exclude_vpAg != MagickFalse)
11580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11581 " vpAg");
11582 if (mng_info->ping_exclude_zCCP != MagickFalse)
11583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11584 " zCCP");
11585 if (mng_info->ping_exclude_zTXt != MagickFalse)
11586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11587 " zTXt");
11588 }
11589
glennrpb9cfe272010-12-21 15:08:06 +000011590 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011591
cristy018f07f2011-09-04 21:15:19 +000011592 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011593
11594 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011595
cristy3ed852e2009-09-05 21:47:34 +000011596 if (logging != MagickFalse)
11597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011598
cristy3ed852e2009-09-05 21:47:34 +000011599 return(status);
11600}
11601
11602#if defined(JNG_SUPPORTED)
11603
11604/* Write one JNG image */
11605static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +000011606 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011607{
11608 Image
11609 *jpeg_image;
11610
11611 ImageInfo
11612 *jpeg_image_info;
11613
11614 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011615 logging,
cristy3ed852e2009-09-05 21:47:34 +000011616 status;
11617
11618 size_t
11619 length;
11620
11621 unsigned char
11622 *blob,
11623 chunk[80],
11624 *p;
11625
11626 unsigned int
11627 jng_alpha_compression_method,
11628 jng_alpha_sample_depth,
11629 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011630 transparent;
11631
cristybb503372010-05-27 20:51:26 +000011632 size_t
glennrp59575fa2011-12-31 21:31:39 +000011633 jng_alpha_quality,
cristy3ed852e2009-09-05 21:47:34 +000011634 jng_quality;
11635
11636 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011637 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011638
11639 blob=(unsigned char *) NULL;
11640 jpeg_image=(Image *) NULL;
11641 jpeg_image_info=(ImageInfo *) NULL;
11642
11643 status=MagickTrue;
11644 transparent=image_info->type==GrayscaleMatteType ||
glennrp59575fa2011-12-31 21:31:39 +000011645 image_info->type==TrueColorMatteType || image->matte != MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +000011646
glennrp59575fa2011-12-31 21:31:39 +000011647 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
11648
11649 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
11650
11651 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
11652 image_info->quality;
11653
11654 if (jng_alpha_quality >= 1000)
11655 jng_alpha_quality /= 1000;
cristy3ed852e2009-09-05 21:47:34 +000011656
11657 if (transparent)
11658 {
cristybd5a96c2011-08-21 00:04:26 +000011659 ChannelType
11660 channel_mask;
11661
cristy3ed852e2009-09-05 21:47:34 +000011662 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011663
cristy3ed852e2009-09-05 21:47:34 +000011664 /* Create JPEG blob, image, and image_info */
11665 if (logging != MagickFalse)
11666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +000011667 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011668
cristy3ed852e2009-09-05 21:47:34 +000011669 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011670
cristy3ed852e2009-09-05 21:47:34 +000011671 if (jpeg_image_info == (ImageInfo *) NULL)
11672 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011673
cristy3ed852e2009-09-05 21:47:34 +000011674 if (logging != MagickFalse)
11675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11676 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011677
cristyc82a27b2011-10-21 01:07:16 +000011678 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011679
cristy3ed852e2009-09-05 21:47:34 +000011680 if (jpeg_image == (Image *) NULL)
11681 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011682
cristy3ed852e2009-09-05 21:47:34 +000011683 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristybd5a96c2011-08-21 00:04:26 +000011684 channel_mask=SetPixelChannelMask(jpeg_image,AlphaChannel);
cristye941a752011-10-15 01:52:48 +000011685 status=SeparateImage(jpeg_image,exception);
cristye2a912b2011-12-05 20:02:07 +000011686 (void) SetPixelChannelMapMask(jpeg_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +000011687 jpeg_image->matte=MagickFalse;
glennrp59575fa2011-12-31 21:31:39 +000011688 jpeg_image->quality=jng_alpha_quality;
cristy3ed852e2009-09-05 21:47:34 +000011689 jpeg_image_info->type=GrayscaleType;
cristy018f07f2011-09-04 21:15:19 +000011690 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000011691 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011692 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011693 "%s",jpeg_image->filename);
11694 }
glennrp59575fa2011-12-31 21:31:39 +000011695 else
11696 {
11697 jng_alpha_compression_method=0;
11698 jng_color_type=10;
11699 jng_alpha_sample_depth=0;
11700 }
cristy3ed852e2009-09-05 21:47:34 +000011701
11702 /* To do: check bit depth of PNG alpha channel */
11703
11704 /* Check if image is grayscale. */
11705 if (image_info->type != TrueColorMatteType && image_info->type !=
cristyc82a27b2011-10-21 01:07:16 +000011706 TrueColorType && ImageIsGray(image,exception))
cristy3ed852e2009-09-05 21:47:34 +000011707 jng_color_type-=2;
11708
glennrp59575fa2011-12-31 21:31:39 +000011709 if (logging != MagickFalse)
11710 {
11711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11712 " JNG Quality = %d",(int) jng_quality);
11713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11714 " JNG Color Type = %d",jng_color_type);
11715 if (transparent)
11716 {
11717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11718 " JNG Alpha Compression = %d",jng_alpha_compression_method);
11719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11720 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
11721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11722 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
11723 }
11724 }
11725
cristy3ed852e2009-09-05 21:47:34 +000011726 if (transparent)
11727 {
11728 if (jng_alpha_compression_method==0)
11729 {
11730 const char
11731 *value;
11732
cristy4c08aed2011-07-01 19:47:50 +000011733 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011734 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000011735 exception);
cristy3ed852e2009-09-05 21:47:34 +000011736 if (logging != MagickFalse)
11737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11738 " Creating PNG blob.");
11739 length=0;
11740
11741 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11742 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11743 jpeg_image_info->interlace=NoInterlace;
11744
11745 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyc82a27b2011-10-21 01:07:16 +000011746 exception);
cristy3ed852e2009-09-05 21:47:34 +000011747
11748 /* Retrieve sample depth used */
cristyd15e6592011-10-15 00:13:06 +000011749 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
cristy3ed852e2009-09-05 21:47:34 +000011750 if (value != (char *) NULL)
11751 jng_alpha_sample_depth= (unsigned int) value[0];
11752 }
11753 else
11754 {
cristy4c08aed2011-07-01 19:47:50 +000011755 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011756
11757 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000011758 exception);
cristy3ed852e2009-09-05 21:47:34 +000011759
11760 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11761 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11762 jpeg_image_info->interlace=NoInterlace;
11763 if (logging != MagickFalse)
11764 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11765 " Creating blob.");
11766 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyc82a27b2011-10-21 01:07:16 +000011767 exception);
cristy3ed852e2009-09-05 21:47:34 +000011768 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011769
cristy3ed852e2009-09-05 21:47:34 +000011770 if (logging != MagickFalse)
11771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011772 " Successfully read jpeg_image into a blob, length=%.20g.",
11773 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011774
11775 }
11776 /* Destroy JPEG image and image_info */
11777 jpeg_image=DestroyImage(jpeg_image);
11778 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11779 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11780 }
11781
11782 /* Write JHDR chunk */
11783 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11784 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011785 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011786 PNGLong(chunk+4,(png_uint_32) image->columns);
11787 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011788 chunk[12]=jng_color_type;
11789 chunk[13]=8; /* sample depth */
11790 chunk[14]=8; /*jng_image_compression_method */
11791 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11792 chunk[16]=jng_alpha_sample_depth;
11793 chunk[17]=jng_alpha_compression_method;
11794 chunk[18]=0; /*jng_alpha_filter_method */
11795 chunk[19]=0; /*jng_alpha_interlace_method */
11796 (void) WriteBlob(image,20,chunk);
11797 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11798 if (logging != MagickFalse)
11799 {
11800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011801 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011802
cristy3ed852e2009-09-05 21:47:34 +000011803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011804 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011805
cristy3ed852e2009-09-05 21:47:34 +000011806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11807 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011808
cristy3ed852e2009-09-05 21:47:34 +000011809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11810 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011811
cristy3ed852e2009-09-05 21:47:34 +000011812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11813 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011814
cristy3ed852e2009-09-05 21:47:34 +000011815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11816 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011817
cristy3ed852e2009-09-05 21:47:34 +000011818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11819 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011820
cristy3ed852e2009-09-05 21:47:34 +000011821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11822 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011823
cristy3ed852e2009-09-05 21:47:34 +000011824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11825 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011826
cristy3ed852e2009-09-05 21:47:34 +000011827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11828 " JNG alpha interlace:%5d",0);
11829 }
11830
glennrp0fe50b42010-11-16 03:52:51 +000011831 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011832 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011833
11834 /*
11835 Write leading ancillary chunks
11836 */
11837
11838 if (transparent)
11839 {
11840 /*
11841 Write JNG bKGD chunk
11842 */
11843
11844 unsigned char
11845 blue,
11846 green,
11847 red;
11848
cristybb503372010-05-27 20:51:26 +000011849 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011850 num_bytes;
11851
11852 if (jng_color_type == 8 || jng_color_type == 12)
11853 num_bytes=6L;
11854 else
11855 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011856 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011857 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011858 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011859 red=ScaleQuantumToChar(image->background_color.red);
11860 green=ScaleQuantumToChar(image->background_color.green);
11861 blue=ScaleQuantumToChar(image->background_color.blue);
11862 *(chunk+4)=0;
11863 *(chunk+5)=red;
11864 *(chunk+6)=0;
11865 *(chunk+7)=green;
11866 *(chunk+8)=0;
11867 *(chunk+9)=blue;
11868 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11869 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11870 }
11871
11872 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11873 {
11874 /*
11875 Write JNG sRGB chunk
11876 */
11877 (void) WriteBlobMSBULong(image,1L);
11878 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011879 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011880
cristy3ed852e2009-09-05 21:47:34 +000011881 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011882 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011883 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011884 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011885
cristy3ed852e2009-09-05 21:47:34 +000011886 else
glennrpe610a072010-08-05 17:08:46 +000011887 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011888 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011889 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011890
cristy3ed852e2009-09-05 21:47:34 +000011891 (void) WriteBlob(image,5,chunk);
11892 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11893 }
11894 else
11895 {
11896 if (image->gamma != 0.0)
11897 {
11898 /*
11899 Write JNG gAMA chunk
11900 */
11901 (void) WriteBlobMSBULong(image,4L);
11902 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011903 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011904 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011905 (void) WriteBlob(image,8,chunk);
11906 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11907 }
glennrp0fe50b42010-11-16 03:52:51 +000011908
cristy3ed852e2009-09-05 21:47:34 +000011909 if ((mng_info->equal_chrms == MagickFalse) &&
11910 (image->chromaticity.red_primary.x != 0.0))
11911 {
11912 PrimaryInfo
11913 primary;
11914
11915 /*
11916 Write JNG cHRM chunk
11917 */
11918 (void) WriteBlobMSBULong(image,32L);
11919 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011920 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011921 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011922 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11923 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011924 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011925 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11926 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011927 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011928 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11929 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011930 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011931 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11932 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011933 (void) WriteBlob(image,36,chunk);
11934 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11935 }
11936 }
glennrp0fe50b42010-11-16 03:52:51 +000011937
cristy2a11bef2011-10-28 18:33:11 +000011938 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000011939 {
11940 /*
11941 Write JNG pHYs chunk
11942 */
11943 (void) WriteBlobMSBULong(image,9L);
11944 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011945 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011946 if (image->units == PixelsPerInchResolution)
11947 {
cristy35ef8242010-06-03 16:24:13 +000011948 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011949 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011950
cristy35ef8242010-06-03 16:24:13 +000011951 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011952 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011953
cristy3ed852e2009-09-05 21:47:34 +000011954 chunk[12]=1;
11955 }
glennrp0fe50b42010-11-16 03:52:51 +000011956
cristy3ed852e2009-09-05 21:47:34 +000011957 else
11958 {
11959 if (image->units == PixelsPerCentimeterResolution)
11960 {
cristy35ef8242010-06-03 16:24:13 +000011961 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011962 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011963
cristy35ef8242010-06-03 16:24:13 +000011964 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011965 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011966
cristy3ed852e2009-09-05 21:47:34 +000011967 chunk[12]=1;
11968 }
glennrp0fe50b42010-11-16 03:52:51 +000011969
cristy3ed852e2009-09-05 21:47:34 +000011970 else
11971 {
cristy2a11bef2011-10-28 18:33:11 +000011972 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
11973 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011974 chunk[12]=0;
11975 }
11976 }
11977 (void) WriteBlob(image,13,chunk);
11978 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11979 }
11980
11981 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11982 {
11983 /*
11984 Write JNG oFFs chunk
11985 */
11986 (void) WriteBlobMSBULong(image,9L);
11987 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011988 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011989 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11990 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011991 chunk[12]=0;
11992 (void) WriteBlob(image,13,chunk);
11993 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11994 }
11995 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11996 {
11997 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11998 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011999 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000012000 PNGLong(chunk+4,(png_uint_32) image->page.width);
12001 PNGLong(chunk+8,(png_uint_32) image->page.height);
12002 chunk[12]=0; /* unit = pixels */
12003 (void) WriteBlob(image,13,chunk);
12004 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12005 }
12006
12007
12008 if (transparent)
12009 {
12010 if (jng_alpha_compression_method==0)
12011 {
cristybb503372010-05-27 20:51:26 +000012012 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012013 i;
12014
cristybb503372010-05-27 20:51:26 +000012015 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012016 len;
12017
12018 /* Write IDAT chunk header */
12019 if (logging != MagickFalse)
12020 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012021 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000012022 length);
cristy3ed852e2009-09-05 21:47:34 +000012023
12024 /* Copy IDAT chunks */
12025 len=0;
12026 p=blob+8;
cristybb503372010-05-27 20:51:26 +000012027 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000012028 {
12029 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12030 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012031
cristy3ed852e2009-09-05 21:47:34 +000012032 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12033 {
12034 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000012035 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000012036 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000012037 (void) WriteBlob(image,(size_t) len+4,p);
12038 (void) WriteBlobMSBULong(image,
12039 crc32(0,p,(uInt) len+4));
12040 }
glennrp0fe50b42010-11-16 03:52:51 +000012041
cristy3ed852e2009-09-05 21:47:34 +000012042 else
12043 {
12044 if (logging != MagickFalse)
12045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012046 " Skipping %c%c%c%c chunk, length=%.20g.",
12047 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012048 }
12049 p+=(8+len);
12050 }
12051 }
12052 else
12053 {
12054 /* Write JDAA chunk header */
12055 if (logging != MagickFalse)
12056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012057 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012058 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012059 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012060 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012061 /* Write JDAT chunk(s) data */
12062 (void) WriteBlob(image,4,chunk);
12063 (void) WriteBlob(image,length,blob);
12064 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12065 (uInt) length));
12066 }
12067 blob=(unsigned char *) RelinquishMagickMemory(blob);
12068 }
12069
12070 /* Encode image as a JPEG blob */
12071 if (logging != MagickFalse)
12072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12073 " Creating jpeg_image_info.");
12074 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12075 if (jpeg_image_info == (ImageInfo *) NULL)
12076 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12077
12078 if (logging != MagickFalse)
12079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12080 " Creating jpeg_image.");
12081
cristyc82a27b2011-10-21 01:07:16 +000012082 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012083 if (jpeg_image == (Image *) NULL)
12084 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12085 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12086
12087 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012088 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012089 jpeg_image->filename);
12090
12091 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000012092 exception);
cristy3ed852e2009-09-05 21:47:34 +000012093
12094 if (logging != MagickFalse)
12095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012096 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12097 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012098
12099 if (jng_color_type == 8 || jng_color_type == 12)
12100 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012101
glennrp59575fa2011-12-31 21:31:39 +000012102 jpeg_image_info->quality=jng_quality;
12103 jpeg_image->quality=jng_quality;
cristy3ed852e2009-09-05 21:47:34 +000012104 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12105 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012106
cristy3ed852e2009-09-05 21:47:34 +000012107 if (logging != MagickFalse)
12108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12109 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012110
cristyc82a27b2011-10-21 01:07:16 +000012111 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
glennrp0fe50b42010-11-16 03:52:51 +000012112
cristy3ed852e2009-09-05 21:47:34 +000012113 if (logging != MagickFalse)
12114 {
12115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012116 " Successfully read jpeg_image into a blob, length=%.20g.",
12117 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012118
12119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012120 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012121 }
glennrp0fe50b42010-11-16 03:52:51 +000012122
cristy3ed852e2009-09-05 21:47:34 +000012123 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012124 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012125 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012126 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012127 (void) WriteBlob(image,4,chunk);
12128 (void) WriteBlob(image,length,blob);
12129 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12130
12131 jpeg_image=DestroyImage(jpeg_image);
12132 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12133 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12134 blob=(unsigned char *) RelinquishMagickMemory(blob);
12135
12136 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012137 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012138
12139 /* Write IEND chunk */
12140 (void) WriteBlobMSBULong(image,0L);
12141 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012142 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012143 (void) WriteBlob(image,4,chunk);
12144 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12145
12146 if (logging != MagickFalse)
12147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12148 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012149
cristy3ed852e2009-09-05 21:47:34 +000012150 return(status);
12151}
12152
12153
12154/*
12155%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12156% %
12157% %
12158% %
12159% W r i t e J N G I m a g e %
12160% %
12161% %
12162% %
12163%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12164%
12165% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12166%
12167% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12168%
12169% The format of the WriteJNGImage method is:
12170%
cristy1e178e72011-08-28 19:44:34 +000012171% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12172% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012173%
12174% A description of each parameter follows:
12175%
12176% o image_info: the image info.
12177%
12178% o image: The image.
12179%
cristy1e178e72011-08-28 19:44:34 +000012180% o exception: return any errors or warnings in this structure.
12181%
cristy3ed852e2009-09-05 21:47:34 +000012182%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12183*/
cristy1e178e72011-08-28 19:44:34 +000012184static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12185 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012186{
12187 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012188 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012189 logging,
cristy3ed852e2009-09-05 21:47:34 +000012190 status;
12191
12192 MngInfo
12193 *mng_info;
12194
cristy3ed852e2009-09-05 21:47:34 +000012195 /*
12196 Open image file.
12197 */
12198 assert(image_info != (const ImageInfo *) NULL);
12199 assert(image_info->signature == MagickSignature);
12200 assert(image != (Image *) NULL);
12201 assert(image->signature == MagickSignature);
12202 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012203 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristyc82a27b2011-10-21 01:07:16 +000012204 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012205 if (status == MagickFalse)
12206 return(status);
12207
12208 /*
12209 Allocate a MngInfo structure.
12210 */
12211 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012212 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012213 if (mng_info == (MngInfo *) NULL)
12214 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12215 /*
12216 Initialize members of the MngInfo structure.
12217 */
12218 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12219 mng_info->image=image;
12220 have_mng_structure=MagickTrue;
12221
12222 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12223
cristy018f07f2011-09-04 21:15:19 +000012224 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012225 (void) CloseBlob(image);
12226
12227 (void) CatchImageException(image);
12228 MngInfoFreeStruct(mng_info,&have_mng_structure);
12229 if (logging != MagickFalse)
12230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12231 return(status);
12232}
12233#endif
12234
cristy1e178e72011-08-28 19:44:34 +000012235static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12236 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012237{
12238 const char
12239 *option;
12240
12241 Image
12242 *next_image;
12243
12244 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012245 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012246 status;
12247
glennrp03812ae2010-12-24 01:31:34 +000012248 volatile MagickBooleanType
12249 logging;
12250
cristy3ed852e2009-09-05 21:47:34 +000012251 MngInfo
12252 *mng_info;
12253
12254 int
cristy3ed852e2009-09-05 21:47:34 +000012255 image_count,
12256 need_iterations,
12257 need_matte;
12258
12259 volatile int
12260#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12261 defined(PNG_MNG_FEATURES_SUPPORTED)
12262 need_local_plte,
12263#endif
12264 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012265 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012266 use_global_plte;
12267
cristybb503372010-05-27 20:51:26 +000012268 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012269 i;
12270
12271 unsigned char
12272 chunk[800];
12273
12274 volatile unsigned int
12275 write_jng,
12276 write_mng;
12277
cristybb503372010-05-27 20:51:26 +000012278 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012279 scene;
12280
cristybb503372010-05-27 20:51:26 +000012281 size_t
cristy3ed852e2009-09-05 21:47:34 +000012282 final_delay=0,
12283 initial_delay;
12284
glennrpd5045b42010-03-24 12:40:35 +000012285#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012286 if (image_info->verbose)
12287 printf("Your PNG library (libpng-%s) is rather old.\n",
12288 PNG_LIBPNG_VER_STRING);
12289#endif
12290
12291 /*
12292 Open image file.
12293 */
12294 assert(image_info != (const ImageInfo *) NULL);
12295 assert(image_info->signature == MagickSignature);
12296 assert(image != (Image *) NULL);
12297 assert(image->signature == MagickSignature);
12298 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012299 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristyc82a27b2011-10-21 01:07:16 +000012300 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012301 if (status == MagickFalse)
12302 return(status);
12303
12304 /*
12305 Allocate a MngInfo structure.
12306 */
12307 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012308 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012309 if (mng_info == (MngInfo *) NULL)
12310 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12311 /*
12312 Initialize members of the MngInfo structure.
12313 */
12314 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12315 mng_info->image=image;
12316 have_mng_structure=MagickTrue;
12317 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12318
12319 /*
12320 * See if user has requested a specific PNG subformat to be used
12321 * for all of the PNGs in the MNG being written, e.g.,
12322 *
12323 * convert *.png png8:animation.mng
12324 *
12325 * To do: check -define png:bit_depth and png:color_type as well,
12326 * or perhaps use mng:bit_depth and mng:color_type instead for
12327 * global settings.
12328 */
12329
12330 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12331 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12332 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12333
12334 write_jng=MagickFalse;
12335 if (image_info->compression == JPEGCompression)
12336 write_jng=MagickTrue;
12337
12338 mng_info->adjoin=image_info->adjoin &&
12339 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12340
cristy3ed852e2009-09-05 21:47:34 +000012341 if (logging != MagickFalse)
12342 {
12343 /* Log some info about the input */
12344 Image
12345 *p;
12346
12347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12348 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012349
cristy3ed852e2009-09-05 21:47:34 +000012350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012351 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012352
cristy3ed852e2009-09-05 21:47:34 +000012353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12354 " Type: %d",image_info->type);
12355
12356 scene=0;
12357 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12358 {
12359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012360 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012361
cristy3ed852e2009-09-05 21:47:34 +000012362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012363 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012364
cristy3ed852e2009-09-05 21:47:34 +000012365 if (p->matte)
12366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12367 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012368
cristy3ed852e2009-09-05 21:47:34 +000012369 else
12370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12371 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012372
cristy3ed852e2009-09-05 21:47:34 +000012373 if (p->storage_class == PseudoClass)
12374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12375 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012376
cristy3ed852e2009-09-05 21:47:34 +000012377 else
12378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12379 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012380
cristy3ed852e2009-09-05 21:47:34 +000012381 if (p->colors)
12382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012383 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012384
cristy3ed852e2009-09-05 21:47:34 +000012385 else
12386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12387 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012388
cristy3ed852e2009-09-05 21:47:34 +000012389 if (mng_info->adjoin == MagickFalse)
12390 break;
12391 }
12392 }
12393
cristy3ed852e2009-09-05 21:47:34 +000012394 use_global_plte=MagickFalse;
12395 all_images_are_gray=MagickFalse;
12396#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12397 need_local_plte=MagickTrue;
12398#endif
12399 need_defi=MagickFalse;
12400 need_matte=MagickFalse;
12401 mng_info->framing_mode=1;
12402 mng_info->old_framing_mode=1;
12403
12404 if (write_mng)
12405 if (image_info->page != (char *) NULL)
12406 {
12407 /*
12408 Determine image bounding box.
12409 */
12410 SetGeometry(image,&mng_info->page);
12411 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12412 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12413 }
12414 if (write_mng)
12415 {
12416 unsigned int
12417 need_geom;
12418
12419 unsigned short
12420 red,
12421 green,
12422 blue;
12423
12424 mng_info->page=image->page;
12425 need_geom=MagickTrue;
12426 if (mng_info->page.width || mng_info->page.height)
12427 need_geom=MagickFalse;
12428 /*
12429 Check all the scenes.
12430 */
12431 initial_delay=image->delay;
12432 need_iterations=MagickFalse;
12433 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12434 mng_info->equal_physs=MagickTrue,
12435 mng_info->equal_gammas=MagickTrue;
12436 mng_info->equal_srgbs=MagickTrue;
12437 mng_info->equal_backgrounds=MagickTrue;
12438 image_count=0;
12439#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12440 defined(PNG_MNG_FEATURES_SUPPORTED)
12441 all_images_are_gray=MagickTrue;
12442 mng_info->equal_palettes=MagickFalse;
12443 need_local_plte=MagickFalse;
12444#endif
12445 for (next_image=image; next_image != (Image *) NULL; )
12446 {
12447 if (need_geom)
12448 {
12449 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12450 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012451
cristy3ed852e2009-09-05 21:47:34 +000012452 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12453 mng_info->page.height=next_image->rows+next_image->page.y;
12454 }
glennrp0fe50b42010-11-16 03:52:51 +000012455
cristy3ed852e2009-09-05 21:47:34 +000012456 if (next_image->page.x || next_image->page.y)
12457 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012458
cristy3ed852e2009-09-05 21:47:34 +000012459 if (next_image->matte)
12460 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012461
cristy3ed852e2009-09-05 21:47:34 +000012462 if ((int) next_image->dispose >= BackgroundDispose)
12463 if (next_image->matte || next_image->page.x || next_image->page.y ||
12464 ((next_image->columns < mng_info->page.width) &&
12465 (next_image->rows < mng_info->page.height)))
12466 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012467
cristy3ed852e2009-09-05 21:47:34 +000012468 if (next_image->iterations)
12469 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012470
cristy3ed852e2009-09-05 21:47:34 +000012471 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012472
cristy3ed852e2009-09-05 21:47:34 +000012473 if (final_delay != initial_delay || final_delay > 1UL*
12474 next_image->ticks_per_second)
12475 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012476
cristy3ed852e2009-09-05 21:47:34 +000012477#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12478 defined(PNG_MNG_FEATURES_SUPPORTED)
12479 /*
12480 check for global palette possibility.
12481 */
12482 if (image->matte != MagickFalse)
12483 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012484
cristy3ed852e2009-09-05 21:47:34 +000012485 if (need_local_plte == 0)
12486 {
cristyc82a27b2011-10-21 01:07:16 +000012487 if (ImageIsGray(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012488 all_images_are_gray=MagickFalse;
12489 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12490 if (use_global_plte == 0)
12491 use_global_plte=mng_info->equal_palettes;
12492 need_local_plte=!mng_info->equal_palettes;
12493 }
12494#endif
12495 if (GetNextImageInList(next_image) != (Image *) NULL)
12496 {
12497 if (next_image->background_color.red !=
12498 next_image->next->background_color.red ||
12499 next_image->background_color.green !=
12500 next_image->next->background_color.green ||
12501 next_image->background_color.blue !=
12502 next_image->next->background_color.blue)
12503 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012504
cristy3ed852e2009-09-05 21:47:34 +000012505 if (next_image->gamma != next_image->next->gamma)
12506 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012507
cristy3ed852e2009-09-05 21:47:34 +000012508 if (next_image->rendering_intent !=
12509 next_image->next->rendering_intent)
12510 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012511
cristy3ed852e2009-09-05 21:47:34 +000012512 if ((next_image->units != next_image->next->units) ||
cristy2a11bef2011-10-28 18:33:11 +000012513 (next_image->resolution.x != next_image->next->resolution.x) ||
12514 (next_image->resolution.y != next_image->next->resolution.y))
cristy3ed852e2009-09-05 21:47:34 +000012515 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012516
cristy3ed852e2009-09-05 21:47:34 +000012517 if (mng_info->equal_chrms)
12518 {
12519 if (next_image->chromaticity.red_primary.x !=
12520 next_image->next->chromaticity.red_primary.x ||
12521 next_image->chromaticity.red_primary.y !=
12522 next_image->next->chromaticity.red_primary.y ||
12523 next_image->chromaticity.green_primary.x !=
12524 next_image->next->chromaticity.green_primary.x ||
12525 next_image->chromaticity.green_primary.y !=
12526 next_image->next->chromaticity.green_primary.y ||
12527 next_image->chromaticity.blue_primary.x !=
12528 next_image->next->chromaticity.blue_primary.x ||
12529 next_image->chromaticity.blue_primary.y !=
12530 next_image->next->chromaticity.blue_primary.y ||
12531 next_image->chromaticity.white_point.x !=
12532 next_image->next->chromaticity.white_point.x ||
12533 next_image->chromaticity.white_point.y !=
12534 next_image->next->chromaticity.white_point.y)
12535 mng_info->equal_chrms=MagickFalse;
12536 }
12537 }
12538 image_count++;
12539 next_image=GetNextImageInList(next_image);
12540 }
12541 if (image_count < 2)
12542 {
12543 mng_info->equal_backgrounds=MagickFalse;
12544 mng_info->equal_chrms=MagickFalse;
12545 mng_info->equal_gammas=MagickFalse;
12546 mng_info->equal_srgbs=MagickFalse;
12547 mng_info->equal_physs=MagickFalse;
12548 use_global_plte=MagickFalse;
12549#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12550 need_local_plte=MagickTrue;
12551#endif
12552 need_iterations=MagickFalse;
12553 }
glennrp0fe50b42010-11-16 03:52:51 +000012554
cristy3ed852e2009-09-05 21:47:34 +000012555 if (mng_info->need_fram == MagickFalse)
12556 {
12557 /*
12558 Only certain framing rates 100/n are exactly representable without
12559 the FRAM chunk but we'll allow some slop in VLC files
12560 */
12561 if (final_delay == 0)
12562 {
12563 if (need_iterations != MagickFalse)
12564 {
12565 /*
12566 It's probably a GIF with loop; don't run it *too* fast.
12567 */
glennrp02617122010-07-28 13:07:35 +000012568 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012569 {
12570 final_delay=10;
cristyc82a27b2011-10-21 01:07:16 +000012571 (void) ThrowMagickException(exception,GetMagickModule(),
12572 CoderWarning,
glennrpd908de42010-07-28 13:28:27 +000012573 "input has zero delay between all frames; assuming",
12574 " 10 cs `%s'","");
12575 }
cristy3ed852e2009-09-05 21:47:34 +000012576 }
12577 else
12578 mng_info->ticks_per_second=0;
12579 }
12580 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012581 mng_info->ticks_per_second=(png_uint_32)
12582 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012583 if (final_delay > 50)
12584 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012585
cristy3ed852e2009-09-05 21:47:34 +000012586 if (final_delay > 75)
12587 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012588
cristy3ed852e2009-09-05 21:47:34 +000012589 if (final_delay > 125)
12590 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012591
cristy3ed852e2009-09-05 21:47:34 +000012592 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12593 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12594 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12595 1UL*image->ticks_per_second))
12596 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12597 }
glennrp0fe50b42010-11-16 03:52:51 +000012598
cristy3ed852e2009-09-05 21:47:34 +000012599 if (mng_info->need_fram != MagickFalse)
12600 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12601 /*
12602 If pseudocolor, we should also check to see if all the
12603 palettes are identical and write a global PLTE if they are.
12604 ../glennrp Feb 99.
12605 */
12606 /*
12607 Write the MNG version 1.0 signature and MHDR chunk.
12608 */
12609 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12610 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12611 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012612 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012613 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12614 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012615 PNGLong(chunk+12,mng_info->ticks_per_second);
12616 PNGLong(chunk+16,0L); /* layer count=unknown */
12617 PNGLong(chunk+20,0L); /* frame count=unknown */
12618 PNGLong(chunk+24,0L); /* play time=unknown */
12619 if (write_jng)
12620 {
12621 if (need_matte)
12622 {
12623 if (need_defi || mng_info->need_fram || use_global_plte)
12624 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012625
cristy3ed852e2009-09-05 21:47:34 +000012626 else
12627 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12628 }
glennrp0fe50b42010-11-16 03:52:51 +000012629
cristy3ed852e2009-09-05 21:47:34 +000012630 else
12631 {
12632 if (need_defi || mng_info->need_fram || use_global_plte)
12633 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012634
cristy3ed852e2009-09-05 21:47:34 +000012635 else
12636 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12637 }
12638 }
glennrp0fe50b42010-11-16 03:52:51 +000012639
cristy3ed852e2009-09-05 21:47:34 +000012640 else
12641 {
12642 if (need_matte)
12643 {
12644 if (need_defi || mng_info->need_fram || use_global_plte)
12645 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012646
cristy3ed852e2009-09-05 21:47:34 +000012647 else
12648 PNGLong(chunk+28,9L); /* simplicity=VLC */
12649 }
glennrp0fe50b42010-11-16 03:52:51 +000012650
cristy3ed852e2009-09-05 21:47:34 +000012651 else
12652 {
12653 if (need_defi || mng_info->need_fram || use_global_plte)
12654 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012655
cristy3ed852e2009-09-05 21:47:34 +000012656 else
12657 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12658 }
12659 }
12660 (void) WriteBlob(image,32,chunk);
12661 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12662 option=GetImageOption(image_info,"mng:need-cacheoff");
12663 if (option != (const char *) NULL)
12664 {
12665 size_t
12666 length;
12667
12668 /*
12669 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12670 */
12671 PNGType(chunk,mng_nEED);
12672 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012673 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012674 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012675 length+=4;
12676 (void) WriteBlob(image,length,chunk);
12677 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12678 }
12679 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12680 (GetNextImageInList(image) != (Image *) NULL) &&
12681 (image->iterations != 1))
12682 {
12683 /*
12684 Write MNG TERM chunk
12685 */
12686 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12687 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012688 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012689 chunk[4]=3; /* repeat animation */
12690 chunk[5]=0; /* show last frame when done */
12691 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12692 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012693
cristy3ed852e2009-09-05 21:47:34 +000012694 if (image->iterations == 0)
12695 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012696
cristy3ed852e2009-09-05 21:47:34 +000012697 else
12698 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012699
cristy3ed852e2009-09-05 21:47:34 +000012700 if (logging != MagickFalse)
12701 {
12702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012703 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12704 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012705
cristy3ed852e2009-09-05 21:47:34 +000012706 if (image->iterations == 0)
12707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012708 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012709
cristy3ed852e2009-09-05 21:47:34 +000012710 else
12711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012712 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012713 }
12714 (void) WriteBlob(image,14,chunk);
12715 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12716 }
12717 /*
12718 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12719 */
12720 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12721 mng_info->equal_srgbs)
12722 {
12723 /*
12724 Write MNG sRGB chunk
12725 */
12726 (void) WriteBlobMSBULong(image,1L);
12727 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012728 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012729
cristy3ed852e2009-09-05 21:47:34 +000012730 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012731 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012732 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012733 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012734
cristy3ed852e2009-09-05 21:47:34 +000012735 else
glennrpe610a072010-08-05 17:08:46 +000012736 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012737 Magick_RenderingIntent_to_PNG_RenderingIntent(
12738 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012739
cristy3ed852e2009-09-05 21:47:34 +000012740 (void) WriteBlob(image,5,chunk);
12741 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12742 mng_info->have_write_global_srgb=MagickTrue;
12743 }
glennrp0fe50b42010-11-16 03:52:51 +000012744
cristy3ed852e2009-09-05 21:47:34 +000012745 else
12746 {
12747 if (image->gamma && mng_info->equal_gammas)
12748 {
12749 /*
12750 Write MNG gAMA chunk
12751 */
12752 (void) WriteBlobMSBULong(image,4L);
12753 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012754 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012755 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012756 (void) WriteBlob(image,8,chunk);
12757 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12758 mng_info->have_write_global_gama=MagickTrue;
12759 }
12760 if (mng_info->equal_chrms)
12761 {
12762 PrimaryInfo
12763 primary;
12764
12765 /*
12766 Write MNG cHRM chunk
12767 */
12768 (void) WriteBlobMSBULong(image,32L);
12769 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012770 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012771 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012772 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12773 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012774 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012775 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12776 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012777 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012778 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12779 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012780 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012781 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12782 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012783 (void) WriteBlob(image,36,chunk);
12784 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12785 mng_info->have_write_global_chrm=MagickTrue;
12786 }
12787 }
cristy2a11bef2011-10-28 18:33:11 +000012788 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012789 {
12790 /*
12791 Write MNG pHYs chunk
12792 */
12793 (void) WriteBlobMSBULong(image,9L);
12794 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012795 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012796
cristy3ed852e2009-09-05 21:47:34 +000012797 if (image->units == PixelsPerInchResolution)
12798 {
cristy35ef8242010-06-03 16:24:13 +000012799 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012800 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012801
cristy35ef8242010-06-03 16:24:13 +000012802 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012803 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012804
cristy3ed852e2009-09-05 21:47:34 +000012805 chunk[12]=1;
12806 }
glennrp0fe50b42010-11-16 03:52:51 +000012807
cristy3ed852e2009-09-05 21:47:34 +000012808 else
12809 {
12810 if (image->units == PixelsPerCentimeterResolution)
12811 {
cristy35ef8242010-06-03 16:24:13 +000012812 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012813 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012814
cristy35ef8242010-06-03 16:24:13 +000012815 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012816 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012817
cristy3ed852e2009-09-05 21:47:34 +000012818 chunk[12]=1;
12819 }
glennrp0fe50b42010-11-16 03:52:51 +000012820
cristy3ed852e2009-09-05 21:47:34 +000012821 else
12822 {
cristy2a11bef2011-10-28 18:33:11 +000012823 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12824 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012825 chunk[12]=0;
12826 }
12827 }
12828 (void) WriteBlob(image,13,chunk);
12829 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12830 }
12831 /*
12832 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12833 or does not cover the entire frame.
12834 */
12835 if (write_mng && (image->matte || image->page.x > 0 ||
12836 image->page.y > 0 || (image->page.width &&
12837 (image->page.width+image->page.x < mng_info->page.width))
12838 || (image->page.height && (image->page.height+image->page.y
12839 < mng_info->page.height))))
12840 {
12841 (void) WriteBlobMSBULong(image,6L);
12842 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012843 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012844 red=ScaleQuantumToShort(image->background_color.red);
12845 green=ScaleQuantumToShort(image->background_color.green);
12846 blue=ScaleQuantumToShort(image->background_color.blue);
12847 PNGShort(chunk+4,red);
12848 PNGShort(chunk+6,green);
12849 PNGShort(chunk+8,blue);
12850 (void) WriteBlob(image,10,chunk);
12851 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12852 if (mng_info->equal_backgrounds)
12853 {
12854 (void) WriteBlobMSBULong(image,6L);
12855 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012856 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012857 (void) WriteBlob(image,10,chunk);
12858 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12859 }
12860 }
12861
12862#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12863 if ((need_local_plte == MagickFalse) &&
12864 (image->storage_class == PseudoClass) &&
12865 (all_images_are_gray == MagickFalse))
12866 {
cristybb503372010-05-27 20:51:26 +000012867 size_t
cristy3ed852e2009-09-05 21:47:34 +000012868 data_length;
12869
12870 /*
12871 Write MNG PLTE chunk
12872 */
12873 data_length=3*image->colors;
12874 (void) WriteBlobMSBULong(image,data_length);
12875 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012876 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012877
cristybb503372010-05-27 20:51:26 +000012878 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012879 {
cristy5f07f702011-09-26 17:29:10 +000012880 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12881 image->colormap[i].red) & 0xff);
12882 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12883 image->colormap[i].green) & 0xff);
12884 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12885 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000012886 }
glennrp0fe50b42010-11-16 03:52:51 +000012887
cristy3ed852e2009-09-05 21:47:34 +000012888 (void) WriteBlob(image,data_length+4,chunk);
12889 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12890 mng_info->have_write_global_plte=MagickTrue;
12891 }
12892#endif
12893 }
12894 scene=0;
12895 mng_info->delay=0;
12896#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12897 defined(PNG_MNG_FEATURES_SUPPORTED)
12898 mng_info->equal_palettes=MagickFalse;
12899#endif
12900 do
12901 {
12902 if (mng_info->adjoin)
12903 {
12904#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12905 defined(PNG_MNG_FEATURES_SUPPORTED)
12906 /*
12907 If we aren't using a global palette for the entire MNG, check to
12908 see if we can use one for two or more consecutive images.
12909 */
12910 if (need_local_plte && use_global_plte && !all_images_are_gray)
12911 {
12912 if (mng_info->IsPalette)
12913 {
12914 /*
12915 When equal_palettes is true, this image has the same palette
12916 as the previous PseudoClass image
12917 */
12918 mng_info->have_write_global_plte=mng_info->equal_palettes;
12919 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12920 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12921 {
12922 /*
12923 Write MNG PLTE chunk
12924 */
cristybb503372010-05-27 20:51:26 +000012925 size_t
cristy3ed852e2009-09-05 21:47:34 +000012926 data_length;
12927
12928 data_length=3*image->colors;
12929 (void) WriteBlobMSBULong(image,data_length);
12930 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012931 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012932
cristybb503372010-05-27 20:51:26 +000012933 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012934 {
12935 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12936 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12937 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12938 }
glennrp0fe50b42010-11-16 03:52:51 +000012939
cristy3ed852e2009-09-05 21:47:34 +000012940 (void) WriteBlob(image,data_length+4,chunk);
12941 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12942 (uInt) (data_length+4)));
12943 mng_info->have_write_global_plte=MagickTrue;
12944 }
12945 }
12946 else
12947 mng_info->have_write_global_plte=MagickFalse;
12948 }
12949#endif
12950 if (need_defi)
12951 {
cristybb503372010-05-27 20:51:26 +000012952 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012953 previous_x,
12954 previous_y;
12955
12956 if (scene)
12957 {
12958 previous_x=mng_info->page.x;
12959 previous_y=mng_info->page.y;
12960 }
12961 else
12962 {
12963 previous_x=0;
12964 previous_y=0;
12965 }
12966 mng_info->page=image->page;
12967 if ((mng_info->page.x != previous_x) ||
12968 (mng_info->page.y != previous_y))
12969 {
12970 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12971 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012972 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012973 chunk[4]=0; /* object 0 MSB */
12974 chunk[5]=0; /* object 0 LSB */
12975 chunk[6]=0; /* visible */
12976 chunk[7]=0; /* abstract */
12977 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12978 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12979 (void) WriteBlob(image,16,chunk);
12980 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12981 }
12982 }
12983 }
12984
12985 mng_info->write_mng=write_mng;
12986
12987 if ((int) image->dispose >= 3)
12988 mng_info->framing_mode=3;
12989
12990 if (mng_info->need_fram && mng_info->adjoin &&
12991 ((image->delay != mng_info->delay) ||
12992 (mng_info->framing_mode != mng_info->old_framing_mode)))
12993 {
12994 if (image->delay == mng_info->delay)
12995 {
12996 /*
12997 Write a MNG FRAM chunk with the new framing mode.
12998 */
12999 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13000 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013001 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000013002 chunk[4]=(unsigned char) mng_info->framing_mode;
13003 (void) WriteBlob(image,5,chunk);
13004 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13005 }
13006 else
13007 {
13008 /*
13009 Write a MNG FRAM chunk with the delay.
13010 */
13011 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13012 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013013 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013014 chunk[4]=(unsigned char) mng_info->framing_mode;
13015 chunk[5]=0; /* frame name separator (no name) */
13016 chunk[6]=2; /* flag for changing default delay */
13017 chunk[7]=0; /* flag for changing frame timeout */
13018 chunk[8]=0; /* flag for changing frame clipping */
13019 chunk[9]=0; /* flag for changing frame sync_id */
13020 PNGLong(chunk+10,(png_uint_32)
13021 ((mng_info->ticks_per_second*
13022 image->delay)/MagickMax(image->ticks_per_second,1)));
13023 (void) WriteBlob(image,14,chunk);
13024 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000013025 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000013026 }
13027 mng_info->old_framing_mode=mng_info->framing_mode;
13028 }
13029
13030#if defined(JNG_SUPPORTED)
13031 if (image_info->compression == JPEGCompression)
13032 {
13033 ImageInfo
13034 *write_info;
13035
13036 if (logging != MagickFalse)
13037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13038 " Writing JNG object.");
13039 /* To do: specify the desired alpha compression method. */
13040 write_info=CloneImageInfo(image_info);
13041 write_info->compression=UndefinedCompression;
cristy018f07f2011-09-04 21:15:19 +000013042 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013043 write_info=DestroyImageInfo(write_info);
13044 }
13045 else
13046#endif
13047 {
13048 if (logging != MagickFalse)
13049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13050 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013051
glennrpb9cfe272010-12-21 15:08:06 +000013052 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013053 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013054
13055 /* We don't want any ancillary chunks written */
13056 mng_info->ping_exclude_bKGD=MagickTrue;
13057 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013058 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013059 mng_info->ping_exclude_EXIF=MagickTrue;
13060 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013061 mng_info->ping_exclude_iCCP=MagickTrue;
13062 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13063 mng_info->ping_exclude_oFFs=MagickTrue;
13064 mng_info->ping_exclude_pHYs=MagickTrue;
13065 mng_info->ping_exclude_sRGB=MagickTrue;
13066 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013067 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013068 mng_info->ping_exclude_vpAg=MagickTrue;
13069 mng_info->ping_exclude_zCCP=MagickTrue;
13070 mng_info->ping_exclude_zTXt=MagickTrue;
13071
cristy018f07f2011-09-04 21:15:19 +000013072 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013073 }
13074
13075 if (status == MagickFalse)
13076 {
13077 MngInfoFreeStruct(mng_info,&have_mng_structure);
13078 (void) CloseBlob(image);
13079 return(MagickFalse);
13080 }
13081 (void) CatchImageException(image);
13082 if (GetNextImageInList(image) == (Image *) NULL)
13083 break;
13084 image=SyncNextImageInList(image);
13085 status=SetImageProgress(image,SaveImagesTag,scene++,
13086 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013087
cristy3ed852e2009-09-05 21:47:34 +000013088 if (status == MagickFalse)
13089 break;
glennrp0fe50b42010-11-16 03:52:51 +000013090
cristy3ed852e2009-09-05 21:47:34 +000013091 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013092
cristy3ed852e2009-09-05 21:47:34 +000013093 if (write_mng)
13094 {
13095 while (GetPreviousImageInList(image) != (Image *) NULL)
13096 image=GetPreviousImageInList(image);
13097 /*
13098 Write the MEND chunk.
13099 */
13100 (void) WriteBlobMSBULong(image,0x00000000L);
13101 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013102 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013103 (void) WriteBlob(image,4,chunk);
13104 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13105 }
13106 /*
13107 Relinquish resources.
13108 */
13109 (void) CloseBlob(image);
13110 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013111
cristy3ed852e2009-09-05 21:47:34 +000013112 if (logging != MagickFalse)
13113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013114
cristy3ed852e2009-09-05 21:47:34 +000013115 return(MagickTrue);
13116}
glennrpd5045b42010-03-24 12:40:35 +000013117#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013118
cristy3ed852e2009-09-05 21:47:34 +000013119static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13120{
glennrp3bd393f2011-12-21 18:54:53 +000013121 (void) image;
cristy3ed852e2009-09-05 21:47:34 +000013122 printf("Your PNG library is too old: You have libpng-%s\n",
13123 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013124
cristy3ed852e2009-09-05 21:47:34 +000013125 ThrowBinaryException(CoderError,"PNG library is too old",
13126 image_info->filename);
13127}
glennrp39992b42010-11-14 00:03:43 +000013128
cristy3ed852e2009-09-05 21:47:34 +000013129static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13130{
13131 return(WritePNGImage(image_info,image));
13132}
glennrpd5045b42010-03-24 12:40:35 +000013133#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013134#endif