blob: 66279362d17b35239d24752cafa20cfe7d78818f [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{
cristydf0d90e2011-12-12 01:03:55 +00001790 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001791 return((png_voidp) AcquireMagickMemory((size_t) size));
cristy3ed852e2009-09-05 21:47:34 +00001792}
1793
1794/*
1795 Free a pointer. It is removed from the list at the same time.
1796*/
glennrpcf002022011-01-30 02:38:15 +00001797static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001798{
glennrp3bd393f2011-12-21 18:54:53 +00001799 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001800 ptr=RelinquishMagickMemory(ptr);
1801 return((png_free_ptr) NULL);
1802}
1803#endif
1804
1805#if defined(__cplusplus) || defined(c_plusplus)
1806}
1807#endif
1808
1809static int
glennrpcf002022011-01-30 02:38:15 +00001810Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristyd15e6592011-10-15 00:13:06 +00001811 png_textp text,int ii,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001812{
cristybb503372010-05-27 20:51:26 +00001813 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001814 i;
1815
1816 register unsigned char
1817 *dp;
1818
1819 register png_charp
1820 sp;
1821
1822 png_uint_32
1823 length,
1824 nibbles;
1825
1826 StringInfo
1827 *profile;
1828
glennrp0c3e06b2010-11-19 13:45:02 +00001829 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001830 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1831 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1832 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1833 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1834 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1835 13,14,15};
1836
1837 sp=text[ii].text+1;
1838 /* look for newline */
1839 while (*sp != '\n')
1840 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001841
cristy3ed852e2009-09-05 21:47:34 +00001842 /* look for length */
1843 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1844 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001845
cristyf2f27272009-12-17 14:48:46 +00001846 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001847
glennrp97f90e22011-02-22 05:47:58 +00001848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1849 " length: %lu",(unsigned long) length);
1850
cristy3ed852e2009-09-05 21:47:34 +00001851 while (*sp != ' ' && *sp != '\n')
1852 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001853
cristy3ed852e2009-09-05 21:47:34 +00001854 /* allocate space */
1855 if (length == 0)
1856 {
cristyc82a27b2011-10-21 01:07:16 +00001857 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001858 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1859 return(MagickFalse);
1860 }
glennrp0fe50b42010-11-16 03:52:51 +00001861
cristy8723e4b2011-09-01 13:11:19 +00001862 profile=BlobToStringInfo((const void *) NULL,length);
glennrp0fe50b42010-11-16 03:52:51 +00001863
cristy3ed852e2009-09-05 21:47:34 +00001864 if (profile == (StringInfo *) NULL)
1865 {
cristyc82a27b2011-10-21 01:07:16 +00001866 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001867 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1868 "unable to copy profile");
1869 return(MagickFalse);
1870 }
glennrp0fe50b42010-11-16 03:52:51 +00001871
cristy3ed852e2009-09-05 21:47:34 +00001872 /* copy profile, skipping white space and column 1 "=" signs */
1873 dp=GetStringInfoDatum(profile);
1874 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001875
cristybb503372010-05-27 20:51:26 +00001876 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001877 {
1878 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1879 {
1880 if (*sp == '\0')
1881 {
cristyc82a27b2011-10-21 01:07:16 +00001882 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001883 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1884 profile=DestroyStringInfo(profile);
1885 return(MagickFalse);
1886 }
1887 sp++;
1888 }
glennrp0fe50b42010-11-16 03:52:51 +00001889
cristy3ed852e2009-09-05 21:47:34 +00001890 if (i%2 == 0)
1891 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001892
cristy3ed852e2009-09-05 21:47:34 +00001893 else
1894 (*dp++)+=unhex[(int) *sp++];
1895 }
1896 /*
1897 We have already read "Raw profile type.
1898 */
cristyd15e6592011-10-15 00:13:06 +00001899 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001900 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001901
cristy3ed852e2009-09-05 21:47:34 +00001902 if (image_info->verbose)
1903 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001904
cristy3ed852e2009-09-05 21:47:34 +00001905 return MagickTrue;
1906}
1907
1908#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1909static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1910{
1911 Image
1912 *image;
1913
1914
1915 /* The unknown chunk structure contains the chunk data:
1916 png_byte name[5];
1917 png_byte *data;
1918 png_size_t size;
1919
1920 Note that libpng has already taken care of the CRC handling.
1921 */
1922
1923
1924 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1925 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1926 return(0); /* Did not recognize */
1927
1928 /* recognized vpAg */
1929
1930 if (chunk->size != 9)
1931 return(-1); /* Error return */
1932
1933 if (chunk->data[8] != 0)
1934 return(0); /* ImageMagick requires pixel units */
1935
1936 image=(Image *) png_get_user_chunk_ptr(ping);
1937
cristybb503372010-05-27 20:51:26 +00001938 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001939 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001940
cristybb503372010-05-27 20:51:26 +00001941 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001942 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1943
1944 /* Return one of the following: */
1945 /* return(-n); chunk had an error */
1946 /* return(0); did not recognize */
1947 /* return(n); success */
1948
1949 return(1);
1950
1951}
1952#endif
1953
1954/*
1955%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1956% %
1957% %
1958% %
1959% R e a d O n e P N G I m a g e %
1960% %
1961% %
1962% %
1963%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1964%
1965% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1966% (minus the 8-byte signature) and returns it. It allocates the memory
1967% necessary for the new Image structure and returns a pointer to the new
1968% image.
1969%
1970% The format of the ReadOnePNGImage method is:
1971%
1972% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1973% ExceptionInfo *exception)
1974%
1975% A description of each parameter follows:
1976%
1977% o mng_info: Specifies a pointer to a MngInfo structure.
1978%
1979% o image_info: the image info.
1980%
1981% o exception: return any errors or warnings in this structure.
1982%
1983*/
1984static Image *ReadOnePNGImage(MngInfo *mng_info,
1985 const ImageInfo *image_info, ExceptionInfo *exception)
1986{
1987 /* Read one PNG image */
1988
glennrpcc95c3f2011-04-18 16:46:48 +00001989 /* To do: Read the tIME chunk into the date:modify property */
1990 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1991
cristy3ed852e2009-09-05 21:47:34 +00001992 Image
1993 *image;
1994
1995 int
glennrp4eb39312011-03-30 21:34:55 +00001996 intent,
glennrpcb395ac2011-03-30 19:50:23 +00001997 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00001998 num_text,
glennrp4eb39312011-03-30 21:34:55 +00001999 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00002000 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00002001 pass,
2002 ping_bit_depth,
2003 ping_color_type,
2004 ping_interlace_method,
2005 ping_compression_method,
2006 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00002007 ping_num_trans,
2008 unit_type;
2009
2010 double
2011 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002012
2013 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002014 logging,
cristy3ed852e2009-09-05 21:47:34 +00002015 status;
2016
cristyc82a27b2011-10-21 01:07:16 +00002017 PixelInfo
2018 transparent_color;
2019
2020 PNGErrorInfo
2021 error_info;
2022
glennrpfaa852b2010-03-30 12:17:00 +00002023 png_bytep
2024 ping_trans_alpha;
2025
2026 png_color_16p
2027 ping_background,
2028 ping_trans_color;
2029
cristy3ed852e2009-09-05 21:47:34 +00002030 png_info
2031 *end_info,
2032 *ping_info;
2033
2034 png_struct
2035 *ping;
2036
2037 png_textp
2038 text;
2039
glennrpfaa852b2010-03-30 12:17:00 +00002040 png_uint_32
2041 ping_height,
2042 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002043 x_resolution,
2044 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002045
cristy3ed852e2009-09-05 21:47:34 +00002046 QuantumInfo
2047 *quantum_info;
2048
2049 unsigned char
glennrpcf002022011-01-30 02:38:15 +00002050 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002051
cristybb503372010-05-27 20:51:26 +00002052 ssize_t
cristy756ae432011-11-19 02:18:25 +00002053 ping_rowbytes,
cristy3ed852e2009-09-05 21:47:34 +00002054 y;
2055
2056 register unsigned char
2057 *p;
2058
cristybb503372010-05-27 20:51:26 +00002059 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002060 i,
2061 x;
2062
cristy4c08aed2011-07-01 19:47:50 +00002063 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002064 *q;
2065
2066 size_t
glennrp39992b42010-11-14 00:03:43 +00002067 length,
cristy3ed852e2009-09-05 21:47:34 +00002068 row_offset;
2069
cristyeb3b22a2011-03-31 20:16:11 +00002070 ssize_t
2071 j;
2072
cristy3ed852e2009-09-05 21:47:34 +00002073#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2074 png_byte unused_chunks[]=
2075 {
2076 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2077 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2078 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2079 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2080 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2081 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2082 };
2083#endif
2084
2085 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002086 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002087
2088#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002089 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002090#endif
2091
glennrp25c1e2b2010-03-25 01:39:56 +00002092#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002093 if (image_info->verbose)
2094 printf("Your PNG library (libpng-%s) is rather old.\n",
2095 PNG_LIBPNG_VER_STRING);
2096#endif
2097
glennrp61b4c952009-11-10 20:40:41 +00002098#if (PNG_LIBPNG_VER >= 10400)
2099# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2100 if (image_info->verbose)
2101 {
2102 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2103 PNG_LIBPNG_VER_STRING);
2104 printf("Please update it.\n");
2105 }
2106# endif
2107#endif
2108
2109
cristyed552522009-10-16 14:04:35 +00002110 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002111 image=mng_info->image;
2112
glennrpa6a06632011-01-19 15:15:34 +00002113 if (logging != MagickFalse)
2114 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2115 " image->matte=%d",(int) image->matte);
2116
glennrp0e319732011-01-25 21:53:13 +00002117 /* Set to an out-of-range color unless tRNS chunk is present */
2118 transparent_color.red=65537;
2119 transparent_color.green=65537;
2120 transparent_color.blue=65537;
cristy4c08aed2011-07-01 19:47:50 +00002121 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002122
glennrpcb395ac2011-03-30 19:50:23 +00002123 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002124 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002125 num_raw_profiles = 0;
2126
cristy3ed852e2009-09-05 21:47:34 +00002127 /*
2128 Allocate the PNG structures
2129 */
2130#ifdef PNG_USER_MEM_SUPPORTED
cristyc82a27b2011-10-21 01:07:16 +00002131 error_info.image=image;
2132 error_info.exception=exception;
2133 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002134 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2135 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002136#else
cristyc82a27b2011-10-21 01:07:16 +00002137 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002138 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002139#endif
2140 if (ping == (png_struct *) NULL)
2141 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002142
cristy3ed852e2009-09-05 21:47:34 +00002143 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002144
cristy3ed852e2009-09-05 21:47:34 +00002145 if (ping_info == (png_info *) NULL)
2146 {
2147 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2148 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2149 }
glennrp0fe50b42010-11-16 03:52:51 +00002150
cristy3ed852e2009-09-05 21:47:34 +00002151 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002152
cristy3ed852e2009-09-05 21:47:34 +00002153 if (end_info == (png_info *) NULL)
2154 {
2155 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2156 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2157 }
glennrp0fe50b42010-11-16 03:52:51 +00002158
glennrpcf002022011-01-30 02:38:15 +00002159 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002160
glennrpfaa852b2010-03-30 12:17:00 +00002161 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002162 {
2163 /*
2164 PNG image is corrupt.
2165 */
2166 png_destroy_read_struct(&ping,&ping_info,&end_info);
2167#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002168 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002169#endif
2170 if (logging != MagickFalse)
2171 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2172 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002173
cristy3ed852e2009-09-05 21:47:34 +00002174 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002175 {
cristyc82a27b2011-10-21 01:07:16 +00002176 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002177 image->columns=0;
2178 }
glennrp0fe50b42010-11-16 03:52:51 +00002179
cristy3ed852e2009-09-05 21:47:34 +00002180 return(GetFirstImageInList(image));
2181 }
2182 /*
2183 Prepare PNG for reading.
2184 */
glennrpfaa852b2010-03-30 12:17:00 +00002185
cristy3ed852e2009-09-05 21:47:34 +00002186 mng_info->image_found++;
2187 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002188
cristy3ed852e2009-09-05 21:47:34 +00002189 if (LocaleCompare(image_info->magick,"MNG") == 0)
2190 {
2191#if defined(PNG_MNG_FEATURES_SUPPORTED)
2192 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2193 png_set_read_fn(ping,image,png_get_data);
2194#else
2195#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2196 png_permit_empty_plte(ping,MagickTrue);
2197 png_set_read_fn(ping,image,png_get_data);
2198#else
2199 mng_info->image=image;
2200 mng_info->bytes_in_read_buffer=0;
2201 mng_info->found_empty_plte=MagickFalse;
2202 mng_info->have_saved_bkgd_index=MagickFalse;
2203 png_set_read_fn(ping,mng_info,mng_get_data);
2204#endif
2205#endif
2206 }
glennrp0fe50b42010-11-16 03:52:51 +00002207
cristy3ed852e2009-09-05 21:47:34 +00002208 else
2209 png_set_read_fn(ping,image,png_get_data);
2210
2211#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2212 /* Ignore unused chunks and all unknown chunks except for vpAg */
2213 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2214 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2215 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2216 (int)sizeof(unused_chunks)/5);
2217 /* Callback for other unknown chunks */
2218 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2219#endif
2220
glennrp991e92a2010-01-28 03:09:00 +00002221#if (PNG_LIBPNG_VER < 10400)
2222# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2223 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002224 /* Disable thread-unsafe features of pnggccrd */
2225 if (png_access_version_number() >= 10200)
2226 {
2227 png_uint_32 mmx_disable_mask=0;
2228 png_uint_32 asm_flags;
2229
2230 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2231 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2232 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2233 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2234 asm_flags=png_get_asm_flags(ping);
2235 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2236 }
glennrp991e92a2010-01-28 03:09:00 +00002237# endif
cristy3ed852e2009-09-05 21:47:34 +00002238#endif
2239
2240 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002241
2242 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2243 &ping_bit_depth,&ping_color_type,
2244 &ping_interlace_method,&ping_compression_method,
2245 &ping_filter_method);
2246
2247 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2248 &ping_trans_color);
2249
2250 (void) png_get_bKGD(ping, ping_info, &ping_background);
2251
2252 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002253 {
glennrpfaa852b2010-03-30 12:17:00 +00002254 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2255 {
2256 png_set_packing(ping);
2257 ping_bit_depth = 8;
2258 }
cristy3ed852e2009-09-05 21:47:34 +00002259 }
glennrpfaa852b2010-03-30 12:17:00 +00002260
2261 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002262 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002263 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00002264 if (logging != MagickFalse)
2265 {
2266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002267 " PNG width: %.20g, height: %.20g",
2268 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002269
cristy3ed852e2009-09-05 21:47:34 +00002270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2271 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002272 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002273
cristy3ed852e2009-09-05 21:47:34 +00002274 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2275 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002276 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002277
cristy3ed852e2009-09-05 21:47:34 +00002278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2279 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002280 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002281 }
2282
glennrpfaa852b2010-03-30 12:17:00 +00002283#ifdef PNG_READ_iCCP_SUPPORTED
2284 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002285 {
2286 int
2287 compression;
2288
glennrpe4017e32011-01-08 17:16:09 +00002289#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002290 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002291 info;
2292#else
2293 png_bytep
2294 info;
2295#endif
2296
2297 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002298 name;
2299
2300 png_uint_32
2301 profile_length;
2302
2303 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2304 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002305
cristy3ed852e2009-09-05 21:47:34 +00002306 if (profile_length != 0)
2307 {
2308 StringInfo
2309 *profile;
2310
2311 if (logging != MagickFalse)
2312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2313 " Reading PNG iCCP chunk.");
cristye8f8f382011-09-01 13:32:37 +00002314 profile=BlobToStringInfo(info,profile_length);
2315 if (profile == (StringInfo *) NULL)
2316 {
cristyc82a27b2011-10-21 01:07:16 +00002317 (void) ThrowMagickException(exception,GetMagickModule(),
cristye8f8f382011-09-01 13:32:37 +00002318 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2319 "unable to copy profile");
cristyad5b0852011-09-08 02:01:21 +00002320 return((Image *) NULL);
cristye8f8f382011-09-01 13:32:37 +00002321 }
cristy3ed852e2009-09-05 21:47:34 +00002322 SetStringInfoDatum(profile,(const unsigned char *) info);
cristyd15e6592011-10-15 00:13:06 +00002323 (void) SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00002324 profile=DestroyStringInfo(profile);
2325 }
2326 }
2327#endif
2328#if defined(PNG_READ_sRGB_SUPPORTED)
2329 {
cristy3ed852e2009-09-05 21:47:34 +00002330 if (mng_info->have_global_srgb)
cristy2ea7a8e2012-02-08 01:04:50 +00002331 {
cristy2ea7a8e2012-02-08 01:04:50 +00002332 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2333 (mng_info->global_srgb_intent);
2334 }
glennrp0fe50b42010-11-16 03:52:51 +00002335
cristy3ed852e2009-09-05 21:47:34 +00002336 if (png_get_sRGB(ping,ping_info,&intent))
2337 {
glennrpcf002022011-01-30 02:38:15 +00002338 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2339 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002340
cristy3ed852e2009-09-05 21:47:34 +00002341 if (logging != MagickFalse)
2342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002343 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002344 }
2345 }
2346#endif
2347 {
glennrpfaa852b2010-03-30 12:17:00 +00002348 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2349 if (mng_info->have_global_gama)
2350 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002351
cristy3ed852e2009-09-05 21:47:34 +00002352 if (png_get_gAMA(ping,ping_info,&file_gamma))
2353 {
2354 image->gamma=(float) file_gamma;
2355 if (logging != MagickFalse)
2356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2357 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2358 }
2359 }
glennrpfaa852b2010-03-30 12:17:00 +00002360 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2361 {
2362 if (mng_info->have_global_chrm != MagickFalse)
2363 {
2364 (void) png_set_cHRM(ping,ping_info,
2365 mng_info->global_chrm.white_point.x,
2366 mng_info->global_chrm.white_point.y,
2367 mng_info->global_chrm.red_primary.x,
2368 mng_info->global_chrm.red_primary.y,
2369 mng_info->global_chrm.green_primary.x,
2370 mng_info->global_chrm.green_primary.y,
2371 mng_info->global_chrm.blue_primary.x,
2372 mng_info->global_chrm.blue_primary.y);
2373 }
2374 }
glennrp0fe50b42010-11-16 03:52:51 +00002375
glennrpfaa852b2010-03-30 12:17:00 +00002376 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002377 {
2378 (void) png_get_cHRM(ping,ping_info,
2379 &image->chromaticity.white_point.x,
2380 &image->chromaticity.white_point.y,
2381 &image->chromaticity.red_primary.x,
2382 &image->chromaticity.red_primary.y,
2383 &image->chromaticity.green_primary.x,
2384 &image->chromaticity.green_primary.y,
2385 &image->chromaticity.blue_primary.x,
2386 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002387
cristy3ed852e2009-09-05 21:47:34 +00002388 if (logging != MagickFalse)
2389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2390 " Reading PNG cHRM chunk.");
2391 }
glennrp0fe50b42010-11-16 03:52:51 +00002392
glennrpe610a072010-08-05 17:08:46 +00002393 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002394 {
glennrpe610a072010-08-05 17:08:46 +00002395 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002396 Magick_RenderingIntent_to_PNG_RenderingIntent
2397 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00002398 png_set_gAMA(ping,ping_info,0.45455f);
2399 png_set_cHRM(ping,ping_info,
2400 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2401 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002402 }
cristy3ed852e2009-09-05 21:47:34 +00002403#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002404 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002405 {
cristy905ef802011-02-23 00:29:18 +00002406 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2407 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002408
cristy3ed852e2009-09-05 21:47:34 +00002409 if (logging != MagickFalse)
2410 if (image->page.x || image->page.y)
2411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002412 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2413 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002414 }
2415#endif
2416#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002417 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2418 {
2419 if (mng_info->have_global_phys)
2420 {
2421 png_set_pHYs(ping,ping_info,
2422 mng_info->global_x_pixels_per_unit,
2423 mng_info->global_y_pixels_per_unit,
2424 mng_info->global_phys_unit_type);
2425 }
2426 }
2427
2428 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002429 {
cristy3ed852e2009-09-05 21:47:34 +00002430 /*
2431 Set image resolution.
2432 */
2433 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002434 &unit_type);
cristy2a11bef2011-10-28 18:33:11 +00002435 image->resolution.x=(double) x_resolution;
2436 image->resolution.y=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002437
cristy3ed852e2009-09-05 21:47:34 +00002438 if (unit_type == PNG_RESOLUTION_METER)
2439 {
2440 image->units=PixelsPerCentimeterResolution;
cristy2a11bef2011-10-28 18:33:11 +00002441 image->resolution.x=(double) x_resolution/100.0;
2442 image->resolution.y=(double) y_resolution/100.0;
cristy3ed852e2009-09-05 21:47:34 +00002443 }
glennrp0fe50b42010-11-16 03:52:51 +00002444
cristy3ed852e2009-09-05 21:47:34 +00002445 if (logging != MagickFalse)
2446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002447 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2448 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002449 }
cristy3ed852e2009-09-05 21:47:34 +00002450#endif
glennrp823b55c2011-03-14 18:46:46 +00002451
glennrpfaa852b2010-03-30 12:17:00 +00002452 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002453 {
2454 int
2455 number_colors;
2456
2457 png_colorp
2458 palette;
2459
2460 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002461
cristy3ed852e2009-09-05 21:47:34 +00002462 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002463 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002464 {
2465 if (mng_info->global_plte_length)
2466 {
2467 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2468 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002469
glennrpfaa852b2010-03-30 12:17:00 +00002470 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002471 if (mng_info->global_trns_length)
2472 {
2473 if (mng_info->global_trns_length >
2474 mng_info->global_plte_length)
cristyc82a27b2011-10-21 01:07:16 +00002475 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00002476 GetMagickModule(),CoderError,
2477 "global tRNS has more entries than global PLTE",
2478 "`%s'",image_info->filename);
2479 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2480 (int) mng_info->global_trns_length,NULL);
2481 }
glennrpbfd9e612011-04-22 14:02:20 +00002482#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002483 if (
2484#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2485 mng_info->have_saved_bkgd_index ||
2486#endif
glennrpfaa852b2010-03-30 12:17:00 +00002487 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002488 {
2489 png_color_16
2490 background;
2491
2492#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2493 if (mng_info->have_saved_bkgd_index)
2494 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002495#endif
glennrpfaa852b2010-03-30 12:17:00 +00002496 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2497 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002498
cristy3ed852e2009-09-05 21:47:34 +00002499 background.red=(png_uint_16)
2500 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002501
cristy3ed852e2009-09-05 21:47:34 +00002502 background.green=(png_uint_16)
2503 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002504
cristy3ed852e2009-09-05 21:47:34 +00002505 background.blue=(png_uint_16)
2506 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002507
glennrpc6c391a2011-04-27 02:23:56 +00002508 background.gray=(png_uint_16)
2509 mng_info->global_plte[background.index].green;
2510
cristy3ed852e2009-09-05 21:47:34 +00002511 png_set_bKGD(ping,ping_info,&background);
2512 }
2513#endif
2514 }
2515 else
cristyc82a27b2011-10-21 01:07:16 +00002516 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00002517 CoderError,"No global PLTE in file","`%s'",
2518 image_info->filename);
2519 }
2520 }
2521
glennrpbfd9e612011-04-22 14:02:20 +00002522#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002523 if (mng_info->have_global_bkgd &&
2524 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002525 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002526
glennrpfaa852b2010-03-30 12:17:00 +00002527 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002528 {
glennrpbfd9e612011-04-22 14:02:20 +00002529 unsigned int
2530 bkgd_scale;
2531
cristy3ed852e2009-09-05 21:47:34 +00002532 /*
2533 Set image background color.
2534 */
2535 if (logging != MagickFalse)
2536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2537 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002538
glennrpbfd9e612011-04-22 14:02:20 +00002539 /* Scale background components to 16-bit, then scale
2540 * to quantum depth
2541 */
2542 if (logging != MagickFalse)
2543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2544 " raw ping_background=(%d,%d,%d).",ping_background->red,
2545 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002546
glennrpbfd9e612011-04-22 14:02:20 +00002547 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002548
glennrpbfd9e612011-04-22 14:02:20 +00002549 if (ping_bit_depth == 1)
2550 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002551
glennrpbfd9e612011-04-22 14:02:20 +00002552 else if (ping_bit_depth == 2)
2553 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002554
glennrpbfd9e612011-04-22 14:02:20 +00002555 else if (ping_bit_depth == 4)
2556 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002557
glennrpbfd9e612011-04-22 14:02:20 +00002558 if (ping_bit_depth <= 8)
2559 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002560
glennrpbfd9e612011-04-22 14:02:20 +00002561 ping_background->red *= bkgd_scale;
2562 ping_background->green *= bkgd_scale;
2563 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002564
glennrpbfd9e612011-04-22 14:02:20 +00002565 if (logging != MagickFalse)
2566 {
glennrp2cbb4482010-06-02 04:37:24 +00002567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2568 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002569
glennrp2cbb4482010-06-02 04:37:24 +00002570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2571 " ping_background=(%d,%d,%d).",ping_background->red,
2572 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002573 }
glennrp2cbb4482010-06-02 04:37:24 +00002574
glennrpbfd9e612011-04-22 14:02:20 +00002575 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002576 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002577
glennrpbfd9e612011-04-22 14:02:20 +00002578 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002579 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002580
glennrpbfd9e612011-04-22 14:02:20 +00002581 image->background_color.blue=
2582 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002583
cristy4c08aed2011-07-01 19:47:50 +00002584 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002585
glennrpbfd9e612011-04-22 14:02:20 +00002586 if (logging != MagickFalse)
2587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2588 " image->background_color=(%.20g,%.20g,%.20g).",
2589 (double) image->background_color.red,
2590 (double) image->background_color.green,
2591 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002592 }
glennrpbfd9e612011-04-22 14:02:20 +00002593#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002594
glennrpfaa852b2010-03-30 12:17:00 +00002595 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002596 {
2597 /*
glennrpa6a06632011-01-19 15:15:34 +00002598 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002599 */
2600 int
2601 max_sample;
2602
cristy35ef8242010-06-03 16:24:13 +00002603 size_t
2604 one=1;
2605
cristy3ed852e2009-09-05 21:47:34 +00002606 if (logging != MagickFalse)
2607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2608 " Reading PNG tRNS chunk.");
2609
cristyf9cca6a2010-06-04 23:49:28 +00002610 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002611
glennrpfaa852b2010-03-30 12:17:00 +00002612 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2613 (int)ping_trans_color->gray > max_sample) ||
2614 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2615 ((int)ping_trans_color->red > max_sample ||
2616 (int)ping_trans_color->green > max_sample ||
2617 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002618 {
2619 if (logging != MagickFalse)
2620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2621 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002622 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002623 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002624 image->matte=MagickFalse;
2625 }
2626 else
2627 {
glennrpa6a06632011-01-19 15:15:34 +00002628 int
2629 scale_to_short;
2630
2631 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2632
2633 /* Scale transparent_color to short */
2634 transparent_color.red= scale_to_short*ping_trans_color->red;
2635 transparent_color.green= scale_to_short*ping_trans_color->green;
2636 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy4c08aed2011-07-01 19:47:50 +00002637 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002638
glennrpfaa852b2010-03-30 12:17:00 +00002639 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002640 {
glennrp0f111982010-07-07 20:18:33 +00002641 if (logging != MagickFalse)
2642 {
2643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2644 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002645
glennrp0f111982010-07-07 20:18:33 +00002646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy101ab702011-10-13 13:06:32 +00002647 " scaled graylevel is %.20g.",transparent_color.alpha);
glennrp0f111982010-07-07 20:18:33 +00002648 }
cristy4c08aed2011-07-01 19:47:50 +00002649 transparent_color.red=transparent_color.alpha;
2650 transparent_color.green=transparent_color.alpha;
2651 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002652 }
2653 }
2654 }
2655#if defined(PNG_READ_sBIT_SUPPORTED)
2656 if (mng_info->have_global_sbit)
2657 {
glennrpfaa852b2010-03-30 12:17:00 +00002658 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002659 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2660 }
2661#endif
2662 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002663
cristy3ed852e2009-09-05 21:47:34 +00002664 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002665
2666 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2667
cristy3ed852e2009-09-05 21:47:34 +00002668 /*
2669 Initialize image structure.
2670 */
2671 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002672 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002673 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002674 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002675 if (mng_info->mng_type == 0)
2676 {
glennrpfaa852b2010-03-30 12:17:00 +00002677 mng_info->mng_width=ping_width;
2678 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002679 mng_info->frame=mng_info->image_box;
2680 mng_info->clip=mng_info->image_box;
2681 }
glennrp0fe50b42010-11-16 03:52:51 +00002682
cristy3ed852e2009-09-05 21:47:34 +00002683 else
2684 {
2685 image->page.y=mng_info->y_off[mng_info->object_id];
2686 }
glennrp0fe50b42010-11-16 03:52:51 +00002687
cristy3ed852e2009-09-05 21:47:34 +00002688 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002689 image->columns=ping_width;
2690 image->rows=ping_height;
cristy897f2bf2012-01-05 02:03:44 +00002691 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2692 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2693 image->colorspace=GRAYColorspace;
glennrpfaa852b2010-03-30 12:17:00 +00002694 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002695 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002696 {
cristybefe4d22010-06-07 01:18:58 +00002697 size_t
2698 one;
2699
cristy3ed852e2009-09-05 21:47:34 +00002700 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002701 one=1;
2702 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002703#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2704 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002705 image->colors=256;
2706#else
2707 if (image->colors > 65536L)
2708 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002709#endif
glennrpfaa852b2010-03-30 12:17:00 +00002710 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002711 {
2712 int
2713 number_colors;
2714
2715 png_colorp
2716 palette;
2717
2718 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002719 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002720
cristy3ed852e2009-09-05 21:47:34 +00002721 if (logging != MagickFalse)
2722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2723 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2724 }
2725 }
2726
2727 if (image->storage_class == PseudoClass)
2728 {
2729 /*
2730 Initialize image colormap.
2731 */
cristy018f07f2011-09-04 21:15:19 +00002732 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002733 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002734
glennrpfaa852b2010-03-30 12:17:00 +00002735 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002736 {
2737 int
2738 number_colors;
2739
2740 png_colorp
2741 palette;
2742
2743 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002744
glennrp6af6cf12011-04-22 13:05:16 +00002745 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002746 {
2747 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2748 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2749 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2750 }
glennrp6af6cf12011-04-22 13:05:16 +00002751
glennrp67b9c1a2011-04-22 18:47:36 +00002752 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002753 {
2754 image->colormap[i].red=0;
2755 image->colormap[i].green=0;
2756 image->colormap[i].blue=0;
2757 }
cristy3ed852e2009-09-05 21:47:34 +00002758 }
glennrp0fe50b42010-11-16 03:52:51 +00002759
cristy3ed852e2009-09-05 21:47:34 +00002760 else
2761 {
cristybb503372010-05-27 20:51:26 +00002762 size_t
cristy3ed852e2009-09-05 21:47:34 +00002763 scale;
2764
glennrpfaa852b2010-03-30 12:17:00 +00002765 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002766
cristy3ed852e2009-09-05 21:47:34 +00002767 if (scale < 1)
2768 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002769
cristybb503372010-05-27 20:51:26 +00002770 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002771 {
2772 image->colormap[i].red=(Quantum) (i*scale);
2773 image->colormap[i].green=(Quantum) (i*scale);
2774 image->colormap[i].blue=(Quantum) (i*scale);
2775 }
2776 }
2777 }
glennrp147bc912011-03-30 18:47:21 +00002778
glennrpcb395ac2011-03-30 19:50:23 +00002779 /* Set some properties for reporting by "identify" */
2780 {
glennrp147bc912011-03-30 18:47:21 +00002781 char
2782 msg[MaxTextExtent];
2783
2784 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2785 ping_interlace_method in value */
2786
cristy3b6fd2e2011-05-20 12:53:50 +00002787 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002788 "%d, %d",(int) ping_width, (int) ping_height);
cristy5d6fc9c2011-12-27 03:10:42 +00002789 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002790
cristy3b6fd2e2011-05-20 12:53:50 +00002791 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
cristy5d6fc9c2011-12-27 03:10:42 +00002792 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002793
cristy3b6fd2e2011-05-20 12:53:50 +00002794 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
cristy5d6fc9c2011-12-27 03:10:42 +00002795 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002796
cristy3b6fd2e2011-05-20 12:53:50 +00002797 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002798 (int) ping_interlace_method);
cristy5d6fc9c2011-12-27 03:10:42 +00002799 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
glennrpcb395ac2011-03-30 19:50:23 +00002800 }
glennrp147bc912011-03-30 18:47:21 +00002801
cristy3ed852e2009-09-05 21:47:34 +00002802 /*
2803 Read image scanlines.
2804 */
2805 if (image->delay != 0)
2806 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002807
glennrp0ca69b12010-07-26 01:57:52 +00002808 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002809 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2810 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002811 {
glennrp1b888c42012-03-02 15:18:14 +00002812 /* This happens later in non-ping decodes */
2813 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2814 image->storage_class=DirectClass;
2815
cristy3ed852e2009-09-05 21:47:34 +00002816 if (logging != MagickFalse)
2817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002818 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002819 mng_info->scenes_found-1);
2820 png_destroy_read_struct(&ping,&ping_info,&end_info);
2821#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002822 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002823#endif
2824 if (logging != MagickFalse)
2825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2826 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002827
cristy3ed852e2009-09-05 21:47:34 +00002828 return(image);
2829 }
glennrp0fe50b42010-11-16 03:52:51 +00002830
cristy3ed852e2009-09-05 21:47:34 +00002831 if (logging != MagickFalse)
2832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2833 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002834
cristy3ed852e2009-09-05 21:47:34 +00002835 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002836 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2837 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002838
cristy3ed852e2009-09-05 21:47:34 +00002839 else
glennrpcf002022011-01-30 02:38:15 +00002840 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2841 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002842
glennrpcf002022011-01-30 02:38:15 +00002843 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002844 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002845
cristy3ed852e2009-09-05 21:47:34 +00002846 if (logging != MagickFalse)
2847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2848 " Converting PNG pixels to pixel packets");
2849 /*
2850 Convert PNG pixels to pixel packets.
2851 */
glennrpfaa852b2010-03-30 12:17:00 +00002852 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002853 {
2854 /*
2855 PNG image is corrupt.
2856 */
2857 png_destroy_read_struct(&ping,&ping_info,&end_info);
2858#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002859 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002860#endif
2861 if (quantum_info != (QuantumInfo *) NULL)
2862 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002863
glennrpcf002022011-01-30 02:38:15 +00002864 if (ping_pixels != (unsigned char *) NULL)
2865 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002866
cristy3ed852e2009-09-05 21:47:34 +00002867 if (logging != MagickFalse)
2868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2869 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002870
cristy3ed852e2009-09-05 21:47:34 +00002871 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002872 {
cristyc82a27b2011-10-21 01:07:16 +00002873 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002874 image->columns=0;
2875 }
glennrp0fe50b42010-11-16 03:52:51 +00002876
cristy3ed852e2009-09-05 21:47:34 +00002877 return(GetFirstImageInList(image));
2878 }
glennrp0fe50b42010-11-16 03:52:51 +00002879
cristyed552522009-10-16 14:04:35 +00002880 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002881
cristyed552522009-10-16 14:04:35 +00002882 if (quantum_info == (QuantumInfo *) NULL)
2883 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002884
glennrpc8cbc5d2011-01-01 00:12:34 +00002885 {
2886
2887 MagickBooleanType
2888 found_transparent_pixel;
2889
2890 found_transparent_pixel=MagickFalse;
2891
cristy3ed852e2009-09-05 21:47:34 +00002892 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002893 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002894 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002895 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002896 /*
2897 Convert image to DirectClass pixel packets.
2898 */
glennrp67b9c1a2011-04-22 18:47:36 +00002899#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2900 int
2901 depth;
2902
2903 depth=(ssize_t) ping_bit_depth;
2904#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002905 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2906 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2907 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2908 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002909
glennrpc8cbc5d2011-01-01 00:12:34 +00002910 for (y=0; y < (ssize_t) image->rows; y++)
2911 {
2912 if (num_passes > 1)
2913 row_offset=ping_rowbytes*y;
2914
2915 else
2916 row_offset=0;
2917
glennrpcf002022011-01-30 02:38:15 +00002918 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002919 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2920
cristyacd2ed22011-08-30 01:44:23 +00002921 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00002922 break;
2923
glennrpc8cbc5d2011-01-01 00:12:34 +00002924 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2925 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002926 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002927
2928 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2929 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002930 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002931
2932 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2933 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002934 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002935
2936 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2937 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002938 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002939
2940 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2941 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002942 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002943
glennrpc8cbc5d2011-01-01 00:12:34 +00002944 if (found_transparent_pixel == MagickFalse)
2945 {
2946 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002947 if (y== 0 && logging != MagickFalse)
2948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2949 " Looking for cheap transparent pixel");
2950
glennrpc8cbc5d2011-01-01 00:12:34 +00002951 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2952 {
glennrp5aa37f62011-01-02 03:07:57 +00002953 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2954 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy4c08aed2011-07-01 19:47:50 +00002955 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00002956 {
glennrpa6a06632011-01-19 15:15:34 +00002957 if (logging != MagickFalse)
2958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2959 " ...got one.");
2960
glennrpc8cbc5d2011-01-01 00:12:34 +00002961 found_transparent_pixel = MagickTrue;
2962 break;
2963 }
glennrp4f25bd02011-01-01 18:51:28 +00002964 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2965 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp847370c2011-07-05 17:37:15 +00002966 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2967 transparent_color.red &&
2968 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2969 transparent_color.green &&
2970 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2971 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002972 {
glennrpa6a06632011-01-19 15:15:34 +00002973 if (logging != MagickFalse)
2974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2975 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002976 found_transparent_pixel = MagickTrue;
2977 break;
2978 }
cristyed231572011-07-14 02:18:59 +00002979 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00002980 }
2981 }
2982
2983 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2984 {
2985 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2986 image->rows);
2987
2988 if (status == MagickFalse)
2989 break;
2990 }
2991 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2992 break;
2993 }
2994
2995 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2996 {
2997 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00002998 if (status == MagickFalse)
2999 break;
3000 }
cristy3ed852e2009-09-05 21:47:34 +00003001 }
cristy3ed852e2009-09-05 21:47:34 +00003002 }
glennrp0fe50b42010-11-16 03:52:51 +00003003
cristy3ed852e2009-09-05 21:47:34 +00003004 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00003005
cristy3ed852e2009-09-05 21:47:34 +00003006 for (pass=0; pass < num_passes; pass++)
3007 {
3008 Quantum
3009 *quantum_scanline;
3010
3011 register Quantum
3012 *r;
3013
3014 /*
3015 Convert grayscale image to PseudoClass pixel packets.
3016 */
glennrpc17d96f2011-06-27 01:20:11 +00003017 if (logging != MagickFalse)
3018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3019 " Converting grayscale pixels to pixel packets");
cristy4c08aed2011-07-01 19:47:50 +00003020
glennrpfaa852b2010-03-30 12:17:00 +00003021 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00003022 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00003023
cristy3ed852e2009-09-05 21:47:34 +00003024 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3025 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003026
cristy3ed852e2009-09-05 21:47:34 +00003027 if (quantum_scanline == (Quantum *) NULL)
3028 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003029
cristybb503372010-05-27 20:51:26 +00003030 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003031 {
3032 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003033 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003034
cristy3ed852e2009-09-05 21:47:34 +00003035 else
3036 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003037
glennrpcf002022011-01-30 02:38:15 +00003038 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00003039 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003040
cristyacd2ed22011-08-30 01:44:23 +00003041 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003042 break;
glennrp0fe50b42010-11-16 03:52:51 +00003043
glennrpcf002022011-01-30 02:38:15 +00003044 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003045 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003046
glennrpfaa852b2010-03-30 12:17:00 +00003047 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003048 {
3049 case 1:
3050 {
cristybb503372010-05-27 20:51:26 +00003051 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003052 bit;
3053
cristybb503372010-05-27 20:51:26 +00003054 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003055 {
3056 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003057 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003058 p++;
3059 }
glennrp0fe50b42010-11-16 03:52:51 +00003060
cristy3ed852e2009-09-05 21:47:34 +00003061 if ((image->columns % 8) != 0)
3062 {
cristybb503372010-05-27 20:51:26 +00003063 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003064 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003065 }
glennrp0fe50b42010-11-16 03:52:51 +00003066
cristy3ed852e2009-09-05 21:47:34 +00003067 break;
3068 }
glennrp47b9dd52010-11-24 18:12:06 +00003069
cristy3ed852e2009-09-05 21:47:34 +00003070 case 2:
3071 {
cristybb503372010-05-27 20:51:26 +00003072 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003073 {
glennrpa18d5bc2011-04-23 14:51:34 +00003074 *r++=(*p >> 6) & 0x03;
3075 *r++=(*p >> 4) & 0x03;
3076 *r++=(*p >> 2) & 0x03;
3077 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003078 }
glennrp0fe50b42010-11-16 03:52:51 +00003079
cristy3ed852e2009-09-05 21:47:34 +00003080 if ((image->columns % 4) != 0)
3081 {
cristybb503372010-05-27 20:51:26 +00003082 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003083 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003084 }
glennrp0fe50b42010-11-16 03:52:51 +00003085
cristy3ed852e2009-09-05 21:47:34 +00003086 break;
3087 }
glennrp47b9dd52010-11-24 18:12:06 +00003088
cristy3ed852e2009-09-05 21:47:34 +00003089 case 4:
3090 {
cristybb503372010-05-27 20:51:26 +00003091 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003092 {
glennrpa18d5bc2011-04-23 14:51:34 +00003093 *r++=(*p >> 4) & 0x0f;
3094 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003095 }
glennrp0fe50b42010-11-16 03:52:51 +00003096
cristy3ed852e2009-09-05 21:47:34 +00003097 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003098 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003099
cristy3ed852e2009-09-05 21:47:34 +00003100 break;
3101 }
glennrp47b9dd52010-11-24 18:12:06 +00003102
cristy3ed852e2009-09-05 21:47:34 +00003103 case 8:
3104 {
glennrpfaa852b2010-03-30 12:17:00 +00003105 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003106 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003107 {
glennrpa18d5bc2011-04-23 14:51:34 +00003108 *r++=*p++;
cristy4c08aed2011-07-01 19:47:50 +00003109 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3110 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003111 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003112 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003113 }
glennrp0fe50b42010-11-16 03:52:51 +00003114
cristy3ed852e2009-09-05 21:47:34 +00003115 else
cristybb503372010-05-27 20:51:26 +00003116 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003117 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003118
cristy3ed852e2009-09-05 21:47:34 +00003119 break;
3120 }
glennrp47b9dd52010-11-24 18:12:06 +00003121
cristy3ed852e2009-09-05 21:47:34 +00003122 case 16:
3123 {
cristybb503372010-05-27 20:51:26 +00003124 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003125 {
glennrpc17d96f2011-06-27 01:20:11 +00003126#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003127 size_t
3128 quantum;
3129
3130 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003131 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003132
3133 else
glennrpc17d96f2011-06-27 01:20:11 +00003134 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003135
glennrp58f77c72011-04-23 14:09:09 +00003136 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003137 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003138 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003139
3140 if (ping_color_type == 4)
3141 {
glennrpc17d96f2011-06-27 01:20:11 +00003142 if (image->colors > 256)
3143 quantum=((*p++) << 8);
3144 else
3145 quantum=0;
3146
glennrp58f77c72011-04-23 14:09:09 +00003147 quantum|=(*p++);
cristy4c08aed2011-07-01 19:47:50 +00003148 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3149 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003150 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003151 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003152 }
glennrp58f77c72011-04-23 14:09:09 +00003153
3154#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3155 *r++=(*p++);
3156 p++; /* strip low byte */
3157
3158 if (ping_color_type == 4)
3159 {
cristy4c08aed2011-07-01 19:47:50 +00003160 SetPixelAlpha(image,*p++,q);
3161 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003162 found_transparent_pixel = MagickTrue;
3163 p++;
cristyed231572011-07-14 02:18:59 +00003164 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003165 }
cristy3ed852e2009-09-05 21:47:34 +00003166#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003167 }
glennrp47b9dd52010-11-24 18:12:06 +00003168
cristy3ed852e2009-09-05 21:47:34 +00003169 break;
3170 }
glennrp47b9dd52010-11-24 18:12:06 +00003171
cristy3ed852e2009-09-05 21:47:34 +00003172 default:
3173 break;
3174 }
glennrp3faa9a32011-04-23 14:00:25 +00003175
cristy3ed852e2009-09-05 21:47:34 +00003176 /*
3177 Transfer image scanline.
3178 */
3179 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003180
cristy4c08aed2011-07-01 19:47:50 +00003181 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3182
cristyacd2ed22011-08-30 01:44:23 +00003183 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00003184 break;
cristybb503372010-05-27 20:51:26 +00003185 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00003186 {
3187 SetPixelIndex(image,*r++,q);
cristyed231572011-07-14 02:18:59 +00003188 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00003189 }
glennrp0fe50b42010-11-16 03:52:51 +00003190
cristy3ed852e2009-09-05 21:47:34 +00003191 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3192 break;
glennrp0fe50b42010-11-16 03:52:51 +00003193
cristy7a287bf2010-02-14 02:18:09 +00003194 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3195 {
cristycee97112010-05-28 00:44:52 +00003196 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003197 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003198
cristy7a287bf2010-02-14 02:18:09 +00003199 if (status == MagickFalse)
3200 break;
3201 }
cristy3ed852e2009-09-05 21:47:34 +00003202 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003203
cristy7a287bf2010-02-14 02:18:09 +00003204 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003205 {
3206 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003207
cristy3ed852e2009-09-05 21:47:34 +00003208 if (status == MagickFalse)
3209 break;
3210 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003211
cristy3ed852e2009-09-05 21:47:34 +00003212 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3213 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003214
3215 image->matte=found_transparent_pixel;
3216
3217 if (logging != MagickFalse)
3218 {
3219 if (found_transparent_pixel != MagickFalse)
3220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3221 " Found transparent pixel");
3222 else
glennrp5aa37f62011-01-02 03:07:57 +00003223 {
3224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3225 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003226
glennrp5aa37f62011-01-02 03:07:57 +00003227 ping_color_type&=0x03;
3228 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003229 }
3230 }
3231
cristyb32b90a2009-09-07 21:45:48 +00003232 if (quantum_info != (QuantumInfo *) NULL)
3233 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00003234
cristy5c6f7892010-05-05 22:53:29 +00003235 if (image->storage_class == PseudoClass)
3236 {
cristyaeb2cbc2010-05-07 13:28:58 +00003237 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003238 matte;
3239
3240 matte=image->matte;
3241 image->matte=MagickFalse;
cristyea1a8aa2011-10-20 13:24:06 +00003242 (void) SyncImage(image,exception);
cristyaeb2cbc2010-05-07 13:28:58 +00003243 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003244 }
glennrp47b9dd52010-11-24 18:12:06 +00003245
glennrp4eb39312011-03-30 21:34:55 +00003246 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003247
3248 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003249 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003250 {
3251 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003252 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003253 image->colors=2;
cristyea1a8aa2011-10-20 13:24:06 +00003254 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003255#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003256 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003257#endif
3258 if (logging != MagickFalse)
3259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3260 " exit ReadOnePNGImage() early.");
3261 return(image);
3262 }
glennrp47b9dd52010-11-24 18:12:06 +00003263
glennrpfaa852b2010-03-30 12:17:00 +00003264 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003265 {
3266 ClassType
3267 storage_class;
3268
3269 /*
3270 Image has a transparent background.
3271 */
3272 storage_class=image->storage_class;
3273 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003274
glennrp3c218112010-11-27 15:31:26 +00003275/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003276
glennrp0fe50b42010-11-16 03:52:51 +00003277 if (storage_class == PseudoClass)
3278 {
3279 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003280 {
glennrp0fe50b42010-11-16 03:52:51 +00003281 for (x=0; x < ping_num_trans; x++)
3282 {
cristy6b7677c2012-01-01 20:59:57 +00003283 image->colormap[x].matte=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00003284 image->colormap[x].alpha =
3285 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003286 }
glennrpc11cf6a2010-03-20 16:46:19 +00003287 }
glennrp47b9dd52010-11-24 18:12:06 +00003288
glennrp0fe50b42010-11-16 03:52:51 +00003289 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3290 {
3291 for (x=0; x < (int) image->colors; x++)
3292 {
3293 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy4c08aed2011-07-01 19:47:50 +00003294 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003295 {
cristy6b7677c2012-01-01 20:59:57 +00003296 image->colormap[x].matte=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00003297 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003298 }
3299 }
3300 }
cristyea1a8aa2011-10-20 13:24:06 +00003301 (void) SyncImage(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003302 }
glennrp47b9dd52010-11-24 18:12:06 +00003303
glennrpa6a06632011-01-19 15:15:34 +00003304#if 1 /* Should have already been done above, but glennrp problem P10
3305 * needs this.
3306 */
glennrp0fe50b42010-11-16 03:52:51 +00003307 else
3308 {
3309 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003310 {
glennrp0fe50b42010-11-16 03:52:51 +00003311 image->storage_class=storage_class;
3312 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3313
cristyacd2ed22011-08-30 01:44:23 +00003314 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003315 break;
3316
glennrp0fe50b42010-11-16 03:52:51 +00003317
glennrpa6a06632011-01-19 15:15:34 +00003318 /* Caution: on a Q8 build, this does not distinguish between
3319 * 16-bit colors that differ only in the low byte
3320 */
glennrp0fe50b42010-11-16 03:52:51 +00003321 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3322 {
glennrp847370c2011-07-05 17:37:15 +00003323 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3324 transparent_color.red &&
3325 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3326 transparent_color.green &&
3327 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3328 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003329 {
cristy4c08aed2011-07-01 19:47:50 +00003330 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003331 }
glennrp0fe50b42010-11-16 03:52:51 +00003332
glennrp67b9c1a2011-04-22 18:47:36 +00003333#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003334 else
glennrp4f25bd02011-01-01 18:51:28 +00003335 {
cristy4c08aed2011-07-01 19:47:50 +00003336 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003337 }
glennrpa6a06632011-01-19 15:15:34 +00003338#endif
glennrp0fe50b42010-11-16 03:52:51 +00003339
cristyed231572011-07-14 02:18:59 +00003340 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003341 }
3342
3343 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3344 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003345 }
glennrp0fe50b42010-11-16 03:52:51 +00003346 }
glennrpa6a06632011-01-19 15:15:34 +00003347#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003348
cristy3ed852e2009-09-05 21:47:34 +00003349 image->storage_class=DirectClass;
3350 }
glennrp3c218112010-11-27 15:31:26 +00003351
cristyeb3b22a2011-03-31 20:16:11 +00003352 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003353 {
3354 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003355 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3356 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003357 else
glennrpa0ed0092011-04-18 16:36:29 +00003358 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3359 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003360
glennrp4eb39312011-03-30 21:34:55 +00003361 if (status != MagickFalse)
3362 for (i=0; i < (ssize_t) num_text; i++)
3363 {
3364 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003365
glennrp4eb39312011-03-30 21:34:55 +00003366 if (logging != MagickFalse)
3367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3368 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003369
glennrp4eb39312011-03-30 21:34:55 +00003370 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003371 {
cristyd15e6592011-10-15 00:13:06 +00003372 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i,
3373 exception);
glennrp4eb39312011-03-30 21:34:55 +00003374 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003375 }
glennrp0fe50b42010-11-16 03:52:51 +00003376
glennrp4eb39312011-03-30 21:34:55 +00003377 else
3378 {
3379 char
3380 *value;
3381
3382 length=text[i].text_length;
3383 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3384 sizeof(*value));
3385 if (value == (char *) NULL)
3386 {
cristyc82a27b2011-10-21 01:07:16 +00003387 (void) ThrowMagickException(exception,GetMagickModule(),
glennrp4eb39312011-03-30 21:34:55 +00003388 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3389 image->filename);
3390 break;
3391 }
3392 *value='\0';
3393 (void) ConcatenateMagickString(value,text[i].text,length+2);
3394
3395 /* Don't save "density" or "units" property if we have a pHYs
3396 * chunk
3397 */
3398 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3399 (LocaleCompare(text[i].key,"density") != 0 &&
3400 LocaleCompare(text[i].key,"units") != 0))
cristyd15e6592011-10-15 00:13:06 +00003401 (void) SetImageProperty(image,text[i].key,value,exception);
glennrp4eb39312011-03-30 21:34:55 +00003402
3403 if (logging != MagickFalse)
3404 {
3405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3406 " length: %lu",(unsigned long) length);
3407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3408 " Keyword: %s",text[i].key);
3409 }
3410
3411 value=DestroyString(value);
3412 }
3413 }
3414 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003415 }
glennrp3c218112010-11-27 15:31:26 +00003416
cristy3ed852e2009-09-05 21:47:34 +00003417#ifdef MNG_OBJECT_BUFFERS
3418 /*
3419 Store the object if necessary.
3420 */
3421 if (object_id && !mng_info->frozen[object_id])
3422 {
3423 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3424 {
3425 /*
3426 create a new object buffer.
3427 */
3428 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003429 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003430
cristy3ed852e2009-09-05 21:47:34 +00003431 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3432 {
3433 mng_info->ob[object_id]->image=(Image *) NULL;
3434 mng_info->ob[object_id]->reference_count=1;
3435 }
3436 }
glennrp47b9dd52010-11-24 18:12:06 +00003437
cristy3ed852e2009-09-05 21:47:34 +00003438 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3439 mng_info->ob[object_id]->frozen)
3440 {
3441 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
cristyc82a27b2011-10-21 01:07:16 +00003442 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003443 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3444 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003445
cristy3ed852e2009-09-05 21:47:34 +00003446 if (mng_info->ob[object_id]->frozen)
cristyc82a27b2011-10-21 01:07:16 +00003447 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003448 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3449 "`%s'",image->filename);
3450 }
glennrp0fe50b42010-11-16 03:52:51 +00003451
cristy3ed852e2009-09-05 21:47:34 +00003452 else
3453 {
cristy3ed852e2009-09-05 21:47:34 +00003454
3455 if (mng_info->ob[object_id]->image != (Image *) NULL)
3456 mng_info->ob[object_id]->image=DestroyImage
3457 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003458
cristy3ed852e2009-09-05 21:47:34 +00003459 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
cristyc82a27b2011-10-21 01:07:16 +00003460 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003461
cristy3ed852e2009-09-05 21:47:34 +00003462 if (mng_info->ob[object_id]->image != (Image *) NULL)
3463 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003464
cristy3ed852e2009-09-05 21:47:34 +00003465 else
cristyc82a27b2011-10-21 01:07:16 +00003466 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003467 ResourceLimitError,"Cloning image for object buffer failed",
3468 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003469
glennrpfaa852b2010-03-30 12:17:00 +00003470 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003471 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003472
glennrpfaa852b2010-03-30 12:17:00 +00003473 mng_info->ob[object_id]->width=ping_width;
3474 mng_info->ob[object_id]->height=ping_height;
3475 mng_info->ob[object_id]->color_type=ping_color_type;
3476 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3477 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3478 mng_info->ob[object_id]->compression_method=
3479 ping_compression_method;
3480 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003481
glennrpfaa852b2010-03-30 12:17:00 +00003482 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003483 {
3484 int
3485 number_colors;
3486
3487 png_colorp
3488 plte;
3489
3490 /*
3491 Copy the PLTE to the object buffer.
3492 */
3493 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3494 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003495
cristy3ed852e2009-09-05 21:47:34 +00003496 for (i=0; i < number_colors; i++)
3497 {
3498 mng_info->ob[object_id]->plte[i]=plte[i];
3499 }
3500 }
glennrp47b9dd52010-11-24 18:12:06 +00003501
cristy3ed852e2009-09-05 21:47:34 +00003502 else
3503 mng_info->ob[object_id]->plte_length=0;
3504 }
3505 }
3506#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003507
3508 /* Set image->matte to MagickTrue if the input colortype supports
3509 * alpha or if a valid tRNS chunk is present, no matter whether there
3510 * is actual transparency present.
3511 */
3512 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3513 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3514 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3515 MagickTrue : MagickFalse;
3516
glennrpcb395ac2011-03-30 19:50:23 +00003517 /* Set more properties for identify to retrieve */
3518 {
3519 char
3520 msg[MaxTextExtent];
3521
glennrp4eb39312011-03-30 21:34:55 +00003522 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003523 {
3524 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003525 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003526 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
cristy5d6fc9c2011-12-27 03:10:42 +00003527 (void) SetImageProperty(image,"png:text ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003528 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003529 }
3530
3531 if (num_raw_profiles != 0)
3532 {
cristy3b6fd2e2011-05-20 12:53:50 +00003533 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003534 "%d were found", num_raw_profiles);
cristy5d6fc9c2011-12-27 03:10:42 +00003535 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003536 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003537 }
3538
glennrpcb395ac2011-03-30 19:50:23 +00003539 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003540 {
cristy3b6fd2e2011-05-20 12:53:50 +00003541 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003542 "chunk was found (see Chromaticity, above)");
cristy5d6fc9c2011-12-27 03:10:42 +00003543 (void) SetImageProperty(image,"png:cHRM ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003544 exception);
glennrp59612252011-03-30 21:45:21 +00003545 }
glennrpcb395ac2011-03-30 19:50:23 +00003546
3547 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003548 {
cristy3b6fd2e2011-05-20 12:53:50 +00003549 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003550 "chunk was found (see Background color, above)");
cristy5d6fc9c2011-12-27 03:10:42 +00003551 (void) SetImageProperty(image,"png:bKGD ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003552 exception);
glennrp59612252011-03-30 21:45:21 +00003553 }
3554
cristy3b6fd2e2011-05-20 12:53:50 +00003555 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003556 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003557
3558 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy5d6fc9c2011-12-27 03:10:42 +00003559 (void) SetImageProperty(image,"png:iCCP ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003560 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003561
glennrpcb395ac2011-03-30 19:50:23 +00003562 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy5d6fc9c2011-12-27 03:10:42 +00003563 (void) SetImageProperty(image,"png:tRNS ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003564 exception);
glennrp4eb39312011-03-30 21:34:55 +00003565
3566#if defined(PNG_sRGB_SUPPORTED)
3567 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3568 {
cristy3b6fd2e2011-05-20 12:53:50 +00003569 (void) FormatLocaleString(msg,MaxTextExtent,
cristy96c4fcb2012-02-08 01:01:05 +00003570 "intent=%d (See Rendering intent)", (int) intent);
cristy5d6fc9c2011-12-27 03:10:42 +00003571 (void) SetImageProperty(image,"png:sRGB ",msg,
cristy96c4fcb2012-02-08 01:01:05 +00003572 exception);
glennrp4eb39312011-03-30 21:34:55 +00003573 }
3574#endif
3575
3576 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3577 {
cristy3b6fd2e2011-05-20 12:53:50 +00003578 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003579 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003580 file_gamma);
cristy5d6fc9c2011-12-27 03:10:42 +00003581 (void) SetImageProperty(image,"png:gAMA ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003582 exception);
glennrp4eb39312011-03-30 21:34:55 +00003583 }
3584
3585#if defined(PNG_pHYs_SUPPORTED)
3586 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3587 {
cristy3b6fd2e2011-05-20 12:53:50 +00003588 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003589 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003590 (double) x_resolution,(double) y_resolution, unit_type);
cristy5d6fc9c2011-12-27 03:10:42 +00003591 (void) SetImageProperty(image,"png:pHYs ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003592 exception);
glennrp4eb39312011-03-30 21:34:55 +00003593 }
3594#endif
3595
3596#if defined(PNG_oFFs_SUPPORTED)
3597 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3598 {
cristy3b6fd2e2011-05-20 12:53:50 +00003599 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003600 (double) image->page.x,(double) image->page.y);
cristy5d6fc9c2011-12-27 03:10:42 +00003601 (void) SetImageProperty(image,"png:oFFs ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003602 exception);
glennrp4eb39312011-03-30 21:34:55 +00003603 }
3604#endif
3605
glennrp07523c72011-03-31 18:12:10 +00003606 if ((image->page.width != 0 && image->page.width != image->columns) ||
3607 (image->page.height != 0 && image->page.height != image->rows))
3608 {
cristy3b6fd2e2011-05-20 12:53:50 +00003609 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003610 "width=%.20g, height=%.20g",
3611 (double) image->page.width,(double) image->page.height);
cristy5d6fc9c2011-12-27 03:10:42 +00003612 (void) SetImageProperty(image,"png:vpAg ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003613 exception);
glennrp07523c72011-03-31 18:12:10 +00003614 }
glennrpcb395ac2011-03-30 19:50:23 +00003615 }
3616
cristy3ed852e2009-09-05 21:47:34 +00003617 /*
3618 Relinquish resources.
3619 */
3620 png_destroy_read_struct(&ping,&ping_info,&end_info);
3621
glennrpcf002022011-01-30 02:38:15 +00003622 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003623#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003624 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003625#endif
3626
3627 if (logging != MagickFalse)
3628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3629 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003630
cristy3ed852e2009-09-05 21:47:34 +00003631 return(image);
3632
3633/* end of reading one PNG image */
3634}
3635
3636static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3637{
3638 Image
3639 *image,
3640 *previous;
3641
3642 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003643 have_mng_structure,
3644 logging,
cristy3ed852e2009-09-05 21:47:34 +00003645 status;
3646
3647 MngInfo
3648 *mng_info;
3649
3650 char
3651 magic_number[MaxTextExtent];
3652
cristy3ed852e2009-09-05 21:47:34 +00003653 ssize_t
3654 count;
3655
3656 /*
3657 Open image file.
3658 */
3659 assert(image_info != (const ImageInfo *) NULL);
3660 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003661
cristy3ed852e2009-09-05 21:47:34 +00003662 if (image_info->debug != MagickFalse)
3663 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3664 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003665
cristy3ed852e2009-09-05 21:47:34 +00003666 assert(exception != (ExceptionInfo *) NULL);
3667 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003668 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy9950d572011-10-01 18:22:35 +00003669 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003670 mng_info=(MngInfo *) NULL;
3671 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003672
cristy3ed852e2009-09-05 21:47:34 +00003673 if (status == MagickFalse)
3674 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003675
cristy3ed852e2009-09-05 21:47:34 +00003676 /*
3677 Verify PNG signature.
3678 */
3679 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003680
glennrpdde35db2011-02-21 12:06:32 +00003681 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003682 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003683
cristy3ed852e2009-09-05 21:47:34 +00003684 /*
3685 Allocate a MngInfo structure.
3686 */
3687 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003688 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003689
cristy3ed852e2009-09-05 21:47:34 +00003690 if (mng_info == (MngInfo *) NULL)
3691 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003692
cristy3ed852e2009-09-05 21:47:34 +00003693 /*
3694 Initialize members of the MngInfo structure.
3695 */
3696 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3697 mng_info->image=image;
3698 have_mng_structure=MagickTrue;
3699
3700 previous=image;
3701 image=ReadOnePNGImage(mng_info,image_info,exception);
3702 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003703
cristy3ed852e2009-09-05 21:47:34 +00003704 if (image == (Image *) NULL)
3705 {
3706 if (previous != (Image *) NULL)
3707 {
3708 if (previous->signature != MagickSignature)
3709 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003710
cristy3ed852e2009-09-05 21:47:34 +00003711 (void) CloseBlob(previous);
3712 (void) DestroyImageList(previous);
3713 }
glennrp0fe50b42010-11-16 03:52:51 +00003714
cristy3ed852e2009-09-05 21:47:34 +00003715 if (logging != MagickFalse)
3716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3717 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003718
cristy3ed852e2009-09-05 21:47:34 +00003719 return((Image *) NULL);
3720 }
glennrp47b9dd52010-11-24 18:12:06 +00003721
cristy3ed852e2009-09-05 21:47:34 +00003722 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003723
cristy3ed852e2009-09-05 21:47:34 +00003724 if ((image->columns == 0) || (image->rows == 0))
3725 {
3726 if (logging != MagickFalse)
3727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3728 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003729
cristy3ed852e2009-09-05 21:47:34 +00003730 ThrowReaderException(CorruptImageError,"CorruptImage");
3731 }
glennrp47b9dd52010-11-24 18:12:06 +00003732
cristy3ed852e2009-09-05 21:47:34 +00003733 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3734 {
cristy018f07f2011-09-04 21:15:19 +00003735 (void) SetImageType(image,TrueColorType,exception);
cristy3ed852e2009-09-05 21:47:34 +00003736 image->matte=MagickFalse;
3737 }
glennrp0fe50b42010-11-16 03:52:51 +00003738
cristy3ed852e2009-09-05 21:47:34 +00003739 if (LocaleCompare(image_info->magick,"PNG32") == 0)
cristy018f07f2011-09-04 21:15:19 +00003740 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003741
cristy3ed852e2009-09-05 21:47:34 +00003742 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3744 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3745 (double) image->page.width,(double) image->page.height,
3746 (double) image->page.x,(double) image->page.y);
3747
3748 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003750
cristy3ed852e2009-09-05 21:47:34 +00003751 return(image);
3752}
3753
3754
3755
3756#if defined(JNG_SUPPORTED)
3757/*
3758%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3759% %
3760% %
3761% %
3762% R e a d O n e J N G I m a g e %
3763% %
3764% %
3765% %
3766%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3767%
3768% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3769% (minus the 8-byte signature) and returns it. It allocates the memory
3770% necessary for the new Image structure and returns a pointer to the new
3771% image.
3772%
3773% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3774%
3775% The format of the ReadOneJNGImage method is:
3776%
3777% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3778% ExceptionInfo *exception)
3779%
3780% A description of each parameter follows:
3781%
3782% o mng_info: Specifies a pointer to a MngInfo structure.
3783%
3784% o image_info: the image info.
3785%
3786% o exception: return any errors or warnings in this structure.
3787%
3788*/
3789static Image *ReadOneJNGImage(MngInfo *mng_info,
3790 const ImageInfo *image_info, ExceptionInfo *exception)
3791{
3792 Image
3793 *alpha_image,
3794 *color_image,
3795 *image,
3796 *jng_image;
3797
3798 ImageInfo
3799 *alpha_image_info,
3800 *color_image_info;
3801
cristy4383ec82011-01-05 15:42:32 +00003802 MagickBooleanType
3803 logging;
3804
cristybb503372010-05-27 20:51:26 +00003805 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003806 y;
3807
3808 MagickBooleanType
3809 status;
3810
3811 png_uint_32
3812 jng_height,
3813 jng_width;
3814
3815 png_byte
3816 jng_color_type,
3817 jng_image_sample_depth,
3818 jng_image_compression_method,
3819 jng_image_interlace_method,
3820 jng_alpha_sample_depth,
3821 jng_alpha_compression_method,
3822 jng_alpha_filter_method,
3823 jng_alpha_interlace_method;
3824
cristy4c08aed2011-07-01 19:47:50 +00003825 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003826 *s;
3827
cristybb503372010-05-27 20:51:26 +00003828 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003829 i,
3830 x;
3831
cristy4c08aed2011-07-01 19:47:50 +00003832 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003833 *q;
3834
3835 register unsigned char
3836 *p;
3837
3838 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003839 read_JSEP,
3840 reading_idat,
3841 skip_to_iend;
3842
cristybb503372010-05-27 20:51:26 +00003843 size_t
cristy3ed852e2009-09-05 21:47:34 +00003844 length;
3845
3846 jng_alpha_compression_method=0;
3847 jng_alpha_sample_depth=8;
3848 jng_color_type=0;
3849 jng_height=0;
3850 jng_width=0;
3851 alpha_image=(Image *) NULL;
3852 color_image=(Image *) NULL;
3853 alpha_image_info=(ImageInfo *) NULL;
3854 color_image_info=(ImageInfo *) NULL;
3855
3856 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003857 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003858
3859 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003860
cristy4c08aed2011-07-01 19:47:50 +00003861 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003862 {
3863 /*
3864 Allocate next image structure.
3865 */
3866 if (logging != MagickFalse)
3867 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3868 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003869
cristy9950d572011-10-01 18:22:35 +00003870 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003871
cristy3ed852e2009-09-05 21:47:34 +00003872 if (GetNextImageInList(image) == (Image *) NULL)
3873 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003874
cristy3ed852e2009-09-05 21:47:34 +00003875 image=SyncNextImageInList(image);
3876 }
3877 mng_info->image=image;
3878
3879 /*
3880 Signature bytes have already been read.
3881 */
3882
3883 read_JSEP=MagickFalse;
3884 reading_idat=MagickFalse;
3885 skip_to_iend=MagickFalse;
3886 for (;;)
3887 {
3888 char
3889 type[MaxTextExtent];
3890
3891 unsigned char
3892 *chunk;
3893
3894 unsigned int
3895 count;
3896
3897 /*
3898 Read a new JNG chunk.
3899 */
3900 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3901 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003902
cristy3ed852e2009-09-05 21:47:34 +00003903 if (status == MagickFalse)
3904 break;
glennrp0fe50b42010-11-16 03:52:51 +00003905
cristy3ed852e2009-09-05 21:47:34 +00003906 type[0]='\0';
3907 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3908 length=ReadBlobMSBLong(image);
3909 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3910
3911 if (logging != MagickFalse)
3912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003913 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3914 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003915
3916 if (length > PNG_UINT_31_MAX || count == 0)
3917 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003918
cristy3ed852e2009-09-05 21:47:34 +00003919 p=NULL;
3920 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003921
cristy3ed852e2009-09-05 21:47:34 +00003922 if (length)
3923 {
3924 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003925
cristy3ed852e2009-09-05 21:47:34 +00003926 if (chunk == (unsigned char *) NULL)
3927 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003928
cristybb503372010-05-27 20:51:26 +00003929 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003930 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003931
cristy3ed852e2009-09-05 21:47:34 +00003932 p=chunk;
3933 }
glennrp47b9dd52010-11-24 18:12:06 +00003934
cristy3ed852e2009-09-05 21:47:34 +00003935 (void) ReadBlobMSBLong(image); /* read crc word */
3936
3937 if (skip_to_iend)
3938 {
3939 if (length)
3940 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003941
cristy3ed852e2009-09-05 21:47:34 +00003942 continue;
3943 }
3944
3945 if (memcmp(type,mng_JHDR,4) == 0)
3946 {
3947 if (length == 16)
3948 {
cristybb503372010-05-27 20:51:26 +00003949 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003950 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003951 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003952 (p[6] << 8) | p[7]);
3953 jng_color_type=p[8];
3954 jng_image_sample_depth=p[9];
3955 jng_image_compression_method=p[10];
3956 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003957
cristy3ed852e2009-09-05 21:47:34 +00003958 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3959 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003960
cristy3ed852e2009-09-05 21:47:34 +00003961 jng_alpha_sample_depth=p[12];
3962 jng_alpha_compression_method=p[13];
3963 jng_alpha_filter_method=p[14];
3964 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003965
cristy3ed852e2009-09-05 21:47:34 +00003966 if (logging != MagickFalse)
3967 {
3968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003969 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003970
cristy3ed852e2009-09-05 21:47:34 +00003971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003972 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003973
cristy3ed852e2009-09-05 21:47:34 +00003974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3975 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003976
cristy3ed852e2009-09-05 21:47:34 +00003977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3978 " jng_image_sample_depth: %3d",
3979 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003980
cristy3ed852e2009-09-05 21:47:34 +00003981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3982 " jng_image_compression_method:%3d",
3983 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003984
cristy3ed852e2009-09-05 21:47:34 +00003985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3986 " jng_image_interlace_method: %3d",
3987 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003988
cristy3ed852e2009-09-05 21:47:34 +00003989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3990 " jng_alpha_sample_depth: %3d",
3991 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003992
cristy3ed852e2009-09-05 21:47:34 +00003993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3994 " jng_alpha_compression_method:%3d",
3995 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003996
cristy3ed852e2009-09-05 21:47:34 +00003997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3998 " jng_alpha_filter_method: %3d",
3999 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00004000
cristy3ed852e2009-09-05 21:47:34 +00004001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4002 " jng_alpha_interlace_method: %3d",
4003 jng_alpha_interlace_method);
4004 }
4005 }
glennrp47b9dd52010-11-24 18:12:06 +00004006
cristy3ed852e2009-09-05 21:47:34 +00004007 if (length)
4008 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004009
cristy3ed852e2009-09-05 21:47:34 +00004010 continue;
4011 }
4012
4013
4014 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4015 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4016 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4017 {
4018 /*
4019 o create color_image
4020 o open color_blob, attached to color_image
4021 o if (color type has alpha)
4022 open alpha_blob, attached to alpha_image
4023 */
4024
cristy73bd4a52010-10-05 11:24:23 +00004025 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004026
cristy3ed852e2009-09-05 21:47:34 +00004027 if (color_image_info == (ImageInfo *) NULL)
4028 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004029
cristy3ed852e2009-09-05 21:47:34 +00004030 GetImageInfo(color_image_info);
cristy9950d572011-10-01 18:22:35 +00004031 color_image=AcquireImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004032
cristy3ed852e2009-09-05 21:47:34 +00004033 if (color_image == (Image *) NULL)
4034 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4035
4036 if (logging != MagickFalse)
4037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4038 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004039
cristy3ed852e2009-09-05 21:47:34 +00004040 (void) AcquireUniqueFilename(color_image->filename);
4041 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4042 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004043
cristy3ed852e2009-09-05 21:47:34 +00004044 if (status == MagickFalse)
4045 return((Image *) NULL);
4046
4047 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4048 {
4049 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004050 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004051
cristy3ed852e2009-09-05 21:47:34 +00004052 if (alpha_image_info == (ImageInfo *) NULL)
4053 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004054
cristy3ed852e2009-09-05 21:47:34 +00004055 GetImageInfo(alpha_image_info);
cristy9950d572011-10-01 18:22:35 +00004056 alpha_image=AcquireImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004057
cristy3ed852e2009-09-05 21:47:34 +00004058 if (alpha_image == (Image *) NULL)
4059 {
4060 alpha_image=DestroyImage(alpha_image);
4061 ThrowReaderException(ResourceLimitError,
4062 "MemoryAllocationFailed");
4063 }
glennrp0fe50b42010-11-16 03:52:51 +00004064
cristy3ed852e2009-09-05 21:47:34 +00004065 if (logging != MagickFalse)
4066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4067 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004068
cristy3ed852e2009-09-05 21:47:34 +00004069 (void) AcquireUniqueFilename(alpha_image->filename);
4070 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4071 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004072
cristy3ed852e2009-09-05 21:47:34 +00004073 if (status == MagickFalse)
4074 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004075
cristy3ed852e2009-09-05 21:47:34 +00004076 if (jng_alpha_compression_method == 0)
4077 {
4078 unsigned char
4079 data[18];
4080
4081 if (logging != MagickFalse)
4082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4083 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004084
cristy3ed852e2009-09-05 21:47:34 +00004085 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4086 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004087
cristy3ed852e2009-09-05 21:47:34 +00004088 (void) WriteBlobMSBULong(alpha_image,13L);
4089 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004090 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004091 PNGLong(data+4,jng_width);
4092 PNGLong(data+8,jng_height);
4093 data[12]=jng_alpha_sample_depth;
4094 data[13]=0; /* color_type gray */
4095 data[14]=0; /* compression method 0 */
4096 data[15]=0; /* filter_method 0 */
4097 data[16]=0; /* interlace_method 0 */
4098 (void) WriteBlob(alpha_image,17,data);
4099 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4100 }
4101 }
4102 reading_idat=MagickTrue;
4103 }
4104
4105 if (memcmp(type,mng_JDAT,4) == 0)
4106 {
glennrp47b9dd52010-11-24 18:12:06 +00004107 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004108
4109 if (logging != MagickFalse)
4110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4111 " Copying JDAT chunk data to color_blob.");
4112
4113 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004114
cristy3ed852e2009-09-05 21:47:34 +00004115 if (length)
4116 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004117
cristy3ed852e2009-09-05 21:47:34 +00004118 continue;
4119 }
4120
4121 if (memcmp(type,mng_IDAT,4) == 0)
4122 {
4123 png_byte
4124 data[5];
4125
glennrp47b9dd52010-11-24 18:12:06 +00004126 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004127
4128 if (image_info->ping == MagickFalse)
4129 {
4130 if (logging != MagickFalse)
4131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4132 " Copying IDAT chunk data to alpha_blob.");
4133
cristybb503372010-05-27 20:51:26 +00004134 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004135 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004136 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004137 (void) WriteBlob(alpha_image,4,data);
4138 (void) WriteBlob(alpha_image,length,chunk);
4139 (void) WriteBlobMSBULong(alpha_image,
4140 crc32(crc32(0,data,4),chunk,(uInt) length));
4141 }
glennrp0fe50b42010-11-16 03:52:51 +00004142
cristy3ed852e2009-09-05 21:47:34 +00004143 if (length)
4144 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004145
cristy3ed852e2009-09-05 21:47:34 +00004146 continue;
4147 }
4148
4149 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4150 {
glennrp47b9dd52010-11-24 18:12:06 +00004151 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004152
4153 if (image_info->ping == MagickFalse)
4154 {
4155 if (logging != MagickFalse)
4156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4157 " Copying JDAA chunk data to alpha_blob.");
4158
4159 (void) WriteBlob(alpha_image,length,chunk);
4160 }
glennrp0fe50b42010-11-16 03:52:51 +00004161
cristy3ed852e2009-09-05 21:47:34 +00004162 if (length)
4163 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004164
cristy3ed852e2009-09-05 21:47:34 +00004165 continue;
4166 }
4167
4168 if (memcmp(type,mng_JSEP,4) == 0)
4169 {
4170 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004171
cristy3ed852e2009-09-05 21:47:34 +00004172 if (length)
4173 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004174
cristy3ed852e2009-09-05 21:47:34 +00004175 continue;
4176 }
4177
4178 if (memcmp(type,mng_bKGD,4) == 0)
4179 {
4180 if (length == 2)
4181 {
4182 image->background_color.red=ScaleCharToQuantum(p[1]);
4183 image->background_color.green=image->background_color.red;
4184 image->background_color.blue=image->background_color.red;
4185 }
glennrp0fe50b42010-11-16 03:52:51 +00004186
cristy3ed852e2009-09-05 21:47:34 +00004187 if (length == 6)
4188 {
4189 image->background_color.red=ScaleCharToQuantum(p[1]);
4190 image->background_color.green=ScaleCharToQuantum(p[3]);
4191 image->background_color.blue=ScaleCharToQuantum(p[5]);
4192 }
glennrp0fe50b42010-11-16 03:52:51 +00004193
cristy3ed852e2009-09-05 21:47:34 +00004194 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4195 continue;
4196 }
4197
4198 if (memcmp(type,mng_gAMA,4) == 0)
4199 {
4200 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004201 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004202
cristy3ed852e2009-09-05 21:47:34 +00004203 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4204 continue;
4205 }
4206
4207 if (memcmp(type,mng_cHRM,4) == 0)
4208 {
4209 if (length == 32)
4210 {
cristy8182b072010-05-30 20:10:53 +00004211 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4212 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4213 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4214 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4215 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4216 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4217 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4218 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004219 }
glennrp47b9dd52010-11-24 18:12:06 +00004220
cristy3ed852e2009-09-05 21:47:34 +00004221 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4222 continue;
4223 }
4224
4225 if (memcmp(type,mng_sRGB,4) == 0)
4226 {
4227 if (length == 1)
4228 {
glennrpe610a072010-08-05 17:08:46 +00004229 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004230 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004231 image->gamma=0.45455f;
4232 image->chromaticity.red_primary.x=0.6400f;
4233 image->chromaticity.red_primary.y=0.3300f;
4234 image->chromaticity.green_primary.x=0.3000f;
4235 image->chromaticity.green_primary.y=0.6000f;
4236 image->chromaticity.blue_primary.x=0.1500f;
4237 image->chromaticity.blue_primary.y=0.0600f;
4238 image->chromaticity.white_point.x=0.3127f;
4239 image->chromaticity.white_point.y=0.3290f;
4240 }
glennrp47b9dd52010-11-24 18:12:06 +00004241
cristy3ed852e2009-09-05 21:47:34 +00004242 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4243 continue;
4244 }
4245
4246 if (memcmp(type,mng_oFFs,4) == 0)
4247 {
4248 if (length > 8)
4249 {
glennrp5eae7602011-02-22 15:21:32 +00004250 image->page.x=(ssize_t) mng_get_long(p);
4251 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004252
cristy3ed852e2009-09-05 21:47:34 +00004253 if ((int) p[8] != 0)
4254 {
4255 image->page.x/=10000;
4256 image->page.y/=10000;
4257 }
4258 }
glennrp47b9dd52010-11-24 18:12:06 +00004259
cristy3ed852e2009-09-05 21:47:34 +00004260 if (length)
4261 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004262
cristy3ed852e2009-09-05 21:47:34 +00004263 continue;
4264 }
4265
4266 if (memcmp(type,mng_pHYs,4) == 0)
4267 {
4268 if (length > 8)
4269 {
cristy2a11bef2011-10-28 18:33:11 +00004270 image->resolution.x=(double) mng_get_long(p);
4271 image->resolution.y=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004272 if ((int) p[8] == PNG_RESOLUTION_METER)
4273 {
4274 image->units=PixelsPerCentimeterResolution;
cristy2a11bef2011-10-28 18:33:11 +00004275 image->resolution.x=image->resolution.x/100.0f;
4276 image->resolution.y=image->resolution.y/100.0f;
cristy3ed852e2009-09-05 21:47:34 +00004277 }
4278 }
glennrp0fe50b42010-11-16 03:52:51 +00004279
cristy3ed852e2009-09-05 21:47:34 +00004280 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4281 continue;
4282 }
4283
4284#if 0
4285 if (memcmp(type,mng_iCCP,4) == 0)
4286 {
glennrpfd05d622011-02-25 04:10:33 +00004287 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004288 if (length)
4289 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004290
cristy3ed852e2009-09-05 21:47:34 +00004291 continue;
4292 }
4293#endif
4294
4295 if (length)
4296 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4297
4298 if (memcmp(type,mng_IEND,4))
4299 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004300
cristy3ed852e2009-09-05 21:47:34 +00004301 break;
4302 }
4303
4304
4305 /* IEND found */
4306
4307 /*
4308 Finish up reading image data:
4309
4310 o read main image from color_blob.
4311
4312 o close color_blob.
4313
4314 o if (color_type has alpha)
4315 if alpha_encoding is PNG
4316 read secondary image from alpha_blob via ReadPNG
4317 if alpha_encoding is JPEG
4318 read secondary image from alpha_blob via ReadJPEG
4319
4320 o close alpha_blob.
4321
4322 o copy intensity of secondary image into
cristy4c08aed2011-07-01 19:47:50 +00004323 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004324
4325 o destroy the secondary image.
4326 */
4327
4328 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004329
cristy3ed852e2009-09-05 21:47:34 +00004330 if (logging != MagickFalse)
4331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4332 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004333
cristy3b6fd2e2011-05-20 12:53:50 +00004334 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004335 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004336
cristy3ed852e2009-09-05 21:47:34 +00004337 color_image_info->ping=MagickFalse; /* To do: avoid this */
4338 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004339
cristy3ed852e2009-09-05 21:47:34 +00004340 if (jng_image == (Image *) NULL)
4341 return((Image *) NULL);
4342
4343 (void) RelinquishUniqueFileResource(color_image->filename);
4344 color_image=DestroyImage(color_image);
4345 color_image_info=DestroyImageInfo(color_image_info);
4346
4347 if (jng_image == (Image *) NULL)
4348 return((Image *) NULL);
4349
4350 if (logging != MagickFalse)
4351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4352 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004353
cristy3ed852e2009-09-05 21:47:34 +00004354 image->rows=jng_height;
4355 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004356
cristybb503372010-05-27 20:51:26 +00004357 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004358 {
cristyc82a27b2011-10-21 01:07:16 +00004359 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00004360 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004361 for (x=(ssize_t) image->columns; x != 0; x--)
4362 {
4363 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4364 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4365 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004366 q+=GetPixelChannels(image);
4367 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004368 }
glennrp47b9dd52010-11-24 18:12:06 +00004369
cristy3ed852e2009-09-05 21:47:34 +00004370 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4371 break;
4372 }
glennrp0fe50b42010-11-16 03:52:51 +00004373
cristy3ed852e2009-09-05 21:47:34 +00004374 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004375
cristy3ed852e2009-09-05 21:47:34 +00004376 if (image_info->ping == MagickFalse)
4377 {
4378 if (jng_color_type >= 12)
4379 {
4380 if (jng_alpha_compression_method == 0)
4381 {
4382 png_byte
4383 data[5];
4384 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4385 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004386 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004387 (void) WriteBlob(alpha_image,4,data);
4388 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4389 }
glennrp0fe50b42010-11-16 03:52:51 +00004390
cristy3ed852e2009-09-05 21:47:34 +00004391 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004392
cristy3ed852e2009-09-05 21:47:34 +00004393 if (logging != MagickFalse)
4394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00004395 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004396
cristy3b6fd2e2011-05-20 12:53:50 +00004397 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004398 "%s",alpha_image->filename);
4399
4400 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004401
cristy3ed852e2009-09-05 21:47:34 +00004402 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004403 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004404 {
4405 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristyc82a27b2011-10-21 01:07:16 +00004406 exception);
cristy3ed852e2009-09-05 21:47:34 +00004407 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004408
cristy3ed852e2009-09-05 21:47:34 +00004409 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00004410 for (x=(ssize_t) image->columns; x != 0; x--)
4411 {
4412 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004413 q+=GetPixelChannels(image);
4414 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004415 }
glennrp0fe50b42010-11-16 03:52:51 +00004416
cristy3ed852e2009-09-05 21:47:34 +00004417 else
cristy4c08aed2011-07-01 19:47:50 +00004418 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004419 {
cristy4c08aed2011-07-01 19:47:50 +00004420 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4421 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004422 image->matte=MagickTrue;
cristyed231572011-07-14 02:18:59 +00004423 q+=GetPixelChannels(image);
4424 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004425 }
glennrp0fe50b42010-11-16 03:52:51 +00004426
cristy3ed852e2009-09-05 21:47:34 +00004427 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4428 break;
4429 }
4430 (void) RelinquishUniqueFileResource(alpha_image->filename);
4431 alpha_image=DestroyImage(alpha_image);
4432 alpha_image_info=DestroyImageInfo(alpha_image_info);
4433 if (jng_image != (Image *) NULL)
4434 jng_image=DestroyImage(jng_image);
4435 }
4436 }
4437
glennrp47b9dd52010-11-24 18:12:06 +00004438 /* Read the JNG image. */
4439
cristy3ed852e2009-09-05 21:47:34 +00004440 if (mng_info->mng_type == 0)
4441 {
4442 mng_info->mng_width=jng_width;
4443 mng_info->mng_height=jng_height;
4444 }
glennrp0fe50b42010-11-16 03:52:51 +00004445
cristy3ed852e2009-09-05 21:47:34 +00004446 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004447 {
4448 image->page.width=jng_width;
4449 image->page.height=jng_height;
4450 }
4451
cristy3ed852e2009-09-05 21:47:34 +00004452 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004453 {
4454 image->page.x=mng_info->x_off[mng_info->object_id];
4455 image->page.y=mng_info->y_off[mng_info->object_id];
4456 }
4457
cristy3ed852e2009-09-05 21:47:34 +00004458 else
glennrp0fe50b42010-11-16 03:52:51 +00004459 {
4460 image->page.y=mng_info->y_off[mng_info->object_id];
4461 }
4462
cristy3ed852e2009-09-05 21:47:34 +00004463 mng_info->image_found++;
4464 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4465 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004466
cristy3ed852e2009-09-05 21:47:34 +00004467 if (logging != MagickFalse)
4468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4469 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004470
cristy3ed852e2009-09-05 21:47:34 +00004471 return(image);
4472}
4473
4474/*
4475%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4476% %
4477% %
4478% %
4479% R e a d J N G I m a g e %
4480% %
4481% %
4482% %
4483%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4484%
4485% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4486% (including the 8-byte signature) and returns it. It allocates the memory
4487% necessary for the new Image structure and returns a pointer to the new
4488% image.
4489%
4490% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4491%
4492% The format of the ReadJNGImage method is:
4493%
4494% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4495% *exception)
4496%
4497% A description of each parameter follows:
4498%
4499% o image_info: the image info.
4500%
4501% o exception: return any errors or warnings in this structure.
4502%
4503*/
4504
4505static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4506{
4507 Image
4508 *image,
4509 *previous;
4510
4511 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004512 have_mng_structure,
4513 logging,
cristy3ed852e2009-09-05 21:47:34 +00004514 status;
4515
4516 MngInfo
4517 *mng_info;
4518
4519 char
4520 magic_number[MaxTextExtent];
4521
cristy3ed852e2009-09-05 21:47:34 +00004522 size_t
4523 count;
4524
4525 /*
4526 Open image file.
4527 */
4528 assert(image_info != (const ImageInfo *) NULL);
4529 assert(image_info->signature == MagickSignature);
4530 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4531 assert(exception != (ExceptionInfo *) NULL);
4532 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004533 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy9950d572011-10-01 18:22:35 +00004534 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004535 mng_info=(MngInfo *) NULL;
4536 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004537
cristy3ed852e2009-09-05 21:47:34 +00004538 if (status == MagickFalse)
4539 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004540
cristy3ed852e2009-09-05 21:47:34 +00004541 if (LocaleCompare(image_info->magick,"JNG") != 0)
4542 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004543
glennrp47b9dd52010-11-24 18:12:06 +00004544 /* Verify JNG signature. */
4545
cristy3ed852e2009-09-05 21:47:34 +00004546 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004547
glennrp3b8763e2011-02-21 12:08:18 +00004548 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004549 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004550
glennrp47b9dd52010-11-24 18:12:06 +00004551 /* Allocate a MngInfo structure. */
4552
cristy3ed852e2009-09-05 21:47:34 +00004553 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004554 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004555
cristy3ed852e2009-09-05 21:47:34 +00004556 if (mng_info == (MngInfo *) NULL)
4557 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004558
glennrp47b9dd52010-11-24 18:12:06 +00004559 /* Initialize members of the MngInfo structure. */
4560
cristy3ed852e2009-09-05 21:47:34 +00004561 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4562 have_mng_structure=MagickTrue;
4563
4564 mng_info->image=image;
4565 previous=image;
4566 image=ReadOneJNGImage(mng_info,image_info,exception);
4567 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004568
cristy3ed852e2009-09-05 21:47:34 +00004569 if (image == (Image *) NULL)
4570 {
4571 if (IsImageObject(previous) != MagickFalse)
4572 {
4573 (void) CloseBlob(previous);
4574 (void) DestroyImageList(previous);
4575 }
glennrp0fe50b42010-11-16 03:52:51 +00004576
cristy3ed852e2009-09-05 21:47:34 +00004577 if (logging != MagickFalse)
4578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4579 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004580
cristy3ed852e2009-09-05 21:47:34 +00004581 return((Image *) NULL);
4582 }
4583 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004584
cristy3ed852e2009-09-05 21:47:34 +00004585 if (image->columns == 0 || image->rows == 0)
4586 {
4587 if (logging != MagickFalse)
4588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4589 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004590
cristy3ed852e2009-09-05 21:47:34 +00004591 ThrowReaderException(CorruptImageError,"CorruptImage");
4592 }
glennrp0fe50b42010-11-16 03:52:51 +00004593
cristy3ed852e2009-09-05 21:47:34 +00004594 if (logging != MagickFalse)
4595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004596
cristy3ed852e2009-09-05 21:47:34 +00004597 return(image);
4598}
4599#endif
4600
4601static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4602{
4603 char
4604 page_geometry[MaxTextExtent];
4605
4606 Image
4607 *image,
4608 *previous;
4609
cristy4383ec82011-01-05 15:42:32 +00004610 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004611 logging,
4612 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004613
cristy3ed852e2009-09-05 21:47:34 +00004614 volatile int
4615 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004616 object_id,
4617 term_chunk_found,
4618 skip_to_iend;
4619
cristybb503372010-05-27 20:51:26 +00004620 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004621 image_count=0;
4622
4623 MagickBooleanType
4624 status;
4625
4626 MagickOffsetType
4627 offset;
4628
4629 MngInfo
4630 *mng_info;
4631
4632 MngBox
4633 default_fb,
4634 fb,
4635 previous_fb;
4636
4637#if defined(MNG_INSERT_LAYERS)
cristy101ab702011-10-13 13:06:32 +00004638 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004639 mng_background_color;
4640#endif
4641
4642 register unsigned char
4643 *p;
4644
cristybb503372010-05-27 20:51:26 +00004645 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004646 i;
4647
4648 size_t
4649 count;
4650
cristybb503372010-05-27 20:51:26 +00004651 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004652 loop_level;
4653
4654 volatile short
4655 skipping_loop;
4656
4657#if defined(MNG_INSERT_LAYERS)
4658 unsigned int
4659 mandatory_back=0;
4660#endif
4661
4662 volatile unsigned int
4663#ifdef MNG_OBJECT_BUFFERS
4664 mng_background_object=0,
4665#endif
4666 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4667
cristybb503372010-05-27 20:51:26 +00004668 size_t
cristy3ed852e2009-09-05 21:47:34 +00004669 default_frame_timeout,
4670 frame_timeout,
4671#if defined(MNG_INSERT_LAYERS)
4672 image_height,
4673 image_width,
4674#endif
4675 length;
4676
glennrp38ea0832010-06-02 18:50:28 +00004677 /* These delays are all measured in image ticks_per_second,
4678 * not in MNG ticks_per_second
4679 */
cristybb503372010-05-27 20:51:26 +00004680 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004681 default_frame_delay,
4682 final_delay,
4683 final_image_delay,
4684 frame_delay,
4685#if defined(MNG_INSERT_LAYERS)
4686 insert_layers,
4687#endif
4688 mng_iterations=1,
4689 simplicity=0,
4690 subframe_height=0,
4691 subframe_width=0;
4692
4693 previous_fb.top=0;
4694 previous_fb.bottom=0;
4695 previous_fb.left=0;
4696 previous_fb.right=0;
4697 default_fb.top=0;
4698 default_fb.bottom=0;
4699 default_fb.left=0;
4700 default_fb.right=0;
4701
glennrp47b9dd52010-11-24 18:12:06 +00004702 /* Open image file. */
4703
cristy3ed852e2009-09-05 21:47:34 +00004704 assert(image_info != (const ImageInfo *) NULL);
4705 assert(image_info->signature == MagickSignature);
4706 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4707 assert(exception != (ExceptionInfo *) NULL);
4708 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004709 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy9950d572011-10-01 18:22:35 +00004710 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004711 mng_info=(MngInfo *) NULL;
4712 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004713
cristy3ed852e2009-09-05 21:47:34 +00004714 if (status == MagickFalse)
4715 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004716
cristy3ed852e2009-09-05 21:47:34 +00004717 first_mng_object=MagickFalse;
4718 skipping_loop=(-1);
4719 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004720
4721 /* Allocate a MngInfo structure. */
4722
cristy73bd4a52010-10-05 11:24:23 +00004723 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004724
cristy3ed852e2009-09-05 21:47:34 +00004725 if (mng_info == (MngInfo *) NULL)
4726 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004727
glennrp47b9dd52010-11-24 18:12:06 +00004728 /* Initialize members of the MngInfo structure. */
4729
cristy3ed852e2009-09-05 21:47:34 +00004730 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4731 mng_info->image=image;
4732 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004733
4734 if (LocaleCompare(image_info->magick,"MNG") == 0)
4735 {
4736 char
4737 magic_number[MaxTextExtent];
4738
glennrp47b9dd52010-11-24 18:12:06 +00004739 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004740 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4741 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4742 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004743
4744 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004745 for (i=0; i < MNG_MAX_OBJECTS; i++)
4746 {
cristybb503372010-05-27 20:51:26 +00004747 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4748 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004749 }
4750 mng_info->exists[0]=MagickTrue;
4751 }
glennrp47b9dd52010-11-24 18:12:06 +00004752
cristy3ed852e2009-09-05 21:47:34 +00004753 first_mng_object=MagickTrue;
4754 mng_type=0;
4755#if defined(MNG_INSERT_LAYERS)
4756 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4757#endif
4758 default_frame_delay=0;
4759 default_frame_timeout=0;
4760 frame_delay=0;
4761 final_delay=1;
4762 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4763 object_id=0;
4764 skip_to_iend=MagickFalse;
4765 term_chunk_found=MagickFalse;
4766 mng_info->framing_mode=1;
4767#if defined(MNG_INSERT_LAYERS)
4768 mandatory_back=MagickFalse;
4769#endif
4770#if defined(MNG_INSERT_LAYERS)
4771 mng_background_color=image->background_color;
4772#endif
4773 default_fb=mng_info->frame;
4774 previous_fb=mng_info->frame;
4775 do
4776 {
4777 char
4778 type[MaxTextExtent];
4779
4780 if (LocaleCompare(image_info->magick,"MNG") == 0)
4781 {
4782 unsigned char
4783 *chunk;
4784
4785 /*
4786 Read a new chunk.
4787 */
4788 type[0]='\0';
4789 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4790 length=ReadBlobMSBLong(image);
4791 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4792
4793 if (logging != MagickFalse)
4794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004795 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4796 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004797
4798 if (length > PNG_UINT_31_MAX)
4799 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004800
cristy3ed852e2009-09-05 21:47:34 +00004801 if (count == 0)
4802 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004803
cristy3ed852e2009-09-05 21:47:34 +00004804 p=NULL;
4805 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004806
cristy3ed852e2009-09-05 21:47:34 +00004807 if (length)
4808 {
4809 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004810
cristy3ed852e2009-09-05 21:47:34 +00004811 if (chunk == (unsigned char *) NULL)
4812 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004813
cristybb503372010-05-27 20:51:26 +00004814 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004815 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004816
cristy3ed852e2009-09-05 21:47:34 +00004817 p=chunk;
4818 }
glennrp0fe50b42010-11-16 03:52:51 +00004819
cristy3ed852e2009-09-05 21:47:34 +00004820 (void) ReadBlobMSBLong(image); /* read crc word */
4821
4822#if !defined(JNG_SUPPORTED)
4823 if (memcmp(type,mng_JHDR,4) == 0)
4824 {
4825 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004826
cristy3ed852e2009-09-05 21:47:34 +00004827 if (mng_info->jhdr_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00004828 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004829 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004830
cristy3ed852e2009-09-05 21:47:34 +00004831 mng_info->jhdr_warning++;
4832 }
4833#endif
4834 if (memcmp(type,mng_DHDR,4) == 0)
4835 {
4836 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004837
cristy3ed852e2009-09-05 21:47:34 +00004838 if (mng_info->dhdr_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00004839 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004840 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004841
cristy3ed852e2009-09-05 21:47:34 +00004842 mng_info->dhdr_warning++;
4843 }
4844 if (memcmp(type,mng_MEND,4) == 0)
4845 break;
glennrp47b9dd52010-11-24 18:12:06 +00004846
cristy3ed852e2009-09-05 21:47:34 +00004847 if (skip_to_iend)
4848 {
4849 if (memcmp(type,mng_IEND,4) == 0)
4850 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004851
cristy3ed852e2009-09-05 21:47:34 +00004852 if (length)
4853 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004854
cristy3ed852e2009-09-05 21:47:34 +00004855 if (logging != MagickFalse)
4856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4857 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004858
cristy3ed852e2009-09-05 21:47:34 +00004859 continue;
4860 }
glennrp0fe50b42010-11-16 03:52:51 +00004861
cristy3ed852e2009-09-05 21:47:34 +00004862 if (memcmp(type,mng_MHDR,4) == 0)
4863 {
cristybb503372010-05-27 20:51:26 +00004864 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004865 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004866
cristybb503372010-05-27 20:51:26 +00004867 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004868 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004869
cristy3ed852e2009-09-05 21:47:34 +00004870 if (logging != MagickFalse)
4871 {
4872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004873 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004875 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004876 }
glennrp0fe50b42010-11-16 03:52:51 +00004877
cristy3ed852e2009-09-05 21:47:34 +00004878 p+=8;
cristy8182b072010-05-30 20:10:53 +00004879 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004880
cristy3ed852e2009-09-05 21:47:34 +00004881 if (mng_info->ticks_per_second == 0)
4882 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004883
cristy3ed852e2009-09-05 21:47:34 +00004884 else
4885 default_frame_delay=1UL*image->ticks_per_second/
4886 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004887
cristy3ed852e2009-09-05 21:47:34 +00004888 frame_delay=default_frame_delay;
4889 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004890
cristy3ed852e2009-09-05 21:47:34 +00004891 if (length > 16)
4892 {
4893 p+=16;
cristy8182b072010-05-30 20:10:53 +00004894 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004895 }
glennrp0fe50b42010-11-16 03:52:51 +00004896
cristy3ed852e2009-09-05 21:47:34 +00004897 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004898
cristy3ed852e2009-09-05 21:47:34 +00004899 if ((simplicity != 0) && ((simplicity | 11) == 11))
4900 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004901
cristy3ed852e2009-09-05 21:47:34 +00004902 if ((simplicity != 0) && ((simplicity | 9) == 9))
4903 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004904
cristy3ed852e2009-09-05 21:47:34 +00004905#if defined(MNG_INSERT_LAYERS)
4906 if (mng_type != 3)
4907 insert_layers=MagickTrue;
4908#endif
cristy4c08aed2011-07-01 19:47:50 +00004909 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004910 {
glennrp47b9dd52010-11-24 18:12:06 +00004911 /* Allocate next image structure. */
cristy9950d572011-10-01 18:22:35 +00004912 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004913
cristy3ed852e2009-09-05 21:47:34 +00004914 if (GetNextImageInList(image) == (Image *) NULL)
4915 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004916
cristy3ed852e2009-09-05 21:47:34 +00004917 image=SyncNextImageInList(image);
4918 mng_info->image=image;
4919 }
4920
4921 if ((mng_info->mng_width > 65535L) ||
4922 (mng_info->mng_height > 65535L))
4923 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004924
cristy3b6fd2e2011-05-20 12:53:50 +00004925 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004926 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004927 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004928
cristy3ed852e2009-09-05 21:47:34 +00004929 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004930 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004931 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004932 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004933 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004934
cristy3ed852e2009-09-05 21:47:34 +00004935 for (i=0; i < MNG_MAX_OBJECTS; i++)
4936 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004937
cristy3ed852e2009-09-05 21:47:34 +00004938 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4939 continue;
4940 }
4941
4942 if (memcmp(type,mng_TERM,4) == 0)
4943 {
4944 int
4945 repeat=0;
4946
4947
4948 if (length)
4949 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004950
cristy3ed852e2009-09-05 21:47:34 +00004951 if (repeat == 3)
4952 {
cristy8182b072010-05-30 20:10:53 +00004953 final_delay=(png_uint_32) mng_get_long(&p[2]);
4954 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004955
cristy3ed852e2009-09-05 21:47:34 +00004956 if (mng_iterations == PNG_UINT_31_MAX)
4957 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004958
cristy3ed852e2009-09-05 21:47:34 +00004959 image->iterations=mng_iterations;
4960 term_chunk_found=MagickTrue;
4961 }
glennrp0fe50b42010-11-16 03:52:51 +00004962
cristy3ed852e2009-09-05 21:47:34 +00004963 if (logging != MagickFalse)
4964 {
4965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4966 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004967
cristy3ed852e2009-09-05 21:47:34 +00004968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004969 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004970
cristy3ed852e2009-09-05 21:47:34 +00004971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004972 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004973 }
glennrp0fe50b42010-11-16 03:52:51 +00004974
cristy3ed852e2009-09-05 21:47:34 +00004975 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4976 continue;
4977 }
4978 if (memcmp(type,mng_DEFI,4) == 0)
4979 {
4980 if (mng_type == 3)
cristyc82a27b2011-10-21 01:07:16 +00004981 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004982 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4983 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004984
cristy3ed852e2009-09-05 21:47:34 +00004985 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004986
cristy3ed852e2009-09-05 21:47:34 +00004987 if (mng_type == 2 && object_id != 0)
cristyc82a27b2011-10-21 01:07:16 +00004988 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004989 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4990 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004991
cristy3ed852e2009-09-05 21:47:34 +00004992 if (object_id > MNG_MAX_OBJECTS)
4993 {
4994 /*
4995 Instead ofsuing a warning we should allocate a larger
4996 MngInfo structure and continue.
4997 */
cristyc82a27b2011-10-21 01:07:16 +00004998 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004999 CoderError,"object id too large","`%s'",image->filename);
5000 object_id=MNG_MAX_OBJECTS;
5001 }
glennrp0fe50b42010-11-16 03:52:51 +00005002
cristy3ed852e2009-09-05 21:47:34 +00005003 if (mng_info->exists[object_id])
5004 if (mng_info->frozen[object_id])
5005 {
5006 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
cristyc82a27b2011-10-21 01:07:16 +00005007 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005008 GetMagickModule(),CoderError,
5009 "DEFI cannot redefine a frozen MNG object","`%s'",
5010 image->filename);
5011 continue;
5012 }
glennrp0fe50b42010-11-16 03:52:51 +00005013
cristy3ed852e2009-09-05 21:47:34 +00005014 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005015
cristy3ed852e2009-09-05 21:47:34 +00005016 if (length > 2)
5017 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00005018
cristy3ed852e2009-09-05 21:47:34 +00005019 /*
5020 Extract object offset info.
5021 */
5022 if (length > 11)
5023 {
glennrp0fe50b42010-11-16 03:52:51 +00005024 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5025 (p[5] << 16) | (p[6] << 8) | p[7]);
5026
5027 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5028 (p[9] << 16) | (p[10] << 8) | p[11]);
5029
cristy3ed852e2009-09-05 21:47:34 +00005030 if (logging != MagickFalse)
5031 {
5032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005033 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005034 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005035
cristy3ed852e2009-09-05 21:47:34 +00005036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005037 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005038 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005039 }
5040 }
glennrp0fe50b42010-11-16 03:52:51 +00005041
cristy3ed852e2009-09-05 21:47:34 +00005042 /*
5043 Extract object clipping info.
5044 */
5045 if (length > 27)
5046 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5047 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005048
cristy3ed852e2009-09-05 21:47:34 +00005049 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5050 continue;
5051 }
5052 if (memcmp(type,mng_bKGD,4) == 0)
5053 {
5054 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005055
cristy3ed852e2009-09-05 21:47:34 +00005056 if (length > 5)
5057 {
5058 mng_info->mng_global_bkgd.red=
5059 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005060
cristy3ed852e2009-09-05 21:47:34 +00005061 mng_info->mng_global_bkgd.green=
5062 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005063
cristy3ed852e2009-09-05 21:47:34 +00005064 mng_info->mng_global_bkgd.blue=
5065 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005066
cristy3ed852e2009-09-05 21:47:34 +00005067 mng_info->have_global_bkgd=MagickTrue;
5068 }
glennrp0fe50b42010-11-16 03:52:51 +00005069
cristy3ed852e2009-09-05 21:47:34 +00005070 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5071 continue;
5072 }
5073 if (memcmp(type,mng_BACK,4) == 0)
5074 {
5075#if defined(MNG_INSERT_LAYERS)
5076 if (length > 6)
5077 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005078
cristy3ed852e2009-09-05 21:47:34 +00005079 else
5080 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005081
cristy3ed852e2009-09-05 21:47:34 +00005082 if (mandatory_back && length > 5)
5083 {
5084 mng_background_color.red=
5085 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005086
cristy3ed852e2009-09-05 21:47:34 +00005087 mng_background_color.green=
5088 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005089
cristy3ed852e2009-09-05 21:47:34 +00005090 mng_background_color.blue=
5091 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005092
cristy4c08aed2011-07-01 19:47:50 +00005093 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005094 }
glennrp0fe50b42010-11-16 03:52:51 +00005095
cristy3ed852e2009-09-05 21:47:34 +00005096#ifdef MNG_OBJECT_BUFFERS
5097 if (length > 8)
5098 mng_background_object=(p[7] << 8) | p[8];
5099#endif
5100#endif
5101 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5102 continue;
5103 }
glennrp47b9dd52010-11-24 18:12:06 +00005104
cristy3ed852e2009-09-05 21:47:34 +00005105 if (memcmp(type,mng_PLTE,4) == 0)
5106 {
glennrp47b9dd52010-11-24 18:12:06 +00005107 /* Read global PLTE. */
5108
cristy3ed852e2009-09-05 21:47:34 +00005109 if (length && (length < 769))
5110 {
5111 if (mng_info->global_plte == (png_colorp) NULL)
5112 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5113 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005114
cristybb503372010-05-27 20:51:26 +00005115 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005116 {
5117 mng_info->global_plte[i].red=p[3*i];
5118 mng_info->global_plte[i].green=p[3*i+1];
5119 mng_info->global_plte[i].blue=p[3*i+2];
5120 }
glennrp0fe50b42010-11-16 03:52:51 +00005121
cristy35ef8242010-06-03 16:24:13 +00005122 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005123 }
5124#ifdef MNG_LOOSE
5125 for ( ; i < 256; i++)
5126 {
5127 mng_info->global_plte[i].red=i;
5128 mng_info->global_plte[i].green=i;
5129 mng_info->global_plte[i].blue=i;
5130 }
glennrp0fe50b42010-11-16 03:52:51 +00005131
cristy3ed852e2009-09-05 21:47:34 +00005132 if (length)
5133 mng_info->global_plte_length=256;
5134#endif
5135 else
5136 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005137
cristy3ed852e2009-09-05 21:47:34 +00005138 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5139 continue;
5140 }
glennrp47b9dd52010-11-24 18:12:06 +00005141
cristy3ed852e2009-09-05 21:47:34 +00005142 if (memcmp(type,mng_tRNS,4) == 0)
5143 {
5144 /* read global tRNS */
5145
5146 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005147 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005148 mng_info->global_trns[i]=p[i];
5149
5150#ifdef MNG_LOOSE
5151 for ( ; i < 256; i++)
5152 mng_info->global_trns[i]=255;
5153#endif
cristy12560f32010-06-03 16:51:08 +00005154 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005155 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5156 continue;
5157 }
5158 if (memcmp(type,mng_gAMA,4) == 0)
5159 {
5160 if (length == 4)
5161 {
cristybb503372010-05-27 20:51:26 +00005162 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005163 igamma;
5164
cristy8182b072010-05-30 20:10:53 +00005165 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005166 mng_info->global_gamma=((float) igamma)*0.00001;
5167 mng_info->have_global_gama=MagickTrue;
5168 }
glennrp0fe50b42010-11-16 03:52:51 +00005169
cristy3ed852e2009-09-05 21:47:34 +00005170 else
5171 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005172
cristy3ed852e2009-09-05 21:47:34 +00005173 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5174 continue;
5175 }
5176
5177 if (memcmp(type,mng_cHRM,4) == 0)
5178 {
glennrp47b9dd52010-11-24 18:12:06 +00005179 /* Read global cHRM */
5180
cristy3ed852e2009-09-05 21:47:34 +00005181 if (length == 32)
5182 {
cristy8182b072010-05-30 20:10:53 +00005183 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5184 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5185 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005186 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005187 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005188 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005189 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005190 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005191 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005192 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005193 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005194 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005195 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005196 mng_info->have_global_chrm=MagickTrue;
5197 }
5198 else
5199 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005200
cristy3ed852e2009-09-05 21:47:34 +00005201 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5202 continue;
5203 }
glennrp47b9dd52010-11-24 18:12:06 +00005204
cristy3ed852e2009-09-05 21:47:34 +00005205 if (memcmp(type,mng_sRGB,4) == 0)
5206 {
5207 /*
5208 Read global sRGB.
5209 */
5210 if (length)
5211 {
glennrpe610a072010-08-05 17:08:46 +00005212 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005213 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005214 mng_info->have_global_srgb=MagickTrue;
5215 }
5216 else
5217 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005218
cristy3ed852e2009-09-05 21:47:34 +00005219 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5220 continue;
5221 }
glennrp47b9dd52010-11-24 18:12:06 +00005222
cristy3ed852e2009-09-05 21:47:34 +00005223 if (memcmp(type,mng_iCCP,4) == 0)
5224 {
glennrpfd05d622011-02-25 04:10:33 +00005225 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005226
5227 /*
5228 Read global iCCP.
5229 */
5230 if (length)
5231 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005232
cristy3ed852e2009-09-05 21:47:34 +00005233 continue;
5234 }
glennrp47b9dd52010-11-24 18:12:06 +00005235
cristy3ed852e2009-09-05 21:47:34 +00005236 if (memcmp(type,mng_FRAM,4) == 0)
5237 {
5238 if (mng_type == 3)
cristyc82a27b2011-10-21 01:07:16 +00005239 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005240 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5241 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005242
cristy3ed852e2009-09-05 21:47:34 +00005243 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5244 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005245
cristy3ed852e2009-09-05 21:47:34 +00005246 frame_delay=default_frame_delay;
5247 frame_timeout=default_frame_timeout;
5248 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005249
cristy3ed852e2009-09-05 21:47:34 +00005250 if (length)
5251 if (p[0])
5252 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005253
cristy3ed852e2009-09-05 21:47:34 +00005254 if (logging != MagickFalse)
5255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5256 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005257
cristy3ed852e2009-09-05 21:47:34 +00005258 if (length > 6)
5259 {
glennrp47b9dd52010-11-24 18:12:06 +00005260 /* Note the delay and frame clipping boundaries. */
5261
cristy3ed852e2009-09-05 21:47:34 +00005262 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005263
cristybb503372010-05-27 20:51:26 +00005264 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005265 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005266
cristy3ed852e2009-09-05 21:47:34 +00005267 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005268
cristybb503372010-05-27 20:51:26 +00005269 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005270 {
5271 int
5272 change_delay,
5273 change_timeout,
5274 change_clipping;
5275
5276 change_delay=(*p++);
5277 change_timeout=(*p++);
5278 change_clipping=(*p++);
5279 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005280
cristy3ed852e2009-09-05 21:47:34 +00005281 if (change_delay)
5282 {
cristy8182b072010-05-30 20:10:53 +00005283 frame_delay=1UL*image->ticks_per_second*
5284 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005285
cristy8182b072010-05-30 20:10:53 +00005286 if (mng_info->ticks_per_second != 0)
5287 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005288
glennrpbb010dd2010-06-01 13:07:15 +00005289 else
5290 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005291
cristy3ed852e2009-09-05 21:47:34 +00005292 if (change_delay == 2)
5293 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005294
cristy3ed852e2009-09-05 21:47:34 +00005295 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005296
cristy3ed852e2009-09-05 21:47:34 +00005297 if (logging != MagickFalse)
5298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005299 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005300 }
glennrp47b9dd52010-11-24 18:12:06 +00005301
cristy3ed852e2009-09-05 21:47:34 +00005302 if (change_timeout)
5303 {
glennrpbb010dd2010-06-01 13:07:15 +00005304 frame_timeout=1UL*image->ticks_per_second*
5305 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005306
glennrpbb010dd2010-06-01 13:07:15 +00005307 if (mng_info->ticks_per_second != 0)
5308 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005309
glennrpbb010dd2010-06-01 13:07:15 +00005310 else
5311 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005312
cristy3ed852e2009-09-05 21:47:34 +00005313 if (change_delay == 2)
5314 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005315
cristy3ed852e2009-09-05 21:47:34 +00005316 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005317
cristy3ed852e2009-09-05 21:47:34 +00005318 if (logging != MagickFalse)
5319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005320 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005321 }
glennrp47b9dd52010-11-24 18:12:06 +00005322
cristy3ed852e2009-09-05 21:47:34 +00005323 if (change_clipping)
5324 {
5325 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5326 p+=17;
5327 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005328
cristy3ed852e2009-09-05 21:47:34 +00005329 if (logging != MagickFalse)
5330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005331 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005332 (double) fb.left,(double) fb.right,(double) fb.top,
5333 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005334
cristy3ed852e2009-09-05 21:47:34 +00005335 if (change_clipping == 2)
5336 default_fb=fb;
5337 }
5338 }
5339 }
5340 mng_info->clip=fb;
5341 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005342
cristybb503372010-05-27 20:51:26 +00005343 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005344 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005345
cristybb503372010-05-27 20:51:26 +00005346 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005347 -mng_info->clip.top);
5348 /*
5349 Insert a background layer behind the frame if framing_mode is 4.
5350 */
5351#if defined(MNG_INSERT_LAYERS)
5352 if (logging != MagickFalse)
5353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005354 " subframe_width=%.20g, subframe_height=%.20g",(double)
5355 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005356
cristy3ed852e2009-09-05 21:47:34 +00005357 if (insert_layers && (mng_info->framing_mode == 4) &&
5358 (subframe_width) && (subframe_height))
5359 {
glennrp47b9dd52010-11-24 18:12:06 +00005360 /* Allocate next image structure. */
cristy4c08aed2011-07-01 19:47:50 +00005361 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005362 {
cristy9950d572011-10-01 18:22:35 +00005363 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005364
cristy3ed852e2009-09-05 21:47:34 +00005365 if (GetNextImageInList(image) == (Image *) NULL)
5366 {
5367 image=DestroyImageList(image);
5368 MngInfoFreeStruct(mng_info,&have_mng_structure);
5369 return((Image *) NULL);
5370 }
glennrp47b9dd52010-11-24 18:12:06 +00005371
cristy3ed852e2009-09-05 21:47:34 +00005372 image=SyncNextImageInList(image);
5373 }
glennrp0fe50b42010-11-16 03:52:51 +00005374
cristy3ed852e2009-09-05 21:47:34 +00005375 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005376
cristy3ed852e2009-09-05 21:47:34 +00005377 if (term_chunk_found)
5378 {
5379 image->start_loop=MagickTrue;
5380 image->iterations=mng_iterations;
5381 term_chunk_found=MagickFalse;
5382 }
glennrp0fe50b42010-11-16 03:52:51 +00005383
cristy3ed852e2009-09-05 21:47:34 +00005384 else
5385 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005386
cristy3ed852e2009-09-05 21:47:34 +00005387 image->columns=subframe_width;
5388 image->rows=subframe_height;
5389 image->page.width=subframe_width;
5390 image->page.height=subframe_height;
5391 image->page.x=mng_info->clip.left;
5392 image->page.y=mng_info->clip.top;
5393 image->background_color=mng_background_color;
5394 image->matte=MagickFalse;
5395 image->delay=0;
cristyea1a8aa2011-10-20 13:24:06 +00005396 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005397
cristy3ed852e2009-09-05 21:47:34 +00005398 if (logging != MagickFalse)
5399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005400 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005401 (double) mng_info->clip.left,(double) mng_info->clip.right,
5402 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005403 }
5404#endif
5405 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5406 continue;
5407 }
5408 if (memcmp(type,mng_CLIP,4) == 0)
5409 {
5410 unsigned int
5411 first_object,
5412 last_object;
5413
5414 /*
5415 Read CLIP.
5416 */
5417 first_object=(p[0] << 8) | p[1];
5418 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005419
cristy3ed852e2009-09-05 21:47:34 +00005420 for (i=(int) first_object; i <= (int) last_object; i++)
5421 {
5422 if (mng_info->exists[i] && !mng_info->frozen[i])
5423 {
5424 MngBox
5425 box;
5426
5427 box=mng_info->object_clip[i];
5428 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5429 }
5430 }
glennrp47b9dd52010-11-24 18:12:06 +00005431
cristy3ed852e2009-09-05 21:47:34 +00005432 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5433 continue;
5434 }
5435 if (memcmp(type,mng_SAVE,4) == 0)
5436 {
5437 for (i=1; i < MNG_MAX_OBJECTS; i++)
5438 if (mng_info->exists[i])
5439 {
5440 mng_info->frozen[i]=MagickTrue;
5441#ifdef MNG_OBJECT_BUFFERS
5442 if (mng_info->ob[i] != (MngBuffer *) NULL)
5443 mng_info->ob[i]->frozen=MagickTrue;
5444#endif
5445 }
glennrp0fe50b42010-11-16 03:52:51 +00005446
cristy3ed852e2009-09-05 21:47:34 +00005447 if (length)
5448 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005449
cristy3ed852e2009-09-05 21:47:34 +00005450 continue;
5451 }
5452
5453 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5454 {
glennrp47b9dd52010-11-24 18:12:06 +00005455 /* Read DISC or SEEK. */
5456
cristy3ed852e2009-09-05 21:47:34 +00005457 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5458 {
5459 for (i=1; i < MNG_MAX_OBJECTS; i++)
5460 MngInfoDiscardObject(mng_info,i);
5461 }
glennrp0fe50b42010-11-16 03:52:51 +00005462
cristy3ed852e2009-09-05 21:47:34 +00005463 else
5464 {
cristybb503372010-05-27 20:51:26 +00005465 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005466 j;
5467
cristybb503372010-05-27 20:51:26 +00005468 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005469 {
5470 i=p[j] << 8 | p[j+1];
5471 MngInfoDiscardObject(mng_info,i);
5472 }
5473 }
glennrp0fe50b42010-11-16 03:52:51 +00005474
cristy3ed852e2009-09-05 21:47:34 +00005475 if (length)
5476 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005477
cristy3ed852e2009-09-05 21:47:34 +00005478 continue;
5479 }
glennrp47b9dd52010-11-24 18:12:06 +00005480
cristy3ed852e2009-09-05 21:47:34 +00005481 if (memcmp(type,mng_MOVE,4) == 0)
5482 {
cristybb503372010-05-27 20:51:26 +00005483 size_t
cristy3ed852e2009-09-05 21:47:34 +00005484 first_object,
5485 last_object;
5486
glennrp47b9dd52010-11-24 18:12:06 +00005487 /* read MOVE */
5488
cristy3ed852e2009-09-05 21:47:34 +00005489 first_object=(p[0] << 8) | p[1];
5490 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005491 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005492 {
5493 if (mng_info->exists[i] && !mng_info->frozen[i])
5494 {
5495 MngPair
5496 new_pair;
5497
5498 MngPair
5499 old_pair;
5500
5501 old_pair.a=mng_info->x_off[i];
5502 old_pair.b=mng_info->y_off[i];
5503 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5504 mng_info->x_off[i]=new_pair.a;
5505 mng_info->y_off[i]=new_pair.b;
5506 }
5507 }
glennrp47b9dd52010-11-24 18:12:06 +00005508
cristy3ed852e2009-09-05 21:47:34 +00005509 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5510 continue;
5511 }
5512
5513 if (memcmp(type,mng_LOOP,4) == 0)
5514 {
cristybb503372010-05-27 20:51:26 +00005515 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005516 loop_level=chunk[0];
5517 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005518
5519 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005520 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005521
cristy3ed852e2009-09-05 21:47:34 +00005522 if (logging != MagickFalse)
5523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005524 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5525 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005526
cristy3ed852e2009-09-05 21:47:34 +00005527 if (loop_iters == 0)
5528 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005529
cristy3ed852e2009-09-05 21:47:34 +00005530 else
5531 {
5532 mng_info->loop_jump[loop_level]=TellBlob(image);
5533 mng_info->loop_count[loop_level]=loop_iters;
5534 }
glennrp0fe50b42010-11-16 03:52:51 +00005535
cristy3ed852e2009-09-05 21:47:34 +00005536 mng_info->loop_iteration[loop_level]=0;
5537 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5538 continue;
5539 }
glennrp47b9dd52010-11-24 18:12:06 +00005540
cristy3ed852e2009-09-05 21:47:34 +00005541 if (memcmp(type,mng_ENDL,4) == 0)
5542 {
5543 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005544
cristy3ed852e2009-09-05 21:47:34 +00005545 if (skipping_loop > 0)
5546 {
5547 if (skipping_loop == loop_level)
5548 {
5549 /*
5550 Found end of zero-iteration loop.
5551 */
5552 skipping_loop=(-1);
5553 mng_info->loop_active[loop_level]=0;
5554 }
5555 }
glennrp47b9dd52010-11-24 18:12:06 +00005556
cristy3ed852e2009-09-05 21:47:34 +00005557 else
5558 {
5559 if (mng_info->loop_active[loop_level] == 1)
5560 {
5561 mng_info->loop_count[loop_level]--;
5562 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005563
cristy3ed852e2009-09-05 21:47:34 +00005564 if (logging != MagickFalse)
5565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005566 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005567 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005568 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005569
cristy3ed852e2009-09-05 21:47:34 +00005570 if (mng_info->loop_count[loop_level] != 0)
5571 {
5572 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5573 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005574
cristy3ed852e2009-09-05 21:47:34 +00005575 if (offset < 0)
5576 ThrowReaderException(CorruptImageError,
5577 "ImproperImageHeader");
5578 }
glennrp47b9dd52010-11-24 18:12:06 +00005579
cristy3ed852e2009-09-05 21:47:34 +00005580 else
5581 {
5582 short
5583 last_level;
5584
5585 /*
5586 Finished loop.
5587 */
5588 mng_info->loop_active[loop_level]=0;
5589 last_level=(-1);
5590 for (i=0; i < loop_level; i++)
5591 if (mng_info->loop_active[i] == 1)
5592 last_level=(short) i;
5593 loop_level=last_level;
5594 }
5595 }
5596 }
glennrp47b9dd52010-11-24 18:12:06 +00005597
cristy3ed852e2009-09-05 21:47:34 +00005598 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5599 continue;
5600 }
glennrp47b9dd52010-11-24 18:12:06 +00005601
cristy3ed852e2009-09-05 21:47:34 +00005602 if (memcmp(type,mng_CLON,4) == 0)
5603 {
5604 if (mng_info->clon_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005605 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005606 CoderError,"CLON is not implemented yet","`%s'",
5607 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005608
cristy3ed852e2009-09-05 21:47:34 +00005609 mng_info->clon_warning++;
5610 }
glennrp47b9dd52010-11-24 18:12:06 +00005611
cristy3ed852e2009-09-05 21:47:34 +00005612 if (memcmp(type,mng_MAGN,4) == 0)
5613 {
5614 png_uint_16
5615 magn_first,
5616 magn_last,
5617 magn_mb,
5618 magn_ml,
5619 magn_mr,
5620 magn_mt,
5621 magn_mx,
5622 magn_my,
5623 magn_methx,
5624 magn_methy;
5625
5626 if (length > 1)
5627 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005628
cristy3ed852e2009-09-05 21:47:34 +00005629 else
5630 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005631
cristy3ed852e2009-09-05 21:47:34 +00005632 if (length > 3)
5633 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005634
cristy3ed852e2009-09-05 21:47:34 +00005635 else
5636 magn_last=magn_first;
5637#ifndef MNG_OBJECT_BUFFERS
5638 if (magn_first || magn_last)
5639 if (mng_info->magn_warning == 0)
5640 {
cristyc82a27b2011-10-21 01:07:16 +00005641 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005642 GetMagickModule(),CoderError,
5643 "MAGN is not implemented yet for nonzero objects",
5644 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005645
cristy3ed852e2009-09-05 21:47:34 +00005646 mng_info->magn_warning++;
5647 }
5648#endif
5649 if (length > 4)
5650 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005651
cristy3ed852e2009-09-05 21:47:34 +00005652 else
5653 magn_methx=0;
5654
5655 if (length > 6)
5656 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005657
cristy3ed852e2009-09-05 21:47:34 +00005658 else
5659 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005660
cristy3ed852e2009-09-05 21:47:34 +00005661 if (magn_mx == 0)
5662 magn_mx=1;
5663
5664 if (length > 8)
5665 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005666
cristy3ed852e2009-09-05 21:47:34 +00005667 else
5668 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005669
cristy3ed852e2009-09-05 21:47:34 +00005670 if (magn_my == 0)
5671 magn_my=1;
5672
5673 if (length > 10)
5674 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005675
cristy3ed852e2009-09-05 21:47:34 +00005676 else
5677 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005678
cristy3ed852e2009-09-05 21:47:34 +00005679 if (magn_ml == 0)
5680 magn_ml=1;
5681
5682 if (length > 12)
5683 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005684
cristy3ed852e2009-09-05 21:47:34 +00005685 else
5686 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005687
cristy3ed852e2009-09-05 21:47:34 +00005688 if (magn_mr == 0)
5689 magn_mr=1;
5690
5691 if (length > 14)
5692 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005693
cristy3ed852e2009-09-05 21:47:34 +00005694 else
5695 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005696
cristy3ed852e2009-09-05 21:47:34 +00005697 if (magn_mt == 0)
5698 magn_mt=1;
5699
5700 if (length > 16)
5701 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005702
cristy3ed852e2009-09-05 21:47:34 +00005703 else
5704 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005705
cristy3ed852e2009-09-05 21:47:34 +00005706 if (magn_mb == 0)
5707 magn_mb=1;
5708
5709 if (length > 17)
5710 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005711
cristy3ed852e2009-09-05 21:47:34 +00005712 else
5713 magn_methy=magn_methx;
5714
glennrp47b9dd52010-11-24 18:12:06 +00005715
cristy3ed852e2009-09-05 21:47:34 +00005716 if (magn_methx > 5 || magn_methy > 5)
5717 if (mng_info->magn_warning == 0)
5718 {
cristyc82a27b2011-10-21 01:07:16 +00005719 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005720 GetMagickModule(),CoderError,
5721 "Unknown MAGN method in MNG datastream","`%s'",
5722 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005723
cristy3ed852e2009-09-05 21:47:34 +00005724 mng_info->magn_warning++;
5725 }
5726#ifdef MNG_OBJECT_BUFFERS
5727 /* Magnify existing objects in the range magn_first to magn_last */
5728#endif
5729 if (magn_first == 0 || magn_last == 0)
5730 {
5731 /* Save the magnification factors for object 0 */
5732 mng_info->magn_mb=magn_mb;
5733 mng_info->magn_ml=magn_ml;
5734 mng_info->magn_mr=magn_mr;
5735 mng_info->magn_mt=magn_mt;
5736 mng_info->magn_mx=magn_mx;
5737 mng_info->magn_my=magn_my;
5738 mng_info->magn_methx=magn_methx;
5739 mng_info->magn_methy=magn_methy;
5740 }
5741 }
glennrp47b9dd52010-11-24 18:12:06 +00005742
cristy3ed852e2009-09-05 21:47:34 +00005743 if (memcmp(type,mng_PAST,4) == 0)
5744 {
5745 if (mng_info->past_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005746 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005747 CoderError,"PAST is not implemented yet","`%s'",
5748 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005749
cristy3ed852e2009-09-05 21:47:34 +00005750 mng_info->past_warning++;
5751 }
glennrp47b9dd52010-11-24 18:12:06 +00005752
cristy3ed852e2009-09-05 21:47:34 +00005753 if (memcmp(type,mng_SHOW,4) == 0)
5754 {
5755 if (mng_info->show_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005756 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005757 CoderError,"SHOW is not implemented yet","`%s'",
5758 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005759
cristy3ed852e2009-09-05 21:47:34 +00005760 mng_info->show_warning++;
5761 }
glennrp47b9dd52010-11-24 18:12:06 +00005762
cristy3ed852e2009-09-05 21:47:34 +00005763 if (memcmp(type,mng_sBIT,4) == 0)
5764 {
5765 if (length < 4)
5766 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005767
cristy3ed852e2009-09-05 21:47:34 +00005768 else
5769 {
5770 mng_info->global_sbit.gray=p[0];
5771 mng_info->global_sbit.red=p[0];
5772 mng_info->global_sbit.green=p[1];
5773 mng_info->global_sbit.blue=p[2];
5774 mng_info->global_sbit.alpha=p[3];
5775 mng_info->have_global_sbit=MagickTrue;
5776 }
5777 }
5778 if (memcmp(type,mng_pHYs,4) == 0)
5779 {
5780 if (length > 8)
5781 {
5782 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005783 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005784 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005785 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005786 mng_info->global_phys_unit_type=p[8];
5787 mng_info->have_global_phys=MagickTrue;
5788 }
glennrp47b9dd52010-11-24 18:12:06 +00005789
cristy3ed852e2009-09-05 21:47:34 +00005790 else
5791 mng_info->have_global_phys=MagickFalse;
5792 }
5793 if (memcmp(type,mng_pHYg,4) == 0)
5794 {
5795 if (mng_info->phyg_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005796 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005797 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005798
cristy3ed852e2009-09-05 21:47:34 +00005799 mng_info->phyg_warning++;
5800 }
5801 if (memcmp(type,mng_BASI,4) == 0)
5802 {
5803 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005804
cristy3ed852e2009-09-05 21:47:34 +00005805 if (mng_info->basi_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005806 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005807 CoderError,"BASI is not implemented yet","`%s'",
5808 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005809
cristy3ed852e2009-09-05 21:47:34 +00005810 mng_info->basi_warning++;
5811#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005812 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005813 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005814 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005815 (p[6] << 8) | p[7]);
5816 basi_color_type=p[8];
5817 basi_compression_method=p[9];
5818 basi_filter_type=p[10];
5819 basi_interlace_method=p[11];
5820 if (length > 11)
5821 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005822
cristy3ed852e2009-09-05 21:47:34 +00005823 else
5824 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005825
cristy3ed852e2009-09-05 21:47:34 +00005826 if (length > 13)
5827 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005828
cristy3ed852e2009-09-05 21:47:34 +00005829 else
5830 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005831
cristy3ed852e2009-09-05 21:47:34 +00005832 if (length > 15)
5833 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005834
cristy3ed852e2009-09-05 21:47:34 +00005835 else
5836 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005837
cristy3ed852e2009-09-05 21:47:34 +00005838 if (length > 17)
5839 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005840
cristy3ed852e2009-09-05 21:47:34 +00005841 else
5842 {
5843 if (basi_sample_depth == 16)
5844 basi_alpha=65535L;
5845 else
5846 basi_alpha=255;
5847 }
glennrp47b9dd52010-11-24 18:12:06 +00005848
cristy3ed852e2009-09-05 21:47:34 +00005849 if (length > 19)
5850 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005851
cristy3ed852e2009-09-05 21:47:34 +00005852 else
5853 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005854
cristy3ed852e2009-09-05 21:47:34 +00005855#endif
5856 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5857 continue;
5858 }
glennrp47b9dd52010-11-24 18:12:06 +00005859
cristy3ed852e2009-09-05 21:47:34 +00005860 if (memcmp(type,mng_IHDR,4)
5861#if defined(JNG_SUPPORTED)
5862 && memcmp(type,mng_JHDR,4)
5863#endif
5864 )
5865 {
5866 /* Not an IHDR or JHDR chunk */
5867 if (length)
5868 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005869
cristy3ed852e2009-09-05 21:47:34 +00005870 continue;
5871 }
5872/* Process IHDR */
5873 if (logging != MagickFalse)
5874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5875 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005876
cristy3ed852e2009-09-05 21:47:34 +00005877 mng_info->exists[object_id]=MagickTrue;
5878 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005879
cristy3ed852e2009-09-05 21:47:34 +00005880 if (mng_info->invisible[object_id])
5881 {
5882 if (logging != MagickFalse)
5883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5884 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005885
cristy3ed852e2009-09-05 21:47:34 +00005886 skip_to_iend=MagickTrue;
5887 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5888 continue;
5889 }
5890#if defined(MNG_INSERT_LAYERS)
5891 if (length < 8)
5892 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005893
cristy8182b072010-05-30 20:10:53 +00005894 image_width=(size_t) mng_get_long(p);
5895 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005896#endif
5897 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5898
5899 /*
5900 Insert a transparent background layer behind the entire animation
5901 if it is not full screen.
5902 */
5903#if defined(MNG_INSERT_LAYERS)
5904 if (insert_layers && mng_type && first_mng_object)
5905 {
5906 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5907 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005908 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005909 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005910 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005911 {
cristy4c08aed2011-07-01 19:47:50 +00005912 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005913 {
5914 /*
5915 Allocate next image structure.
5916 */
cristy9950d572011-10-01 18:22:35 +00005917 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005918
cristy3ed852e2009-09-05 21:47:34 +00005919 if (GetNextImageInList(image) == (Image *) NULL)
5920 {
5921 image=DestroyImageList(image);
5922 MngInfoFreeStruct(mng_info,&have_mng_structure);
5923 return((Image *) NULL);
5924 }
glennrp47b9dd52010-11-24 18:12:06 +00005925
cristy3ed852e2009-09-05 21:47:34 +00005926 image=SyncNextImageInList(image);
5927 }
5928 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005929
cristy3ed852e2009-09-05 21:47:34 +00005930 if (term_chunk_found)
5931 {
5932 image->start_loop=MagickTrue;
5933 image->iterations=mng_iterations;
5934 term_chunk_found=MagickFalse;
5935 }
glennrp47b9dd52010-11-24 18:12:06 +00005936
cristy3ed852e2009-09-05 21:47:34 +00005937 else
5938 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005939
5940 /* Make a background rectangle. */
5941
cristy3ed852e2009-09-05 21:47:34 +00005942 image->delay=0;
5943 image->columns=mng_info->mng_width;
5944 image->rows=mng_info->mng_height;
5945 image->page.width=mng_info->mng_width;
5946 image->page.height=mng_info->mng_height;
5947 image->page.x=0;
5948 image->page.y=0;
5949 image->background_color=mng_background_color;
cristyea1a8aa2011-10-20 13:24:06 +00005950 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005951 if (logging != MagickFalse)
5952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005953 " Inserted transparent background layer, W=%.20g, H=%.20g",
5954 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005955 }
5956 }
5957 /*
5958 Insert a background layer behind the upcoming image if
5959 framing_mode is 3, and we haven't already inserted one.
5960 */
5961 if (insert_layers && (mng_info->framing_mode == 3) &&
5962 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5963 (simplicity & 0x08)))
5964 {
cristy4c08aed2011-07-01 19:47:50 +00005965 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005966 {
5967 /*
5968 Allocate next image structure.
5969 */
cristy9950d572011-10-01 18:22:35 +00005970 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005971
cristy3ed852e2009-09-05 21:47:34 +00005972 if (GetNextImageInList(image) == (Image *) NULL)
5973 {
5974 image=DestroyImageList(image);
5975 MngInfoFreeStruct(mng_info,&have_mng_structure);
5976 return((Image *) NULL);
5977 }
glennrp47b9dd52010-11-24 18:12:06 +00005978
cristy3ed852e2009-09-05 21:47:34 +00005979 image=SyncNextImageInList(image);
5980 }
glennrp0fe50b42010-11-16 03:52:51 +00005981
cristy3ed852e2009-09-05 21:47:34 +00005982 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005983
cristy3ed852e2009-09-05 21:47:34 +00005984 if (term_chunk_found)
5985 {
5986 image->start_loop=MagickTrue;
5987 image->iterations=mng_iterations;
5988 term_chunk_found=MagickFalse;
5989 }
glennrp0fe50b42010-11-16 03:52:51 +00005990
cristy3ed852e2009-09-05 21:47:34 +00005991 else
5992 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005993
cristy3ed852e2009-09-05 21:47:34 +00005994 image->delay=0;
5995 image->columns=subframe_width;
5996 image->rows=subframe_height;
5997 image->page.width=subframe_width;
5998 image->page.height=subframe_height;
5999 image->page.x=mng_info->clip.left;
6000 image->page.y=mng_info->clip.top;
6001 image->background_color=mng_background_color;
6002 image->matte=MagickFalse;
cristyea1a8aa2011-10-20 13:24:06 +00006003 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006004
cristy3ed852e2009-09-05 21:47:34 +00006005 if (logging != MagickFalse)
6006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00006007 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00006008 (double) mng_info->clip.left,(double) mng_info->clip.right,
6009 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00006010 }
6011#endif /* MNG_INSERT_LAYERS */
6012 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006013
cristy4c08aed2011-07-01 19:47:50 +00006014 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006015 {
6016 /*
6017 Allocate next image structure.
6018 */
cristy9950d572011-10-01 18:22:35 +00006019 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006020
cristy3ed852e2009-09-05 21:47:34 +00006021 if (GetNextImageInList(image) == (Image *) NULL)
6022 {
6023 image=DestroyImageList(image);
6024 MngInfoFreeStruct(mng_info,&have_mng_structure);
6025 return((Image *) NULL);
6026 }
glennrp47b9dd52010-11-24 18:12:06 +00006027
cristy3ed852e2009-09-05 21:47:34 +00006028 image=SyncNextImageInList(image);
6029 }
6030 mng_info->image=image;
6031 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6032 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006033
cristy3ed852e2009-09-05 21:47:34 +00006034 if (status == MagickFalse)
6035 break;
glennrp0fe50b42010-11-16 03:52:51 +00006036
cristy3ed852e2009-09-05 21:47:34 +00006037 if (term_chunk_found)
6038 {
6039 image->start_loop=MagickTrue;
6040 term_chunk_found=MagickFalse;
6041 }
glennrp0fe50b42010-11-16 03:52:51 +00006042
cristy3ed852e2009-09-05 21:47:34 +00006043 else
6044 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006045
cristy3ed852e2009-09-05 21:47:34 +00006046 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6047 {
6048 image->delay=frame_delay;
6049 frame_delay=default_frame_delay;
6050 }
glennrp0fe50b42010-11-16 03:52:51 +00006051
cristy3ed852e2009-09-05 21:47:34 +00006052 else
6053 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006054
cristy3ed852e2009-09-05 21:47:34 +00006055 image->page.width=mng_info->mng_width;
6056 image->page.height=mng_info->mng_height;
6057 image->page.x=mng_info->x_off[object_id];
6058 image->page.y=mng_info->y_off[object_id];
6059 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006060
cristy3ed852e2009-09-05 21:47:34 +00006061 /*
6062 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6063 */
glennrp47b9dd52010-11-24 18:12:06 +00006064
cristy3ed852e2009-09-05 21:47:34 +00006065 if (logging != MagickFalse)
6066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6067 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6068 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006069
cristybb503372010-05-27 20:51:26 +00006070 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006071
cristy3ed852e2009-09-05 21:47:34 +00006072 if (offset < 0)
6073 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6074 }
6075
6076 previous=image;
6077 mng_info->image=image;
6078 mng_info->mng_type=mng_type;
6079 mng_info->object_id=object_id;
6080
6081 if (memcmp(type,mng_IHDR,4) == 0)
6082 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006083
cristy3ed852e2009-09-05 21:47:34 +00006084#if defined(JNG_SUPPORTED)
6085 else
6086 image=ReadOneJNGImage(mng_info,image_info,exception);
6087#endif
6088
6089 if (image == (Image *) NULL)
6090 {
6091 if (IsImageObject(previous) != MagickFalse)
6092 {
6093 (void) DestroyImageList(previous);
6094 (void) CloseBlob(previous);
6095 }
glennrp47b9dd52010-11-24 18:12:06 +00006096
cristy3ed852e2009-09-05 21:47:34 +00006097 MngInfoFreeStruct(mng_info,&have_mng_structure);
6098 return((Image *) NULL);
6099 }
glennrp0fe50b42010-11-16 03:52:51 +00006100
cristy3ed852e2009-09-05 21:47:34 +00006101 if (image->columns == 0 || image->rows == 0)
6102 {
6103 (void) CloseBlob(image);
6104 image=DestroyImageList(image);
6105 MngInfoFreeStruct(mng_info,&have_mng_structure);
6106 return((Image *) NULL);
6107 }
glennrp0fe50b42010-11-16 03:52:51 +00006108
cristy3ed852e2009-09-05 21:47:34 +00006109 mng_info->image=image;
6110
6111 if (mng_type)
6112 {
6113 MngBox
6114 crop_box;
6115
6116 if (mng_info->magn_methx || mng_info->magn_methy)
6117 {
6118 png_uint_32
6119 magnified_height,
6120 magnified_width;
6121
6122 if (logging != MagickFalse)
6123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6124 " Processing MNG MAGN chunk");
6125
6126 if (mng_info->magn_methx == 1)
6127 {
6128 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006129
cristy3ed852e2009-09-05 21:47:34 +00006130 if (image->columns > 1)
6131 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006132
cristy3ed852e2009-09-05 21:47:34 +00006133 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006134 magnified_width += (png_uint_32)
6135 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006136 }
glennrp47b9dd52010-11-24 18:12:06 +00006137
cristy3ed852e2009-09-05 21:47:34 +00006138 else
6139 {
cristy4e5bc842010-06-09 13:56:01 +00006140 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006141
cristy3ed852e2009-09-05 21:47:34 +00006142 if (image->columns > 1)
6143 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006144
cristy3ed852e2009-09-05 21:47:34 +00006145 if (image->columns > 2)
6146 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006147
cristy3ed852e2009-09-05 21:47:34 +00006148 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006149 magnified_width += (png_uint_32)
6150 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006151 }
glennrp47b9dd52010-11-24 18:12:06 +00006152
cristy3ed852e2009-09-05 21:47:34 +00006153 if (mng_info->magn_methy == 1)
6154 {
6155 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006156
cristy3ed852e2009-09-05 21:47:34 +00006157 if (image->rows > 1)
6158 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006159
cristy3ed852e2009-09-05 21:47:34 +00006160 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006161 magnified_height += (png_uint_32)
6162 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006163 }
glennrp47b9dd52010-11-24 18:12:06 +00006164
cristy3ed852e2009-09-05 21:47:34 +00006165 else
6166 {
cristy4e5bc842010-06-09 13:56:01 +00006167 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006168
cristy3ed852e2009-09-05 21:47:34 +00006169 if (image->rows > 1)
6170 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006171
cristy3ed852e2009-09-05 21:47:34 +00006172 if (image->rows > 2)
6173 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006174
cristy3ed852e2009-09-05 21:47:34 +00006175 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006176 magnified_height += (png_uint_32)
6177 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006178 }
glennrp47b9dd52010-11-24 18:12:06 +00006179
cristy3ed852e2009-09-05 21:47:34 +00006180 if (magnified_height > image->rows ||
6181 magnified_width > image->columns)
6182 {
6183 Image
6184 *large_image;
6185
6186 int
6187 yy;
6188
cristy4c08aed2011-07-01 19:47:50 +00006189 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006190 *next,
6191 *prev;
6192
6193 png_uint_16
6194 magn_methx,
6195 magn_methy;
6196
cristy4c08aed2011-07-01 19:47:50 +00006197 ssize_t
6198 m,
6199 y;
6200
6201 register Quantum
6202 *n,
6203 *q;
6204
6205 register ssize_t
6206 x;
6207
glennrp47b9dd52010-11-24 18:12:06 +00006208 /* Allocate next image structure. */
6209
cristy3ed852e2009-09-05 21:47:34 +00006210 if (logging != MagickFalse)
6211 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6212 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006213
cristy9950d572011-10-01 18:22:35 +00006214 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006215
cristy3ed852e2009-09-05 21:47:34 +00006216 if (GetNextImageInList(image) == (Image *) NULL)
6217 {
6218 image=DestroyImageList(image);
6219 MngInfoFreeStruct(mng_info,&have_mng_structure);
6220 return((Image *) NULL);
6221 }
6222
6223 large_image=SyncNextImageInList(image);
6224
6225 large_image->columns=magnified_width;
6226 large_image->rows=magnified_height;
6227
6228 magn_methx=mng_info->magn_methx;
6229 magn_methy=mng_info->magn_methy;
6230
glennrp3faa9a32011-04-23 14:00:25 +00006231#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006232#define QM unsigned short
6233 if (magn_methx != 1 || magn_methy != 1)
6234 {
6235 /*
6236 Scale pixels to unsigned shorts to prevent
6237 overflow of intermediate values of interpolations
6238 */
cristybb503372010-05-27 20:51:26 +00006239 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006240 {
6241 q=GetAuthenticPixels(image,0,y,image->columns,1,
6242 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006243
cristybb503372010-05-27 20:51:26 +00006244 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006245 {
cristy4c08aed2011-07-01 19:47:50 +00006246 SetPixelRed(image,ScaleQuantumToShort(
6247 GetPixelRed(image,q)),q);
6248 SetPixelGreen(image,ScaleQuantumToShort(
6249 GetPixelGreen(image,q)),q);
6250 SetPixelBlue(image,ScaleQuantumToShort(
6251 GetPixelBlue(image,q)),q);
6252 SetPixelAlpha(image,ScaleQuantumToShort(
6253 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006254 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006255 }
glennrp47b9dd52010-11-24 18:12:06 +00006256
cristy3ed852e2009-09-05 21:47:34 +00006257 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6258 break;
6259 }
6260 }
6261#else
6262#define QM Quantum
6263#endif
6264
6265 if (image->matte != MagickFalse)
cristyea1a8aa2011-10-20 13:24:06 +00006266 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006267
cristy3ed852e2009-09-05 21:47:34 +00006268 else
6269 {
cristy4c08aed2011-07-01 19:47:50 +00006270 large_image->background_color.alpha=OpaqueAlpha;
cristyea1a8aa2011-10-20 13:24:06 +00006271 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006272
cristy3ed852e2009-09-05 21:47:34 +00006273 if (magn_methx == 4)
6274 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006275
cristy3ed852e2009-09-05 21:47:34 +00006276 if (magn_methx == 5)
6277 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006278
cristy3ed852e2009-09-05 21:47:34 +00006279 if (magn_methy == 4)
6280 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006281
cristy3ed852e2009-09-05 21:47:34 +00006282 if (magn_methy == 5)
6283 magn_methy=3;
6284 }
6285
6286 /* magnify the rows into the right side of the large image */
6287
6288 if (logging != MagickFalse)
6289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006290 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006291 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006292 yy=0;
cristy8a20fa02011-12-27 15:54:31 +00006293 length=(size_t) image->columns*GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00006294 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6295 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006296
cristy4c08aed2011-07-01 19:47:50 +00006297 if ((prev == (Quantum *) NULL) ||
6298 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006299 {
6300 image=DestroyImageList(image);
6301 MngInfoFreeStruct(mng_info,&have_mng_structure);
6302 ThrowReaderException(ResourceLimitError,
6303 "MemoryAllocationFailed");
6304 }
glennrp47b9dd52010-11-24 18:12:06 +00006305
cristy3ed852e2009-09-05 21:47:34 +00006306 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6307 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006308
cristybb503372010-05-27 20:51:26 +00006309 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006310 {
6311 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006312 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006313
cristybb503372010-05-27 20:51:26 +00006314 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6315 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006316
cristybb503372010-05-27 20:51:26 +00006317 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6318 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006319
cristybb503372010-05-27 20:51:26 +00006320 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006321 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006322
cristy3ed852e2009-09-05 21:47:34 +00006323 else
cristybb503372010-05-27 20:51:26 +00006324 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006325
cristy3ed852e2009-09-05 21:47:34 +00006326 n=prev;
6327 prev=next;
6328 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006329
cristybb503372010-05-27 20:51:26 +00006330 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006331 {
6332 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6333 exception);
6334 (void) CopyMagickMemory(next,n,length);
6335 }
glennrp47b9dd52010-11-24 18:12:06 +00006336
cristy3ed852e2009-09-05 21:47:34 +00006337 for (i=0; i < m; i++, yy++)
6338 {
cristy4c08aed2011-07-01 19:47:50 +00006339 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006340 *pixels;
6341
cristybb503372010-05-27 20:51:26 +00006342 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006343 pixels=prev;
6344 n=next;
6345 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006346 1,exception);
cristy97707062011-12-27 18:25:00 +00006347 q+=(large_image->columns-image->columns)*
6348 GetPixelChannels(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006349
cristybb503372010-05-27 20:51:26 +00006350 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006351 {
glennrpfd05d622011-02-25 04:10:33 +00006352 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006353 /*
6354 if (image->storage_class == PseudoClass)
6355 {
6356 }
6357 */
6358
6359 if (magn_methy <= 1)
6360 {
glennrpbb4f99d2011-05-22 11:13:17 +00006361 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006362 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
glennrp847370c2011-07-05 17:37:15 +00006363 SetPixelGreen(large_image,GetPixelGreen(image,
6364 pixels),q);
6365 SetPixelBlue(large_image,GetPixelBlue(image,
6366 pixels),q);
6367 SetPixelAlpha(large_image,GetPixelAlpha(image,
6368 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006369 }
glennrp47b9dd52010-11-24 18:12:06 +00006370
cristy3ed852e2009-09-05 21:47:34 +00006371 else if (magn_methy == 2 || magn_methy == 4)
6372 {
6373 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006374 {
glennrp847370c2011-07-05 17:37:15 +00006375 SetPixelRed(large_image,GetPixelRed(image,
6376 pixels),q);
6377 SetPixelGreen(large_image,GetPixelGreen(image,
6378 pixels),q);
6379 SetPixelBlue(large_image,GetPixelBlue(image,
6380 pixels),q);
6381 SetPixelAlpha(large_image,GetPixelAlpha(image,
6382 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006383 }
glennrp47b9dd52010-11-24 18:12:06 +00006384
cristy3ed852e2009-09-05 21:47:34 +00006385 else
6386 {
6387 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006388 SetPixelRed(large_image,((QM) (((ssize_t)
6389 (2*i*(GetPixelRed(image,n)
6390 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006391 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006392 +GetPixelRed(image,pixels)))),q);
6393 SetPixelGreen(large_image,((QM) (((ssize_t)
6394 (2*i*(GetPixelGreen(image,n)
6395 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006396 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006397 +GetPixelGreen(image,pixels)))),q);
6398 SetPixelBlue(large_image,((QM) (((ssize_t)
6399 (2*i*(GetPixelBlue(image,n)
6400 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006401 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006402 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006403
cristy3ed852e2009-09-05 21:47:34 +00006404 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006405 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6406 (2*i*(GetPixelAlpha(image,n)
6407 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006408 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006409 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006410 }
glennrp47b9dd52010-11-24 18:12:06 +00006411
cristy3ed852e2009-09-05 21:47:34 +00006412 if (magn_methy == 4)
6413 {
6414 /* Replicate nearest */
6415 if (i <= ((m+1) << 1))
glennrp847370c2011-07-05 17:37:15 +00006416 SetPixelAlpha(large_image,GetPixelAlpha(image,
6417 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006418 else
glennrp847370c2011-07-05 17:37:15 +00006419 SetPixelAlpha(large_image,GetPixelAlpha(image,
6420 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006421 }
6422 }
glennrp47b9dd52010-11-24 18:12:06 +00006423
cristy3ed852e2009-09-05 21:47:34 +00006424 else /* if (magn_methy == 3 || magn_methy == 5) */
6425 {
6426 /* Replicate nearest */
6427 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006428 {
glennrp847370c2011-07-05 17:37:15 +00006429 SetPixelRed(large_image,GetPixelRed(image,
6430 pixels),q);
6431 SetPixelGreen(large_image,GetPixelGreen(image,
6432 pixels),q);
6433 SetPixelBlue(large_image,GetPixelBlue(image,
6434 pixels),q);
6435 SetPixelAlpha(large_image,GetPixelAlpha(image,
6436 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006437 }
glennrp47b9dd52010-11-24 18:12:06 +00006438
cristy3ed852e2009-09-05 21:47:34 +00006439 else
glennrpbb4f99d2011-05-22 11:13:17 +00006440 {
cristy4c08aed2011-07-01 19:47:50 +00006441 SetPixelRed(large_image,GetPixelRed(image,n),q);
glennrp847370c2011-07-05 17:37:15 +00006442 SetPixelGreen(large_image,GetPixelGreen(image,n),
6443 q);
6444 SetPixelBlue(large_image,GetPixelBlue(image,n),
6445 q);
6446 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6447 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006448 }
glennrp47b9dd52010-11-24 18:12:06 +00006449
cristy3ed852e2009-09-05 21:47:34 +00006450 if (magn_methy == 5)
6451 {
cristy4c08aed2011-07-01 19:47:50 +00006452 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6453 (GetPixelAlpha(image,n)
6454 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006455 +m))/((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006456 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006457 }
6458 }
cristyed231572011-07-14 02:18:59 +00006459 n+=GetPixelChannels(image);
6460 q+=GetPixelChannels(large_image);
6461 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006462 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006463
cristy3ed852e2009-09-05 21:47:34 +00006464 if (SyncAuthenticPixels(large_image,exception) == 0)
6465 break;
glennrp47b9dd52010-11-24 18:12:06 +00006466
cristy3ed852e2009-09-05 21:47:34 +00006467 } /* i */
6468 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006469
cristy4c08aed2011-07-01 19:47:50 +00006470 prev=(Quantum *) RelinquishMagickMemory(prev);
6471 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006472
6473 length=image->columns;
6474
6475 if (logging != MagickFalse)
6476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6477 " Delete original image");
6478
6479 DeleteImageFromList(&image);
6480
6481 image=large_image;
6482
6483 mng_info->image=image;
6484
6485 /* magnify the columns */
6486 if (logging != MagickFalse)
6487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006488 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006489
cristybb503372010-05-27 20:51:26 +00006490 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006491 {
cristy4c08aed2011-07-01 19:47:50 +00006492 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006493 *pixels;
6494
6495 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristyed231572011-07-14 02:18:59 +00006496 pixels=q+(image->columns-length)*GetPixelChannels(image);
6497 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006498
cristybb503372010-05-27 20:51:26 +00006499 for (x=(ssize_t) (image->columns-length);
6500 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006501 {
cristyed231572011-07-14 02:18:59 +00006502 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006503
cristybb503372010-05-27 20:51:26 +00006504 if (x == (ssize_t) (image->columns-length))
6505 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006506
cristybb503372010-05-27 20:51:26 +00006507 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6508 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006509
cristybb503372010-05-27 20:51:26 +00006510 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6511 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006512
cristybb503372010-05-27 20:51:26 +00006513 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006514 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006515
cristy3ed852e2009-09-05 21:47:34 +00006516 else
cristybb503372010-05-27 20:51:26 +00006517 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006518
cristy3ed852e2009-09-05 21:47:34 +00006519 for (i=0; i < m; i++)
6520 {
6521 if (magn_methx <= 1)
6522 {
6523 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006524 SetPixelRed(image,GetPixelRed(image,pixels),q);
6525 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6526 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6527 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006528 }
glennrp47b9dd52010-11-24 18:12:06 +00006529
cristy3ed852e2009-09-05 21:47:34 +00006530 else if (magn_methx == 2 || magn_methx == 4)
6531 {
6532 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006533 {
cristy4c08aed2011-07-01 19:47:50 +00006534 SetPixelRed(image,GetPixelRed(image,pixels),q);
6535 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6536 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6537 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006538 }
glennrp47b9dd52010-11-24 18:12:06 +00006539
cristyed231572011-07-14 02:18:59 +00006540 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006541 else
6542 {
6543 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006544 SetPixelRed(image,(QM) ((2*i*(
6545 GetPixelRed(image,n)
6546 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006547 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006548 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006549
cristy4c08aed2011-07-01 19:47:50 +00006550 SetPixelGreen(image,(QM) ((2*i*(
6551 GetPixelGreen(image,n)
6552 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006553 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006554 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006555
cristy4c08aed2011-07-01 19:47:50 +00006556 SetPixelBlue(image,(QM) ((2*i*(
6557 GetPixelBlue(image,n)
6558 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006559 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006560 GetPixelBlue(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006561 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006562 SetPixelAlpha(image,(QM) ((2*i*(
6563 GetPixelAlpha(image,n)
6564 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006565 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006566 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006567 }
glennrp47b9dd52010-11-24 18:12:06 +00006568
cristy3ed852e2009-09-05 21:47:34 +00006569 if (magn_methx == 4)
6570 {
6571 /* Replicate nearest */
6572 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006573 {
cristy4c08aed2011-07-01 19:47:50 +00006574 SetPixelAlpha(image,
6575 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006576 }
cristy3ed852e2009-09-05 21:47:34 +00006577 else
glennrpbb4f99d2011-05-22 11:13:17 +00006578 {
cristy4c08aed2011-07-01 19:47:50 +00006579 SetPixelAlpha(image,
6580 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006581 }
cristy3ed852e2009-09-05 21:47:34 +00006582 }
6583 }
glennrp47b9dd52010-11-24 18:12:06 +00006584
cristy3ed852e2009-09-05 21:47:34 +00006585 else /* if (magn_methx == 3 || magn_methx == 5) */
6586 {
6587 /* Replicate nearest */
6588 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006589 {
cristy4c08aed2011-07-01 19:47:50 +00006590 SetPixelRed(image,GetPixelRed(image,pixels),q);
6591 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6592 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6593 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006594 }
glennrp47b9dd52010-11-24 18:12:06 +00006595
cristy3ed852e2009-09-05 21:47:34 +00006596 else
glennrpbb4f99d2011-05-22 11:13:17 +00006597 {
cristy4c08aed2011-07-01 19:47:50 +00006598 SetPixelRed(image,GetPixelRed(image,n),q);
6599 SetPixelGreen(image,GetPixelGreen(image,n),q);
6600 SetPixelBlue(image,GetPixelBlue(image,n),q);
6601 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006602 }
glennrp47b9dd52010-11-24 18:12:06 +00006603
cristy3ed852e2009-09-05 21:47:34 +00006604 if (magn_methx == 5)
6605 {
6606 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006607 SetPixelAlpha(image,
6608 (QM) ((2*i*( GetPixelAlpha(image,n)
6609 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006610 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006611 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006612 }
6613 }
cristyed231572011-07-14 02:18:59 +00006614 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006615 }
cristyed231572011-07-14 02:18:59 +00006616 n+=GetPixelChannels(image);
6617 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006618 }
glennrp47b9dd52010-11-24 18:12:06 +00006619
cristy3ed852e2009-09-05 21:47:34 +00006620 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6621 break;
6622 }
glennrp3faa9a32011-04-23 14:00:25 +00006623#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006624 if (magn_methx != 1 || magn_methy != 1)
6625 {
6626 /*
6627 Rescale pixels to Quantum
6628 */
cristybb503372010-05-27 20:51:26 +00006629 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006630 {
6631 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006632
cristybb503372010-05-27 20:51:26 +00006633 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006634 {
cristy4c08aed2011-07-01 19:47:50 +00006635 SetPixelRed(image,ScaleShortToQuantum(
6636 GetPixelRed(image,q)),q);
6637 SetPixelGreen(image,ScaleShortToQuantum(
6638 GetPixelGreen(image,q)),q);
6639 SetPixelBlue(image,ScaleShortToQuantum(
6640 GetPixelBlue(image,q)),q);
6641 SetPixelAlpha(image,ScaleShortToQuantum(
6642 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006643 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006644 }
glennrp47b9dd52010-11-24 18:12:06 +00006645
cristy3ed852e2009-09-05 21:47:34 +00006646 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6647 break;
6648 }
6649 }
6650#endif
6651 if (logging != MagickFalse)
6652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6653 " Finished MAGN processing");
6654 }
6655 }
6656
6657 /*
6658 Crop_box is with respect to the upper left corner of the MNG.
6659 */
6660 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6661 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6662 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6663 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6664 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6665 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6666 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6667 if ((crop_box.left != (mng_info->image_box.left
6668 +mng_info->x_off[object_id])) ||
6669 (crop_box.right != (mng_info->image_box.right
6670 +mng_info->x_off[object_id])) ||
6671 (crop_box.top != (mng_info->image_box.top
6672 +mng_info->y_off[object_id])) ||
6673 (crop_box.bottom != (mng_info->image_box.bottom
6674 +mng_info->y_off[object_id])))
6675 {
6676 if (logging != MagickFalse)
6677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6678 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006679
cristy3ed852e2009-09-05 21:47:34 +00006680 if ((crop_box.left < crop_box.right) &&
6681 (crop_box.top < crop_box.bottom))
6682 {
6683 Image
6684 *im;
6685
6686 RectangleInfo
6687 crop_info;
6688
6689 /*
6690 Crop_info is with respect to the upper left corner of
6691 the image.
6692 */
6693 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6694 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006695 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6696 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006697 image->page.width=image->columns;
6698 image->page.height=image->rows;
6699 image->page.x=0;
6700 image->page.y=0;
6701 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006702
cristy3ed852e2009-09-05 21:47:34 +00006703 if (im != (Image *) NULL)
6704 {
6705 image->columns=im->columns;
6706 image->rows=im->rows;
6707 im=DestroyImage(im);
6708 image->page.width=image->columns;
6709 image->page.height=image->rows;
6710 image->page.x=crop_box.left;
6711 image->page.y=crop_box.top;
6712 }
6713 }
glennrp47b9dd52010-11-24 18:12:06 +00006714
cristy3ed852e2009-09-05 21:47:34 +00006715 else
6716 {
6717 /*
6718 No pixels in crop area. The MNG spec still requires
6719 a layer, though, so make a single transparent pixel in
6720 the top left corner.
6721 */
6722 image->columns=1;
6723 image->rows=1;
6724 image->colors=2;
cristyea1a8aa2011-10-20 13:24:06 +00006725 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006726 image->page.width=1;
6727 image->page.height=1;
6728 image->page.x=0;
6729 image->page.y=0;
6730 }
6731 }
6732#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6733 image=mng_info->image;
6734#endif
6735 }
6736
glennrp2b013e42010-11-24 16:55:50 +00006737#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6738 /* PNG does not handle depths greater than 16 so reduce it even
glennrpcc5d45b2012-01-06 04:06:10 +00006739 * if lossy.
glennrp2b013e42010-11-24 16:55:50 +00006740 */
6741 if (image->depth > 16)
6742 image->depth=16;
6743#endif
6744
glennrp3faa9a32011-04-23 14:00:25 +00006745#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00006746 if (image->depth > 8)
6747 {
6748 /* To do: fill low byte properly */
6749 image->depth=16;
6750 }
6751
cristyc82a27b2011-10-21 01:07:16 +00006752 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00006753 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006754#endif
glennrpd6afd542010-11-19 01:53:05 +00006755
cristy3ed852e2009-09-05 21:47:34 +00006756 if (image_info->number_scenes != 0)
6757 {
6758 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006759 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006760 break;
6761 }
glennrpd6afd542010-11-19 01:53:05 +00006762
cristy3ed852e2009-09-05 21:47:34 +00006763 if (logging != MagickFalse)
6764 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6765 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006766
cristy3ed852e2009-09-05 21:47:34 +00006767 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006768
cristy3ed852e2009-09-05 21:47:34 +00006769 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006770
cristy3ed852e2009-09-05 21:47:34 +00006771 if (logging != MagickFalse)
6772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6773 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006774
cristy3ed852e2009-09-05 21:47:34 +00006775#if defined(MNG_INSERT_LAYERS)
6776 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6777 (mng_info->mng_height))
6778 {
6779 /*
6780 Insert a background layer if nothing else was found.
6781 */
6782 if (logging != MagickFalse)
6783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6784 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006785
cristy4c08aed2011-07-01 19:47:50 +00006786 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006787 {
6788 /*
6789 Allocate next image structure.
6790 */
cristy9950d572011-10-01 18:22:35 +00006791 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006792 if (GetNextImageInList(image) == (Image *) NULL)
6793 {
6794 image=DestroyImageList(image);
6795 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006796
cristy3ed852e2009-09-05 21:47:34 +00006797 if (logging != MagickFalse)
6798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6799 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006800
cristy3ed852e2009-09-05 21:47:34 +00006801 return((Image *) NULL);
6802 }
6803 image=SyncNextImageInList(image);
6804 }
6805 image->columns=mng_info->mng_width;
6806 image->rows=mng_info->mng_height;
6807 image->page.width=mng_info->mng_width;
6808 image->page.height=mng_info->mng_height;
6809 image->page.x=0;
6810 image->page.y=0;
6811 image->background_color=mng_background_color;
6812 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006813
cristy3ed852e2009-09-05 21:47:34 +00006814 if (image_info->ping == MagickFalse)
cristyea1a8aa2011-10-20 13:24:06 +00006815 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006816
cristy3ed852e2009-09-05 21:47:34 +00006817 mng_info->image_found++;
6818 }
6819#endif
6820 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006821
cristy3ed852e2009-09-05 21:47:34 +00006822 if (mng_iterations == 1)
6823 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006824
cristy3ed852e2009-09-05 21:47:34 +00006825 while (GetPreviousImageInList(image) != (Image *) NULL)
6826 {
6827 image_count++;
6828 if (image_count > 10*mng_info->image_found)
6829 {
6830 if (logging != MagickFalse)
6831 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006832
cristyc82a27b2011-10-21 01:07:16 +00006833 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006834 CoderError,"Linked list is corrupted, beginning of list not found",
6835 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006836
cristy3ed852e2009-09-05 21:47:34 +00006837 return((Image *) NULL);
6838 }
glennrp0fe50b42010-11-16 03:52:51 +00006839
cristy3ed852e2009-09-05 21:47:34 +00006840 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006841
cristy3ed852e2009-09-05 21:47:34 +00006842 if (GetNextImageInList(image) == (Image *) NULL)
6843 {
6844 if (logging != MagickFalse)
6845 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006846
cristyc82a27b2011-10-21 01:07:16 +00006847 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006848 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6849 image_info->filename);
6850 }
6851 }
glennrp47b9dd52010-11-24 18:12:06 +00006852
cristy3ed852e2009-09-05 21:47:34 +00006853 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6854 GetNextImageInList(image) ==
6855 (Image *) NULL)
6856 {
6857 if (logging != MagickFalse)
6858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6859 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006860
cristyc82a27b2011-10-21 01:07:16 +00006861 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006862 CoderError,"image->next for first image is NULL but shouldn't be.",
6863 "`%s'",image_info->filename);
6864 }
glennrp47b9dd52010-11-24 18:12:06 +00006865
cristy3ed852e2009-09-05 21:47:34 +00006866 if (mng_info->image_found == 0)
6867 {
6868 if (logging != MagickFalse)
6869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6870 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006871
cristyc82a27b2011-10-21 01:07:16 +00006872 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006873 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006874
cristy3ed852e2009-09-05 21:47:34 +00006875 if (image != (Image *) NULL)
6876 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006877
cristy3ed852e2009-09-05 21:47:34 +00006878 MngInfoFreeStruct(mng_info,&have_mng_structure);
6879 return((Image *) NULL);
6880 }
6881
6882 if (mng_info->ticks_per_second)
6883 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6884 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006885
cristy3ed852e2009-09-05 21:47:34 +00006886 else
6887 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006888
cristy3ed852e2009-09-05 21:47:34 +00006889 /* Find final nonzero image delay */
6890 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006891
cristy3ed852e2009-09-05 21:47:34 +00006892 while (GetNextImageInList(image) != (Image *) NULL)
6893 {
6894 if (image->delay)
6895 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006896
cristy3ed852e2009-09-05 21:47:34 +00006897 image=GetNextImageInList(image);
6898 }
glennrp0fe50b42010-11-16 03:52:51 +00006899
cristy3ed852e2009-09-05 21:47:34 +00006900 if (final_delay < final_image_delay)
6901 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006902
cristy3ed852e2009-09-05 21:47:34 +00006903 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006904
cristy3ed852e2009-09-05 21:47:34 +00006905 if (logging != MagickFalse)
6906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006907 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6908 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006909
cristy3ed852e2009-09-05 21:47:34 +00006910 if (logging != MagickFalse)
6911 {
6912 int
6913 scene;
6914
6915 scene=0;
6916 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006917
cristy3ed852e2009-09-05 21:47:34 +00006918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6919 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006920
cristy3ed852e2009-09-05 21:47:34 +00006921 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006922 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006923
cristy3ed852e2009-09-05 21:47:34 +00006924 while (GetNextImageInList(image) != (Image *) NULL)
6925 {
6926 image=GetNextImageInList(image);
6927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006928 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006929 }
6930 }
6931
6932 image=GetFirstImageInList(image);
6933#ifdef MNG_COALESCE_LAYERS
6934 if (insert_layers)
6935 {
6936 Image
6937 *next_image,
6938 *next;
6939
cristybb503372010-05-27 20:51:26 +00006940 size_t
cristy3ed852e2009-09-05 21:47:34 +00006941 scene;
6942
6943 if (logging != MagickFalse)
6944 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006945
cristy3ed852e2009-09-05 21:47:34 +00006946 scene=image->scene;
cristyc82a27b2011-10-21 01:07:16 +00006947 next_image=CoalesceImages(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006948
cristy3ed852e2009-09-05 21:47:34 +00006949 if (next_image == (Image *) NULL)
6950 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006951
cristy3ed852e2009-09-05 21:47:34 +00006952 image=DestroyImageList(image);
6953 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006954
cristy3ed852e2009-09-05 21:47:34 +00006955 for (next=image; next != (Image *) NULL; next=next_image)
6956 {
6957 next->page.width=mng_info->mng_width;
6958 next->page.height=mng_info->mng_height;
6959 next->page.x=0;
6960 next->page.y=0;
6961 next->scene=scene++;
6962 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006963
cristy3ed852e2009-09-05 21:47:34 +00006964 if (next_image == (Image *) NULL)
6965 break;
glennrp47b9dd52010-11-24 18:12:06 +00006966
cristy3ed852e2009-09-05 21:47:34 +00006967 if (next->delay == 0)
6968 {
6969 scene--;
6970 next_image->previous=GetPreviousImageInList(next);
6971 if (GetPreviousImageInList(next) == (Image *) NULL)
6972 image=next_image;
6973 else
6974 next->previous->next=next_image;
6975 next=DestroyImage(next);
6976 }
6977 }
6978 }
6979#endif
6980
6981 while (GetNextImageInList(image) != (Image *) NULL)
6982 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006983
cristy3ed852e2009-09-05 21:47:34 +00006984 image->dispose=BackgroundDispose;
6985
6986 if (logging != MagickFalse)
6987 {
6988 int
6989 scene;
6990
6991 scene=0;
6992 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006993
cristy3ed852e2009-09-05 21:47:34 +00006994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6995 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006996
cristy3ed852e2009-09-05 21:47:34 +00006997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006998 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6999 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00007000
cristy3ed852e2009-09-05 21:47:34 +00007001 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00007002 {
7003 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007004
cristyf2faecf2010-05-28 19:19:36 +00007005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007006 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7007 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00007008 }
7009 }
glennrp47b9dd52010-11-24 18:12:06 +00007010
cristy3ed852e2009-09-05 21:47:34 +00007011 image=GetFirstImageInList(image);
7012 MngInfoFreeStruct(mng_info,&have_mng_structure);
7013 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00007014
cristy3ed852e2009-09-05 21:47:34 +00007015 if (logging != MagickFalse)
7016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00007017
cristy3ed852e2009-09-05 21:47:34 +00007018 return(GetFirstImageInList(image));
7019}
glennrp25c1e2b2010-03-25 01:39:56 +00007020#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007021static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7022{
7023 printf("Your PNG library is too old: You have libpng-%s\n",
7024 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00007025
cristy3ed852e2009-09-05 21:47:34 +00007026 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7027 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007028
cristy3ed852e2009-09-05 21:47:34 +00007029 return(Image *) NULL;
7030}
glennrp47b9dd52010-11-24 18:12:06 +00007031
cristy3ed852e2009-09-05 21:47:34 +00007032static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7033{
7034 return(ReadPNGImage(image_info,exception));
7035}
glennrp25c1e2b2010-03-25 01:39:56 +00007036#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007037#endif
7038
7039/*
7040%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7041% %
7042% %
7043% %
7044% R e g i s t e r P N G I m a g e %
7045% %
7046% %
7047% %
7048%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7049%
7050% RegisterPNGImage() adds properties for the PNG image format to
7051% the list of supported formats. The properties include the image format
7052% tag, a method to read and/or write the format, whether the format
7053% supports the saving of more than one frame to the same file or blob,
7054% whether the format supports native in-memory I/O, and a brief
7055% description of the format.
7056%
7057% The format of the RegisterPNGImage method is:
7058%
cristybb503372010-05-27 20:51:26 +00007059% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007060%
7061*/
cristybb503372010-05-27 20:51:26 +00007062ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007063{
7064 char
7065 version[MaxTextExtent];
7066
7067 MagickInfo
7068 *entry;
7069
7070 static const char
7071 *PNGNote=
7072 {
7073 "See http://www.libpng.org/ for details about the PNG format."
7074 },
glennrp47b9dd52010-11-24 18:12:06 +00007075
cristy3ed852e2009-09-05 21:47:34 +00007076 *JNGNote=
7077 {
7078 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7079 "format."
7080 },
glennrp47b9dd52010-11-24 18:12:06 +00007081
cristy3ed852e2009-09-05 21:47:34 +00007082 *MNGNote=
7083 {
7084 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7085 "format."
7086 };
7087
7088 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007089
cristy3ed852e2009-09-05 21:47:34 +00007090#if defined(PNG_LIBPNG_VER_STRING)
7091 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7092 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007093
cristy3ed852e2009-09-05 21:47:34 +00007094 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7095 {
7096 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7097 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7098 MaxTextExtent);
7099 }
7100#endif
glennrp47b9dd52010-11-24 18:12:06 +00007101
cristy3ed852e2009-09-05 21:47:34 +00007102 entry=SetMagickInfo("MNG");
7103 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007104
cristy3ed852e2009-09-05 21:47:34 +00007105#if defined(MAGICKCORE_PNG_DELEGATE)
7106 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7107 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7108#endif
glennrp47b9dd52010-11-24 18:12:06 +00007109
cristy3ed852e2009-09-05 21:47:34 +00007110 entry->magick=(IsImageFormatHandler *) IsMNG;
7111 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007112
cristy3ed852e2009-09-05 21:47:34 +00007113 if (*version != '\0')
7114 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007115
cristy3ed852e2009-09-05 21:47:34 +00007116 entry->module=ConstantString("PNG");
7117 entry->note=ConstantString(MNGNote);
7118 (void) RegisterMagickInfo(entry);
7119
7120 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007121
cristy3ed852e2009-09-05 21:47:34 +00007122#if defined(MAGICKCORE_PNG_DELEGATE)
7123 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7124 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7125#endif
glennrp47b9dd52010-11-24 18:12:06 +00007126
cristy3ed852e2009-09-05 21:47:34 +00007127 entry->magick=(IsImageFormatHandler *) IsPNG;
7128 entry->adjoin=MagickFalse;
7129 entry->description=ConstantString("Portable Network Graphics");
7130 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007131
cristy3ed852e2009-09-05 21:47:34 +00007132 if (*version != '\0')
7133 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007134
cristy3ed852e2009-09-05 21:47:34 +00007135 entry->note=ConstantString(PNGNote);
7136 (void) RegisterMagickInfo(entry);
7137
7138 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007139
cristy3ed852e2009-09-05 21:47:34 +00007140#if defined(MAGICKCORE_PNG_DELEGATE)
7141 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7142 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7143#endif
glennrp47b9dd52010-11-24 18:12:06 +00007144
cristy3ed852e2009-09-05 21:47:34 +00007145 entry->magick=(IsImageFormatHandler *) IsPNG;
7146 entry->adjoin=MagickFalse;
7147 entry->description=ConstantString(
7148 "8-bit indexed with optional binary transparency");
7149 entry->module=ConstantString("PNG");
7150 (void) RegisterMagickInfo(entry);
7151
7152 entry=SetMagickInfo("PNG24");
7153 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007154
cristy3ed852e2009-09-05 21:47:34 +00007155#if defined(ZLIB_VERSION)
7156 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7157 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007158
cristy3ed852e2009-09-05 21:47:34 +00007159 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7160 {
7161 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7162 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7163 }
7164#endif
glennrp47b9dd52010-11-24 18:12:06 +00007165
cristy3ed852e2009-09-05 21:47:34 +00007166 if (*version != '\0')
7167 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007168
cristy3ed852e2009-09-05 21:47:34 +00007169#if defined(MAGICKCORE_PNG_DELEGATE)
7170 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7171 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7172#endif
glennrp47b9dd52010-11-24 18:12:06 +00007173
cristy3ed852e2009-09-05 21:47:34 +00007174 entry->magick=(IsImageFormatHandler *) IsPNG;
7175 entry->adjoin=MagickFalse;
7176 entry->description=ConstantString("opaque 24-bit RGB");
7177 entry->module=ConstantString("PNG");
7178 (void) RegisterMagickInfo(entry);
7179
7180 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007181
cristy3ed852e2009-09-05 21:47:34 +00007182#if defined(MAGICKCORE_PNG_DELEGATE)
7183 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7184 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7185#endif
glennrp47b9dd52010-11-24 18:12:06 +00007186
cristy3ed852e2009-09-05 21:47:34 +00007187 entry->magick=(IsImageFormatHandler *) IsPNG;
7188 entry->adjoin=MagickFalse;
7189 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7190 entry->module=ConstantString("PNG");
7191 (void) RegisterMagickInfo(entry);
7192
7193 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007194
cristy3ed852e2009-09-05 21:47:34 +00007195#if defined(JNG_SUPPORTED)
7196#if defined(MAGICKCORE_PNG_DELEGATE)
7197 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7198 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7199#endif
7200#endif
glennrp47b9dd52010-11-24 18:12:06 +00007201
cristy3ed852e2009-09-05 21:47:34 +00007202 entry->magick=(IsImageFormatHandler *) IsJNG;
7203 entry->adjoin=MagickFalse;
7204 entry->description=ConstantString("JPEG Network Graphics");
7205 entry->module=ConstantString("PNG");
7206 entry->note=ConstantString(JNGNote);
7207 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007208
cristy18b17442009-10-25 18:36:48 +00007209#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007210 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007211#endif
glennrp47b9dd52010-11-24 18:12:06 +00007212
cristy3ed852e2009-09-05 21:47:34 +00007213 return(MagickImageCoderSignature);
7214}
7215
7216/*
7217%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7218% %
7219% %
7220% %
7221% U n r e g i s t e r P N G I m a g e %
7222% %
7223% %
7224% %
7225%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7226%
7227% UnregisterPNGImage() removes format registrations made by the
7228% PNG module from the list of supported formats.
7229%
7230% The format of the UnregisterPNGImage method is:
7231%
7232% UnregisterPNGImage(void)
7233%
7234*/
7235ModuleExport void UnregisterPNGImage(void)
7236{
7237 (void) UnregisterMagickInfo("MNG");
7238 (void) UnregisterMagickInfo("PNG");
7239 (void) UnregisterMagickInfo("PNG8");
7240 (void) UnregisterMagickInfo("PNG24");
7241 (void) UnregisterMagickInfo("PNG32");
7242 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007243
cristy3ed852e2009-09-05 21:47:34 +00007244#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007245 if (ping_semaphore != (SemaphoreInfo *) NULL)
7246 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007247#endif
7248}
7249
7250#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007251#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007252/*
7253%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7254% %
7255% %
7256% %
7257% W r i t e M N G I m a g e %
7258% %
7259% %
7260% %
7261%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7262%
7263% WriteMNGImage() writes an image in the Portable Network Graphics
7264% Group's "Multiple-image Network Graphics" encoded image format.
7265%
7266% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7267%
7268% The format of the WriteMNGImage method is:
7269%
cristy1e178e72011-08-28 19:44:34 +00007270% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7271% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007272%
7273% A description of each parameter follows.
7274%
7275% o image_info: the image info.
7276%
7277% o image: The image.
7278%
cristy1e178e72011-08-28 19:44:34 +00007279% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007280%
7281% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7282% "To do" under ReadPNGImage):
7283%
cristy3ed852e2009-09-05 21:47:34 +00007284% Preserve all unknown and not-yet-handled known chunks found in input
7285% PNG file and copy them into output PNG files according to the PNG
7286% copying rules.
7287%
7288% Write the iCCP chunk at MNG level when (icc profile length > 0)
7289%
7290% Improve selection of color type (use indexed-colour or indexed-colour
7291% with tRNS when 256 or fewer unique RGBA values are present).
7292%
7293% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7294% This will be complicated if we limit ourselves to generating MNG-LC
7295% files. For now we ignore disposal method 3 and simply overlay the next
7296% image on it.
7297%
7298% Check for identical PLTE's or PLTE/tRNS combinations and use a
7299% global MNG PLTE or PLTE/tRNS combination when appropriate.
7300% [mostly done 15 June 1999 but still need to take care of tRNS]
7301%
7302% Check for identical sRGB and replace with a global sRGB (and remove
7303% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7304% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7305% local gAMA/cHRM with local sRGB if appropriate).
7306%
7307% Check for identical sBIT chunks and write global ones.
7308%
7309% Provide option to skip writing the signature tEXt chunks.
7310%
7311% Use signatures to detect identical objects and reuse the first
7312% instance of such objects instead of writing duplicate objects.
7313%
7314% Use a smaller-than-32k value of compression window size when
7315% appropriate.
7316%
7317% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7318% ancillary text chunks and save profiles.
7319%
7320% Provide an option to force LC files (to ensure exact framing rate)
7321% instead of VLC.
7322%
7323% Provide an option to force VLC files instead of LC, even when offsets
7324% are present. This will involve expanding the embedded images with a
7325% transparent region at the top and/or left.
7326*/
7327
cristy3ed852e2009-09-05 21:47:34 +00007328static void
glennrpcf002022011-01-30 02:38:15 +00007329Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007330 png_info *ping_info, unsigned char *profile_type, unsigned char
7331 *profile_description, unsigned char *profile_data, png_uint_32 length)
7332{
cristy3ed852e2009-09-05 21:47:34 +00007333 png_textp
7334 text;
7335
cristybb503372010-05-27 20:51:26 +00007336 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007337 i;
7338
7339 unsigned char
7340 *sp;
7341
7342 png_charp
7343 dp;
7344
7345 png_uint_32
7346 allocated_length,
7347 description_length;
7348
7349 unsigned char
7350 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007351
cristy3ed852e2009-09-05 21:47:34 +00007352 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7353 return;
7354
7355 if (image_info->verbose)
7356 {
glennrp0fe50b42010-11-16 03:52:51 +00007357 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7358 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007359 }
glennrp0fe50b42010-11-16 03:52:51 +00007360
cristy3ed852e2009-09-05 21:47:34 +00007361 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7362 description_length=(png_uint_32) strlen((const char *) profile_description);
7363 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7364 + description_length);
7365 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7366 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7367 text[0].key[0]='\0';
7368 (void) ConcatenateMagickString(text[0].key,
7369 "Raw profile type ",MaxTextExtent);
7370 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7371 sp=profile_data;
7372 dp=text[0].text;
7373 *dp++='\n';
7374 (void) CopyMagickString(dp,(const char *) profile_description,
7375 allocated_length);
7376 dp+=description_length;
7377 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007378 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007379 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007380 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007381
cristybb503372010-05-27 20:51:26 +00007382 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007383 {
7384 if (i%36 == 0)
7385 *dp++='\n';
7386 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7387 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7388 }
glennrp47b9dd52010-11-24 18:12:06 +00007389
cristy3ed852e2009-09-05 21:47:34 +00007390 *dp++='\n';
7391 *dp='\0';
7392 text[0].text_length=(png_size_t) (dp-text[0].text);
7393 text[0].compression=image_info->compression == NoCompression ||
7394 (image_info->compression == UndefinedCompression &&
7395 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007396
cristy3ed852e2009-09-05 21:47:34 +00007397 if (text[0].text_length <= allocated_length)
7398 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007399
cristy3ed852e2009-09-05 21:47:34 +00007400 png_free(ping,text[0].text);
7401 png_free(ping,text[0].key);
7402 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007403}
7404
glennrpcf002022011-01-30 02:38:15 +00007405static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007406 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007407{
7408 char
7409 *name;
7410
7411 const StringInfo
7412 *profile;
7413
7414 unsigned char
7415 *data;
7416
7417 png_uint_32 length;
7418
7419 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007420
7421 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7422 {
cristy3ed852e2009-09-05 21:47:34 +00007423 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007424
cristy3ed852e2009-09-05 21:47:34 +00007425 if (profile != (const StringInfo *) NULL)
7426 {
7427 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007428 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007429
glennrp47b9dd52010-11-24 18:12:06 +00007430 if (LocaleNCompare(name,string,11) == 0)
7431 {
7432 if (logging != MagickFalse)
7433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7434 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007435
glennrpcf002022011-01-30 02:38:15 +00007436 ping_profile=CloneStringInfo(profile);
7437 data=GetStringInfoDatum(ping_profile),
7438 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007439 data[4]=data[3];
7440 data[3]=data[2];
7441 data[2]=data[1];
7442 data[1]=data[0];
7443 (void) WriteBlobMSBULong(image,length-5); /* data length */
7444 (void) WriteBlob(image,length-1,data+1);
7445 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007446 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007447 }
cristy3ed852e2009-09-05 21:47:34 +00007448 }
glennrp47b9dd52010-11-24 18:12:06 +00007449
cristy3ed852e2009-09-05 21:47:34 +00007450 name=GetNextImageProfile(image);
7451 }
glennrp47b9dd52010-11-24 18:12:06 +00007452
cristy3ed852e2009-09-05 21:47:34 +00007453 return(MagickTrue);
7454}
7455
glennrpb9cfe272010-12-21 15:08:06 +00007456
cristy3ed852e2009-09-05 21:47:34 +00007457/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007458static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +00007459 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007460{
7461 Image
7462 *image;
7463
7464 ImageInfo
7465 *image_info;
7466
cristy3ed852e2009-09-05 21:47:34 +00007467 char
7468 s[2];
7469
7470 const char
7471 *name,
7472 *property,
7473 *value;
7474
7475 const StringInfo
7476 *profile;
7477
cristy3ed852e2009-09-05 21:47:34 +00007478 int
cristy3ed852e2009-09-05 21:47:34 +00007479 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007480 pass;
7481
glennrpe9c26dc2010-05-30 01:56:35 +00007482 png_byte
7483 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007484
glennrp39992b42010-11-14 00:03:43 +00007485 png_color
7486 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007487
glennrp5af765f2010-03-30 11:12:18 +00007488 png_color_16
7489 ping_background,
7490 ping_trans_color;
7491
cristy3ed852e2009-09-05 21:47:34 +00007492 png_info
7493 *ping_info;
7494
7495 png_struct
7496 *ping;
7497
glennrp5af765f2010-03-30 11:12:18 +00007498 png_uint_32
7499 ping_height,
7500 ping_width;
7501
cristybb503372010-05-27 20:51:26 +00007502 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007503 y;
7504
7505 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007506 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007507 logging,
glennrp58e01762011-01-07 15:28:54 +00007508 matte,
7509
glennrpda8f3a72011-02-27 23:54:12 +00007510 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007511 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007512 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007513 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007514 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007515 ping_have_bKGD,
7516 ping_have_pHYs,
7517 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007518
7519 ping_exclude_bKGD,
7520 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007521 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007522 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007523 ping_exclude_gAMA,
7524 ping_exclude_iCCP,
7525 /* ping_exclude_iTXt, */
7526 ping_exclude_oFFs,
7527 ping_exclude_pHYs,
7528 ping_exclude_sRGB,
7529 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007530 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007531 ping_exclude_vpAg,
7532 ping_exclude_zCCP, /* hex-encoded iCCP */
7533 ping_exclude_zTXt,
7534
glennrp8d3d6e52011-04-19 04:39:51 +00007535 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007536 ping_need_colortype_warning,
7537
glennrp82b3c532011-03-22 19:20:54 +00007538 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007539 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007540 tried_333,
7541 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007542
7543 QuantumInfo
7544 *quantum_info;
7545
cristyc82a27b2011-10-21 01:07:16 +00007546 PNGErrorInfo
7547 error_info;
7548
cristybb503372010-05-27 20:51:26 +00007549 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007550 i,
7551 x;
7552
7553 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007554 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007555
glennrp5af765f2010-03-30 11:12:18 +00007556 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007557 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007558 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007559 ping_color_type,
7560 ping_interlace_method,
7561 ping_compression_method,
7562 ping_filter_method,
7563 ping_num_trans;
7564
cristybb503372010-05-27 20:51:26 +00007565 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007566 image_depth,
7567 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007568
cristybb503372010-05-27 20:51:26 +00007569 size_t
cristy3ed852e2009-09-05 21:47:34 +00007570 quality,
7571 rowbytes,
7572 save_image_depth;
7573
glennrpdfd70802010-11-14 01:23:35 +00007574 int
glennrpfd05d622011-02-25 04:10:33 +00007575 j,
glennrpf09bded2011-01-08 01:15:59 +00007576 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007577 number_opaque,
7578 number_semitransparent,
7579 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007580 ping_pHYs_unit_type;
7581
7582 png_uint_32
7583 ping_pHYs_x_resolution,
7584 ping_pHYs_y_resolution;
7585
cristy3ed852e2009-09-05 21:47:34 +00007586 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007587 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007588
cristyc82a27b2011-10-21 01:07:16 +00007589 image = CloneImage(IMimage,0,0,MagickFalse,exception);
glennrpb9cfe272010-12-21 15:08:06 +00007590 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007591 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007592 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007593
cristy3ed852e2009-09-05 21:47:34 +00007594#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007595 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007596#endif
7597
glennrp5af765f2010-03-30 11:12:18 +00007598 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007599 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007600 ping_color_type=0,
7601 ping_interlace_method=0,
7602 ping_compression_method=0,
7603 ping_filter_method=0,
7604 ping_num_trans = 0;
7605
7606 ping_background.red = 0;
7607 ping_background.green = 0;
7608 ping_background.blue = 0;
7609 ping_background.gray = 0;
7610 ping_background.index = 0;
7611
7612 ping_trans_color.red=0;
7613 ping_trans_color.green=0;
7614 ping_trans_color.blue=0;
7615 ping_trans_color.gray=0;
7616
glennrpdfd70802010-11-14 01:23:35 +00007617 ping_pHYs_unit_type = 0;
7618 ping_pHYs_x_resolution = 0;
7619 ping_pHYs_y_resolution = 0;
7620
glennrpda8f3a72011-02-27 23:54:12 +00007621 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007622 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007623 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007624 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007625 ping_have_bKGD=MagickFalse;
7626 ping_have_pHYs=MagickFalse;
7627 ping_have_tRNS=MagickFalse;
7628
glennrp0e8ea192010-12-24 18:00:33 +00007629 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7630 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007631 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007632 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007633 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007634 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7635 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7636 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7637 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7638 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7639 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007640 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007641 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7642 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7643 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7644
glennrp8d3d6e52011-04-19 04:39:51 +00007645 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007646 ping_need_colortype_warning = MagickFalse;
7647
cristy0d57eec2011-09-04 22:13:56 +00007648 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7649 * i.e., eliminate the ICC profile and set image->rendering_intent.
7650 * Note that this will not involve any changes to the actual pixels
7651 * but merely passes information to applications that read the resulting
7652 * PNG image.
7653 */
7654 if (ping_exclude_sRGB == MagickFalse)
7655 {
7656 char
7657 *name;
7658
7659 const StringInfo
7660 *profile;
7661
7662 ResetImageProfileIterator(image);
7663 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7664 {
7665 profile=GetImageProfile(image,name);
7666
7667 if (profile != (StringInfo *) NULL)
7668 {
7669 if ((LocaleCompare(name,"ICC") == 0) ||
glennrp29a106e2011-09-06 17:11:42 +00007670 (LocaleCompare(name,"ICM") == 0))
7671 {
glennrpee7b4c02011-10-04 01:21:09 +00007672 int
7673 icheck;
7674
7675 /* 0: not a known sRGB profile
7676 * 1: HP-Microsoft sRGB v2
7677 * 2: ICC sRGB v4 perceptual
7678 * 3: ICC sRGB v2 perceptual no black-compensation
7679 */
7680 png_uint_32
7681 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7682 check_len[4] = {0, 3144, 60960, 3052};
7683
7684 png_uint_32
7685 length,
7686 profile_crc;
7687
cristy0d57eec2011-09-04 22:13:56 +00007688 unsigned char
7689 *data;
7690
glennrp29a106e2011-09-06 17:11:42 +00007691 length=(png_uint_32) GetStringInfoLength(profile);
7692
glennrpee7b4c02011-10-04 01:21:09 +00007693 for (icheck=3; icheck > 0; icheck--)
cristy0d57eec2011-09-04 22:13:56 +00007694 {
glennrpee7b4c02011-10-04 01:21:09 +00007695 if (length == check_len[icheck])
glennrp29a106e2011-09-06 17:11:42 +00007696 {
glennrpee7b4c02011-10-04 01:21:09 +00007697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7698 " Got a %lu-byte ICC profile (potentially sRGB)",
7699 (unsigned long) length);
glennrp29a106e2011-09-06 17:11:42 +00007700
glennrpee7b4c02011-10-04 01:21:09 +00007701 data=GetStringInfoDatum(profile);
7702 profile_crc=crc32(0,data,length);
glennrp29a106e2011-09-06 17:11:42 +00007703
glennrpee7b4c02011-10-04 01:21:09 +00007704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe54cd4b2011-10-04 01:31:35 +00007705 " with crc=%8x",(unsigned int) profile_crc);
glennrpee7b4c02011-10-04 01:21:09 +00007706
7707 if (profile_crc == check_crc[icheck])
7708 {
7709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7710 " It is sRGB.");
7711 if (image->rendering_intent==UndefinedIntent)
7712 image->rendering_intent=PerceptualIntent;
7713 break;
7714 }
glennrp29a106e2011-09-06 17:11:42 +00007715 }
glennrp29a106e2011-09-06 17:11:42 +00007716 }
glennrpee7b4c02011-10-04 01:21:09 +00007717 if (icheck == 0)
glennrp29a106e2011-09-06 17:11:42 +00007718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpee7b4c02011-10-04 01:21:09 +00007719 " Got a %lu-byte ICC profile",
glennrp29a106e2011-09-06 17:11:42 +00007720 (unsigned long) length);
7721 }
cristy0d57eec2011-09-04 22:13:56 +00007722 }
7723 name=GetNextImageProfile(image);
7724 }
7725 }
7726
glennrp8bb3a022010-12-13 20:40:04 +00007727 number_opaque = 0;
7728 number_semitransparent = 0;
7729 number_transparent = 0;
7730
glennrpfd05d622011-02-25 04:10:33 +00007731 if (logging != MagickFalse)
7732 {
7733 if (image->storage_class == UndefinedClass)
7734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7735 " storage_class=UndefinedClass");
7736 if (image->storage_class == DirectClass)
7737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7738 " storage_class=DirectClass");
7739 if (image->storage_class == PseudoClass)
7740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7741 " storage_class=PseudoClass");
7742 }
glennrp28af3712011-04-06 18:07:30 +00007743
glennrp7e65e932011-08-19 02:31:16 +00007744 if (image->storage_class == PseudoClass &&
7745 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7746 (mng_info->write_png_colortype != 0 &&
7747 mng_info->write_png_colortype != 4)))
7748 {
cristyea1a8aa2011-10-20 13:24:06 +00007749 (void) SyncImage(image,exception);
glennrp7e65e932011-08-19 02:31:16 +00007750 image->storage_class = DirectClass;
7751 }
7752
glennrpc6c391a2011-04-27 02:23:56 +00007753 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007754 {
glennrpc6c391a2011-04-27 02:23:56 +00007755 if (image->storage_class != PseudoClass && image->colormap != NULL)
7756 {
7757 /* Free the bogus colormap; it can cause trouble later */
7758 if (logging != MagickFalse)
7759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7760 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00007761 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00007762 image->colormap=NULL;
7763 }
glennrp28af3712011-04-06 18:07:30 +00007764 }
glennrpbb4f99d2011-05-22 11:13:17 +00007765
cristy510d06a2011-07-06 23:43:54 +00007766 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristy8d951092012-02-08 18:54:56 +00007767 (void) TransformImageColorspace(image,sRGBColorspace,exception);
glennrp0fe50b42010-11-16 03:52:51 +00007768
glennrp3241bd02010-12-12 04:36:28 +00007769 /*
7770 Sometimes we get PseudoClass images whose RGB values don't match
7771 the colors in the colormap. This code syncs the RGB values.
7772 */
7773 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
cristyea1a8aa2011-10-20 13:24:06 +00007774 (void) SyncImage(image,exception);
glennrp3241bd02010-12-12 04:36:28 +00007775
glennrpa6a06632011-01-19 15:15:34 +00007776#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7777 if (image->depth > 8)
7778 {
7779 if (logging != MagickFalse)
7780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7781 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7782
7783 image->depth=8;
7784 }
7785#endif
7786
glennrp8e58efd2011-05-20 12:16:29 +00007787 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007788 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7789 {
cristy4c08aed2011-07-01 19:47:50 +00007790 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007791 *r;
7792
glennrp8e58efd2011-05-20 12:16:29 +00007793 if (image->depth > 8)
7794 {
7795#if MAGICKCORE_QUANTUM_DEPTH > 16
7796 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007797 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007798
7799 for (y=0; y < (ssize_t) image->rows; y++)
7800 {
cristy8a20fa02011-12-27 15:54:31 +00007801 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007802
cristy4c08aed2011-07-01 19:47:50 +00007803 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007804 break;
7805
7806 for (x=0; x < (ssize_t) image->columns; x++)
7807 {
glennrp54cf7972011-08-06 14:28:09 +00007808 LBR16PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007809 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007810 }
glennrpbb4f99d2011-05-22 11:13:17 +00007811
glennrp8e58efd2011-05-20 12:16:29 +00007812 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7813 break;
7814 }
7815
7816 if (image->storage_class == PseudoClass && image->colormap != NULL)
7817 {
cristy3e08f112011-05-24 13:19:30 +00007818 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007819 {
glennrp91d99252011-06-25 14:30:13 +00007820 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007821 }
7822 }
7823#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7824 }
7825
7826 else if (image->depth > 4)
7827 {
7828#if MAGICKCORE_QUANTUM_DEPTH > 8
7829 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007830 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007831
7832 for (y=0; y < (ssize_t) image->rows; y++)
7833 {
cristyc82a27b2011-10-21 01:07:16 +00007834 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007835
cristy4c08aed2011-07-01 19:47:50 +00007836 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007837 break;
7838
7839 for (x=0; x < (ssize_t) image->columns; x++)
7840 {
glennrp54cf7972011-08-06 14:28:09 +00007841 LBR08PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007842 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007843 }
glennrpbb4f99d2011-05-22 11:13:17 +00007844
glennrp8e58efd2011-05-20 12:16:29 +00007845 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7846 break;
7847 }
7848
7849 if (image->storage_class == PseudoClass && image->colormap != NULL)
7850 {
cristy3e08f112011-05-24 13:19:30 +00007851 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007852 {
glennrp91d99252011-06-25 14:30:13 +00007853 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007854 }
7855 }
7856#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7857 }
7858 else
7859 if (image->depth > 2)
7860 {
7861 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007862 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007863
7864 for (y=0; y < (ssize_t) image->rows; y++)
7865 {
cristy8a20fa02011-12-27 15:54:31 +00007866 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007867
cristy4c08aed2011-07-01 19:47:50 +00007868 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007869 break;
7870
7871 for (x=0; x < (ssize_t) image->columns; x++)
7872 {
glennrp54cf7972011-08-06 14:28:09 +00007873 LBR04PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007874 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007875 }
glennrpbb4f99d2011-05-22 11:13:17 +00007876
glennrp8e58efd2011-05-20 12:16:29 +00007877 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7878 break;
7879 }
7880
7881 if (image->storage_class == PseudoClass && image->colormap != NULL)
7882 {
cristy3e08f112011-05-24 13:19:30 +00007883 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007884 {
glennrp91d99252011-06-25 14:30:13 +00007885 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007886 }
7887 }
7888 }
7889
7890 else if (image->depth > 1)
7891 {
7892 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007893 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007894
7895 for (y=0; y < (ssize_t) image->rows; y++)
7896 {
cristy8a20fa02011-12-27 15:54:31 +00007897 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007898
cristy4c08aed2011-07-01 19:47:50 +00007899 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007900 break;
7901
7902 for (x=0; x < (ssize_t) image->columns; x++)
7903 {
glennrp54cf7972011-08-06 14:28:09 +00007904 LBR02PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007905 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007906 }
glennrpbb4f99d2011-05-22 11:13:17 +00007907
glennrp8e58efd2011-05-20 12:16:29 +00007908 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7909 break;
7910 }
7911
7912 if (image->storage_class == PseudoClass && image->colormap != NULL)
7913 {
cristy3e08f112011-05-24 13:19:30 +00007914 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007915 {
glennrp91d99252011-06-25 14:30:13 +00007916 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007917 }
7918 }
7919 }
7920 else
7921 {
7922 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007923 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007924
7925 for (y=0; y < (ssize_t) image->rows; y++)
7926 {
cristy8a20fa02011-12-27 15:54:31 +00007927 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007928
cristy4c08aed2011-07-01 19:47:50 +00007929 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007930 break;
7931
7932 for (x=0; x < (ssize_t) image->columns; x++)
7933 {
glennrp54cf7972011-08-06 14:28:09 +00007934 LBR01PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007935 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007936 }
glennrpbb4f99d2011-05-22 11:13:17 +00007937
glennrp8e58efd2011-05-20 12:16:29 +00007938 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7939 break;
7940 }
7941
7942 if (image->storage_class == PseudoClass && image->colormap != NULL)
7943 {
cristy3e08f112011-05-24 13:19:30 +00007944 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007945 {
glennrp91d99252011-06-25 14:30:13 +00007946 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007947 }
7948 }
7949 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007950 }
7951
glennrp67b9c1a2011-04-22 18:47:36 +00007952 /* To do: set to next higher multiple of 8 */
7953 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007954 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007955
glennrp2b013e42010-11-24 16:55:50 +00007956#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7957 /* PNG does not handle depths greater than 16 so reduce it even
7958 * if lossy
7959 */
glennrp8e58efd2011-05-20 12:16:29 +00007960 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007961 image->depth=16;
7962#endif
7963
glennrp3faa9a32011-04-23 14:00:25 +00007964#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00007965 if (image->depth > 8)
7966 {
7967 /* To do: fill low byte properly */
7968 image->depth=16;
7969 }
7970
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
glennrpb381a262012-02-11 17:49:49 +000010991 value=GetImageOption(image_info,"png:format");
10992
10993 if (value != (char *) NULL)
10994 {
10995 if (LocaleCompare(value,"png8") == 0)
10996 {
10997 mng_info->write_png8 = MagickTrue;
10998 mng_info->write_png24 = MagickFalse;
10999 mng_info->write_png32 = MagickFalse;
11000 }
11001
11002 else if (LocaleCompare(value,"png24") == 0)
11003 {
11004 mng_info->write_png8 = MagickFalse;
11005 mng_info->write_png24 = MagickTrue;
11006 mng_info->write_png32 = MagickFalse;
11007 }
11008
11009 else if (LocaleCompare(value,"png32") == 0)
11010 {
11011 mng_info->write_png8 = MagickFalse;
11012 mng_info->write_png24 = MagickFalse;
11013 mng_info->write_png32 = MagickTrue;
11014 }
11015 }
cristy3ed852e2009-09-05 21:47:34 +000011016 if (mng_info->write_png8)
11017 {
glennrp9c1eb072010-06-06 22:19:15 +000011018 mng_info->write_png_colortype = /* 3 */ 4;
11019 mng_info->write_png_depth = 8;
11020 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000011021 }
11022
11023 if (mng_info->write_png24)
11024 {
glennrp9c1eb072010-06-06 22:19:15 +000011025 mng_info->write_png_colortype = /* 2 */ 3;
11026 mng_info->write_png_depth = 8;
11027 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011028
glennrp9c1eb072010-06-06 22:19:15 +000011029 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011030 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011031
glennrp9c1eb072010-06-06 22:19:15 +000011032 else
cristy018f07f2011-09-04 21:15:19 +000011033 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011034
cristyea1a8aa2011-10-20 13:24:06 +000011035 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011036 }
11037
11038 if (mng_info->write_png32)
11039 {
glennrp9c1eb072010-06-06 22:19:15 +000011040 mng_info->write_png_colortype = /* 6 */ 7;
11041 mng_info->write_png_depth = 8;
11042 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011043
glennrp9c1eb072010-06-06 22:19:15 +000011044 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011045 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011046
glennrp9c1eb072010-06-06 22:19:15 +000011047 else
cristy018f07f2011-09-04 21:15:19 +000011048 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011049
cristyea1a8aa2011-10-20 13:24:06 +000011050 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011051 }
11052
11053 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011054
cristy3ed852e2009-09-05 21:47:34 +000011055 if (value != (char *) NULL)
11056 {
11057 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011058 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011059
cristy3ed852e2009-09-05 21:47:34 +000011060 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011061 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011062
cristy3ed852e2009-09-05 21:47:34 +000011063 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011064 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011065
cristy3ed852e2009-09-05 21:47:34 +000011066 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011067 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011068
cristy3ed852e2009-09-05 21:47:34 +000011069 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011070 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011071
glennrpbb8a7332010-11-13 15:17:35 +000011072 else
cristyc82a27b2011-10-21 01:07:16 +000011073 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011074 GetMagickModule(),CoderWarning,
11075 "ignoring invalid defined png:bit-depth",
11076 "=%s",value);
11077
cristy3ed852e2009-09-05 21:47:34 +000011078 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011080 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011081 }
glennrp0fe50b42010-11-16 03:52:51 +000011082
cristy3ed852e2009-09-05 21:47:34 +000011083 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011084
cristy3ed852e2009-09-05 21:47:34 +000011085 if (value != (char *) NULL)
11086 {
11087 /* We must store colortype+1 because 0 is a valid colortype */
11088 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011089 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011090
glennrpaa9c3c72012-01-30 21:14:50 +000011091 else if (LocaleCompare(value,"1") == 0)
11092 mng_info->write_png_colortype = 2;
11093
cristy3ed852e2009-09-05 21:47:34 +000011094 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011095 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011096
cristy3ed852e2009-09-05 21:47:34 +000011097 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011098 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011099
cristy3ed852e2009-09-05 21:47:34 +000011100 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011101 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011102
cristy3ed852e2009-09-05 21:47:34 +000011103 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011104 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011105
glennrpbb8a7332010-11-13 15:17:35 +000011106 else
cristyc82a27b2011-10-21 01:07:16 +000011107 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011108 GetMagickModule(),CoderWarning,
11109 "ignoring invalid defined png:color-type",
11110 "=%s",value);
11111
cristy3ed852e2009-09-05 21:47:34 +000011112 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011114 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011115 }
11116
glennrp0e8ea192010-12-24 18:00:33 +000011117 /* Check for chunks to be excluded:
11118 *
glennrp0dff56c2011-01-29 19:10:02 +000011119 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011120 * listed in the "unused_chunks" array, above.
11121 *
cristy5d6fc9c2011-12-27 03:10:42 +000011122 * Chunks can be listed for exclusion via a "png:exclude-chunk"
glennrp0e8ea192010-12-24 18:00:33 +000011123 * define (in the image properties or in the image artifacts)
11124 * or via a mng_info member. For convenience, in addition
11125 * to or instead of a comma-separated list of chunks, the
11126 * "exclude-chunk" string can be simply "all" or "none".
11127 *
11128 * The exclude-chunk define takes priority over the mng_info.
11129 *
cristy5d6fc9c2011-12-27 03:10:42 +000011130 * A "png:include-chunk" define takes priority over both the
11131 * mng_info and the "png:exclude-chunk" define. Like the
glennrp0e8ea192010-12-24 18:00:33 +000011132 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011133 * well as a comma-separated list. Chunks that are unknown to
11134 * ImageMagick are always excluded, regardless of their "copy-safe"
11135 * status according to the PNG specification, and even if they
glennrpaa192b12012-01-17 21:35:21 +000011136 * appear in the "include-chunk" list. Such defines appearing among
11137 * the image options take priority over those found among the image
11138 * artifacts.
glennrp0e8ea192010-12-24 18:00:33 +000011139 *
11140 * Finally, all chunks listed in the "unused_chunks" array are
11141 * automatically excluded, regardless of the other instructions
11142 * or lack thereof.
11143 *
11144 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11145 * will not be written and the gAMA chunk will only be written if it
11146 * is not between .45 and .46, or approximately (1.0/2.2).
11147 *
11148 * If you exclude tRNS and the image has transparency, the colortype
11149 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11150 *
11151 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011152 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011153 */
11154
glennrp26f37912010-12-23 16:22:42 +000011155 mng_info->ping_exclude_bKGD=MagickFalse;
11156 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011157 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011158 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11159 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011160 mng_info->ping_exclude_iCCP=MagickFalse;
11161 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11162 mng_info->ping_exclude_oFFs=MagickFalse;
11163 mng_info->ping_exclude_pHYs=MagickFalse;
11164 mng_info->ping_exclude_sRGB=MagickFalse;
11165 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011166 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011167 mng_info->ping_exclude_vpAg=MagickFalse;
11168 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11169 mng_info->ping_exclude_zTXt=MagickFalse;
11170
glennrp8d3d6e52011-04-19 04:39:51 +000011171 mng_info->ping_preserve_colormap=MagickFalse;
11172
11173 value=GetImageArtifact(image,"png:preserve-colormap");
11174 if (value == NULL)
11175 value=GetImageOption(image_info,"png:preserve-colormap");
11176 if (value != NULL)
11177 mng_info->ping_preserve_colormap=MagickTrue;
11178
glennrp18682582011-06-30 18:11:47 +000011179 /* Thes compression-level, compression-strategy, and compression-filter
11180 * defines take precedence over values from the -quality option.
11181 */
11182 value=GetImageArtifact(image,"png:compression-level");
11183 if (value == NULL)
11184 value=GetImageOption(image_info,"png:compression-level");
11185 if (value != NULL)
11186 {
glennrp18682582011-06-30 18:11:47 +000011187 /* We have to add 1 to everything because 0 is a valid input,
11188 * and we want to use 0 (the default) to mean undefined.
11189 */
11190 if (LocaleCompare(value,"0") == 0)
11191 mng_info->write_png_compression_level = 1;
11192
glennrp0ffb95c2012-01-30 21:16:22 +000011193 else if (LocaleCompare(value,"1") == 0)
glennrp18682582011-06-30 18:11:47 +000011194 mng_info->write_png_compression_level = 2;
11195
11196 else if (LocaleCompare(value,"2") == 0)
11197 mng_info->write_png_compression_level = 3;
11198
11199 else if (LocaleCompare(value,"3") == 0)
11200 mng_info->write_png_compression_level = 4;
11201
11202 else if (LocaleCompare(value,"4") == 0)
11203 mng_info->write_png_compression_level = 5;
11204
11205 else if (LocaleCompare(value,"5") == 0)
11206 mng_info->write_png_compression_level = 6;
11207
11208 else if (LocaleCompare(value,"6") == 0)
11209 mng_info->write_png_compression_level = 7;
11210
11211 else if (LocaleCompare(value,"7") == 0)
11212 mng_info->write_png_compression_level = 8;
11213
11214 else if (LocaleCompare(value,"8") == 0)
11215 mng_info->write_png_compression_level = 9;
11216
11217 else if (LocaleCompare(value,"9") == 0)
11218 mng_info->write_png_compression_level = 10;
11219
11220 else
cristyc82a27b2011-10-21 01:07:16 +000011221 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011222 GetMagickModule(),CoderWarning,
11223 "ignoring invalid defined png:compression-level",
11224 "=%s",value);
11225 }
11226
11227 value=GetImageArtifact(image,"png:compression-strategy");
11228 if (value == NULL)
11229 value=GetImageOption(image_info,"png:compression-strategy");
11230 if (value != NULL)
11231 {
11232
11233 if (LocaleCompare(value,"0") == 0)
11234 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11235
11236 else if (LocaleCompare(value,"1") == 0)
11237 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11238
11239 else if (LocaleCompare(value,"2") == 0)
11240 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11241
11242 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011243#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011244 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011245#else
11246 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11247#endif
glennrp18682582011-06-30 18:11:47 +000011248
11249 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011250#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011251 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011252#else
11253 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11254#endif
glennrp18682582011-06-30 18:11:47 +000011255
11256 else
cristyc82a27b2011-10-21 01:07:16 +000011257 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011258 GetMagickModule(),CoderWarning,
11259 "ignoring invalid defined png:compression-strategy",
11260 "=%s",value);
11261 }
11262
11263 value=GetImageArtifact(image,"png:compression-filter");
11264 if (value == NULL)
11265 value=GetImageOption(image_info,"png:compression-filter");
11266 if (value != NULL)
11267 {
11268
11269 /* To do: combinations of filters allowed by libpng
11270 * masks 0x08 through 0xf8
11271 *
11272 * Implement this as a comma-separated list of 0,1,2,3,4,5
11273 * where 5 is a special case meaning PNG_ALL_FILTERS.
11274 */
11275
11276 if (LocaleCompare(value,"0") == 0)
11277 mng_info->write_png_compression_filter = 1;
11278
11279 if (LocaleCompare(value,"1") == 0)
11280 mng_info->write_png_compression_filter = 2;
11281
11282 else if (LocaleCompare(value,"2") == 0)
11283 mng_info->write_png_compression_filter = 3;
11284
11285 else if (LocaleCompare(value,"3") == 0)
11286 mng_info->write_png_compression_filter = 4;
11287
11288 else if (LocaleCompare(value,"4") == 0)
11289 mng_info->write_png_compression_filter = 5;
11290
11291 else if (LocaleCompare(value,"5") == 0)
11292 mng_info->write_png_compression_filter = 6;
11293
glennrp18682582011-06-30 18:11:47 +000011294 else
cristyc82a27b2011-10-21 01:07:16 +000011295 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011296 GetMagickModule(),CoderWarning,
11297 "ignoring invalid defined png:compression-filter",
11298 "=%s",value);
11299 }
11300
glennrp03812ae2010-12-24 01:31:34 +000011301 excluding=MagickFalse;
11302
glennrp5c7cf4e2010-12-24 00:30:00 +000011303 for (source=0; source<1; source++)
11304 {
11305 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011306 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011307 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011308
11309 if (value == NULL)
11310 value=GetImageArtifact(image,"png:exclude-chunks");
11311 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011312 else
glennrpacba0042010-12-24 14:27:26 +000011313 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011314 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011315
glennrpacba0042010-12-24 14:27:26 +000011316 if (value == NULL)
11317 value=GetImageOption(image_info,"png:exclude-chunks");
11318 }
11319
glennrp03812ae2010-12-24 01:31:34 +000011320 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011321 {
glennrp03812ae2010-12-24 01:31:34 +000011322
11323 size_t
11324 last;
11325
11326 excluding=MagickTrue;
11327
11328 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011329 {
11330 if (source == 0)
11331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11332 " png:exclude-chunk=%s found in image artifacts.\n", value);
11333 else
11334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11335 " png:exclude-chunk=%s found in image properties.\n", value);
11336 }
glennrp03812ae2010-12-24 01:31:34 +000011337
11338 last=strlen(value);
11339
11340 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011341 {
glennrp03812ae2010-12-24 01:31:34 +000011342
11343 if (LocaleNCompare(value+i,"all",3) == 0)
11344 {
11345 mng_info->ping_exclude_bKGD=MagickTrue;
11346 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011347 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011348 mng_info->ping_exclude_EXIF=MagickTrue;
11349 mng_info->ping_exclude_gAMA=MagickTrue;
11350 mng_info->ping_exclude_iCCP=MagickTrue;
11351 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11352 mng_info->ping_exclude_oFFs=MagickTrue;
11353 mng_info->ping_exclude_pHYs=MagickTrue;
11354 mng_info->ping_exclude_sRGB=MagickTrue;
11355 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011356 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011357 mng_info->ping_exclude_vpAg=MagickTrue;
11358 mng_info->ping_exclude_zCCP=MagickTrue;
11359 mng_info->ping_exclude_zTXt=MagickTrue;
11360 i--;
11361 }
glennrp2cc891a2010-12-24 13:44:32 +000011362
glennrp03812ae2010-12-24 01:31:34 +000011363 if (LocaleNCompare(value+i,"none",4) == 0)
11364 {
11365 mng_info->ping_exclude_bKGD=MagickFalse;
11366 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011367 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011368 mng_info->ping_exclude_EXIF=MagickFalse;
11369 mng_info->ping_exclude_gAMA=MagickFalse;
11370 mng_info->ping_exclude_iCCP=MagickFalse;
11371 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11372 mng_info->ping_exclude_oFFs=MagickFalse;
11373 mng_info->ping_exclude_pHYs=MagickFalse;
11374 mng_info->ping_exclude_sRGB=MagickFalse;
11375 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011376 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011377 mng_info->ping_exclude_vpAg=MagickFalse;
11378 mng_info->ping_exclude_zCCP=MagickFalse;
11379 mng_info->ping_exclude_zTXt=MagickFalse;
11380 }
glennrp2cc891a2010-12-24 13:44:32 +000011381
glennrp03812ae2010-12-24 01:31:34 +000011382 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11383 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011384
glennrp03812ae2010-12-24 01:31:34 +000011385 if (LocaleNCompare(value+i,"chrm",4) == 0)
11386 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011387
glennrpa0ed0092011-04-18 16:36:29 +000011388 if (LocaleNCompare(value+i,"date",4) == 0)
11389 mng_info->ping_exclude_date=MagickTrue;
11390
glennrp03812ae2010-12-24 01:31:34 +000011391 if (LocaleNCompare(value+i,"exif",4) == 0)
11392 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011393
glennrp03812ae2010-12-24 01:31:34 +000011394 if (LocaleNCompare(value+i,"gama",4) == 0)
11395 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011396
glennrp03812ae2010-12-24 01:31:34 +000011397 if (LocaleNCompare(value+i,"iccp",4) == 0)
11398 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011399
glennrp03812ae2010-12-24 01:31:34 +000011400 /*
11401 if (LocaleNCompare(value+i,"itxt",4) == 0)
11402 mng_info->ping_exclude_iTXt=MagickTrue;
11403 */
glennrp2cc891a2010-12-24 13:44:32 +000011404
glennrp03812ae2010-12-24 01:31:34 +000011405 if (LocaleNCompare(value+i,"gama",4) == 0)
11406 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011407
glennrp03812ae2010-12-24 01:31:34 +000011408 if (LocaleNCompare(value+i,"offs",4) == 0)
11409 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011410
glennrp03812ae2010-12-24 01:31:34 +000011411 if (LocaleNCompare(value+i,"phys",4) == 0)
11412 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011413
glennrpa1e3b7b2010-12-24 16:37:33 +000011414 if (LocaleNCompare(value+i,"srgb",4) == 0)
11415 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011416
glennrp03812ae2010-12-24 01:31:34 +000011417 if (LocaleNCompare(value+i,"text",4) == 0)
11418 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011419
glennrpa1e3b7b2010-12-24 16:37:33 +000011420 if (LocaleNCompare(value+i,"trns",4) == 0)
11421 mng_info->ping_exclude_tRNS=MagickTrue;
11422
glennrp03812ae2010-12-24 01:31:34 +000011423 if (LocaleNCompare(value+i,"vpag",4) == 0)
11424 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011425
glennrp03812ae2010-12-24 01:31:34 +000011426 if (LocaleNCompare(value+i,"zccp",4) == 0)
11427 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011428
glennrp03812ae2010-12-24 01:31:34 +000011429 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11430 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011431
glennrp03812ae2010-12-24 01:31:34 +000011432 }
glennrpce91ed52010-12-23 22:37:49 +000011433 }
glennrp26f37912010-12-23 16:22:42 +000011434 }
11435
glennrp5c7cf4e2010-12-24 00:30:00 +000011436 for (source=0; source<1; source++)
11437 {
11438 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011439 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011440 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011441
11442 if (value == NULL)
11443 value=GetImageArtifact(image,"png:include-chunks");
11444 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011445 else
glennrpacba0042010-12-24 14:27:26 +000011446 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011447 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011448
glennrpacba0042010-12-24 14:27:26 +000011449 if (value == NULL)
11450 value=GetImageOption(image_info,"png:include-chunks");
11451 }
11452
glennrp03812ae2010-12-24 01:31:34 +000011453 if (value != NULL)
11454 {
11455 size_t
11456 last;
glennrp26f37912010-12-23 16:22:42 +000011457
glennrp03812ae2010-12-24 01:31:34 +000011458 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011459
glennrp03812ae2010-12-24 01:31:34 +000011460 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011461 {
11462 if (source == 0)
11463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11464 " png:include-chunk=%s found in image artifacts.\n", value);
11465 else
11466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11467 " png:include-chunk=%s found in image properties.\n", value);
11468 }
glennrp03812ae2010-12-24 01:31:34 +000011469
11470 last=strlen(value);
11471
11472 for (i=0; i<(int) last; i+=5)
11473 {
11474 if (LocaleNCompare(value+i,"all",3) == 0)
11475 {
11476 mng_info->ping_exclude_bKGD=MagickFalse;
11477 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011478 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011479 mng_info->ping_exclude_EXIF=MagickFalse;
11480 mng_info->ping_exclude_gAMA=MagickFalse;
11481 mng_info->ping_exclude_iCCP=MagickFalse;
11482 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11483 mng_info->ping_exclude_oFFs=MagickFalse;
11484 mng_info->ping_exclude_pHYs=MagickFalse;
11485 mng_info->ping_exclude_sRGB=MagickFalse;
11486 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011487 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011488 mng_info->ping_exclude_vpAg=MagickFalse;
11489 mng_info->ping_exclude_zCCP=MagickFalse;
11490 mng_info->ping_exclude_zTXt=MagickFalse;
11491 i--;
11492 }
glennrp2cc891a2010-12-24 13:44:32 +000011493
glennrp03812ae2010-12-24 01:31:34 +000011494 if (LocaleNCompare(value+i,"none",4) == 0)
11495 {
11496 mng_info->ping_exclude_bKGD=MagickTrue;
11497 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011498 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011499 mng_info->ping_exclude_EXIF=MagickTrue;
11500 mng_info->ping_exclude_gAMA=MagickTrue;
11501 mng_info->ping_exclude_iCCP=MagickTrue;
11502 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11503 mng_info->ping_exclude_oFFs=MagickTrue;
11504 mng_info->ping_exclude_pHYs=MagickTrue;
11505 mng_info->ping_exclude_sRGB=MagickTrue;
11506 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011507 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011508 mng_info->ping_exclude_vpAg=MagickTrue;
11509 mng_info->ping_exclude_zCCP=MagickTrue;
11510 mng_info->ping_exclude_zTXt=MagickTrue;
11511 }
glennrp2cc891a2010-12-24 13:44:32 +000011512
glennrp03812ae2010-12-24 01:31:34 +000011513 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11514 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011515
glennrp03812ae2010-12-24 01:31:34 +000011516 if (LocaleNCompare(value+i,"chrm",4) == 0)
11517 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011518
glennrpa0ed0092011-04-18 16:36:29 +000011519 if (LocaleNCompare(value+i,"date",4) == 0)
11520 mng_info->ping_exclude_date=MagickFalse;
11521
glennrp03812ae2010-12-24 01:31:34 +000011522 if (LocaleNCompare(value+i,"exif",4) == 0)
11523 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011524
glennrp03812ae2010-12-24 01:31:34 +000011525 if (LocaleNCompare(value+i,"gama",4) == 0)
11526 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011527
glennrp03812ae2010-12-24 01:31:34 +000011528 if (LocaleNCompare(value+i,"iccp",4) == 0)
11529 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011530
glennrp03812ae2010-12-24 01:31:34 +000011531 /*
11532 if (LocaleNCompare(value+i,"itxt",4) == 0)
11533 mng_info->ping_exclude_iTXt=MagickFalse;
11534 */
glennrp2cc891a2010-12-24 13:44:32 +000011535
glennrp03812ae2010-12-24 01:31:34 +000011536 if (LocaleNCompare(value+i,"gama",4) == 0)
11537 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011538
glennrp03812ae2010-12-24 01:31:34 +000011539 if (LocaleNCompare(value+i,"offs",4) == 0)
11540 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011541
glennrp03812ae2010-12-24 01:31:34 +000011542 if (LocaleNCompare(value+i,"phys",4) == 0)
11543 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011544
glennrpa1e3b7b2010-12-24 16:37:33 +000011545 if (LocaleNCompare(value+i,"srgb",4) == 0)
11546 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011547
glennrp03812ae2010-12-24 01:31:34 +000011548 if (LocaleNCompare(value+i,"text",4) == 0)
11549 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011550
glennrpa1e3b7b2010-12-24 16:37:33 +000011551 if (LocaleNCompare(value+i,"trns",4) == 0)
11552 mng_info->ping_exclude_tRNS=MagickFalse;
11553
glennrp03812ae2010-12-24 01:31:34 +000011554 if (LocaleNCompare(value+i,"vpag",4) == 0)
11555 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011556
glennrp03812ae2010-12-24 01:31:34 +000011557 if (LocaleNCompare(value+i,"zccp",4) == 0)
11558 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011559
glennrp03812ae2010-12-24 01:31:34 +000011560 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11561 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011562
glennrp03812ae2010-12-24 01:31:34 +000011563 }
glennrpce91ed52010-12-23 22:37:49 +000011564 }
glennrp26f37912010-12-23 16:22:42 +000011565 }
11566
glennrp03812ae2010-12-24 01:31:34 +000011567 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011568 {
11569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011570 " Chunks to be excluded from the output png:");
glennrp26f37912010-12-23 16:22:42 +000011571 if (mng_info->ping_exclude_bKGD != MagickFalse)
11572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11573 " bKGD");
11574 if (mng_info->ping_exclude_cHRM != MagickFalse)
11575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11576 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011577 if (mng_info->ping_exclude_date != MagickFalse)
11578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11579 " date");
glennrp26f37912010-12-23 16:22:42 +000011580 if (mng_info->ping_exclude_EXIF != MagickFalse)
11581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11582 " EXIF");
11583 if (mng_info->ping_exclude_gAMA != MagickFalse)
11584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11585 " gAMA");
11586 if (mng_info->ping_exclude_iCCP != MagickFalse)
11587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11588 " iCCP");
11589/*
11590 if (mng_info->ping_exclude_iTXt != MagickFalse)
11591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11592 " iTXt");
11593*/
11594 if (mng_info->ping_exclude_oFFs != MagickFalse)
11595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11596 " oFFs");
11597 if (mng_info->ping_exclude_pHYs != MagickFalse)
11598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11599 " pHYs");
11600 if (mng_info->ping_exclude_sRGB != MagickFalse)
11601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11602 " sRGB");
11603 if (mng_info->ping_exclude_tEXt != MagickFalse)
11604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11605 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011606 if (mng_info->ping_exclude_tRNS != MagickFalse)
11607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11608 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011609 if (mng_info->ping_exclude_vpAg != MagickFalse)
11610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11611 " vpAg");
11612 if (mng_info->ping_exclude_zCCP != MagickFalse)
11613 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11614 " zCCP");
11615 if (mng_info->ping_exclude_zTXt != MagickFalse)
11616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11617 " zTXt");
11618 }
11619
glennrpb9cfe272010-12-21 15:08:06 +000011620 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011621
cristy018f07f2011-09-04 21:15:19 +000011622 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011623
11624 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011625
cristy3ed852e2009-09-05 21:47:34 +000011626 if (logging != MagickFalse)
11627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011628
cristy3ed852e2009-09-05 21:47:34 +000011629 return(status);
11630}
11631
11632#if defined(JNG_SUPPORTED)
11633
11634/* Write one JNG image */
11635static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +000011636 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011637{
11638 Image
11639 *jpeg_image;
11640
11641 ImageInfo
11642 *jpeg_image_info;
11643
11644 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011645 logging,
cristy3ed852e2009-09-05 21:47:34 +000011646 status;
11647
11648 size_t
11649 length;
11650
11651 unsigned char
11652 *blob,
11653 chunk[80],
11654 *p;
11655
11656 unsigned int
11657 jng_alpha_compression_method,
11658 jng_alpha_sample_depth,
11659 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011660 transparent;
11661
cristybb503372010-05-27 20:51:26 +000011662 size_t
glennrp59575fa2011-12-31 21:31:39 +000011663 jng_alpha_quality,
cristy3ed852e2009-09-05 21:47:34 +000011664 jng_quality;
11665
11666 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011667 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011668
11669 blob=(unsigned char *) NULL;
11670 jpeg_image=(Image *) NULL;
11671 jpeg_image_info=(ImageInfo *) NULL;
11672
11673 status=MagickTrue;
11674 transparent=image_info->type==GrayscaleMatteType ||
glennrp59575fa2011-12-31 21:31:39 +000011675 image_info->type==TrueColorMatteType || image->matte != MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +000011676
glennrp59575fa2011-12-31 21:31:39 +000011677 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
11678
11679 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
11680
11681 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
11682 image_info->quality;
11683
11684 if (jng_alpha_quality >= 1000)
11685 jng_alpha_quality /= 1000;
cristy3ed852e2009-09-05 21:47:34 +000011686
11687 if (transparent)
11688 {
11689 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011690
cristy3ed852e2009-09-05 21:47:34 +000011691 /* Create JPEG blob, image, and image_info */
11692 if (logging != MagickFalse)
11693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +000011694 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011695
cristy3ed852e2009-09-05 21:47:34 +000011696 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011697
cristy3ed852e2009-09-05 21:47:34 +000011698 if (jpeg_image_info == (ImageInfo *) NULL)
11699 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011700
cristy3ed852e2009-09-05 21:47:34 +000011701 if (logging != MagickFalse)
11702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11703 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011704
cristy262346f2012-01-11 19:34:10 +000011705 jpeg_image=SeparateImage(image,AlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +000011706 if (jpeg_image == (Image *) NULL)
11707 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11708 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +000011709 jpeg_image->matte=MagickFalse;
glennrp59575fa2011-12-31 21:31:39 +000011710 jpeg_image->quality=jng_alpha_quality;
cristy3ed852e2009-09-05 21:47:34 +000011711 jpeg_image_info->type=GrayscaleType;
cristy018f07f2011-09-04 21:15:19 +000011712 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000011713 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011714 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011715 "%s",jpeg_image->filename);
11716 }
glennrp59575fa2011-12-31 21:31:39 +000011717 else
11718 {
11719 jng_alpha_compression_method=0;
11720 jng_color_type=10;
11721 jng_alpha_sample_depth=0;
11722 }
cristy3ed852e2009-09-05 21:47:34 +000011723
11724 /* To do: check bit depth of PNG alpha channel */
11725
11726 /* Check if image is grayscale. */
11727 if (image_info->type != TrueColorMatteType && image_info->type !=
cristyc82a27b2011-10-21 01:07:16 +000011728 TrueColorType && ImageIsGray(image,exception))
cristy3ed852e2009-09-05 21:47:34 +000011729 jng_color_type-=2;
11730
glennrp59575fa2011-12-31 21:31:39 +000011731 if (logging != MagickFalse)
11732 {
11733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11734 " JNG Quality = %d",(int) jng_quality);
11735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11736 " JNG Color Type = %d",jng_color_type);
11737 if (transparent)
11738 {
11739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11740 " JNG Alpha Compression = %d",jng_alpha_compression_method);
11741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11742 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
11743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11744 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
11745 }
11746 }
11747
cristy3ed852e2009-09-05 21:47:34 +000011748 if (transparent)
11749 {
11750 if (jng_alpha_compression_method==0)
11751 {
11752 const char
11753 *value;
11754
cristy4c08aed2011-07-01 19:47:50 +000011755 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011756 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000011757 exception);
cristy3ed852e2009-09-05 21:47:34 +000011758 if (logging != MagickFalse)
11759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11760 " Creating PNG blob.");
11761 length=0;
11762
11763 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11764 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11765 jpeg_image_info->interlace=NoInterlace;
11766
glennrpcc5d45b2012-01-06 04:06:10 +000011767 /* Exclude all ancillary chunks */
11768 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
11769
cristy3ed852e2009-09-05 21:47:34 +000011770 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyc82a27b2011-10-21 01:07:16 +000011771 exception);
cristy3ed852e2009-09-05 21:47:34 +000011772
11773 /* Retrieve sample depth used */
cristyd15e6592011-10-15 00:13:06 +000011774 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
cristy3ed852e2009-09-05 21:47:34 +000011775 if (value != (char *) NULL)
11776 jng_alpha_sample_depth= (unsigned int) value[0];
11777 }
11778 else
11779 {
cristy4c08aed2011-07-01 19:47:50 +000011780 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011781
11782 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000011783 exception);
cristy3ed852e2009-09-05 21:47:34 +000011784
11785 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11786 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11787 jpeg_image_info->interlace=NoInterlace;
11788 if (logging != MagickFalse)
11789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11790 " Creating blob.");
11791 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyc82a27b2011-10-21 01:07:16 +000011792 exception);
cristy3ed852e2009-09-05 21:47:34 +000011793 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011794
cristy3ed852e2009-09-05 21:47:34 +000011795 if (logging != MagickFalse)
11796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011797 " Successfully read jpeg_image into a blob, length=%.20g.",
11798 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011799
11800 }
11801 /* Destroy JPEG image and image_info */
11802 jpeg_image=DestroyImage(jpeg_image);
11803 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11804 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11805 }
11806
11807 /* Write JHDR chunk */
11808 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11809 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011810 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011811 PNGLong(chunk+4,(png_uint_32) image->columns);
11812 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011813 chunk[12]=jng_color_type;
11814 chunk[13]=8; /* sample depth */
11815 chunk[14]=8; /*jng_image_compression_method */
11816 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11817 chunk[16]=jng_alpha_sample_depth;
11818 chunk[17]=jng_alpha_compression_method;
11819 chunk[18]=0; /*jng_alpha_filter_method */
11820 chunk[19]=0; /*jng_alpha_interlace_method */
11821 (void) WriteBlob(image,20,chunk);
11822 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11823 if (logging != MagickFalse)
11824 {
11825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011826 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011827
cristy3ed852e2009-09-05 21:47:34 +000011828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011829 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011830
cristy3ed852e2009-09-05 21:47:34 +000011831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11832 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011833
cristy3ed852e2009-09-05 21:47:34 +000011834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11835 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011836
cristy3ed852e2009-09-05 21:47:34 +000011837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11838 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011839
cristy3ed852e2009-09-05 21:47:34 +000011840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11841 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011842
cristy3ed852e2009-09-05 21:47:34 +000011843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11844 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011845
cristy3ed852e2009-09-05 21:47:34 +000011846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11847 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011848
cristy3ed852e2009-09-05 21:47:34 +000011849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11850 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011851
cristy3ed852e2009-09-05 21:47:34 +000011852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11853 " JNG alpha interlace:%5d",0);
11854 }
11855
glennrp0fe50b42010-11-16 03:52:51 +000011856 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011857 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011858
11859 /*
11860 Write leading ancillary chunks
11861 */
11862
11863 if (transparent)
11864 {
11865 /*
11866 Write JNG bKGD chunk
11867 */
11868
11869 unsigned char
11870 blue,
11871 green,
11872 red;
11873
cristybb503372010-05-27 20:51:26 +000011874 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011875 num_bytes;
11876
11877 if (jng_color_type == 8 || jng_color_type == 12)
11878 num_bytes=6L;
11879 else
11880 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011881 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011882 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011883 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011884 red=ScaleQuantumToChar(image->background_color.red);
11885 green=ScaleQuantumToChar(image->background_color.green);
11886 blue=ScaleQuantumToChar(image->background_color.blue);
11887 *(chunk+4)=0;
11888 *(chunk+5)=red;
11889 *(chunk+6)=0;
11890 *(chunk+7)=green;
11891 *(chunk+8)=0;
11892 *(chunk+9)=blue;
11893 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11894 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11895 }
11896
11897 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11898 {
11899 /*
11900 Write JNG sRGB chunk
11901 */
11902 (void) WriteBlobMSBULong(image,1L);
11903 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011904 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011905
cristy3ed852e2009-09-05 21:47:34 +000011906 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011907 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011908 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011909 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011910
cristy3ed852e2009-09-05 21:47:34 +000011911 else
glennrpe610a072010-08-05 17:08:46 +000011912 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011913 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011914 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011915
cristy3ed852e2009-09-05 21:47:34 +000011916 (void) WriteBlob(image,5,chunk);
11917 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11918 }
11919 else
11920 {
11921 if (image->gamma != 0.0)
11922 {
11923 /*
11924 Write JNG gAMA chunk
11925 */
11926 (void) WriteBlobMSBULong(image,4L);
11927 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011928 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011929 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011930 (void) WriteBlob(image,8,chunk);
11931 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11932 }
glennrp0fe50b42010-11-16 03:52:51 +000011933
cristy3ed852e2009-09-05 21:47:34 +000011934 if ((mng_info->equal_chrms == MagickFalse) &&
11935 (image->chromaticity.red_primary.x != 0.0))
11936 {
11937 PrimaryInfo
11938 primary;
11939
11940 /*
11941 Write JNG cHRM chunk
11942 */
11943 (void) WriteBlobMSBULong(image,32L);
11944 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011945 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011946 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011947 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11948 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011949 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011950 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11951 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011952 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011953 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11954 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011955 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011956 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11957 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011958 (void) WriteBlob(image,36,chunk);
11959 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11960 }
11961 }
glennrp0fe50b42010-11-16 03:52:51 +000011962
cristy2a11bef2011-10-28 18:33:11 +000011963 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000011964 {
11965 /*
11966 Write JNG pHYs chunk
11967 */
11968 (void) WriteBlobMSBULong(image,9L);
11969 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011970 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011971 if (image->units == PixelsPerInchResolution)
11972 {
cristy35ef8242010-06-03 16:24:13 +000011973 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011974 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011975
cristy35ef8242010-06-03 16:24:13 +000011976 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011977 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011978
cristy3ed852e2009-09-05 21:47:34 +000011979 chunk[12]=1;
11980 }
glennrp0fe50b42010-11-16 03:52:51 +000011981
cristy3ed852e2009-09-05 21:47:34 +000011982 else
11983 {
11984 if (image->units == PixelsPerCentimeterResolution)
11985 {
cristy35ef8242010-06-03 16:24:13 +000011986 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011987 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011988
cristy35ef8242010-06-03 16:24:13 +000011989 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011990 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011991
cristy3ed852e2009-09-05 21:47:34 +000011992 chunk[12]=1;
11993 }
glennrp0fe50b42010-11-16 03:52:51 +000011994
cristy3ed852e2009-09-05 21:47:34 +000011995 else
11996 {
cristy2a11bef2011-10-28 18:33:11 +000011997 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
11998 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011999 chunk[12]=0;
12000 }
12001 }
12002 (void) WriteBlob(image,13,chunk);
12003 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12004 }
12005
12006 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12007 {
12008 /*
12009 Write JNG oFFs chunk
12010 */
12011 (void) WriteBlobMSBULong(image,9L);
12012 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000012013 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000012014 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12015 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000012016 chunk[12]=0;
12017 (void) WriteBlob(image,13,chunk);
12018 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12019 }
12020 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12021 {
12022 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12023 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000012024 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000012025 PNGLong(chunk+4,(png_uint_32) image->page.width);
12026 PNGLong(chunk+8,(png_uint_32) image->page.height);
12027 chunk[12]=0; /* unit = pixels */
12028 (void) WriteBlob(image,13,chunk);
12029 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12030 }
12031
12032
12033 if (transparent)
12034 {
12035 if (jng_alpha_compression_method==0)
12036 {
cristybb503372010-05-27 20:51:26 +000012037 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012038 i;
12039
cristybb503372010-05-27 20:51:26 +000012040 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012041 len;
12042
12043 /* Write IDAT chunk header */
12044 if (logging != MagickFalse)
12045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012046 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000012047 length);
cristy3ed852e2009-09-05 21:47:34 +000012048
12049 /* Copy IDAT chunks */
12050 len=0;
12051 p=blob+8;
cristybb503372010-05-27 20:51:26 +000012052 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000012053 {
12054 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12055 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012056
cristy3ed852e2009-09-05 21:47:34 +000012057 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12058 {
12059 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000012060 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000012061 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000012062 (void) WriteBlob(image,(size_t) len+4,p);
12063 (void) WriteBlobMSBULong(image,
12064 crc32(0,p,(uInt) len+4));
12065 }
glennrp0fe50b42010-11-16 03:52:51 +000012066
cristy3ed852e2009-09-05 21:47:34 +000012067 else
12068 {
12069 if (logging != MagickFalse)
12070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012071 " Skipping %c%c%c%c chunk, length=%.20g.",
12072 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012073 }
12074 p+=(8+len);
12075 }
12076 }
12077 else
12078 {
12079 /* Write JDAA chunk header */
12080 if (logging != MagickFalse)
12081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012082 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012083 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012084 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012085 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012086 /* Write JDAT chunk(s) data */
12087 (void) WriteBlob(image,4,chunk);
12088 (void) WriteBlob(image,length,blob);
12089 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12090 (uInt) length));
12091 }
12092 blob=(unsigned char *) RelinquishMagickMemory(blob);
12093 }
12094
12095 /* Encode image as a JPEG blob */
12096 if (logging != MagickFalse)
12097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12098 " Creating jpeg_image_info.");
12099 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12100 if (jpeg_image_info == (ImageInfo *) NULL)
12101 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12102
12103 if (logging != MagickFalse)
12104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12105 " Creating jpeg_image.");
12106
cristyc82a27b2011-10-21 01:07:16 +000012107 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012108 if (jpeg_image == (Image *) NULL)
12109 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12110 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12111
12112 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012113 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012114 jpeg_image->filename);
12115
12116 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000012117 exception);
cristy3ed852e2009-09-05 21:47:34 +000012118
12119 if (logging != MagickFalse)
12120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012121 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12122 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012123
12124 if (jng_color_type == 8 || jng_color_type == 12)
12125 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012126
glennrp59575fa2011-12-31 21:31:39 +000012127 jpeg_image_info->quality=jng_quality;
12128 jpeg_image->quality=jng_quality;
cristy3ed852e2009-09-05 21:47:34 +000012129 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12130 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012131
cristy3ed852e2009-09-05 21:47:34 +000012132 if (logging != MagickFalse)
12133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12134 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012135
cristyc82a27b2011-10-21 01:07:16 +000012136 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
glennrp0fe50b42010-11-16 03:52:51 +000012137
cristy3ed852e2009-09-05 21:47:34 +000012138 if (logging != MagickFalse)
12139 {
12140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012141 " Successfully read jpeg_image into a blob, length=%.20g.",
12142 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012143
12144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012145 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012146 }
glennrp0fe50b42010-11-16 03:52:51 +000012147
cristy3ed852e2009-09-05 21:47:34 +000012148 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012149 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012150 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012151 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012152 (void) WriteBlob(image,4,chunk);
12153 (void) WriteBlob(image,length,blob);
12154 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12155
12156 jpeg_image=DestroyImage(jpeg_image);
12157 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12158 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12159 blob=(unsigned char *) RelinquishMagickMemory(blob);
12160
12161 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012162 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012163
12164 /* Write IEND chunk */
12165 (void) WriteBlobMSBULong(image,0L);
12166 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012167 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012168 (void) WriteBlob(image,4,chunk);
12169 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12170
12171 if (logging != MagickFalse)
12172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12173 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012174
cristy3ed852e2009-09-05 21:47:34 +000012175 return(status);
12176}
12177
12178
12179/*
12180%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12181% %
12182% %
12183% %
12184% W r i t e J N G I m a g e %
12185% %
12186% %
12187% %
12188%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12189%
12190% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12191%
12192% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12193%
12194% The format of the WriteJNGImage method is:
12195%
cristy1e178e72011-08-28 19:44:34 +000012196% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12197% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012198%
12199% A description of each parameter follows:
12200%
12201% o image_info: the image info.
12202%
12203% o image: The image.
12204%
cristy1e178e72011-08-28 19:44:34 +000012205% o exception: return any errors or warnings in this structure.
12206%
cristy3ed852e2009-09-05 21:47:34 +000012207%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12208*/
cristy1e178e72011-08-28 19:44:34 +000012209static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12210 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012211{
12212 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012213 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012214 logging,
cristy3ed852e2009-09-05 21:47:34 +000012215 status;
12216
12217 MngInfo
12218 *mng_info;
12219
cristy3ed852e2009-09-05 21:47:34 +000012220 /*
12221 Open image file.
12222 */
12223 assert(image_info != (const ImageInfo *) NULL);
12224 assert(image_info->signature == MagickSignature);
12225 assert(image != (Image *) NULL);
12226 assert(image->signature == MagickSignature);
12227 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012228 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristyc82a27b2011-10-21 01:07:16 +000012229 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012230 if (status == MagickFalse)
12231 return(status);
12232
12233 /*
12234 Allocate a MngInfo structure.
12235 */
12236 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012237 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012238 if (mng_info == (MngInfo *) NULL)
12239 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12240 /*
12241 Initialize members of the MngInfo structure.
12242 */
12243 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12244 mng_info->image=image;
12245 have_mng_structure=MagickTrue;
12246
12247 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12248
cristy018f07f2011-09-04 21:15:19 +000012249 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012250 (void) CloseBlob(image);
12251
12252 (void) CatchImageException(image);
12253 MngInfoFreeStruct(mng_info,&have_mng_structure);
12254 if (logging != MagickFalse)
12255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12256 return(status);
12257}
12258#endif
12259
cristy1e178e72011-08-28 19:44:34 +000012260static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12261 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012262{
12263 const char
12264 *option;
12265
12266 Image
12267 *next_image;
12268
12269 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012270 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012271 status;
12272
glennrp03812ae2010-12-24 01:31:34 +000012273 volatile MagickBooleanType
12274 logging;
12275
cristy3ed852e2009-09-05 21:47:34 +000012276 MngInfo
12277 *mng_info;
12278
12279 int
cristy3ed852e2009-09-05 21:47:34 +000012280 image_count,
12281 need_iterations,
12282 need_matte;
12283
12284 volatile int
12285#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12286 defined(PNG_MNG_FEATURES_SUPPORTED)
12287 need_local_plte,
12288#endif
12289 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012290 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012291 use_global_plte;
12292
cristybb503372010-05-27 20:51:26 +000012293 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012294 i;
12295
12296 unsigned char
12297 chunk[800];
12298
12299 volatile unsigned int
12300 write_jng,
12301 write_mng;
12302
cristybb503372010-05-27 20:51:26 +000012303 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012304 scene;
12305
cristybb503372010-05-27 20:51:26 +000012306 size_t
cristy3ed852e2009-09-05 21:47:34 +000012307 final_delay=0,
12308 initial_delay;
12309
glennrpd5045b42010-03-24 12:40:35 +000012310#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012311 if (image_info->verbose)
12312 printf("Your PNG library (libpng-%s) is rather old.\n",
12313 PNG_LIBPNG_VER_STRING);
12314#endif
12315
12316 /*
12317 Open image file.
12318 */
12319 assert(image_info != (const ImageInfo *) NULL);
12320 assert(image_info->signature == MagickSignature);
12321 assert(image != (Image *) NULL);
12322 assert(image->signature == MagickSignature);
12323 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012324 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristyc82a27b2011-10-21 01:07:16 +000012325 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012326 if (status == MagickFalse)
12327 return(status);
12328
12329 /*
12330 Allocate a MngInfo structure.
12331 */
12332 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012333 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012334 if (mng_info == (MngInfo *) NULL)
12335 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12336 /*
12337 Initialize members of the MngInfo structure.
12338 */
12339 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12340 mng_info->image=image;
12341 have_mng_structure=MagickTrue;
12342 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12343
12344 /*
12345 * See if user has requested a specific PNG subformat to be used
12346 * for all of the PNGs in the MNG being written, e.g.,
12347 *
12348 * convert *.png png8:animation.mng
12349 *
12350 * To do: check -define png:bit_depth and png:color_type as well,
12351 * or perhaps use mng:bit_depth and mng:color_type instead for
12352 * global settings.
12353 */
12354
12355 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12356 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12357 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12358
12359 write_jng=MagickFalse;
12360 if (image_info->compression == JPEGCompression)
12361 write_jng=MagickTrue;
12362
12363 mng_info->adjoin=image_info->adjoin &&
12364 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12365
cristy3ed852e2009-09-05 21:47:34 +000012366 if (logging != MagickFalse)
12367 {
12368 /* Log some info about the input */
12369 Image
12370 *p;
12371
12372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12373 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012374
cristy3ed852e2009-09-05 21:47:34 +000012375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012376 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012377
cristy3ed852e2009-09-05 21:47:34 +000012378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12379 " Type: %d",image_info->type);
12380
12381 scene=0;
12382 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12383 {
12384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012385 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012386
cristy3ed852e2009-09-05 21:47:34 +000012387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012388 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012389
cristy3ed852e2009-09-05 21:47:34 +000012390 if (p->matte)
12391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12392 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012393
cristy3ed852e2009-09-05 21:47:34 +000012394 else
12395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12396 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012397
cristy3ed852e2009-09-05 21:47:34 +000012398 if (p->storage_class == PseudoClass)
12399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12400 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012401
cristy3ed852e2009-09-05 21:47:34 +000012402 else
12403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12404 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012405
cristy3ed852e2009-09-05 21:47:34 +000012406 if (p->colors)
12407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012408 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012409
cristy3ed852e2009-09-05 21:47:34 +000012410 else
12411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12412 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012413
cristy3ed852e2009-09-05 21:47:34 +000012414 if (mng_info->adjoin == MagickFalse)
12415 break;
12416 }
12417 }
12418
cristy3ed852e2009-09-05 21:47:34 +000012419 use_global_plte=MagickFalse;
12420 all_images_are_gray=MagickFalse;
12421#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12422 need_local_plte=MagickTrue;
12423#endif
12424 need_defi=MagickFalse;
12425 need_matte=MagickFalse;
12426 mng_info->framing_mode=1;
12427 mng_info->old_framing_mode=1;
12428
12429 if (write_mng)
12430 if (image_info->page != (char *) NULL)
12431 {
12432 /*
12433 Determine image bounding box.
12434 */
12435 SetGeometry(image,&mng_info->page);
12436 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12437 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12438 }
12439 if (write_mng)
12440 {
12441 unsigned int
12442 need_geom;
12443
12444 unsigned short
12445 red,
12446 green,
12447 blue;
12448
12449 mng_info->page=image->page;
12450 need_geom=MagickTrue;
12451 if (mng_info->page.width || mng_info->page.height)
12452 need_geom=MagickFalse;
12453 /*
12454 Check all the scenes.
12455 */
12456 initial_delay=image->delay;
12457 need_iterations=MagickFalse;
12458 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12459 mng_info->equal_physs=MagickTrue,
12460 mng_info->equal_gammas=MagickTrue;
12461 mng_info->equal_srgbs=MagickTrue;
12462 mng_info->equal_backgrounds=MagickTrue;
12463 image_count=0;
12464#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12465 defined(PNG_MNG_FEATURES_SUPPORTED)
12466 all_images_are_gray=MagickTrue;
12467 mng_info->equal_palettes=MagickFalse;
12468 need_local_plte=MagickFalse;
12469#endif
12470 for (next_image=image; next_image != (Image *) NULL; )
12471 {
12472 if (need_geom)
12473 {
12474 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12475 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012476
cristy3ed852e2009-09-05 21:47:34 +000012477 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12478 mng_info->page.height=next_image->rows+next_image->page.y;
12479 }
glennrp0fe50b42010-11-16 03:52:51 +000012480
cristy3ed852e2009-09-05 21:47:34 +000012481 if (next_image->page.x || next_image->page.y)
12482 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012483
cristy3ed852e2009-09-05 21:47:34 +000012484 if (next_image->matte)
12485 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012486
cristy3ed852e2009-09-05 21:47:34 +000012487 if ((int) next_image->dispose >= BackgroundDispose)
12488 if (next_image->matte || next_image->page.x || next_image->page.y ||
12489 ((next_image->columns < mng_info->page.width) &&
12490 (next_image->rows < mng_info->page.height)))
12491 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012492
cristy3ed852e2009-09-05 21:47:34 +000012493 if (next_image->iterations)
12494 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012495
cristy3ed852e2009-09-05 21:47:34 +000012496 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012497
cristy3ed852e2009-09-05 21:47:34 +000012498 if (final_delay != initial_delay || final_delay > 1UL*
12499 next_image->ticks_per_second)
12500 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012501
cristy3ed852e2009-09-05 21:47:34 +000012502#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12503 defined(PNG_MNG_FEATURES_SUPPORTED)
12504 /*
12505 check for global palette possibility.
12506 */
12507 if (image->matte != MagickFalse)
12508 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012509
cristy3ed852e2009-09-05 21:47:34 +000012510 if (need_local_plte == 0)
12511 {
cristyc82a27b2011-10-21 01:07:16 +000012512 if (ImageIsGray(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012513 all_images_are_gray=MagickFalse;
12514 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12515 if (use_global_plte == 0)
12516 use_global_plte=mng_info->equal_palettes;
12517 need_local_plte=!mng_info->equal_palettes;
12518 }
12519#endif
12520 if (GetNextImageInList(next_image) != (Image *) NULL)
12521 {
12522 if (next_image->background_color.red !=
12523 next_image->next->background_color.red ||
12524 next_image->background_color.green !=
12525 next_image->next->background_color.green ||
12526 next_image->background_color.blue !=
12527 next_image->next->background_color.blue)
12528 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012529
cristy3ed852e2009-09-05 21:47:34 +000012530 if (next_image->gamma != next_image->next->gamma)
12531 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012532
cristy3ed852e2009-09-05 21:47:34 +000012533 if (next_image->rendering_intent !=
12534 next_image->next->rendering_intent)
12535 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012536
cristy3ed852e2009-09-05 21:47:34 +000012537 if ((next_image->units != next_image->next->units) ||
cristy2a11bef2011-10-28 18:33:11 +000012538 (next_image->resolution.x != next_image->next->resolution.x) ||
12539 (next_image->resolution.y != next_image->next->resolution.y))
cristy3ed852e2009-09-05 21:47:34 +000012540 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012541
cristy3ed852e2009-09-05 21:47:34 +000012542 if (mng_info->equal_chrms)
12543 {
12544 if (next_image->chromaticity.red_primary.x !=
12545 next_image->next->chromaticity.red_primary.x ||
12546 next_image->chromaticity.red_primary.y !=
12547 next_image->next->chromaticity.red_primary.y ||
12548 next_image->chromaticity.green_primary.x !=
12549 next_image->next->chromaticity.green_primary.x ||
12550 next_image->chromaticity.green_primary.y !=
12551 next_image->next->chromaticity.green_primary.y ||
12552 next_image->chromaticity.blue_primary.x !=
12553 next_image->next->chromaticity.blue_primary.x ||
12554 next_image->chromaticity.blue_primary.y !=
12555 next_image->next->chromaticity.blue_primary.y ||
12556 next_image->chromaticity.white_point.x !=
12557 next_image->next->chromaticity.white_point.x ||
12558 next_image->chromaticity.white_point.y !=
12559 next_image->next->chromaticity.white_point.y)
12560 mng_info->equal_chrms=MagickFalse;
12561 }
12562 }
12563 image_count++;
12564 next_image=GetNextImageInList(next_image);
12565 }
12566 if (image_count < 2)
12567 {
12568 mng_info->equal_backgrounds=MagickFalse;
12569 mng_info->equal_chrms=MagickFalse;
12570 mng_info->equal_gammas=MagickFalse;
12571 mng_info->equal_srgbs=MagickFalse;
12572 mng_info->equal_physs=MagickFalse;
12573 use_global_plte=MagickFalse;
12574#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12575 need_local_plte=MagickTrue;
12576#endif
12577 need_iterations=MagickFalse;
12578 }
glennrp0fe50b42010-11-16 03:52:51 +000012579
cristy3ed852e2009-09-05 21:47:34 +000012580 if (mng_info->need_fram == MagickFalse)
12581 {
12582 /*
12583 Only certain framing rates 100/n are exactly representable without
12584 the FRAM chunk but we'll allow some slop in VLC files
12585 */
12586 if (final_delay == 0)
12587 {
12588 if (need_iterations != MagickFalse)
12589 {
12590 /*
12591 It's probably a GIF with loop; don't run it *too* fast.
12592 */
glennrp02617122010-07-28 13:07:35 +000012593 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012594 {
12595 final_delay=10;
cristyc82a27b2011-10-21 01:07:16 +000012596 (void) ThrowMagickException(exception,GetMagickModule(),
12597 CoderWarning,
glennrpd908de42010-07-28 13:28:27 +000012598 "input has zero delay between all frames; assuming",
12599 " 10 cs `%s'","");
12600 }
cristy3ed852e2009-09-05 21:47:34 +000012601 }
12602 else
12603 mng_info->ticks_per_second=0;
12604 }
12605 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012606 mng_info->ticks_per_second=(png_uint_32)
12607 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012608 if (final_delay > 50)
12609 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012610
cristy3ed852e2009-09-05 21:47:34 +000012611 if (final_delay > 75)
12612 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012613
cristy3ed852e2009-09-05 21:47:34 +000012614 if (final_delay > 125)
12615 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012616
cristy3ed852e2009-09-05 21:47:34 +000012617 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12618 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12619 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12620 1UL*image->ticks_per_second))
12621 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12622 }
glennrp0fe50b42010-11-16 03:52:51 +000012623
cristy3ed852e2009-09-05 21:47:34 +000012624 if (mng_info->need_fram != MagickFalse)
12625 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12626 /*
12627 If pseudocolor, we should also check to see if all the
12628 palettes are identical and write a global PLTE if they are.
12629 ../glennrp Feb 99.
12630 */
12631 /*
12632 Write the MNG version 1.0 signature and MHDR chunk.
12633 */
12634 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12635 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12636 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012637 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012638 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12639 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012640 PNGLong(chunk+12,mng_info->ticks_per_second);
12641 PNGLong(chunk+16,0L); /* layer count=unknown */
12642 PNGLong(chunk+20,0L); /* frame count=unknown */
12643 PNGLong(chunk+24,0L); /* play time=unknown */
12644 if (write_jng)
12645 {
12646 if (need_matte)
12647 {
12648 if (need_defi || mng_info->need_fram || use_global_plte)
12649 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012650
cristy3ed852e2009-09-05 21:47:34 +000012651 else
12652 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12653 }
glennrp0fe50b42010-11-16 03:52:51 +000012654
cristy3ed852e2009-09-05 21:47:34 +000012655 else
12656 {
12657 if (need_defi || mng_info->need_fram || use_global_plte)
12658 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012659
cristy3ed852e2009-09-05 21:47:34 +000012660 else
12661 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12662 }
12663 }
glennrp0fe50b42010-11-16 03:52:51 +000012664
cristy3ed852e2009-09-05 21:47:34 +000012665 else
12666 {
12667 if (need_matte)
12668 {
12669 if (need_defi || mng_info->need_fram || use_global_plte)
12670 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012671
cristy3ed852e2009-09-05 21:47:34 +000012672 else
12673 PNGLong(chunk+28,9L); /* simplicity=VLC */
12674 }
glennrp0fe50b42010-11-16 03:52:51 +000012675
cristy3ed852e2009-09-05 21:47:34 +000012676 else
12677 {
12678 if (need_defi || mng_info->need_fram || use_global_plte)
12679 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012680
cristy3ed852e2009-09-05 21:47:34 +000012681 else
12682 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12683 }
12684 }
12685 (void) WriteBlob(image,32,chunk);
12686 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12687 option=GetImageOption(image_info,"mng:need-cacheoff");
12688 if (option != (const char *) NULL)
12689 {
12690 size_t
12691 length;
12692
12693 /*
12694 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12695 */
12696 PNGType(chunk,mng_nEED);
12697 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012698 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012699 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012700 length+=4;
12701 (void) WriteBlob(image,length,chunk);
12702 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12703 }
12704 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12705 (GetNextImageInList(image) != (Image *) NULL) &&
12706 (image->iterations != 1))
12707 {
12708 /*
12709 Write MNG TERM chunk
12710 */
12711 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12712 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012713 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012714 chunk[4]=3; /* repeat animation */
12715 chunk[5]=0; /* show last frame when done */
12716 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12717 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012718
cristy3ed852e2009-09-05 21:47:34 +000012719 if (image->iterations == 0)
12720 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012721
cristy3ed852e2009-09-05 21:47:34 +000012722 else
12723 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012724
cristy3ed852e2009-09-05 21:47:34 +000012725 if (logging != MagickFalse)
12726 {
12727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012728 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12729 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012730
cristy3ed852e2009-09-05 21:47:34 +000012731 if (image->iterations == 0)
12732 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012733 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012734
cristy3ed852e2009-09-05 21:47:34 +000012735 else
12736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012737 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012738 }
12739 (void) WriteBlob(image,14,chunk);
12740 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12741 }
12742 /*
12743 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12744 */
12745 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12746 mng_info->equal_srgbs)
12747 {
12748 /*
12749 Write MNG sRGB chunk
12750 */
12751 (void) WriteBlobMSBULong(image,1L);
12752 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012753 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012754
cristy3ed852e2009-09-05 21:47:34 +000012755 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012756 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012757 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012758 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012759
cristy3ed852e2009-09-05 21:47:34 +000012760 else
glennrpe610a072010-08-05 17:08:46 +000012761 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012762 Magick_RenderingIntent_to_PNG_RenderingIntent(
12763 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012764
cristy3ed852e2009-09-05 21:47:34 +000012765 (void) WriteBlob(image,5,chunk);
12766 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12767 mng_info->have_write_global_srgb=MagickTrue;
12768 }
glennrp0fe50b42010-11-16 03:52:51 +000012769
cristy3ed852e2009-09-05 21:47:34 +000012770 else
12771 {
12772 if (image->gamma && mng_info->equal_gammas)
12773 {
12774 /*
12775 Write MNG gAMA chunk
12776 */
12777 (void) WriteBlobMSBULong(image,4L);
12778 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012779 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012780 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012781 (void) WriteBlob(image,8,chunk);
12782 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12783 mng_info->have_write_global_gama=MagickTrue;
12784 }
12785 if (mng_info->equal_chrms)
12786 {
12787 PrimaryInfo
12788 primary;
12789
12790 /*
12791 Write MNG cHRM chunk
12792 */
12793 (void) WriteBlobMSBULong(image,32L);
12794 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012795 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012796 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012797 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12798 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012799 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012800 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12801 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012802 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012803 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12804 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012805 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012806 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12807 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012808 (void) WriteBlob(image,36,chunk);
12809 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12810 mng_info->have_write_global_chrm=MagickTrue;
12811 }
12812 }
cristy2a11bef2011-10-28 18:33:11 +000012813 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012814 {
12815 /*
12816 Write MNG pHYs chunk
12817 */
12818 (void) WriteBlobMSBULong(image,9L);
12819 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012820 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012821
cristy3ed852e2009-09-05 21:47:34 +000012822 if (image->units == PixelsPerInchResolution)
12823 {
cristy35ef8242010-06-03 16:24:13 +000012824 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012825 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012826
cristy35ef8242010-06-03 16:24:13 +000012827 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012828 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012829
cristy3ed852e2009-09-05 21:47:34 +000012830 chunk[12]=1;
12831 }
glennrp0fe50b42010-11-16 03:52:51 +000012832
cristy3ed852e2009-09-05 21:47:34 +000012833 else
12834 {
12835 if (image->units == PixelsPerCentimeterResolution)
12836 {
cristy35ef8242010-06-03 16:24:13 +000012837 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012838 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012839
cristy35ef8242010-06-03 16:24:13 +000012840 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012841 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012842
cristy3ed852e2009-09-05 21:47:34 +000012843 chunk[12]=1;
12844 }
glennrp0fe50b42010-11-16 03:52:51 +000012845
cristy3ed852e2009-09-05 21:47:34 +000012846 else
12847 {
cristy2a11bef2011-10-28 18:33:11 +000012848 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12849 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012850 chunk[12]=0;
12851 }
12852 }
12853 (void) WriteBlob(image,13,chunk);
12854 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12855 }
12856 /*
12857 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12858 or does not cover the entire frame.
12859 */
12860 if (write_mng && (image->matte || image->page.x > 0 ||
12861 image->page.y > 0 || (image->page.width &&
12862 (image->page.width+image->page.x < mng_info->page.width))
12863 || (image->page.height && (image->page.height+image->page.y
12864 < mng_info->page.height))))
12865 {
12866 (void) WriteBlobMSBULong(image,6L);
12867 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012868 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012869 red=ScaleQuantumToShort(image->background_color.red);
12870 green=ScaleQuantumToShort(image->background_color.green);
12871 blue=ScaleQuantumToShort(image->background_color.blue);
12872 PNGShort(chunk+4,red);
12873 PNGShort(chunk+6,green);
12874 PNGShort(chunk+8,blue);
12875 (void) WriteBlob(image,10,chunk);
12876 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12877 if (mng_info->equal_backgrounds)
12878 {
12879 (void) WriteBlobMSBULong(image,6L);
12880 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012881 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012882 (void) WriteBlob(image,10,chunk);
12883 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12884 }
12885 }
12886
12887#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12888 if ((need_local_plte == MagickFalse) &&
12889 (image->storage_class == PseudoClass) &&
12890 (all_images_are_gray == MagickFalse))
12891 {
cristybb503372010-05-27 20:51:26 +000012892 size_t
cristy3ed852e2009-09-05 21:47:34 +000012893 data_length;
12894
12895 /*
12896 Write MNG PLTE chunk
12897 */
12898 data_length=3*image->colors;
12899 (void) WriteBlobMSBULong(image,data_length);
12900 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012901 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012902
cristybb503372010-05-27 20:51:26 +000012903 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012904 {
cristy5f07f702011-09-26 17:29:10 +000012905 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12906 image->colormap[i].red) & 0xff);
12907 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12908 image->colormap[i].green) & 0xff);
12909 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12910 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000012911 }
glennrp0fe50b42010-11-16 03:52:51 +000012912
cristy3ed852e2009-09-05 21:47:34 +000012913 (void) WriteBlob(image,data_length+4,chunk);
12914 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12915 mng_info->have_write_global_plte=MagickTrue;
12916 }
12917#endif
12918 }
12919 scene=0;
12920 mng_info->delay=0;
12921#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12922 defined(PNG_MNG_FEATURES_SUPPORTED)
12923 mng_info->equal_palettes=MagickFalse;
12924#endif
12925 do
12926 {
12927 if (mng_info->adjoin)
12928 {
12929#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12930 defined(PNG_MNG_FEATURES_SUPPORTED)
12931 /*
12932 If we aren't using a global palette for the entire MNG, check to
12933 see if we can use one for two or more consecutive images.
12934 */
12935 if (need_local_plte && use_global_plte && !all_images_are_gray)
12936 {
12937 if (mng_info->IsPalette)
12938 {
12939 /*
12940 When equal_palettes is true, this image has the same palette
12941 as the previous PseudoClass image
12942 */
12943 mng_info->have_write_global_plte=mng_info->equal_palettes;
12944 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12945 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12946 {
12947 /*
12948 Write MNG PLTE chunk
12949 */
cristybb503372010-05-27 20:51:26 +000012950 size_t
cristy3ed852e2009-09-05 21:47:34 +000012951 data_length;
12952
12953 data_length=3*image->colors;
12954 (void) WriteBlobMSBULong(image,data_length);
12955 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012956 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012957
cristybb503372010-05-27 20:51:26 +000012958 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012959 {
12960 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12961 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12962 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12963 }
glennrp0fe50b42010-11-16 03:52:51 +000012964
cristy3ed852e2009-09-05 21:47:34 +000012965 (void) WriteBlob(image,data_length+4,chunk);
12966 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12967 (uInt) (data_length+4)));
12968 mng_info->have_write_global_plte=MagickTrue;
12969 }
12970 }
12971 else
12972 mng_info->have_write_global_plte=MagickFalse;
12973 }
12974#endif
12975 if (need_defi)
12976 {
cristybb503372010-05-27 20:51:26 +000012977 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012978 previous_x,
12979 previous_y;
12980
12981 if (scene)
12982 {
12983 previous_x=mng_info->page.x;
12984 previous_y=mng_info->page.y;
12985 }
12986 else
12987 {
12988 previous_x=0;
12989 previous_y=0;
12990 }
12991 mng_info->page=image->page;
12992 if ((mng_info->page.x != previous_x) ||
12993 (mng_info->page.y != previous_y))
12994 {
12995 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12996 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012997 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012998 chunk[4]=0; /* object 0 MSB */
12999 chunk[5]=0; /* object 0 LSB */
13000 chunk[6]=0; /* visible */
13001 chunk[7]=0; /* abstract */
13002 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13003 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13004 (void) WriteBlob(image,16,chunk);
13005 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13006 }
13007 }
13008 }
13009
13010 mng_info->write_mng=write_mng;
13011
13012 if ((int) image->dispose >= 3)
13013 mng_info->framing_mode=3;
13014
13015 if (mng_info->need_fram && mng_info->adjoin &&
13016 ((image->delay != mng_info->delay) ||
13017 (mng_info->framing_mode != mng_info->old_framing_mode)))
13018 {
13019 if (image->delay == mng_info->delay)
13020 {
13021 /*
13022 Write a MNG FRAM chunk with the new framing mode.
13023 */
13024 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13025 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013026 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000013027 chunk[4]=(unsigned char) mng_info->framing_mode;
13028 (void) WriteBlob(image,5,chunk);
13029 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13030 }
13031 else
13032 {
13033 /*
13034 Write a MNG FRAM chunk with the delay.
13035 */
13036 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13037 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013038 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013039 chunk[4]=(unsigned char) mng_info->framing_mode;
13040 chunk[5]=0; /* frame name separator (no name) */
13041 chunk[6]=2; /* flag for changing default delay */
13042 chunk[7]=0; /* flag for changing frame timeout */
13043 chunk[8]=0; /* flag for changing frame clipping */
13044 chunk[9]=0; /* flag for changing frame sync_id */
13045 PNGLong(chunk+10,(png_uint_32)
13046 ((mng_info->ticks_per_second*
13047 image->delay)/MagickMax(image->ticks_per_second,1)));
13048 (void) WriteBlob(image,14,chunk);
13049 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000013050 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000013051 }
13052 mng_info->old_framing_mode=mng_info->framing_mode;
13053 }
13054
13055#if defined(JNG_SUPPORTED)
13056 if (image_info->compression == JPEGCompression)
13057 {
13058 ImageInfo
13059 *write_info;
13060
13061 if (logging != MagickFalse)
13062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13063 " Writing JNG object.");
13064 /* To do: specify the desired alpha compression method. */
13065 write_info=CloneImageInfo(image_info);
13066 write_info->compression=UndefinedCompression;
cristy018f07f2011-09-04 21:15:19 +000013067 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013068 write_info=DestroyImageInfo(write_info);
13069 }
13070 else
13071#endif
13072 {
13073 if (logging != MagickFalse)
13074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13075 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013076
glennrpb9cfe272010-12-21 15:08:06 +000013077 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013078 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013079
13080 /* We don't want any ancillary chunks written */
13081 mng_info->ping_exclude_bKGD=MagickTrue;
13082 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013083 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013084 mng_info->ping_exclude_EXIF=MagickTrue;
13085 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013086 mng_info->ping_exclude_iCCP=MagickTrue;
13087 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13088 mng_info->ping_exclude_oFFs=MagickTrue;
13089 mng_info->ping_exclude_pHYs=MagickTrue;
13090 mng_info->ping_exclude_sRGB=MagickTrue;
13091 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013092 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013093 mng_info->ping_exclude_vpAg=MagickTrue;
13094 mng_info->ping_exclude_zCCP=MagickTrue;
13095 mng_info->ping_exclude_zTXt=MagickTrue;
13096
cristy018f07f2011-09-04 21:15:19 +000013097 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013098 }
13099
13100 if (status == MagickFalse)
13101 {
13102 MngInfoFreeStruct(mng_info,&have_mng_structure);
13103 (void) CloseBlob(image);
13104 return(MagickFalse);
13105 }
13106 (void) CatchImageException(image);
13107 if (GetNextImageInList(image) == (Image *) NULL)
13108 break;
13109 image=SyncNextImageInList(image);
13110 status=SetImageProgress(image,SaveImagesTag,scene++,
13111 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013112
cristy3ed852e2009-09-05 21:47:34 +000013113 if (status == MagickFalse)
13114 break;
glennrp0fe50b42010-11-16 03:52:51 +000013115
cristy3ed852e2009-09-05 21:47:34 +000013116 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013117
cristy3ed852e2009-09-05 21:47:34 +000013118 if (write_mng)
13119 {
13120 while (GetPreviousImageInList(image) != (Image *) NULL)
13121 image=GetPreviousImageInList(image);
13122 /*
13123 Write the MEND chunk.
13124 */
13125 (void) WriteBlobMSBULong(image,0x00000000L);
13126 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013127 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013128 (void) WriteBlob(image,4,chunk);
13129 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13130 }
13131 /*
13132 Relinquish resources.
13133 */
13134 (void) CloseBlob(image);
13135 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013136
cristy3ed852e2009-09-05 21:47:34 +000013137 if (logging != MagickFalse)
13138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013139
cristy3ed852e2009-09-05 21:47:34 +000013140 return(MagickTrue);
13141}
glennrpd5045b42010-03-24 12:40:35 +000013142#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013143
cristy3ed852e2009-09-05 21:47:34 +000013144static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13145{
glennrp3bd393f2011-12-21 18:54:53 +000013146 (void) image;
cristy3ed852e2009-09-05 21:47:34 +000013147 printf("Your PNG library is too old: You have libpng-%s\n",
13148 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013149
cristy3ed852e2009-09-05 21:47:34 +000013150 ThrowBinaryException(CoderError,"PNG library is too old",
13151 image_info->filename);
13152}
glennrp39992b42010-11-14 00:03:43 +000013153
cristy3ed852e2009-09-05 21:47:34 +000013154static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13155{
13156 return(WritePNGImage(image_info,image));
13157}
glennrpd5045b42010-03-24 12:40:35 +000013158#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013159#endif