blob: fa709897bd23ae4f4341656ff835b2edd6b11d7f [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP N N GGGG %
7% P P NN N G %
8% PPPP N N N G GG %
9% P N NN G G %
10% P N N GGG %
11% %
12% %
13% Read/Write Portable Network Graphics Image Format %
14% %
15% Software Design %
16% John Cristy %
17% Glenn Randers-Pehrson %
18% November 1997 %
19% %
20% %
cristy1454be72011-12-19 01:52:48 +000021% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000022% dedicated to making software imaging solutions freely available. %
23% %
24% You may not use this file except in compliance with the License. You may %
25% obtain a copy of the License at %
26% %
27% http://www.imagemagick.org/script/license.php %
28% %
29% Unless required by applicable law or agreed to in writing, software %
30% distributed under the License is distributed on an "AS IS" BASIS, %
31% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32% See the License for the specific language governing permissions and %
33% limitations under the License. %
34% %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38*/
39
40
41/*
42 Include declarations.
43*/
cristy4c08aed2011-07-01 19:47:50 +000044#include "MagickCore/studio.h"
45#include "MagickCore/artifact.h"
46#include "MagickCore/attribute.h"
47#include "MagickCore/blob.h"
48#include "MagickCore/blob-private.h"
49#include "MagickCore/cache.h"
50#include "MagickCore/color.h"
51#include "MagickCore/color-private.h"
52#include "MagickCore/colormap.h"
53#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000054#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000055#include "MagickCore/constitute.h"
56#include "MagickCore/enhance.h"
57#include "MagickCore/exception.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/geometry.h"
60#include "MagickCore/histogram.h"
61#include "MagickCore/image.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/layer.h"
64#include "MagickCore/list.h"
65#include "MagickCore/log.h"
66#include "MagickCore/MagickCore.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/module.h"
69#include "MagickCore/monitor.h"
70#include "MagickCore/monitor-private.h"
71#include "MagickCore/option.h"
72#include "MagickCore/pixel.h"
73#include "MagickCore/pixel-accessor.h"
74#include "MagickCore/profile.h"
75#include "MagickCore/property.h"
76#include "MagickCore/quantum-private.h"
77#include "MagickCore/resource_.h"
78#include "MagickCore/semaphore.h"
79#include "MagickCore/quantum-private.h"
80#include "MagickCore/static.h"
81#include "MagickCore/statistic.h"
82#include "MagickCore/string_.h"
83#include "MagickCore/string-private.h"
84#include "MagickCore/transform.h"
85#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000086#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp286a6352009-11-09 02:58:50 +000087
glennrp7ef138c2009-11-10 13:50:20 +000088/* Suppress libpng pedantic warnings that were added in
89 * libpng-1.2.41 and libpng-1.4.0. If you are working on
glennrpfaa852b2010-03-30 12:17:00 +000090 * migration to libpng-1.5, remove these defines and then
glennrp7ef138c2009-11-10 13:50:20 +000091 * fix any code that generates warnings.
92 */
glennrp991e92a2010-01-28 03:09:00 +000093/* #define PNG_DEPRECATED Use of this function is deprecated */
glennrpfaa852b2010-03-30 12:17:00 +000094/* #define PNG_USE_RESULT The result of this function must be checked */
95/* #define PNG_NORETURN This function does not return */
96/* #define PNG_ALLOCATED The result of the function is new memory */
glennrp8371ecc2011-02-19 19:15:38 +000097/* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
glennrp5b927342011-04-14 14:31:48 +000098
99/* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
cristy75cfe702011-04-14 14:27:17 +0000100#define PNG_PTR_NORETURN
glennrp286a6352009-11-09 02:58:50 +0000101
cristy3ed852e2009-09-05 21:47:34 +0000102#include "png.h"
103#include "zlib.h"
104
105/* ImageMagick differences */
106#define first_scene scene
107
glennrpd5045b42010-03-24 12:40:35 +0000108#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000109/*
110 Optional declarations. Define or undefine them as you like.
111*/
112/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
113
114/*
115 Features under construction. Define these to work on them.
116*/
117#undef MNG_OBJECT_BUFFERS
118#undef MNG_BASI_SUPPORTED
119#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
120#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
cristy3ed852e2009-09-05 21:47:34 +0000121#if defined(MAGICKCORE_JPEG_DELEGATE)
122# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
123#endif
124#if !defined(RGBColorMatchExact)
125#define IsPNGColorEqual(color,target) \
glennrp8e045c82011-04-27 16:40:27 +0000126 (((color).red == (target).red) && \
127 ((color).green == (target).green) && \
128 ((color).blue == (target).blue))
cristy3ed852e2009-09-05 21:47:34 +0000129#endif
130
glennrp8e58efd2011-05-20 12:16:29 +0000131/* Macros for left-bit-replication to ensure that pixels
glennrp1a2061f2011-10-18 12:30:45 +0000132 * and PixelInfos all have the same image->depth, and for use
glennrp8e58efd2011-05-20 12:16:29 +0000133 * in PNG8 quantization.
134 */
135
136
137/* LBR01: Replicate top bit */
138
glennrp05001c32011-08-06 13:04:16 +0000139#define LBR01PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000140 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
141 0 : QuantumRange);
142
glennrp91d99252011-06-25 14:30:13 +0000143#define LBR01PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000144 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
145 0 : QuantumRange);
146
glennrp91d99252011-06-25 14:30:13 +0000147#define LBR01PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000148 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
149 0 : QuantumRange);
150
cristy4c08aed2011-07-01 19:47:50 +0000151#define LBR01PacketAlpha(pixelpacket) \
152 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000153 0 : QuantumRange);
154
glennrp91d99252011-06-25 14:30:13 +0000155#define LBR01PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000156 { \
glennrp05001c32011-08-06 13:04:16 +0000157 LBR01PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000158 LBR01PacketGreen((pixelpacket)); \
159 LBR01PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000160 }
glennrp8e58efd2011-05-20 12:16:29 +0000161
glennrp91d99252011-06-25 14:30:13 +0000162#define LBR01PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000163 { \
glennrp91d99252011-06-25 14:30:13 +0000164 LBR01PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000165 LBR01PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000166 }
glennrp8e58efd2011-05-20 12:16:29 +0000167
cristyef618312011-06-25 12:26:44 +0000168#define LBR01PixelRed(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000169 (ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000170 0 : QuantumRange);
171
glennrp54cf7972011-08-06 14:28:09 +0000172#define LBR01PixelGreen(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000173 (ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000174 0 : QuantumRange);
175
glennrp54cf7972011-08-06 14:28:09 +0000176#define LBR01PixelBlue(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000177 (ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000178 0 : QuantumRange);
179
glennrp54cf7972011-08-06 14:28:09 +0000180#define LBR01PixelAlpha(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000181 (ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000182 0 : QuantumRange);
183
glennrp54cf7972011-08-06 14:28:09 +0000184#define LBR01PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000185 { \
cristyef618312011-06-25 12:26:44 +0000186 LBR01PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000187 LBR01PixelGreen((pixel)); \
188 LBR01PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000189 }
glennrp8e58efd2011-05-20 12:16:29 +0000190
glennrp54cf7972011-08-06 14:28:09 +0000191#define LBR01PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000192 { \
glennrp54cf7972011-08-06 14:28:09 +0000193 LBR01PixelRGB((pixel)); \
194 LBR01PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000195 }
glennrp8e58efd2011-05-20 12:16:29 +0000196
197/* LBR02: Replicate top 2 bits */
198
glennrp05001c32011-08-06 13:04:16 +0000199#define LBR02PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000200 { \
201 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
202 (pixelpacket).red=ScaleCharToQuantum( \
203 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
204 }
glennrp91d99252011-06-25 14:30:13 +0000205#define LBR02PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000206 { \
207 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
208 (pixelpacket).green=ScaleCharToQuantum( \
209 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
210 }
glennrp91d99252011-06-25 14:30:13 +0000211#define LBR02PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000212 { \
213 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
214 (pixelpacket).blue=ScaleCharToQuantum( \
215 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
216 }
cristy4c08aed2011-07-01 19:47:50 +0000217#define LBR02PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000218 { \
cristy4c08aed2011-07-01 19:47:50 +0000219 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
220 (pixelpacket).alpha=ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000221 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
222 }
223
glennrp91d99252011-06-25 14:30:13 +0000224#define LBR02PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000225 { \
glennrp05001c32011-08-06 13:04:16 +0000226 LBR02PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000227 LBR02PacketGreen((pixelpacket)); \
228 LBR02PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000229 }
glennrp8e58efd2011-05-20 12:16:29 +0000230
glennrp91d99252011-06-25 14:30:13 +0000231#define LBR02PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000232 { \
glennrp91d99252011-06-25 14:30:13 +0000233 LBR02PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000234 LBR02PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000235 }
glennrp8e58efd2011-05-20 12:16:29 +0000236
cristyef618312011-06-25 12:26:44 +0000237#define LBR02PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000238 { \
cristy4c08aed2011-07-01 19:47:50 +0000239 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000240 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000241 SetPixelRed(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000242 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
243 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000244 }
glennrp54cf7972011-08-06 14:28:09 +0000245#define LBR02PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000246 { \
cristy4c08aed2011-07-01 19:47:50 +0000247 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000248 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000249 SetPixelGreen(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000250 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
251 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000252 }
glennrp54cf7972011-08-06 14:28:09 +0000253#define LBR02PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000254 { \
255 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000256 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
257 SetPixelBlue(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000258 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
259 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000260 }
glennrp54cf7972011-08-06 14:28:09 +0000261#define LBR02PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000262 { \
263 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000264 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
265 SetPixelAlpha(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000266 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
267 (pixel) ); \
glennrp8e58efd2011-05-20 12:16:29 +0000268 }
269
glennrp54cf7972011-08-06 14:28:09 +0000270#define LBR02PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000271 { \
cristyef618312011-06-25 12:26:44 +0000272 LBR02PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000273 LBR02PixelGreen((pixel)); \
274 LBR02PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000275 }
glennrp8e58efd2011-05-20 12:16:29 +0000276
glennrp54cf7972011-08-06 14:28:09 +0000277#define LBR02PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000278 { \
glennrp54cf7972011-08-06 14:28:09 +0000279 LBR02PixelRGB((pixel)); \
280 LBR02PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000281 }
glennrp8e58efd2011-05-20 12:16:29 +0000282
283/* LBR03: Replicate top 3 bits (only used with opaque pixels during
284 PNG8 quantization) */
285
glennrp05001c32011-08-06 13:04:16 +0000286#define LBR03PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000287 { \
288 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
289 (pixelpacket).red=ScaleCharToQuantum( \
290 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
291 }
glennrp91d99252011-06-25 14:30:13 +0000292#define LBR03PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000293 { \
294 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
295 (pixelpacket).green=ScaleCharToQuantum( \
296 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
297 }
glennrp91d99252011-06-25 14:30:13 +0000298#define LBR03PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000299 { \
300 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
301 (pixelpacket).blue=ScaleCharToQuantum( \
302 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
303 }
304
glennrp91d99252011-06-25 14:30:13 +0000305#define LBR03PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000306 { \
glennrp05001c32011-08-06 13:04:16 +0000307 LBR03PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000308 LBR03PacketGreen((pixelpacket)); \
309 LBR03PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000310 }
glennrp8e58efd2011-05-20 12:16:29 +0000311
cristyef618312011-06-25 12:26:44 +0000312#define LBR03PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000313 { \
cristy4c08aed2011-07-01 19:47:50 +0000314 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000315 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000316 SetPixelRed(image, ScaleCharToQuantum( \
317 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000318 }
cristy4c08aed2011-07-01 19:47:50 +0000319#define LBR03Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000320 { \
cristy4c08aed2011-07-01 19:47:50 +0000321 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000322 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000323 SetPixelGreen(image, ScaleCharToQuantum( \
324 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000325 }
cristy4c08aed2011-07-01 19:47:50 +0000326#define LBR03Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000327 { \
cristy4c08aed2011-07-01 19:47:50 +0000328 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000329 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000330 SetPixelBlue(image, ScaleCharToQuantum( \
331 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000332 }
333
cristy4c08aed2011-07-01 19:47:50 +0000334#define LBR03RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000335 { \
cristyef618312011-06-25 12:26:44 +0000336 LBR03PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000337 LBR03Green((pixel)); \
338 LBR03Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000339 }
glennrp8e58efd2011-05-20 12:16:29 +0000340
341/* LBR04: Replicate top 4 bits */
342
glennrp05001c32011-08-06 13:04:16 +0000343#define LBR04PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000344 { \
345 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
346 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
347 }
glennrp91d99252011-06-25 14:30:13 +0000348#define LBR04PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000349 { \
350 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
351 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
352 }
glennrp91d99252011-06-25 14:30:13 +0000353#define LBR04PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000354 { \
355 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
356 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
357 }
cristy4c08aed2011-07-01 19:47:50 +0000358#define LBR04PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000359 { \
cristy4c08aed2011-07-01 19:47:50 +0000360 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
361 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
glennrp8e58efd2011-05-20 12:16:29 +0000362 }
363
glennrp91d99252011-06-25 14:30:13 +0000364#define LBR04PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000365 { \
glennrp05001c32011-08-06 13:04:16 +0000366 LBR04PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000367 LBR04PacketGreen((pixelpacket)); \
368 LBR04PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000369 }
glennrp8e58efd2011-05-20 12:16:29 +0000370
glennrp91d99252011-06-25 14:30:13 +0000371#define LBR04PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000372 { \
glennrp91d99252011-06-25 14:30:13 +0000373 LBR04PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000374 LBR04PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000375 }
glennrp8e58efd2011-05-20 12:16:29 +0000376
cristyef618312011-06-25 12:26:44 +0000377#define LBR04PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000378 { \
cristy4c08aed2011-07-01 19:47:50 +0000379 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000380 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000381 SetPixelRed(image,\
382 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000383 }
glennrp54cf7972011-08-06 14:28:09 +0000384#define LBR04PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000385 { \
cristy4c08aed2011-07-01 19:47:50 +0000386 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000387 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000388 SetPixelGreen(image,\
389 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000390 }
glennrp54cf7972011-08-06 14:28:09 +0000391#define LBR04PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000392 { \
393 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000394 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
395 SetPixelBlue(image,\
396 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000397 }
glennrp54cf7972011-08-06 14:28:09 +0000398#define LBR04PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000399 { \
400 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000401 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
402 SetPixelAlpha(image,\
403 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000404 }
405
glennrp54cf7972011-08-06 14:28:09 +0000406#define LBR04PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000407 { \
cristyef618312011-06-25 12:26:44 +0000408 LBR04PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000409 LBR04PixelGreen((pixel)); \
410 LBR04PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000411 }
glennrp8e58efd2011-05-20 12:16:29 +0000412
glennrp54cf7972011-08-06 14:28:09 +0000413#define LBR04PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000414 { \
glennrp54cf7972011-08-06 14:28:09 +0000415 LBR04PixelRGB((pixel)); \
416 LBR04PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000417 }
glennrp8e58efd2011-05-20 12:16:29 +0000418
419
420/* LBR08: Replicate top 8 bits */
421
glennrp05001c32011-08-06 13:04:16 +0000422#define LBR08PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000423 { \
424 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
425 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
426 }
glennrp91d99252011-06-25 14:30:13 +0000427#define LBR08PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000428 { \
429 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
430 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
431 }
glennrp91d99252011-06-25 14:30:13 +0000432#define LBR08PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000433 { \
434 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
435 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
436 }
cristy4c08aed2011-07-01 19:47:50 +0000437#define LBR08PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000438 { \
cristy4c08aed2011-07-01 19:47:50 +0000439 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
440 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000441 }
442
glennrp91d99252011-06-25 14:30:13 +0000443#define LBR08PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000444 { \
glennrp05001c32011-08-06 13:04:16 +0000445 LBR08PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000446 LBR08PacketGreen((pixelpacket)); \
447 LBR08PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000448 }
glennrp8e58efd2011-05-20 12:16:29 +0000449
glennrp91d99252011-06-25 14:30:13 +0000450#define LBR08PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000451 { \
glennrp91d99252011-06-25 14:30:13 +0000452 LBR08PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000453 LBR08PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000454 }
glennrp8e58efd2011-05-20 12:16:29 +0000455
cristyef618312011-06-25 12:26:44 +0000456#define LBR08PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000457 { \
458 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000459 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
460 SetPixelRed(image,\
461 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000462 }
glennrp54cf7972011-08-06 14:28:09 +0000463#define LBR08PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000464 { \
465 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000466 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
467 SetPixelGreen(image,\
468 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000469 }
glennrp54cf7972011-08-06 14:28:09 +0000470#define LBR08PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000471 { \
472 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000473 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
474 SetPixelBlue(image,\
475 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000476 }
glennrp54cf7972011-08-06 14:28:09 +0000477#define LBR08PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000478 { \
479 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000480 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
481 SetPixelAlpha(image,\
482 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000483 }
484
glennrp54cf7972011-08-06 14:28:09 +0000485#define LBR08PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000486 { \
cristyef618312011-06-25 12:26:44 +0000487 LBR08PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000488 LBR08PixelGreen((pixel)); \
489 LBR08PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000490 }
glennrp8e58efd2011-05-20 12:16:29 +0000491
glennrp54cf7972011-08-06 14:28:09 +0000492#define LBR08PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000493 { \
glennrp54cf7972011-08-06 14:28:09 +0000494 LBR08PixelRGB((pixel)); \
495 LBR08PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000496 }
glennrp8e58efd2011-05-20 12:16:29 +0000497
498
499/* LBR16: Replicate top 16 bits */
500
glennrp05001c32011-08-06 13:04:16 +0000501#define LBR16PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000502 { \
503 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
504 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
505 }
glennrp91d99252011-06-25 14:30:13 +0000506#define LBR16PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000507 { \
508 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
509 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
510 }
glennrp91d99252011-06-25 14:30:13 +0000511#define LBR16PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000512 { \
513 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
514 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
515 }
cristy4c08aed2011-07-01 19:47:50 +0000516#define LBR16PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000517 { \
cristy4c08aed2011-07-01 19:47:50 +0000518 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
519 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000520 }
521
glennrp91d99252011-06-25 14:30:13 +0000522#define LBR16PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000523 { \
glennrp05001c32011-08-06 13:04:16 +0000524 LBR16PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000525 LBR16PacketGreen((pixelpacket)); \
526 LBR16PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000527 }
glennrp8e58efd2011-05-20 12:16:29 +0000528
glennrp91d99252011-06-25 14:30:13 +0000529#define LBR16PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000530 { \
glennrp91d99252011-06-25 14:30:13 +0000531 LBR16PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000532 LBR16PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000533 }
glennrp8e58efd2011-05-20 12:16:29 +0000534
cristyef618312011-06-25 12:26:44 +0000535#define LBR16PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000536 { \
537 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000538 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
539 SetPixelRed(image,\
540 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000541 }
glennrp54cf7972011-08-06 14:28:09 +0000542#define LBR16PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000543 { \
544 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000545 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
546 SetPixelGreen(image,\
547 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000548 }
glennrp54cf7972011-08-06 14:28:09 +0000549#define LBR16PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000550 { \
551 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000552 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
553 SetPixelBlue(image,\
554 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000555 }
glennrp54cf7972011-08-06 14:28:09 +0000556#define LBR16PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000557 { \
558 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000559 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
560 SetPixelAlpha(image,\
561 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000562 }
563
glennrp54cf7972011-08-06 14:28:09 +0000564#define LBR16PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000565 { \
cristyef618312011-06-25 12:26:44 +0000566 LBR16PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000567 LBR16PixelGreen((pixel)); \
568 LBR16PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000569 }
glennrp8e58efd2011-05-20 12:16:29 +0000570
glennrp54cf7972011-08-06 14:28:09 +0000571#define LBR16PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000572 { \
glennrp54cf7972011-08-06 14:28:09 +0000573 LBR16PixelRGB((pixel)); \
574 LBR16PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000575 }
glennrp8e58efd2011-05-20 12:16:29 +0000576
cristy3ed852e2009-09-05 21:47:34 +0000577/*
578 Establish thread safety.
579 setjmp/longjmp is claimed to be safe on these platforms:
580 setjmp/longjmp is alleged to be unsafe on these platforms:
581*/
582#ifndef SETJMP_IS_THREAD_SAFE
583#define PNG_SETJMP_NOT_THREAD_SAFE
584#endif
585
586#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
587static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000588 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000589#endif
590
591/*
592 This temporary until I set up malloc'ed object attributes array.
593 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
594 waste more memory.
595*/
596#define MNG_MAX_OBJECTS 256
597
598/*
599 If this not defined, spec is interpreted strictly. If it is
600 defined, an attempt will be made to recover from some errors,
601 including
602 o global PLTE too short
603*/
604#undef MNG_LOOSE
605
606/*
607 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
608 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
609 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
610 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
611 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
612 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
613 will be enabled by default in libpng-1.2.0.
614*/
cristy3ed852e2009-09-05 21:47:34 +0000615#ifdef PNG_MNG_FEATURES_SUPPORTED
616# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
617# define PNG_READ_EMPTY_PLTE_SUPPORTED
618# endif
619# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
620# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
621# endif
622#endif
623
624/*
cristybb503372010-05-27 20:51:26 +0000625 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000626 This macro is only defined in libpng-1.0.3 and later.
627 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
628*/
629#ifndef PNG_UINT_31_MAX
630#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
631#endif
632
633/*
634 Constant strings for known chunk types. If you need to add a chunk,
635 add a string holding the name here. To make the code more
636 portable, we use ASCII numbers like this, not characters.
637*/
638
glennrp85dcf872011-12-07 02:51:47 +0000639static png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
640static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
641static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
642static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
643static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
644static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
645static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
646static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
647static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
648static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
649static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
650static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
651static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
652static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
653static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
654static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
655static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
656static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
657static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
658static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
659static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
660static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
661static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
662static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
663static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
664static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
665static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
666static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
667static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
668static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
669static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
670static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
671static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
672static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000673
674#if defined(JNG_SUPPORTED)
glennrp85dcf872011-12-07 02:51:47 +0000675static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
676static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
677static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
678static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
679static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
680static png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000681#endif
682
683/*
684Other known chunks that are not yet supported by ImageMagick:
glennrp85dcf872011-12-07 02:51:47 +0000685static png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
686static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
687static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
688static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
689static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
690static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
691static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
692static png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000693*/
694
695typedef struct _MngBox
696{
cristy8182b072010-05-30 20:10:53 +0000697 long
cristy3ed852e2009-09-05 21:47:34 +0000698 left,
699 right,
700 top,
701 bottom;
702} MngBox;
703
704typedef struct _MngPair
705{
cristy8182b072010-05-30 20:10:53 +0000706 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000707 a,
708 b;
709} MngPair;
710
711#ifdef MNG_OBJECT_BUFFERS
712typedef struct _MngBuffer
713{
714
cristybb503372010-05-27 20:51:26 +0000715 size_t
cristy3ed852e2009-09-05 21:47:34 +0000716 height,
717 width;
718
719 Image
720 *image;
721
722 png_color
723 plte[256];
724
725 int
726 reference_count;
727
728 unsigned char
729 alpha_sample_depth,
730 compression_method,
731 color_type,
732 concrete,
733 filter_method,
734 frozen,
735 image_type,
736 interlace_method,
737 pixel_sample_depth,
738 plte_length,
739 sample_depth,
740 viewable;
741} MngBuffer;
742#endif
743
744typedef struct _MngInfo
745{
746
747#ifdef MNG_OBJECT_BUFFERS
748 MngBuffer
749 *ob[MNG_MAX_OBJECTS];
750#endif
751
752 Image *
753 image;
754
755 RectangleInfo
756 page;
757
758 int
759 adjoin,
760#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
761 bytes_in_read_buffer,
762 found_empty_plte,
763#endif
764 equal_backgrounds,
765 equal_chrms,
766 equal_gammas,
767#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
768 defined(PNG_MNG_FEATURES_SUPPORTED)
769 equal_palettes,
770#endif
771 equal_physs,
772 equal_srgbs,
773 framing_mode,
774 have_global_bkgd,
775 have_global_chrm,
776 have_global_gama,
777 have_global_phys,
778 have_global_sbit,
779 have_global_srgb,
780 have_saved_bkgd_index,
781 have_write_global_chrm,
782 have_write_global_gama,
783 have_write_global_plte,
784 have_write_global_srgb,
785 need_fram,
786 object_id,
787 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000788 saved_bkgd_index;
789
790 int
791 new_number_colors;
792
cristybb503372010-05-27 20:51:26 +0000793 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000794 image_found,
795 loop_count[256],
796 loop_iteration[256],
797 scenes_found,
798 x_off[MNG_MAX_OBJECTS],
799 y_off[MNG_MAX_OBJECTS];
800
801 MngBox
802 clip,
803 frame,
804 image_box,
805 object_clip[MNG_MAX_OBJECTS];
806
807 unsigned char
808 /* These flags could be combined into one byte */
809 exists[MNG_MAX_OBJECTS],
810 frozen[MNG_MAX_OBJECTS],
811 loop_active[256],
812 invisible[MNG_MAX_OBJECTS],
813 viewable[MNG_MAX_OBJECTS];
814
815 MagickOffsetType
816 loop_jump[256];
817
818 png_colorp
819 global_plte;
820
821 png_color_8
822 global_sbit;
823
824 png_byte
825#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
826 read_buffer[8],
827#endif
828 global_trns[256];
829
830 float
831 global_gamma;
832
833 ChromaticityInfo
834 global_chrm;
835
836 RenderingIntent
837 global_srgb_intent;
838
cristy35ef8242010-06-03 16:24:13 +0000839 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000840 delay,
841 global_plte_length,
842 global_trns_length,
843 global_x_pixels_per_unit,
844 global_y_pixels_per_unit,
845 mng_width,
846 mng_height,
847 ticks_per_second;
848
glennrpb9cfe272010-12-21 15:08:06 +0000849 MagickBooleanType
850 need_blob;
851
cristy3ed852e2009-09-05 21:47:34 +0000852 unsigned int
853 IsPalette,
854 global_phys_unit_type,
855 basi_warning,
856 clon_warning,
857 dhdr_warning,
858 jhdr_warning,
859 magn_warning,
860 past_warning,
861 phyg_warning,
862 phys_warning,
863 sbit_warning,
864 show_warning,
865 mng_type,
866 write_mng,
867 write_png_colortype,
868 write_png_depth,
glennrp18682582011-06-30 18:11:47 +0000869 write_png_compression_level,
870 write_png_compression_strategy,
871 write_png_compression_filter,
cristy3ed852e2009-09-05 21:47:34 +0000872 write_png8,
873 write_png24,
874 write_png32;
875
876#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000877 size_t
cristy3ed852e2009-09-05 21:47:34 +0000878 basi_width,
879 basi_height;
880
881 unsigned int
882 basi_depth,
883 basi_color_type,
884 basi_compression_method,
885 basi_filter_type,
886 basi_interlace_method,
887 basi_red,
888 basi_green,
889 basi_blue,
890 basi_alpha,
891 basi_viewable;
892#endif
893
894 png_uint_16
895 magn_first,
896 magn_last,
897 magn_mb,
898 magn_ml,
899 magn_mr,
900 magn_mt,
901 magn_mx,
902 magn_my,
903 magn_methx,
904 magn_methy;
905
cristy101ab702011-10-13 13:06:32 +0000906 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000907 mng_global_bkgd;
908
glennrp26f37912010-12-23 16:22:42 +0000909 /* Added at version 6.6.6-7 */
910 MagickBooleanType
911 ping_exclude_bKGD,
912 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000913 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000914 ping_exclude_EXIF,
915 ping_exclude_gAMA,
916 ping_exclude_iCCP,
917 /* ping_exclude_iTXt, */
918 ping_exclude_oFFs,
919 ping_exclude_pHYs,
920 ping_exclude_sRGB,
921 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000922 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000923 ping_exclude_vpAg,
924 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000925 ping_exclude_zTXt,
926 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000927
cristy3ed852e2009-09-05 21:47:34 +0000928} MngInfo;
929#endif /* VER */
930
931/*
932 Forward declarations.
933*/
934static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000935 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000936
cristy3ed852e2009-09-05 21:47:34 +0000937static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000938 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000939
cristy3ed852e2009-09-05 21:47:34 +0000940#if defined(JNG_SUPPORTED)
941static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000942 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000943#endif
944
glennrp0c3e06b2010-11-19 13:45:02 +0000945#if PNG_LIBPNG_VER > 10011
946
glennrpfd05d622011-02-25 04:10:33 +0000947
glennrp0c3e06b2010-11-19 13:45:02 +0000948#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
949static MagickBooleanType
cristyc82a27b2011-10-21 01:07:16 +0000950LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
glennrp0c3e06b2010-11-19 13:45:02 +0000951{
glennrp67b9c1a2011-04-22 18:47:36 +0000952 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
953 *
954 * This is true if the high byte and the next highest byte of
955 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000956 * are equal to each other. We check this by seeing if the samples
957 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000958 *
959 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000960 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000961 */
962
glennrp3faa9a32011-04-23 14:00:25 +0000963#define QuantumToCharToQuantumEqQuantum(quantum) \
964 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
965
glennrp0c3e06b2010-11-19 13:45:02 +0000966 MagickBooleanType
967 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000968
glennrp03e11f62011-04-22 13:30:16 +0000969 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000970 {
971
cristy4c08aed2011-07-01 19:47:50 +0000972 const Quantum
glennrp0c3e06b2010-11-19 13:45:02 +0000973 *p;
974
975 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000976 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
977 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
978 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
979 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000980
981 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
982 {
983 int indx;
984
985 for (indx=0; indx < (ssize_t) image->colors; indx++)
986 {
glennrp3faa9a32011-04-23 14:00:25 +0000987 ok_to_reduce=(
988 QuantumToCharToQuantumEqQuantum(
989 image->colormap[indx].red) &&
990 QuantumToCharToQuantumEqQuantum(
991 image->colormap[indx].green) &&
992 QuantumToCharToQuantumEqQuantum(
993 image->colormap[indx].blue)) ?
994 MagickTrue : MagickFalse;
995
glennrp0c3e06b2010-11-19 13:45:02 +0000996 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000997 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000998 }
999 }
1000
1001 if ((ok_to_reduce != MagickFalse) &&
1002 (image->storage_class != PseudoClass))
1003 {
1004 ssize_t
1005 y;
1006
1007 register ssize_t
1008 x;
1009
1010 for (y=0; y < (ssize_t) image->rows; y++)
1011 {
cristyc82a27b2011-10-21 01:07:16 +00001012 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0c3e06b2010-11-19 13:45:02 +00001013
cristy4c08aed2011-07-01 19:47:50 +00001014 if (p == (const Quantum *) NULL)
glennrp0c3e06b2010-11-19 13:45:02 +00001015 {
1016 ok_to_reduce = MagickFalse;
1017 break;
1018 }
1019
1020 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1021 {
glennrp3faa9a32011-04-23 14:00:25 +00001022 ok_to_reduce=
cristy4c08aed2011-07-01 19:47:50 +00001023 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1024 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1025 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
glennrp3faa9a32011-04-23 14:00:25 +00001026 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001027
1028 if (ok_to_reduce == MagickFalse)
1029 break;
1030
cristyed231572011-07-14 02:18:59 +00001031 p+=GetPixelChannels(image);
glennrp0c3e06b2010-11-19 13:45:02 +00001032 }
glennrp8640fb52010-11-23 15:48:26 +00001033 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +00001034 break;
1035 }
1036 }
1037
1038 if (ok_to_reduce != MagickFalse)
1039 {
glennrp0c3e06b2010-11-19 13:45:02 +00001040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001041 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +00001042 }
glennrpa6a06632011-01-19 15:15:34 +00001043 else
1044 {
1045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001046 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +00001047 }
glennrp0c3e06b2010-11-19 13:45:02 +00001048 }
1049
1050 return ok_to_reduce;
1051}
1052#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1053
glennrpe610a072010-08-05 17:08:46 +00001054static int
glennrpcf002022011-01-30 02:38:15 +00001055Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +00001056{
glennrpe610a072010-08-05 17:08:46 +00001057 switch (intent)
1058 {
1059 case PerceptualIntent:
1060 return 0;
glennrp0fe50b42010-11-16 03:52:51 +00001061
glennrpe610a072010-08-05 17:08:46 +00001062 case RelativeIntent:
1063 return 1;
glennrp0fe50b42010-11-16 03:52:51 +00001064
glennrpe610a072010-08-05 17:08:46 +00001065 case SaturationIntent:
1066 return 2;
glennrp0fe50b42010-11-16 03:52:51 +00001067
glennrpe610a072010-08-05 17:08:46 +00001068 case AbsoluteIntent:
1069 return 3;
glennrp0fe50b42010-11-16 03:52:51 +00001070
glennrpe610a072010-08-05 17:08:46 +00001071 default:
1072 return -1;
1073 }
1074}
1075
1076static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +00001077Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +00001078{
glennrpcf002022011-01-30 02:38:15 +00001079 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001080 {
1081 case 0:
1082 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001083
glennrpe610a072010-08-05 17:08:46 +00001084 case 1:
1085 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001086
glennrpe610a072010-08-05 17:08:46 +00001087 case 2:
1088 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001089
glennrpe610a072010-08-05 17:08:46 +00001090 case 3:
1091 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001092
glennrpe610a072010-08-05 17:08:46 +00001093 default:
1094 return UndefinedIntent;
1095 }
1096}
1097
cristybb503372010-05-27 20:51:26 +00001098static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001099{
1100 if (x > y)
1101 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001102
cristy3ed852e2009-09-05 21:47:34 +00001103 return(y);
1104}
glennrp0c3e06b2010-11-19 13:45:02 +00001105
cristybb503372010-05-27 20:51:26 +00001106static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001107{
1108 if (x < y)
1109 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001110
cristy3ed852e2009-09-05 21:47:34 +00001111 return(y);
1112}
glennrp0c3e06b2010-11-19 13:45:02 +00001113
cristy3ed852e2009-09-05 21:47:34 +00001114
1115/*
1116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1117% %
1118% %
1119% %
1120% I m a g e I s G r a y %
1121% %
1122% %
1123% %
1124%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1125% %
cristy4c08aed2011-07-01 19:47:50 +00001126% Like IsImageGray except does not change DirectClass to PseudoClass %
cristy3ed852e2009-09-05 21:47:34 +00001127% %
1128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1129*/
cristyc82a27b2011-10-21 01:07:16 +00001130static MagickBooleanType ImageIsGray(Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001131{
cristy4c08aed2011-07-01 19:47:50 +00001132 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001133 *p;
1134
cristybb503372010-05-27 20:51:26 +00001135 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001136 i,
1137 x,
1138 y;
1139
1140 assert(image != (Image *) NULL);
1141 assert(image->signature == MagickSignature);
1142 if (image->debug != MagickFalse)
1143 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1144
1145 if (image->storage_class == PseudoClass)
1146 {
cristybb503372010-05-27 20:51:26 +00001147 for (i=0; i < (ssize_t) image->colors; i++)
cristy101ab702011-10-13 13:06:32 +00001148 if (IsPixelInfoGray(image->colormap+i) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001149 return(MagickFalse);
1150 return(MagickTrue);
1151 }
cristybb503372010-05-27 20:51:26 +00001152 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001153 {
cristyc82a27b2011-10-21 01:07:16 +00001154 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001155 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001156 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +00001157 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001158 {
cristy4c08aed2011-07-01 19:47:50 +00001159 if (IsPixelGray(image,p) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001160 return(MagickFalse);
cristyed231572011-07-14 02:18:59 +00001161 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001162 }
1163 }
1164 return(MagickTrue);
1165}
glennrpd5045b42010-03-24 12:40:35 +00001166#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001167#endif /* MAGICKCORE_PNG_DELEGATE */
1168
1169/*
1170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1171% %
1172% %
1173% %
1174% I s M N G %
1175% %
1176% %
1177% %
1178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1179%
1180% IsMNG() returns MagickTrue if the image format type, identified by the
1181% magick string, is MNG.
1182%
1183% The format of the IsMNG method is:
1184%
1185% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1186%
1187% A description of each parameter follows:
1188%
1189% o magick: compare image format pattern against these bytes.
1190%
1191% o length: Specifies the length of the magick string.
1192%
1193%
1194*/
1195static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1196{
1197 if (length < 8)
1198 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001199
cristy3ed852e2009-09-05 21:47:34 +00001200 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1201 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001202
cristy3ed852e2009-09-05 21:47:34 +00001203 return(MagickFalse);
1204}
1205
1206/*
1207%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1208% %
1209% %
1210% %
1211% I s J N G %
1212% %
1213% %
1214% %
1215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1216%
1217% IsJNG() returns MagickTrue if the image format type, identified by the
1218% magick string, is JNG.
1219%
1220% The format of the IsJNG method is:
1221%
1222% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1223%
1224% A description of each parameter follows:
1225%
1226% o magick: compare image format pattern against these bytes.
1227%
1228% o length: Specifies the length of the magick string.
1229%
1230%
1231*/
1232static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1233{
1234 if (length < 8)
1235 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001236
cristy3ed852e2009-09-05 21:47:34 +00001237 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1238 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001239
cristy3ed852e2009-09-05 21:47:34 +00001240 return(MagickFalse);
1241}
1242
1243/*
1244%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245% %
1246% %
1247% %
1248% I s P N G %
1249% %
1250% %
1251% %
1252%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1253%
1254% IsPNG() returns MagickTrue if the image format type, identified by the
1255% magick string, is PNG.
1256%
1257% The format of the IsPNG method is:
1258%
1259% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1260%
1261% A description of each parameter follows:
1262%
1263% o magick: compare image format pattern against these bytes.
1264%
1265% o length: Specifies the length of the magick string.
1266%
1267*/
1268static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1269{
1270 if (length < 8)
1271 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001272
cristy3ed852e2009-09-05 21:47:34 +00001273 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1274 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001275
cristy3ed852e2009-09-05 21:47:34 +00001276 return(MagickFalse);
1277}
1278
1279#if defined(MAGICKCORE_PNG_DELEGATE)
1280#if defined(__cplusplus) || defined(c_plusplus)
1281extern "C" {
1282#endif
1283
glennrpd5045b42010-03-24 12:40:35 +00001284#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001285static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001286{
1287 unsigned char
1288 buffer[4];
1289
1290 assert(image != (Image *) NULL);
1291 assert(image->signature == MagickSignature);
1292 buffer[0]=(unsigned char) (value >> 24);
1293 buffer[1]=(unsigned char) (value >> 16);
1294 buffer[2]=(unsigned char) (value >> 8);
1295 buffer[3]=(unsigned char) value;
1296 return((size_t) WriteBlob(image,4,buffer));
1297}
1298
1299static void PNGLong(png_bytep p,png_uint_32 value)
1300{
1301 *p++=(png_byte) ((value >> 24) & 0xff);
1302 *p++=(png_byte) ((value >> 16) & 0xff);
1303 *p++=(png_byte) ((value >> 8) & 0xff);
1304 *p++=(png_byte) (value & 0xff);
1305}
1306
glennrpa521b2f2010-10-29 04:11:03 +00001307#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001308static void PNGsLong(png_bytep p,png_int_32 value)
1309{
1310 *p++=(png_byte) ((value >> 24) & 0xff);
1311 *p++=(png_byte) ((value >> 16) & 0xff);
1312 *p++=(png_byte) ((value >> 8) & 0xff);
1313 *p++=(png_byte) (value & 0xff);
1314}
glennrpa521b2f2010-10-29 04:11:03 +00001315#endif
cristy3ed852e2009-09-05 21:47:34 +00001316
1317static void PNGShort(png_bytep p,png_uint_16 value)
1318{
1319 *p++=(png_byte) ((value >> 8) & 0xff);
1320 *p++=(png_byte) (value & 0xff);
1321}
1322
1323static void PNGType(png_bytep p,png_bytep type)
1324{
1325 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1326}
1327
glennrp03812ae2010-12-24 01:31:34 +00001328static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1329 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001330{
1331 if (logging != MagickFalse)
1332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001333 " Writing %c%c%c%c chunk, length: %.20g",
1334 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001335}
glennrpd5045b42010-03-24 12:40:35 +00001336#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001337
1338#if defined(__cplusplus) || defined(c_plusplus)
1339}
1340#endif
1341
glennrpd5045b42010-03-24 12:40:35 +00001342#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001343/*
1344%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1345% %
1346% %
1347% %
1348% R e a d P N G I m a g e %
1349% %
1350% %
1351% %
1352%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1353%
1354% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1355% Multiple-image Network Graphics (MNG) image file and returns it. It
1356% allocates the memory necessary for the new Image structure and returns a
1357% pointer to the new image or set of images.
1358%
1359% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1360%
1361% The format of the ReadPNGImage method is:
1362%
1363% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1364%
1365% A description of each parameter follows:
1366%
1367% o image_info: the image info.
1368%
1369% o exception: return any errors or warnings in this structure.
1370%
1371% To do, more or less in chronological order (as of version 5.5.2,
1372% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1373%
1374% Get 16-bit cheap transparency working.
1375%
1376% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1377%
1378% Preserve all unknown and not-yet-handled known chunks found in input
1379% PNG file and copy them into output PNG files according to the PNG
1380% copying rules.
1381%
1382% (At this point, PNG encoding should be in full MNG compliance)
1383%
1384% Provide options for choice of background to use when the MNG BACK
1385% chunk is not present or is not mandatory (i.e., leave transparent,
1386% user specified, MNG BACK, PNG bKGD)
1387%
1388% Implement LOOP/ENDL [done, but could do discretionary loops more
1389% efficiently by linking in the duplicate frames.].
1390%
1391% Decode and act on the MHDR simplicity profile (offer option to reject
1392% files or attempt to process them anyway when the profile isn't LC or VLC).
1393%
1394% Upgrade to full MNG without Delta-PNG.
1395%
1396% o BACK [done a while ago except for background image ID]
1397% o MOVE [done 15 May 1999]
1398% o CLIP [done 15 May 1999]
1399% o DISC [done 19 May 1999]
1400% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1401% o SEEK [partially done 19 May 1999 (discard function only)]
1402% o SHOW
1403% o PAST
1404% o BASI
1405% o MNG-level tEXt/iTXt/zTXt
1406% o pHYg
1407% o pHYs
1408% o sBIT
1409% o bKGD
1410% o iTXt (wait for libpng implementation).
1411%
1412% Use the scene signature to discover when an identical scene is
1413% being reused, and just point to the original image->exception instead
1414% of storing another set of pixels. This not specific to MNG
1415% but could be applied generally.
1416%
1417% Upgrade to full MNG with Delta-PNG.
1418%
1419% JNG tEXt/iTXt/zTXt
1420%
1421% We will not attempt to read files containing the CgBI chunk.
1422% They are really Xcode files meant for display on the iPhone.
1423% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001424% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001425% since irretrievable loss of color data has occurred due to the
1426% use of premultiplied alpha.
1427*/
1428
1429#if defined(__cplusplus) || defined(c_plusplus)
1430extern "C" {
1431#endif
1432
1433/*
1434 This the function that does the actual reading of data. It is
1435 the same as the one supplied in libpng, except that it receives the
1436 datastream from the ReadBlob() function instead of standard input.
1437*/
1438static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1439{
1440 Image
1441 *image;
1442
1443 image=(Image *) png_get_io_ptr(png_ptr);
1444 if (length)
1445 {
1446 png_size_t
1447 check;
1448
1449 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1450 if (check != length)
1451 {
1452 char
1453 msg[MaxTextExtent];
1454
cristy3b6fd2e2011-05-20 12:53:50 +00001455 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001456 "Expected %.20g bytes; found %.20g bytes",(double) length,
1457 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001458 png_warning(png_ptr,msg);
1459 png_error(png_ptr,"Read Exception");
1460 }
1461 }
1462}
1463
1464#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1465 !defined(PNG_MNG_FEATURES_SUPPORTED)
1466/* We use mng_get_data() instead of png_get_data() if we have a libpng
1467 * older than libpng-1.0.3a, which was the first to allow the empty
1468 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1469 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1470 * encountered after an empty PLTE, so we have to look ahead for bKGD
1471 * chunks and remove them from the datastream that is passed to libpng,
1472 * and store their contents for later use.
1473 */
1474static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1475{
1476 MngInfo
1477 *mng_info;
1478
1479 Image
1480 *image;
1481
1482 png_size_t
1483 check;
1484
cristybb503372010-05-27 20:51:26 +00001485 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001486 i;
1487
1488 i=0;
1489 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1490 image=(Image *) mng_info->image;
1491 while (mng_info->bytes_in_read_buffer && length)
1492 {
1493 data[i]=mng_info->read_buffer[i];
1494 mng_info->bytes_in_read_buffer--;
1495 length--;
1496 i++;
1497 }
1498 if (length)
1499 {
1500 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001501
cristy3ed852e2009-09-05 21:47:34 +00001502 if (check != length)
1503 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001504
cristy3ed852e2009-09-05 21:47:34 +00001505 if (length == 4)
1506 {
1507 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1508 (data[3] == 0))
1509 {
1510 check=(png_size_t) ReadBlob(image,(size_t) length,
1511 (char *) mng_info->read_buffer);
1512 mng_info->read_buffer[4]=0;
1513 mng_info->bytes_in_read_buffer=4;
1514 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1515 mng_info->found_empty_plte=MagickTrue;
1516 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1517 {
1518 mng_info->found_empty_plte=MagickFalse;
1519 mng_info->have_saved_bkgd_index=MagickFalse;
1520 }
1521 }
glennrp0fe50b42010-11-16 03:52:51 +00001522
cristy3ed852e2009-09-05 21:47:34 +00001523 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1524 (data[3] == 1))
1525 {
1526 check=(png_size_t) ReadBlob(image,(size_t) length,
1527 (char *) mng_info->read_buffer);
1528 mng_info->read_buffer[4]=0;
1529 mng_info->bytes_in_read_buffer=4;
1530 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1531 if (mng_info->found_empty_plte)
1532 {
1533 /*
1534 Skip the bKGD data byte and CRC.
1535 */
1536 check=(png_size_t)
1537 ReadBlob(image,5,(char *) mng_info->read_buffer);
1538 check=(png_size_t) ReadBlob(image,(size_t) length,
1539 (char *) mng_info->read_buffer);
1540 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1541 mng_info->have_saved_bkgd_index=MagickTrue;
1542 mng_info->bytes_in_read_buffer=0;
1543 }
1544 }
1545 }
1546 }
1547}
1548#endif
1549
1550static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1551{
1552 Image
1553 *image;
1554
1555 image=(Image *) png_get_io_ptr(png_ptr);
1556 if (length)
1557 {
1558 png_size_t
1559 check;
1560
cristybb503372010-05-27 20:51:26 +00001561 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001562
cristy3ed852e2009-09-05 21:47:34 +00001563 if (check != length)
1564 png_error(png_ptr,"WriteBlob Failed");
1565 }
1566}
1567
1568static void png_flush_data(png_structp png_ptr)
1569{
1570 (void) png_ptr;
1571}
1572
1573#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1574static int PalettesAreEqual(Image *a,Image *b)
1575{
cristybb503372010-05-27 20:51:26 +00001576 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001577 i;
1578
1579 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1580 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001581
cristy3ed852e2009-09-05 21:47:34 +00001582 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1583 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001584
cristy3ed852e2009-09-05 21:47:34 +00001585 if (a->colors != b->colors)
1586 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001587
cristybb503372010-05-27 20:51:26 +00001588 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001589 {
1590 if ((a->colormap[i].red != b->colormap[i].red) ||
1591 (a->colormap[i].green != b->colormap[i].green) ||
1592 (a->colormap[i].blue != b->colormap[i].blue))
1593 return((int) MagickFalse);
1594 }
glennrp0fe50b42010-11-16 03:52:51 +00001595
cristy3ed852e2009-09-05 21:47:34 +00001596 return((int) MagickTrue);
1597}
1598#endif
1599
1600static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1601{
1602 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1603 mng_info->exists[i] && !mng_info->frozen[i])
1604 {
1605#ifdef MNG_OBJECT_BUFFERS
1606 if (mng_info->ob[i] != (MngBuffer *) NULL)
1607 {
1608 if (mng_info->ob[i]->reference_count > 0)
1609 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001610
cristy3ed852e2009-09-05 21:47:34 +00001611 if (mng_info->ob[i]->reference_count == 0)
1612 {
1613 if (mng_info->ob[i]->image != (Image *) NULL)
1614 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001615
cristy3ed852e2009-09-05 21:47:34 +00001616 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1617 }
1618 }
1619 mng_info->ob[i]=(MngBuffer *) NULL;
1620#endif
1621 mng_info->exists[i]=MagickFalse;
1622 mng_info->invisible[i]=MagickFalse;
1623 mng_info->viewable[i]=MagickFalse;
1624 mng_info->frozen[i]=MagickFalse;
1625 mng_info->x_off[i]=0;
1626 mng_info->y_off[i]=0;
1627 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001628 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001629 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001630 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001631 }
1632}
1633
glennrp21f0e622011-01-07 16:20:57 +00001634static void MngInfoFreeStruct(MngInfo *mng_info,
1635 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001636{
glennrp21f0e622011-01-07 16:20:57 +00001637 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001638 {
cristybb503372010-05-27 20:51:26 +00001639 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001640 i;
1641
1642 for (i=1; i < MNG_MAX_OBJECTS; i++)
1643 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001644
cristy3ed852e2009-09-05 21:47:34 +00001645 if (mng_info->global_plte != (png_colorp) NULL)
1646 mng_info->global_plte=(png_colorp)
1647 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001648
cristy3ed852e2009-09-05 21:47:34 +00001649 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1650 *have_mng_structure=MagickFalse;
1651 }
1652}
1653
1654static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1655{
1656 MngBox
1657 box;
1658
1659 box=box1;
1660 if (box.left < box2.left)
1661 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001662
cristy3ed852e2009-09-05 21:47:34 +00001663 if (box.top < box2.top)
1664 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001665
cristy3ed852e2009-09-05 21:47:34 +00001666 if (box.right > box2.right)
1667 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001668
cristy3ed852e2009-09-05 21:47:34 +00001669 if (box.bottom > box2.bottom)
1670 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001671
cristy3ed852e2009-09-05 21:47:34 +00001672 return box;
1673}
1674
1675static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1676{
1677 MngBox
1678 box;
1679
1680 /*
1681 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1682 */
cristybb503372010-05-27 20:51:26 +00001683 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1684 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1685 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1686 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001687 if (delta_type != 0)
1688 {
1689 box.left+=previous_box.left;
1690 box.right+=previous_box.right;
1691 box.top+=previous_box.top;
1692 box.bottom+=previous_box.bottom;
1693 }
glennrp0fe50b42010-11-16 03:52:51 +00001694
cristy3ed852e2009-09-05 21:47:34 +00001695 return(box);
1696}
1697
1698static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1699 unsigned char *p)
1700{
1701 MngPair
1702 pair;
1703 /*
cristybb503372010-05-27 20:51:26 +00001704 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001705 */
cristy8182b072010-05-30 20:10:53 +00001706 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1707 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001708
cristy3ed852e2009-09-05 21:47:34 +00001709 if (delta_type != 0)
1710 {
1711 pair.a+=previous_pair.a;
1712 pair.b+=previous_pair.b;
1713 }
glennrp0fe50b42010-11-16 03:52:51 +00001714
cristy3ed852e2009-09-05 21:47:34 +00001715 return(pair);
1716}
1717
cristy8182b072010-05-30 20:10:53 +00001718static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001719{
cristy8182b072010-05-30 20:10:53 +00001720 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001721}
1722
cristyc82a27b2011-10-21 01:07:16 +00001723typedef struct _PNGErrorInfo
cristy3ed852e2009-09-05 21:47:34 +00001724{
1725 Image
1726 *image;
1727
cristyc82a27b2011-10-21 01:07:16 +00001728 ExceptionInfo
1729 *exception;
1730} PNGErrorInfo;
glennrp0fe50b42010-11-16 03:52:51 +00001731
cristyc82a27b2011-10-21 01:07:16 +00001732static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1733{
1734 ExceptionInfo
1735 *exception;
glennrp0fe50b42010-11-16 03:52:51 +00001736
cristyc82a27b2011-10-21 01:07:16 +00001737 Image
1738 *image;
1739
1740 PNGErrorInfo
1741 *error_info;
1742
1743 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1744 image=error_info->image;
1745 exception=error_info->exception;
1746
1747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1748 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1749
1750 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1751 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001752
glennrpe4017e32011-01-08 17:16:09 +00001753#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001754 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1755 * are building with libpng-1.4.x and can be ignored.
1756 */
cristy3ed852e2009-09-05 21:47:34 +00001757 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001758#else
1759 png_longjmp(ping,1);
1760#endif
cristy3ed852e2009-09-05 21:47:34 +00001761}
1762
glennrpcf002022011-01-30 02:38:15 +00001763static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001764{
cristyc82a27b2011-10-21 01:07:16 +00001765 ExceptionInfo
1766 *exception;
1767
cristy3ed852e2009-09-05 21:47:34 +00001768 Image
1769 *image;
1770
cristyc82a27b2011-10-21 01:07:16 +00001771 PNGErrorInfo
1772 *error_info;
1773
cristy3ed852e2009-09-05 21:47:34 +00001774 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1775 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001776
cristyc82a27b2011-10-21 01:07:16 +00001777 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1778 image=error_info->image;
1779 exception=error_info->exception;
1780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1781 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001782
cristyc82a27b2011-10-21 01:07:16 +00001783 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
cristy3ed852e2009-09-05 21:47:34 +00001784 message,"`%s'",image->filename);
1785}
1786
1787#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001788static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001789{
1790#if (PNG_LIBPNG_VER < 10011)
1791 png_voidp
1792 ret;
1793
cristydf0d90e2011-12-12 01:03:55 +00001794 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001795 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001796
cristy3ed852e2009-09-05 21:47:34 +00001797 if (ret == NULL)
1798 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001799
cristy3ed852e2009-09-05 21:47:34 +00001800 return(ret);
1801#else
cristydf0d90e2011-12-12 01:03:55 +00001802 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001803 return((png_voidp) AcquireMagickMemory((size_t) size));
1804#endif
1805}
1806
1807/*
1808 Free a pointer. It is removed from the list at the same time.
1809*/
glennrpcf002022011-01-30 02:38:15 +00001810static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001811{
glennrp3bd393f2011-12-21 18:54:53 +00001812 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001813 ptr=RelinquishMagickMemory(ptr);
1814 return((png_free_ptr) NULL);
1815}
1816#endif
1817
1818#if defined(__cplusplus) || defined(c_plusplus)
1819}
1820#endif
1821
1822static int
glennrpcf002022011-01-30 02:38:15 +00001823Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristyd15e6592011-10-15 00:13:06 +00001824 png_textp text,int ii,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001825{
cristybb503372010-05-27 20:51:26 +00001826 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001827 i;
1828
1829 register unsigned char
1830 *dp;
1831
1832 register png_charp
1833 sp;
1834
1835 png_uint_32
1836 length,
1837 nibbles;
1838
1839 StringInfo
1840 *profile;
1841
glennrp0c3e06b2010-11-19 13:45:02 +00001842 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001843 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1844 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1845 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1846 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1847 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1848 13,14,15};
1849
1850 sp=text[ii].text+1;
1851 /* look for newline */
1852 while (*sp != '\n')
1853 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001854
cristy3ed852e2009-09-05 21:47:34 +00001855 /* look for length */
1856 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1857 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001858
cristyf2f27272009-12-17 14:48:46 +00001859 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001860
glennrp97f90e22011-02-22 05:47:58 +00001861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1862 " length: %lu",(unsigned long) length);
1863
cristy3ed852e2009-09-05 21:47:34 +00001864 while (*sp != ' ' && *sp != '\n')
1865 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001866
cristy3ed852e2009-09-05 21:47:34 +00001867 /* allocate space */
1868 if (length == 0)
1869 {
cristyc82a27b2011-10-21 01:07:16 +00001870 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001871 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1872 return(MagickFalse);
1873 }
glennrp0fe50b42010-11-16 03:52:51 +00001874
cristy8723e4b2011-09-01 13:11:19 +00001875 profile=BlobToStringInfo((const void *) NULL,length);
glennrp0fe50b42010-11-16 03:52:51 +00001876
cristy3ed852e2009-09-05 21:47:34 +00001877 if (profile == (StringInfo *) NULL)
1878 {
cristyc82a27b2011-10-21 01:07:16 +00001879 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001880 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1881 "unable to copy profile");
1882 return(MagickFalse);
1883 }
glennrp0fe50b42010-11-16 03:52:51 +00001884
cristy3ed852e2009-09-05 21:47:34 +00001885 /* copy profile, skipping white space and column 1 "=" signs */
1886 dp=GetStringInfoDatum(profile);
1887 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001888
cristybb503372010-05-27 20:51:26 +00001889 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001890 {
1891 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1892 {
1893 if (*sp == '\0')
1894 {
cristyc82a27b2011-10-21 01:07:16 +00001895 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001896 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1897 profile=DestroyStringInfo(profile);
1898 return(MagickFalse);
1899 }
1900 sp++;
1901 }
glennrp0fe50b42010-11-16 03:52:51 +00001902
cristy3ed852e2009-09-05 21:47:34 +00001903 if (i%2 == 0)
1904 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001905
cristy3ed852e2009-09-05 21:47:34 +00001906 else
1907 (*dp++)+=unhex[(int) *sp++];
1908 }
1909 /*
1910 We have already read "Raw profile type.
1911 */
cristyd15e6592011-10-15 00:13:06 +00001912 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001913 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001914
cristy3ed852e2009-09-05 21:47:34 +00001915 if (image_info->verbose)
1916 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001917
cristy3ed852e2009-09-05 21:47:34 +00001918 return MagickTrue;
1919}
1920
1921#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1922static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1923{
1924 Image
1925 *image;
1926
1927
1928 /* The unknown chunk structure contains the chunk data:
1929 png_byte name[5];
1930 png_byte *data;
1931 png_size_t size;
1932
1933 Note that libpng has already taken care of the CRC handling.
1934 */
1935
1936
1937 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1938 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1939 return(0); /* Did not recognize */
1940
1941 /* recognized vpAg */
1942
1943 if (chunk->size != 9)
1944 return(-1); /* Error return */
1945
1946 if (chunk->data[8] != 0)
1947 return(0); /* ImageMagick requires pixel units */
1948
1949 image=(Image *) png_get_user_chunk_ptr(ping);
1950
cristybb503372010-05-27 20:51:26 +00001951 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001952 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001953
cristybb503372010-05-27 20:51:26 +00001954 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001955 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1956
1957 /* Return one of the following: */
1958 /* return(-n); chunk had an error */
1959 /* return(0); did not recognize */
1960 /* return(n); success */
1961
1962 return(1);
1963
1964}
1965#endif
1966
1967/*
1968%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1969% %
1970% %
1971% %
1972% R e a d O n e P N G I m a g e %
1973% %
1974% %
1975% %
1976%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1977%
1978% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1979% (minus the 8-byte signature) and returns it. It allocates the memory
1980% necessary for the new Image structure and returns a pointer to the new
1981% image.
1982%
1983% The format of the ReadOnePNGImage method is:
1984%
1985% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1986% ExceptionInfo *exception)
1987%
1988% A description of each parameter follows:
1989%
1990% o mng_info: Specifies a pointer to a MngInfo structure.
1991%
1992% o image_info: the image info.
1993%
1994% o exception: return any errors or warnings in this structure.
1995%
1996*/
1997static Image *ReadOnePNGImage(MngInfo *mng_info,
1998 const ImageInfo *image_info, ExceptionInfo *exception)
1999{
2000 /* Read one PNG image */
2001
glennrpcc95c3f2011-04-18 16:46:48 +00002002 /* To do: Read the tIME chunk into the date:modify property */
2003 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2004
cristy3ed852e2009-09-05 21:47:34 +00002005 Image
2006 *image;
2007
2008 int
glennrp4eb39312011-03-30 21:34:55 +00002009 intent,
glennrpcb395ac2011-03-30 19:50:23 +00002010 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00002011 num_text,
glennrp4eb39312011-03-30 21:34:55 +00002012 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00002013 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00002014 pass,
2015 ping_bit_depth,
2016 ping_color_type,
2017 ping_interlace_method,
2018 ping_compression_method,
2019 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00002020 ping_num_trans,
2021 unit_type;
2022
2023 double
2024 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002025
2026 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002027 logging,
cristy3ed852e2009-09-05 21:47:34 +00002028 status;
2029
cristyc82a27b2011-10-21 01:07:16 +00002030 PixelInfo
2031 transparent_color;
2032
2033 PNGErrorInfo
2034 error_info;
2035
glennrpfaa852b2010-03-30 12:17:00 +00002036 png_bytep
2037 ping_trans_alpha;
2038
2039 png_color_16p
2040 ping_background,
2041 ping_trans_color;
2042
cristy3ed852e2009-09-05 21:47:34 +00002043 png_info
2044 *end_info,
2045 *ping_info;
2046
2047 png_struct
2048 *ping;
2049
2050 png_textp
2051 text;
2052
glennrpfaa852b2010-03-30 12:17:00 +00002053 png_uint_32
2054 ping_height,
2055 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002056 x_resolution,
2057 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002058
cristy3ed852e2009-09-05 21:47:34 +00002059 QuantumInfo
2060 *quantum_info;
2061
2062 unsigned char
glennrpcf002022011-01-30 02:38:15 +00002063 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002064
cristybb503372010-05-27 20:51:26 +00002065 ssize_t
cristy756ae432011-11-19 02:18:25 +00002066 ping_rowbytes,
cristy3ed852e2009-09-05 21:47:34 +00002067 y;
2068
2069 register unsigned char
2070 *p;
2071
cristybb503372010-05-27 20:51:26 +00002072 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002073 i,
2074 x;
2075
cristy4c08aed2011-07-01 19:47:50 +00002076 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002077 *q;
2078
2079 size_t
glennrp39992b42010-11-14 00:03:43 +00002080 length,
cristy3ed852e2009-09-05 21:47:34 +00002081 row_offset;
2082
cristyeb3b22a2011-03-31 20:16:11 +00002083 ssize_t
2084 j;
2085
cristy3ed852e2009-09-05 21:47:34 +00002086#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2087 png_byte unused_chunks[]=
2088 {
2089 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2090 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2091 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2092 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2093 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2094 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2095 };
2096#endif
2097
2098 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002099 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002100
2101#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002102 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002103#endif
2104
glennrp25c1e2b2010-03-25 01:39:56 +00002105#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002106 if (image_info->verbose)
2107 printf("Your PNG library (libpng-%s) is rather old.\n",
2108 PNG_LIBPNG_VER_STRING);
2109#endif
2110
glennrp61b4c952009-11-10 20:40:41 +00002111#if (PNG_LIBPNG_VER >= 10400)
2112# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2113 if (image_info->verbose)
2114 {
2115 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2116 PNG_LIBPNG_VER_STRING);
2117 printf("Please update it.\n");
2118 }
2119# endif
2120#endif
2121
2122
cristyed552522009-10-16 14:04:35 +00002123 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002124 image=mng_info->image;
2125
glennrpa6a06632011-01-19 15:15:34 +00002126 if (logging != MagickFalse)
2127 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2128 " image->matte=%d",(int) image->matte);
2129
glennrp0e319732011-01-25 21:53:13 +00002130 /* Set to an out-of-range color unless tRNS chunk is present */
2131 transparent_color.red=65537;
2132 transparent_color.green=65537;
2133 transparent_color.blue=65537;
cristy4c08aed2011-07-01 19:47:50 +00002134 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002135
glennrpcb395ac2011-03-30 19:50:23 +00002136 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002137 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002138 num_raw_profiles = 0;
2139
cristy3ed852e2009-09-05 21:47:34 +00002140 /*
2141 Allocate the PNG structures
2142 */
2143#ifdef PNG_USER_MEM_SUPPORTED
cristyc82a27b2011-10-21 01:07:16 +00002144 error_info.image=image;
2145 error_info.exception=exception;
2146 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002147 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2148 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002149#else
cristyc82a27b2011-10-21 01:07:16 +00002150 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002151 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002152#endif
2153 if (ping == (png_struct *) NULL)
2154 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002155
cristy3ed852e2009-09-05 21:47:34 +00002156 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002157
cristy3ed852e2009-09-05 21:47:34 +00002158 if (ping_info == (png_info *) NULL)
2159 {
2160 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2161 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2162 }
glennrp0fe50b42010-11-16 03:52:51 +00002163
cristy3ed852e2009-09-05 21:47:34 +00002164 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002165
cristy3ed852e2009-09-05 21:47:34 +00002166 if (end_info == (png_info *) NULL)
2167 {
2168 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2169 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2170 }
glennrp0fe50b42010-11-16 03:52:51 +00002171
glennrpcf002022011-01-30 02:38:15 +00002172 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002173
glennrpfaa852b2010-03-30 12:17:00 +00002174 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002175 {
2176 /*
2177 PNG image is corrupt.
2178 */
2179 png_destroy_read_struct(&ping,&ping_info,&end_info);
2180#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002181 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002182#endif
2183 if (logging != MagickFalse)
2184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2185 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002186
cristy3ed852e2009-09-05 21:47:34 +00002187 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002188 {
cristyc82a27b2011-10-21 01:07:16 +00002189 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002190 image->columns=0;
2191 }
glennrp0fe50b42010-11-16 03:52:51 +00002192
cristy3ed852e2009-09-05 21:47:34 +00002193 return(GetFirstImageInList(image));
2194 }
2195 /*
2196 Prepare PNG for reading.
2197 */
glennrpfaa852b2010-03-30 12:17:00 +00002198
cristy3ed852e2009-09-05 21:47:34 +00002199 mng_info->image_found++;
2200 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002201
cristy3ed852e2009-09-05 21:47:34 +00002202 if (LocaleCompare(image_info->magick,"MNG") == 0)
2203 {
2204#if defined(PNG_MNG_FEATURES_SUPPORTED)
2205 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2206 png_set_read_fn(ping,image,png_get_data);
2207#else
2208#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2209 png_permit_empty_plte(ping,MagickTrue);
2210 png_set_read_fn(ping,image,png_get_data);
2211#else
2212 mng_info->image=image;
2213 mng_info->bytes_in_read_buffer=0;
2214 mng_info->found_empty_plte=MagickFalse;
2215 mng_info->have_saved_bkgd_index=MagickFalse;
2216 png_set_read_fn(ping,mng_info,mng_get_data);
2217#endif
2218#endif
2219 }
glennrp0fe50b42010-11-16 03:52:51 +00002220
cristy3ed852e2009-09-05 21:47:34 +00002221 else
2222 png_set_read_fn(ping,image,png_get_data);
2223
2224#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2225 /* Ignore unused chunks and all unknown chunks except for vpAg */
2226 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2227 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2228 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2229 (int)sizeof(unused_chunks)/5);
2230 /* Callback for other unknown chunks */
2231 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2232#endif
2233
glennrp991e92a2010-01-28 03:09:00 +00002234#if (PNG_LIBPNG_VER < 10400)
2235# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2236 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002237 /* Disable thread-unsafe features of pnggccrd */
2238 if (png_access_version_number() >= 10200)
2239 {
2240 png_uint_32 mmx_disable_mask=0;
2241 png_uint_32 asm_flags;
2242
2243 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2244 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2245 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2246 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2247 asm_flags=png_get_asm_flags(ping);
2248 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2249 }
glennrp991e92a2010-01-28 03:09:00 +00002250# endif
cristy3ed852e2009-09-05 21:47:34 +00002251#endif
2252
2253 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002254
2255 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2256 &ping_bit_depth,&ping_color_type,
2257 &ping_interlace_method,&ping_compression_method,
2258 &ping_filter_method);
2259
2260 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2261 &ping_trans_color);
2262
2263 (void) png_get_bKGD(ping, ping_info, &ping_background);
2264
2265 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002266 {
glennrpfaa852b2010-03-30 12:17:00 +00002267 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2268 {
2269 png_set_packing(ping);
2270 ping_bit_depth = 8;
2271 }
cristy3ed852e2009-09-05 21:47:34 +00002272 }
glennrpfaa852b2010-03-30 12:17:00 +00002273
2274 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002275 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002276 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00002277 if (logging != MagickFalse)
2278 {
2279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002280 " PNG width: %.20g, height: %.20g",
2281 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002282
cristy3ed852e2009-09-05 21:47:34 +00002283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2284 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002285 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002286
cristy3ed852e2009-09-05 21:47:34 +00002287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2288 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002289 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002290
cristy3ed852e2009-09-05 21:47:34 +00002291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2292 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002293 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002294 }
2295
glennrpfaa852b2010-03-30 12:17:00 +00002296#ifdef PNG_READ_iCCP_SUPPORTED
2297 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002298 {
2299 int
2300 compression;
2301
glennrpe4017e32011-01-08 17:16:09 +00002302#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002303 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002304 info;
2305#else
2306 png_bytep
2307 info;
2308#endif
2309
2310 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002311 name;
2312
2313 png_uint_32
2314 profile_length;
2315
2316 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2317 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002318
cristy3ed852e2009-09-05 21:47:34 +00002319 if (profile_length != 0)
2320 {
2321 StringInfo
2322 *profile;
2323
2324 if (logging != MagickFalse)
2325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2326 " Reading PNG iCCP chunk.");
cristye8f8f382011-09-01 13:32:37 +00002327 profile=BlobToStringInfo(info,profile_length);
2328 if (profile == (StringInfo *) NULL)
2329 {
cristyc82a27b2011-10-21 01:07:16 +00002330 (void) ThrowMagickException(exception,GetMagickModule(),
cristye8f8f382011-09-01 13:32:37 +00002331 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2332 "unable to copy profile");
cristyad5b0852011-09-08 02:01:21 +00002333 return((Image *) NULL);
cristye8f8f382011-09-01 13:32:37 +00002334 }
cristy3ed852e2009-09-05 21:47:34 +00002335 SetStringInfoDatum(profile,(const unsigned char *) info);
cristyd15e6592011-10-15 00:13:06 +00002336 (void) SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00002337 profile=DestroyStringInfo(profile);
2338 }
2339 }
2340#endif
2341#if defined(PNG_READ_sRGB_SUPPORTED)
2342 {
cristy3ed852e2009-09-05 21:47:34 +00002343 if (mng_info->have_global_srgb)
cristy2ea7a8e2012-02-08 01:04:50 +00002344 {
2345 image->colorspace=sRGBColorspace;
2346 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2347 (mng_info->global_srgb_intent);
2348 }
glennrp0fe50b42010-11-16 03:52:51 +00002349
cristy3ed852e2009-09-05 21:47:34 +00002350 if (png_get_sRGB(ping,ping_info,&intent))
2351 {
cristy96c4fcb2012-02-08 01:01:05 +00002352 image->colorspace=sRGBColorspace;
glennrpcf002022011-01-30 02:38:15 +00002353 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2354 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002355
cristy3ed852e2009-09-05 21:47:34 +00002356 if (logging != MagickFalse)
2357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002358 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002359 }
2360 }
2361#endif
2362 {
glennrpfaa852b2010-03-30 12:17:00 +00002363 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2364 if (mng_info->have_global_gama)
2365 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002366
cristy3ed852e2009-09-05 21:47:34 +00002367 if (png_get_gAMA(ping,ping_info,&file_gamma))
2368 {
2369 image->gamma=(float) file_gamma;
2370 if (logging != MagickFalse)
2371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2372 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2373 }
2374 }
glennrpfaa852b2010-03-30 12:17:00 +00002375 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2376 {
2377 if (mng_info->have_global_chrm != MagickFalse)
2378 {
2379 (void) png_set_cHRM(ping,ping_info,
2380 mng_info->global_chrm.white_point.x,
2381 mng_info->global_chrm.white_point.y,
2382 mng_info->global_chrm.red_primary.x,
2383 mng_info->global_chrm.red_primary.y,
2384 mng_info->global_chrm.green_primary.x,
2385 mng_info->global_chrm.green_primary.y,
2386 mng_info->global_chrm.blue_primary.x,
2387 mng_info->global_chrm.blue_primary.y);
2388 }
2389 }
glennrp0fe50b42010-11-16 03:52:51 +00002390
glennrpfaa852b2010-03-30 12:17:00 +00002391 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002392 {
2393 (void) png_get_cHRM(ping,ping_info,
2394 &image->chromaticity.white_point.x,
2395 &image->chromaticity.white_point.y,
2396 &image->chromaticity.red_primary.x,
2397 &image->chromaticity.red_primary.y,
2398 &image->chromaticity.green_primary.x,
2399 &image->chromaticity.green_primary.y,
2400 &image->chromaticity.blue_primary.x,
2401 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002402
cristy3ed852e2009-09-05 21:47:34 +00002403 if (logging != MagickFalse)
2404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2405 " Reading PNG cHRM chunk.");
2406 }
glennrp0fe50b42010-11-16 03:52:51 +00002407
glennrpe610a072010-08-05 17:08:46 +00002408 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002409 {
glennrpe610a072010-08-05 17:08:46 +00002410 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002411 Magick_RenderingIntent_to_PNG_RenderingIntent
2412 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00002413 png_set_gAMA(ping,ping_info,0.45455f);
2414 png_set_cHRM(ping,ping_info,
2415 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2416 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002417 }
cristy3ed852e2009-09-05 21:47:34 +00002418#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002419 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002420 {
cristy905ef802011-02-23 00:29:18 +00002421 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2422 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002423
cristy3ed852e2009-09-05 21:47:34 +00002424 if (logging != MagickFalse)
2425 if (image->page.x || image->page.y)
2426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002427 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2428 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002429 }
2430#endif
2431#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002432 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2433 {
2434 if (mng_info->have_global_phys)
2435 {
2436 png_set_pHYs(ping,ping_info,
2437 mng_info->global_x_pixels_per_unit,
2438 mng_info->global_y_pixels_per_unit,
2439 mng_info->global_phys_unit_type);
2440 }
2441 }
2442
2443 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002444 {
cristy3ed852e2009-09-05 21:47:34 +00002445 /*
2446 Set image resolution.
2447 */
2448 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002449 &unit_type);
cristy2a11bef2011-10-28 18:33:11 +00002450 image->resolution.x=(double) x_resolution;
2451 image->resolution.y=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002452
cristy3ed852e2009-09-05 21:47:34 +00002453 if (unit_type == PNG_RESOLUTION_METER)
2454 {
2455 image->units=PixelsPerCentimeterResolution;
cristy2a11bef2011-10-28 18:33:11 +00002456 image->resolution.x=(double) x_resolution/100.0;
2457 image->resolution.y=(double) y_resolution/100.0;
cristy3ed852e2009-09-05 21:47:34 +00002458 }
glennrp0fe50b42010-11-16 03:52:51 +00002459
cristy3ed852e2009-09-05 21:47:34 +00002460 if (logging != MagickFalse)
2461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002462 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2463 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002464 }
cristy3ed852e2009-09-05 21:47:34 +00002465#endif
glennrp823b55c2011-03-14 18:46:46 +00002466
glennrpfaa852b2010-03-30 12:17:00 +00002467 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002468 {
2469 int
2470 number_colors;
2471
2472 png_colorp
2473 palette;
2474
2475 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002476
cristy3ed852e2009-09-05 21:47:34 +00002477 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002478 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002479 {
2480 if (mng_info->global_plte_length)
2481 {
2482 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2483 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002484
glennrpfaa852b2010-03-30 12:17:00 +00002485 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002486 if (mng_info->global_trns_length)
2487 {
2488 if (mng_info->global_trns_length >
2489 mng_info->global_plte_length)
cristyc82a27b2011-10-21 01:07:16 +00002490 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00002491 GetMagickModule(),CoderError,
2492 "global tRNS has more entries than global PLTE",
2493 "`%s'",image_info->filename);
2494 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2495 (int) mng_info->global_trns_length,NULL);
2496 }
glennrpbfd9e612011-04-22 14:02:20 +00002497#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002498 if (
2499#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2500 mng_info->have_saved_bkgd_index ||
2501#endif
glennrpfaa852b2010-03-30 12:17:00 +00002502 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002503 {
2504 png_color_16
2505 background;
2506
2507#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2508 if (mng_info->have_saved_bkgd_index)
2509 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002510#endif
glennrpfaa852b2010-03-30 12:17:00 +00002511 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2512 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002513
cristy3ed852e2009-09-05 21:47:34 +00002514 background.red=(png_uint_16)
2515 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002516
cristy3ed852e2009-09-05 21:47:34 +00002517 background.green=(png_uint_16)
2518 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002519
cristy3ed852e2009-09-05 21:47:34 +00002520 background.blue=(png_uint_16)
2521 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002522
glennrpc6c391a2011-04-27 02:23:56 +00002523 background.gray=(png_uint_16)
2524 mng_info->global_plte[background.index].green;
2525
cristy3ed852e2009-09-05 21:47:34 +00002526 png_set_bKGD(ping,ping_info,&background);
2527 }
2528#endif
2529 }
2530 else
cristyc82a27b2011-10-21 01:07:16 +00002531 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00002532 CoderError,"No global PLTE in file","`%s'",
2533 image_info->filename);
2534 }
2535 }
2536
glennrpbfd9e612011-04-22 14:02:20 +00002537#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002538 if (mng_info->have_global_bkgd &&
2539 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002540 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002541
glennrpfaa852b2010-03-30 12:17:00 +00002542 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002543 {
glennrpbfd9e612011-04-22 14:02:20 +00002544 unsigned int
2545 bkgd_scale;
2546
cristy3ed852e2009-09-05 21:47:34 +00002547 /*
2548 Set image background color.
2549 */
2550 if (logging != MagickFalse)
2551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2552 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002553
glennrpbfd9e612011-04-22 14:02:20 +00002554 /* Scale background components to 16-bit, then scale
2555 * to quantum depth
2556 */
2557 if (logging != MagickFalse)
2558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2559 " raw ping_background=(%d,%d,%d).",ping_background->red,
2560 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002561
glennrpbfd9e612011-04-22 14:02:20 +00002562 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002563
glennrpbfd9e612011-04-22 14:02:20 +00002564 if (ping_bit_depth == 1)
2565 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002566
glennrpbfd9e612011-04-22 14:02:20 +00002567 else if (ping_bit_depth == 2)
2568 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002569
glennrpbfd9e612011-04-22 14:02:20 +00002570 else if (ping_bit_depth == 4)
2571 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002572
glennrpbfd9e612011-04-22 14:02:20 +00002573 if (ping_bit_depth <= 8)
2574 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002575
glennrpbfd9e612011-04-22 14:02:20 +00002576 ping_background->red *= bkgd_scale;
2577 ping_background->green *= bkgd_scale;
2578 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002579
glennrpbfd9e612011-04-22 14:02:20 +00002580 if (logging != MagickFalse)
2581 {
glennrp2cbb4482010-06-02 04:37:24 +00002582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2583 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002584
glennrp2cbb4482010-06-02 04:37:24 +00002585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2586 " ping_background=(%d,%d,%d).",ping_background->red,
2587 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002588 }
glennrp2cbb4482010-06-02 04:37:24 +00002589
glennrpbfd9e612011-04-22 14:02:20 +00002590 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002591 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002592
glennrpbfd9e612011-04-22 14:02:20 +00002593 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002594 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002595
glennrpbfd9e612011-04-22 14:02:20 +00002596 image->background_color.blue=
2597 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002598
cristy4c08aed2011-07-01 19:47:50 +00002599 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002600
glennrpbfd9e612011-04-22 14:02:20 +00002601 if (logging != MagickFalse)
2602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2603 " image->background_color=(%.20g,%.20g,%.20g).",
2604 (double) image->background_color.red,
2605 (double) image->background_color.green,
2606 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002607 }
glennrpbfd9e612011-04-22 14:02:20 +00002608#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002609
glennrpfaa852b2010-03-30 12:17:00 +00002610 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002611 {
2612 /*
glennrpa6a06632011-01-19 15:15:34 +00002613 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002614 */
2615 int
2616 max_sample;
2617
cristy35ef8242010-06-03 16:24:13 +00002618 size_t
2619 one=1;
2620
cristy3ed852e2009-09-05 21:47:34 +00002621 if (logging != MagickFalse)
2622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2623 " Reading PNG tRNS chunk.");
2624
cristyf9cca6a2010-06-04 23:49:28 +00002625 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002626
glennrpfaa852b2010-03-30 12:17:00 +00002627 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2628 (int)ping_trans_color->gray > max_sample) ||
2629 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2630 ((int)ping_trans_color->red > max_sample ||
2631 (int)ping_trans_color->green > max_sample ||
2632 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002633 {
2634 if (logging != MagickFalse)
2635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2636 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002637 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002638 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002639 image->matte=MagickFalse;
2640 }
2641 else
2642 {
glennrpa6a06632011-01-19 15:15:34 +00002643 int
2644 scale_to_short;
2645
2646 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2647
2648 /* Scale transparent_color to short */
2649 transparent_color.red= scale_to_short*ping_trans_color->red;
2650 transparent_color.green= scale_to_short*ping_trans_color->green;
2651 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy4c08aed2011-07-01 19:47:50 +00002652 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002653
glennrpfaa852b2010-03-30 12:17:00 +00002654 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002655 {
glennrp0f111982010-07-07 20:18:33 +00002656 if (logging != MagickFalse)
2657 {
2658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2659 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002660
glennrp0f111982010-07-07 20:18:33 +00002661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy101ab702011-10-13 13:06:32 +00002662 " scaled graylevel is %.20g.",transparent_color.alpha);
glennrp0f111982010-07-07 20:18:33 +00002663 }
cristy4c08aed2011-07-01 19:47:50 +00002664 transparent_color.red=transparent_color.alpha;
2665 transparent_color.green=transparent_color.alpha;
2666 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002667 }
2668 }
2669 }
2670#if defined(PNG_READ_sBIT_SUPPORTED)
2671 if (mng_info->have_global_sbit)
2672 {
glennrpfaa852b2010-03-30 12:17:00 +00002673 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002674 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2675 }
2676#endif
2677 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002678
cristy3ed852e2009-09-05 21:47:34 +00002679 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002680
2681 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2682
cristy3ed852e2009-09-05 21:47:34 +00002683 /*
2684 Initialize image structure.
2685 */
2686 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002687 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002688 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002689 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002690 if (mng_info->mng_type == 0)
2691 {
glennrpfaa852b2010-03-30 12:17:00 +00002692 mng_info->mng_width=ping_width;
2693 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002694 mng_info->frame=mng_info->image_box;
2695 mng_info->clip=mng_info->image_box;
2696 }
glennrp0fe50b42010-11-16 03:52:51 +00002697
cristy3ed852e2009-09-05 21:47:34 +00002698 else
2699 {
2700 image->page.y=mng_info->y_off[mng_info->object_id];
2701 }
glennrp0fe50b42010-11-16 03:52:51 +00002702
cristy3ed852e2009-09-05 21:47:34 +00002703 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002704 image->columns=ping_width;
2705 image->rows=ping_height;
cristy897f2bf2012-01-05 02:03:44 +00002706 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2707 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2708 image->colorspace=GRAYColorspace;
glennrpfaa852b2010-03-30 12:17:00 +00002709 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002710 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002711 {
cristybefe4d22010-06-07 01:18:58 +00002712 size_t
2713 one;
2714
cristy3ed852e2009-09-05 21:47:34 +00002715 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002716 one=1;
2717 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002718#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2719 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002720 image->colors=256;
2721#else
2722 if (image->colors > 65536L)
2723 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002724#endif
glennrpfaa852b2010-03-30 12:17:00 +00002725 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002726 {
2727 int
2728 number_colors;
2729
2730 png_colorp
2731 palette;
2732
2733 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002734 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002735
cristy3ed852e2009-09-05 21:47:34 +00002736 if (logging != MagickFalse)
2737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2738 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2739 }
2740 }
2741
2742 if (image->storage_class == PseudoClass)
2743 {
2744 /*
2745 Initialize image colormap.
2746 */
cristy018f07f2011-09-04 21:15:19 +00002747 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002748 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002749
glennrpfaa852b2010-03-30 12:17:00 +00002750 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002751 {
2752 int
2753 number_colors;
2754
2755 png_colorp
2756 palette;
2757
2758 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002759
glennrp6af6cf12011-04-22 13:05:16 +00002760 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002761 {
2762 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2763 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2764 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2765 }
glennrp6af6cf12011-04-22 13:05:16 +00002766
glennrp67b9c1a2011-04-22 18:47:36 +00002767 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002768 {
2769 image->colormap[i].red=0;
2770 image->colormap[i].green=0;
2771 image->colormap[i].blue=0;
2772 }
cristy3ed852e2009-09-05 21:47:34 +00002773 }
glennrp0fe50b42010-11-16 03:52:51 +00002774
cristy3ed852e2009-09-05 21:47:34 +00002775 else
2776 {
cristybb503372010-05-27 20:51:26 +00002777 size_t
cristy3ed852e2009-09-05 21:47:34 +00002778 scale;
2779
glennrpfaa852b2010-03-30 12:17:00 +00002780 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002781
cristy3ed852e2009-09-05 21:47:34 +00002782 if (scale < 1)
2783 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002784
cristybb503372010-05-27 20:51:26 +00002785 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002786 {
2787 image->colormap[i].red=(Quantum) (i*scale);
2788 image->colormap[i].green=(Quantum) (i*scale);
2789 image->colormap[i].blue=(Quantum) (i*scale);
2790 }
2791 }
2792 }
glennrp147bc912011-03-30 18:47:21 +00002793
glennrpcb395ac2011-03-30 19:50:23 +00002794 /* Set some properties for reporting by "identify" */
2795 {
glennrp147bc912011-03-30 18:47:21 +00002796 char
2797 msg[MaxTextExtent];
2798
2799 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2800 ping_interlace_method in value */
2801
cristy3b6fd2e2011-05-20 12:53:50 +00002802 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002803 "%d, %d",(int) ping_width, (int) ping_height);
cristy5d6fc9c2011-12-27 03:10:42 +00002804 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002805
cristy3b6fd2e2011-05-20 12:53:50 +00002806 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
cristy5d6fc9c2011-12-27 03:10:42 +00002807 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002808
cristy3b6fd2e2011-05-20 12:53:50 +00002809 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
cristy5d6fc9c2011-12-27 03:10:42 +00002810 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002811
cristy3b6fd2e2011-05-20 12:53:50 +00002812 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002813 (int) ping_interlace_method);
cristy5d6fc9c2011-12-27 03:10:42 +00002814 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
glennrpcb395ac2011-03-30 19:50:23 +00002815 }
glennrp147bc912011-03-30 18:47:21 +00002816
cristy3ed852e2009-09-05 21:47:34 +00002817 /*
2818 Read image scanlines.
2819 */
2820 if (image->delay != 0)
2821 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002822
glennrp0ca69b12010-07-26 01:57:52 +00002823 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002824 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2825 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002826 {
2827 if (logging != MagickFalse)
2828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002829 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002830 mng_info->scenes_found-1);
2831 png_destroy_read_struct(&ping,&ping_info,&end_info);
2832#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002833 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002834#endif
2835 if (logging != MagickFalse)
2836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2837 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002838
cristy3ed852e2009-09-05 21:47:34 +00002839 return(image);
2840 }
glennrp0fe50b42010-11-16 03:52:51 +00002841
cristy3ed852e2009-09-05 21:47:34 +00002842 if (logging != MagickFalse)
2843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2844 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002845
cristy3ed852e2009-09-05 21:47:34 +00002846 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002847 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2848 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002849
cristy3ed852e2009-09-05 21:47:34 +00002850 else
glennrpcf002022011-01-30 02:38:15 +00002851 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2852 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002853
glennrpcf002022011-01-30 02:38:15 +00002854 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002855 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002856
cristy3ed852e2009-09-05 21:47:34 +00002857 if (logging != MagickFalse)
2858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2859 " Converting PNG pixels to pixel packets");
2860 /*
2861 Convert PNG pixels to pixel packets.
2862 */
glennrpfaa852b2010-03-30 12:17:00 +00002863 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002864 {
2865 /*
2866 PNG image is corrupt.
2867 */
2868 png_destroy_read_struct(&ping,&ping_info,&end_info);
2869#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002870 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002871#endif
2872 if (quantum_info != (QuantumInfo *) NULL)
2873 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002874
glennrpcf002022011-01-30 02:38:15 +00002875 if (ping_pixels != (unsigned char *) NULL)
2876 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002877
cristy3ed852e2009-09-05 21:47:34 +00002878 if (logging != MagickFalse)
2879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2880 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002881
cristy3ed852e2009-09-05 21:47:34 +00002882 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002883 {
cristyc82a27b2011-10-21 01:07:16 +00002884 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002885 image->columns=0;
2886 }
glennrp0fe50b42010-11-16 03:52:51 +00002887
cristy3ed852e2009-09-05 21:47:34 +00002888 return(GetFirstImageInList(image));
2889 }
glennrp0fe50b42010-11-16 03:52:51 +00002890
cristyed552522009-10-16 14:04:35 +00002891 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002892
cristyed552522009-10-16 14:04:35 +00002893 if (quantum_info == (QuantumInfo *) NULL)
2894 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002895
glennrpc8cbc5d2011-01-01 00:12:34 +00002896 {
2897
2898 MagickBooleanType
2899 found_transparent_pixel;
2900
2901 found_transparent_pixel=MagickFalse;
2902
cristy3ed852e2009-09-05 21:47:34 +00002903 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002904 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002905 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002906 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002907 /*
2908 Convert image to DirectClass pixel packets.
2909 */
glennrp67b9c1a2011-04-22 18:47:36 +00002910#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2911 int
2912 depth;
2913
2914 depth=(ssize_t) ping_bit_depth;
2915#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002916 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2917 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2918 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2919 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002920
glennrpc8cbc5d2011-01-01 00:12:34 +00002921 for (y=0; y < (ssize_t) image->rows; y++)
2922 {
2923 if (num_passes > 1)
2924 row_offset=ping_rowbytes*y;
2925
2926 else
2927 row_offset=0;
2928
glennrpcf002022011-01-30 02:38:15 +00002929 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002930 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2931
cristyacd2ed22011-08-30 01:44:23 +00002932 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00002933 break;
2934
glennrpc8cbc5d2011-01-01 00:12:34 +00002935 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2936 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002937 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002938
2939 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2940 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002941 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002942
2943 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2944 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002945 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002946
2947 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2948 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002949 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002950
2951 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2952 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002953 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002954
glennrpc8cbc5d2011-01-01 00:12:34 +00002955 if (found_transparent_pixel == MagickFalse)
2956 {
2957 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002958 if (y== 0 && logging != MagickFalse)
2959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2960 " Looking for cheap transparent pixel");
2961
glennrpc8cbc5d2011-01-01 00:12:34 +00002962 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2963 {
glennrp5aa37f62011-01-02 03:07:57 +00002964 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2965 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy4c08aed2011-07-01 19:47:50 +00002966 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00002967 {
glennrpa6a06632011-01-19 15:15:34 +00002968 if (logging != MagickFalse)
2969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2970 " ...got one.");
2971
glennrpc8cbc5d2011-01-01 00:12:34 +00002972 found_transparent_pixel = MagickTrue;
2973 break;
2974 }
glennrp4f25bd02011-01-01 18:51:28 +00002975 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2976 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp847370c2011-07-05 17:37:15 +00002977 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2978 transparent_color.red &&
2979 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2980 transparent_color.green &&
2981 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2982 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002983 {
glennrpa6a06632011-01-19 15:15:34 +00002984 if (logging != MagickFalse)
2985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2986 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002987 found_transparent_pixel = MagickTrue;
2988 break;
2989 }
cristyed231572011-07-14 02:18:59 +00002990 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00002991 }
2992 }
2993
2994 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2995 {
2996 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2997 image->rows);
2998
2999 if (status == MagickFalse)
3000 break;
3001 }
3002 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3003 break;
3004 }
3005
3006 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3007 {
3008 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00003009 if (status == MagickFalse)
3010 break;
3011 }
cristy3ed852e2009-09-05 21:47:34 +00003012 }
cristy3ed852e2009-09-05 21:47:34 +00003013 }
glennrp0fe50b42010-11-16 03:52:51 +00003014
cristy3ed852e2009-09-05 21:47:34 +00003015 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00003016
cristy3ed852e2009-09-05 21:47:34 +00003017 for (pass=0; pass < num_passes; pass++)
3018 {
3019 Quantum
3020 *quantum_scanline;
3021
3022 register Quantum
3023 *r;
3024
3025 /*
3026 Convert grayscale image to PseudoClass pixel packets.
3027 */
glennrpc17d96f2011-06-27 01:20:11 +00003028 if (logging != MagickFalse)
3029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3030 " Converting grayscale pixels to pixel packets");
cristy4c08aed2011-07-01 19:47:50 +00003031
glennrpfaa852b2010-03-30 12:17:00 +00003032 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00003033 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00003034
cristy3ed852e2009-09-05 21:47:34 +00003035 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3036 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003037
cristy3ed852e2009-09-05 21:47:34 +00003038 if (quantum_scanline == (Quantum *) NULL)
3039 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003040
cristybb503372010-05-27 20:51:26 +00003041 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003042 {
3043 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003044 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003045
cristy3ed852e2009-09-05 21:47:34 +00003046 else
3047 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003048
glennrpcf002022011-01-30 02:38:15 +00003049 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00003050 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003051
cristyacd2ed22011-08-30 01:44:23 +00003052 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003053 break;
glennrp0fe50b42010-11-16 03:52:51 +00003054
glennrpcf002022011-01-30 02:38:15 +00003055 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003056 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003057
glennrpfaa852b2010-03-30 12:17:00 +00003058 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003059 {
3060 case 1:
3061 {
cristybb503372010-05-27 20:51:26 +00003062 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003063 bit;
3064
cristybb503372010-05-27 20:51:26 +00003065 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003066 {
3067 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003068 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003069 p++;
3070 }
glennrp0fe50b42010-11-16 03:52:51 +00003071
cristy3ed852e2009-09-05 21:47:34 +00003072 if ((image->columns % 8) != 0)
3073 {
cristybb503372010-05-27 20:51:26 +00003074 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003075 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003076 }
glennrp0fe50b42010-11-16 03:52:51 +00003077
cristy3ed852e2009-09-05 21:47:34 +00003078 break;
3079 }
glennrp47b9dd52010-11-24 18:12:06 +00003080
cristy3ed852e2009-09-05 21:47:34 +00003081 case 2:
3082 {
cristybb503372010-05-27 20:51:26 +00003083 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003084 {
glennrpa18d5bc2011-04-23 14:51:34 +00003085 *r++=(*p >> 6) & 0x03;
3086 *r++=(*p >> 4) & 0x03;
3087 *r++=(*p >> 2) & 0x03;
3088 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003089 }
glennrp0fe50b42010-11-16 03:52:51 +00003090
cristy3ed852e2009-09-05 21:47:34 +00003091 if ((image->columns % 4) != 0)
3092 {
cristybb503372010-05-27 20:51:26 +00003093 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003094 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003095 }
glennrp0fe50b42010-11-16 03:52:51 +00003096
cristy3ed852e2009-09-05 21:47:34 +00003097 break;
3098 }
glennrp47b9dd52010-11-24 18:12:06 +00003099
cristy3ed852e2009-09-05 21:47:34 +00003100 case 4:
3101 {
cristybb503372010-05-27 20:51:26 +00003102 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003103 {
glennrpa18d5bc2011-04-23 14:51:34 +00003104 *r++=(*p >> 4) & 0x0f;
3105 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003106 }
glennrp0fe50b42010-11-16 03:52:51 +00003107
cristy3ed852e2009-09-05 21:47:34 +00003108 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003109 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003110
cristy3ed852e2009-09-05 21:47:34 +00003111 break;
3112 }
glennrp47b9dd52010-11-24 18:12:06 +00003113
cristy3ed852e2009-09-05 21:47:34 +00003114 case 8:
3115 {
glennrpfaa852b2010-03-30 12:17:00 +00003116 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003117 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003118 {
glennrpa18d5bc2011-04-23 14:51:34 +00003119 *r++=*p++;
cristy4c08aed2011-07-01 19:47:50 +00003120 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3121 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003122 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003123 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003124 }
glennrp0fe50b42010-11-16 03:52:51 +00003125
cristy3ed852e2009-09-05 21:47:34 +00003126 else
cristybb503372010-05-27 20:51:26 +00003127 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003128 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003129
cristy3ed852e2009-09-05 21:47:34 +00003130 break;
3131 }
glennrp47b9dd52010-11-24 18:12:06 +00003132
cristy3ed852e2009-09-05 21:47:34 +00003133 case 16:
3134 {
cristybb503372010-05-27 20:51:26 +00003135 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003136 {
glennrpc17d96f2011-06-27 01:20:11 +00003137#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003138 size_t
3139 quantum;
3140
3141 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003142 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003143
3144 else
glennrpc17d96f2011-06-27 01:20:11 +00003145 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003146
glennrp58f77c72011-04-23 14:09:09 +00003147 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003148 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003149 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003150
3151 if (ping_color_type == 4)
3152 {
glennrpc17d96f2011-06-27 01:20:11 +00003153 if (image->colors > 256)
3154 quantum=((*p++) << 8);
3155 else
3156 quantum=0;
3157
glennrp58f77c72011-04-23 14:09:09 +00003158 quantum|=(*p++);
cristy4c08aed2011-07-01 19:47:50 +00003159 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3160 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003161 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003162 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003163 }
glennrp58f77c72011-04-23 14:09:09 +00003164
3165#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3166 *r++=(*p++);
3167 p++; /* strip low byte */
3168
3169 if (ping_color_type == 4)
3170 {
cristy4c08aed2011-07-01 19:47:50 +00003171 SetPixelAlpha(image,*p++,q);
3172 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003173 found_transparent_pixel = MagickTrue;
3174 p++;
cristyed231572011-07-14 02:18:59 +00003175 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003176 }
cristy3ed852e2009-09-05 21:47:34 +00003177#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003178 }
glennrp47b9dd52010-11-24 18:12:06 +00003179
cristy3ed852e2009-09-05 21:47:34 +00003180 break;
3181 }
glennrp47b9dd52010-11-24 18:12:06 +00003182
cristy3ed852e2009-09-05 21:47:34 +00003183 default:
3184 break;
3185 }
glennrp3faa9a32011-04-23 14:00:25 +00003186
cristy3ed852e2009-09-05 21:47:34 +00003187 /*
3188 Transfer image scanline.
3189 */
3190 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003191
cristy4c08aed2011-07-01 19:47:50 +00003192 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3193
cristyacd2ed22011-08-30 01:44:23 +00003194 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00003195 break;
cristybb503372010-05-27 20:51:26 +00003196 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00003197 {
3198 SetPixelIndex(image,*r++,q);
cristyed231572011-07-14 02:18:59 +00003199 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00003200 }
glennrp0fe50b42010-11-16 03:52:51 +00003201
cristy3ed852e2009-09-05 21:47:34 +00003202 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3203 break;
glennrp0fe50b42010-11-16 03:52:51 +00003204
cristy7a287bf2010-02-14 02:18:09 +00003205 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3206 {
cristycee97112010-05-28 00:44:52 +00003207 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003208 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003209
cristy7a287bf2010-02-14 02:18:09 +00003210 if (status == MagickFalse)
3211 break;
3212 }
cristy3ed852e2009-09-05 21:47:34 +00003213 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003214
cristy7a287bf2010-02-14 02:18:09 +00003215 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003216 {
3217 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003218
cristy3ed852e2009-09-05 21:47:34 +00003219 if (status == MagickFalse)
3220 break;
3221 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003222
cristy3ed852e2009-09-05 21:47:34 +00003223 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3224 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003225
3226 image->matte=found_transparent_pixel;
3227
3228 if (logging != MagickFalse)
3229 {
3230 if (found_transparent_pixel != MagickFalse)
3231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3232 " Found transparent pixel");
3233 else
glennrp5aa37f62011-01-02 03:07:57 +00003234 {
3235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3236 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003237
glennrp5aa37f62011-01-02 03:07:57 +00003238 ping_color_type&=0x03;
3239 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003240 }
3241 }
3242
cristyb32b90a2009-09-07 21:45:48 +00003243 if (quantum_info != (QuantumInfo *) NULL)
3244 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00003245
cristy5c6f7892010-05-05 22:53:29 +00003246 if (image->storage_class == PseudoClass)
3247 {
cristyaeb2cbc2010-05-07 13:28:58 +00003248 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003249 matte;
3250
3251 matte=image->matte;
3252 image->matte=MagickFalse;
cristyea1a8aa2011-10-20 13:24:06 +00003253 (void) SyncImage(image,exception);
cristyaeb2cbc2010-05-07 13:28:58 +00003254 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003255 }
glennrp47b9dd52010-11-24 18:12:06 +00003256
glennrp4eb39312011-03-30 21:34:55 +00003257 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003258
3259 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003260 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003261 {
3262 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003263 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003264 image->colors=2;
cristyea1a8aa2011-10-20 13:24:06 +00003265 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003266#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003267 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003268#endif
3269 if (logging != MagickFalse)
3270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3271 " exit ReadOnePNGImage() early.");
3272 return(image);
3273 }
glennrp47b9dd52010-11-24 18:12:06 +00003274
glennrpfaa852b2010-03-30 12:17:00 +00003275 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003276 {
3277 ClassType
3278 storage_class;
3279
3280 /*
3281 Image has a transparent background.
3282 */
3283 storage_class=image->storage_class;
3284 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003285
glennrp3c218112010-11-27 15:31:26 +00003286/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003287
glennrp0fe50b42010-11-16 03:52:51 +00003288 if (storage_class == PseudoClass)
3289 {
3290 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003291 {
glennrp0fe50b42010-11-16 03:52:51 +00003292 for (x=0; x < ping_num_trans; x++)
3293 {
cristy6b7677c2012-01-01 20:59:57 +00003294 image->colormap[x].matte=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00003295 image->colormap[x].alpha =
3296 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003297 }
glennrpc11cf6a2010-03-20 16:46:19 +00003298 }
glennrp47b9dd52010-11-24 18:12:06 +00003299
glennrp0fe50b42010-11-16 03:52:51 +00003300 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3301 {
3302 for (x=0; x < (int) image->colors; x++)
3303 {
3304 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy4c08aed2011-07-01 19:47:50 +00003305 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003306 {
cristy6b7677c2012-01-01 20:59:57 +00003307 image->colormap[x].matte=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00003308 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003309 }
3310 }
3311 }
cristyea1a8aa2011-10-20 13:24:06 +00003312 (void) SyncImage(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003313 }
glennrp47b9dd52010-11-24 18:12:06 +00003314
glennrpa6a06632011-01-19 15:15:34 +00003315#if 1 /* Should have already been done above, but glennrp problem P10
3316 * needs this.
3317 */
glennrp0fe50b42010-11-16 03:52:51 +00003318 else
3319 {
3320 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003321 {
glennrp0fe50b42010-11-16 03:52:51 +00003322 image->storage_class=storage_class;
3323 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3324
cristyacd2ed22011-08-30 01:44:23 +00003325 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003326 break;
3327
glennrp0fe50b42010-11-16 03:52:51 +00003328
glennrpa6a06632011-01-19 15:15:34 +00003329 /* Caution: on a Q8 build, this does not distinguish between
3330 * 16-bit colors that differ only in the low byte
3331 */
glennrp0fe50b42010-11-16 03:52:51 +00003332 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3333 {
glennrp847370c2011-07-05 17:37:15 +00003334 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3335 transparent_color.red &&
3336 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3337 transparent_color.green &&
3338 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3339 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003340 {
cristy4c08aed2011-07-01 19:47:50 +00003341 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003342 }
glennrp0fe50b42010-11-16 03:52:51 +00003343
glennrp67b9c1a2011-04-22 18:47:36 +00003344#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003345 else
glennrp4f25bd02011-01-01 18:51:28 +00003346 {
cristy4c08aed2011-07-01 19:47:50 +00003347 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003348 }
glennrpa6a06632011-01-19 15:15:34 +00003349#endif
glennrp0fe50b42010-11-16 03:52:51 +00003350
cristyed231572011-07-14 02:18:59 +00003351 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003352 }
3353
3354 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3355 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003356 }
glennrp0fe50b42010-11-16 03:52:51 +00003357 }
glennrpa6a06632011-01-19 15:15:34 +00003358#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003359
cristy3ed852e2009-09-05 21:47:34 +00003360 image->storage_class=DirectClass;
3361 }
glennrp3c218112010-11-27 15:31:26 +00003362
cristyeb3b22a2011-03-31 20:16:11 +00003363 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003364 {
3365 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003366 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3367 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003368 else
glennrpa0ed0092011-04-18 16:36:29 +00003369 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3370 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003371
glennrp4eb39312011-03-30 21:34:55 +00003372 if (status != MagickFalse)
3373 for (i=0; i < (ssize_t) num_text; i++)
3374 {
3375 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003376
glennrp4eb39312011-03-30 21:34:55 +00003377 if (logging != MagickFalse)
3378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3379 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003380
glennrp4eb39312011-03-30 21:34:55 +00003381 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003382 {
cristyd15e6592011-10-15 00:13:06 +00003383 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i,
3384 exception);
glennrp4eb39312011-03-30 21:34:55 +00003385 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003386 }
glennrp0fe50b42010-11-16 03:52:51 +00003387
glennrp4eb39312011-03-30 21:34:55 +00003388 else
3389 {
3390 char
3391 *value;
3392
3393 length=text[i].text_length;
3394 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3395 sizeof(*value));
3396 if (value == (char *) NULL)
3397 {
cristyc82a27b2011-10-21 01:07:16 +00003398 (void) ThrowMagickException(exception,GetMagickModule(),
glennrp4eb39312011-03-30 21:34:55 +00003399 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3400 image->filename);
3401 break;
3402 }
3403 *value='\0';
3404 (void) ConcatenateMagickString(value,text[i].text,length+2);
3405
3406 /* Don't save "density" or "units" property if we have a pHYs
3407 * chunk
3408 */
3409 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3410 (LocaleCompare(text[i].key,"density") != 0 &&
3411 LocaleCompare(text[i].key,"units") != 0))
cristyd15e6592011-10-15 00:13:06 +00003412 (void) SetImageProperty(image,text[i].key,value,exception);
glennrp4eb39312011-03-30 21:34:55 +00003413
3414 if (logging != MagickFalse)
3415 {
3416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3417 " length: %lu",(unsigned long) length);
3418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3419 " Keyword: %s",text[i].key);
3420 }
3421
3422 value=DestroyString(value);
3423 }
3424 }
3425 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003426 }
glennrp3c218112010-11-27 15:31:26 +00003427
cristy3ed852e2009-09-05 21:47:34 +00003428#ifdef MNG_OBJECT_BUFFERS
3429 /*
3430 Store the object if necessary.
3431 */
3432 if (object_id && !mng_info->frozen[object_id])
3433 {
3434 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3435 {
3436 /*
3437 create a new object buffer.
3438 */
3439 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003440 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003441
cristy3ed852e2009-09-05 21:47:34 +00003442 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3443 {
3444 mng_info->ob[object_id]->image=(Image *) NULL;
3445 mng_info->ob[object_id]->reference_count=1;
3446 }
3447 }
glennrp47b9dd52010-11-24 18:12:06 +00003448
cristy3ed852e2009-09-05 21:47:34 +00003449 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3450 mng_info->ob[object_id]->frozen)
3451 {
3452 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
cristyc82a27b2011-10-21 01:07:16 +00003453 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003454 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3455 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003456
cristy3ed852e2009-09-05 21:47:34 +00003457 if (mng_info->ob[object_id]->frozen)
cristyc82a27b2011-10-21 01:07:16 +00003458 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003459 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3460 "`%s'",image->filename);
3461 }
glennrp0fe50b42010-11-16 03:52:51 +00003462
cristy3ed852e2009-09-05 21:47:34 +00003463 else
3464 {
cristy3ed852e2009-09-05 21:47:34 +00003465
3466 if (mng_info->ob[object_id]->image != (Image *) NULL)
3467 mng_info->ob[object_id]->image=DestroyImage
3468 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003469
cristy3ed852e2009-09-05 21:47:34 +00003470 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
cristyc82a27b2011-10-21 01:07:16 +00003471 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003472
cristy3ed852e2009-09-05 21:47:34 +00003473 if (mng_info->ob[object_id]->image != (Image *) NULL)
3474 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003475
cristy3ed852e2009-09-05 21:47:34 +00003476 else
cristyc82a27b2011-10-21 01:07:16 +00003477 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003478 ResourceLimitError,"Cloning image for object buffer failed",
3479 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003480
glennrpfaa852b2010-03-30 12:17:00 +00003481 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003482 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003483
glennrpfaa852b2010-03-30 12:17:00 +00003484 mng_info->ob[object_id]->width=ping_width;
3485 mng_info->ob[object_id]->height=ping_height;
3486 mng_info->ob[object_id]->color_type=ping_color_type;
3487 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3488 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3489 mng_info->ob[object_id]->compression_method=
3490 ping_compression_method;
3491 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003492
glennrpfaa852b2010-03-30 12:17:00 +00003493 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003494 {
3495 int
3496 number_colors;
3497
3498 png_colorp
3499 plte;
3500
3501 /*
3502 Copy the PLTE to the object buffer.
3503 */
3504 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3505 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003506
cristy3ed852e2009-09-05 21:47:34 +00003507 for (i=0; i < number_colors; i++)
3508 {
3509 mng_info->ob[object_id]->plte[i]=plte[i];
3510 }
3511 }
glennrp47b9dd52010-11-24 18:12:06 +00003512
cristy3ed852e2009-09-05 21:47:34 +00003513 else
3514 mng_info->ob[object_id]->plte_length=0;
3515 }
3516 }
3517#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003518
3519 /* Set image->matte to MagickTrue if the input colortype supports
3520 * alpha or if a valid tRNS chunk is present, no matter whether there
3521 * is actual transparency present.
3522 */
3523 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3524 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3525 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3526 MagickTrue : MagickFalse;
3527
glennrpcb395ac2011-03-30 19:50:23 +00003528 /* Set more properties for identify to retrieve */
3529 {
3530 char
3531 msg[MaxTextExtent];
3532
glennrp4eb39312011-03-30 21:34:55 +00003533 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003534 {
3535 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003536 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003537 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
cristy5d6fc9c2011-12-27 03:10:42 +00003538 (void) SetImageProperty(image,"png:text ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003539 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003540 }
3541
3542 if (num_raw_profiles != 0)
3543 {
cristy3b6fd2e2011-05-20 12:53:50 +00003544 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003545 "%d were found", num_raw_profiles);
cristy5d6fc9c2011-12-27 03:10:42 +00003546 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003547 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003548 }
3549
glennrpcb395ac2011-03-30 19:50:23 +00003550 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003551 {
cristy3b6fd2e2011-05-20 12:53:50 +00003552 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003553 "chunk was found (see Chromaticity, above)");
cristy5d6fc9c2011-12-27 03:10:42 +00003554 (void) SetImageProperty(image,"png:cHRM ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003555 exception);
glennrp59612252011-03-30 21:45:21 +00003556 }
glennrpcb395ac2011-03-30 19:50:23 +00003557
3558 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003559 {
cristy3b6fd2e2011-05-20 12:53:50 +00003560 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003561 "chunk was found (see Background color, above)");
cristy5d6fc9c2011-12-27 03:10:42 +00003562 (void) SetImageProperty(image,"png:bKGD ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003563 exception);
glennrp59612252011-03-30 21:45:21 +00003564 }
3565
cristy3b6fd2e2011-05-20 12:53:50 +00003566 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003567 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003568
3569 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy5d6fc9c2011-12-27 03:10:42 +00003570 (void) SetImageProperty(image,"png:iCCP ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003571 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003572
glennrpcb395ac2011-03-30 19:50:23 +00003573 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy5d6fc9c2011-12-27 03:10:42 +00003574 (void) SetImageProperty(image,"png:tRNS ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003575 exception);
glennrp4eb39312011-03-30 21:34:55 +00003576
3577#if defined(PNG_sRGB_SUPPORTED)
3578 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3579 {
cristy96c4fcb2012-02-08 01:01:05 +00003580 image->colorspace=sRGBColorspace;
cristy3b6fd2e2011-05-20 12:53:50 +00003581 (void) FormatLocaleString(msg,MaxTextExtent,
cristy96c4fcb2012-02-08 01:01:05 +00003582 "intent=%d (See Rendering intent)", (int) intent);
cristy5d6fc9c2011-12-27 03:10:42 +00003583 (void) SetImageProperty(image,"png:sRGB ",msg,
cristy96c4fcb2012-02-08 01:01:05 +00003584 exception);
glennrp4eb39312011-03-30 21:34:55 +00003585 }
3586#endif
3587
3588 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3589 {
cristy3b6fd2e2011-05-20 12:53:50 +00003590 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003591 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003592 file_gamma);
cristy5d6fc9c2011-12-27 03:10:42 +00003593 (void) SetImageProperty(image,"png:gAMA ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003594 exception);
glennrp4eb39312011-03-30 21:34:55 +00003595 }
3596
3597#if defined(PNG_pHYs_SUPPORTED)
3598 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3599 {
cristy3b6fd2e2011-05-20 12:53:50 +00003600 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003601 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003602 (double) x_resolution,(double) y_resolution, unit_type);
cristy5d6fc9c2011-12-27 03:10:42 +00003603 (void) SetImageProperty(image,"png:pHYs ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003604 exception);
glennrp4eb39312011-03-30 21:34:55 +00003605 }
3606#endif
3607
3608#if defined(PNG_oFFs_SUPPORTED)
3609 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3610 {
cristy3b6fd2e2011-05-20 12:53:50 +00003611 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003612 (double) image->page.x,(double) image->page.y);
cristy5d6fc9c2011-12-27 03:10:42 +00003613 (void) SetImageProperty(image,"png:oFFs ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003614 exception);
glennrp4eb39312011-03-30 21:34:55 +00003615 }
3616#endif
3617
glennrp07523c72011-03-31 18:12:10 +00003618 if ((image->page.width != 0 && image->page.width != image->columns) ||
3619 (image->page.height != 0 && image->page.height != image->rows))
3620 {
cristy3b6fd2e2011-05-20 12:53:50 +00003621 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003622 "width=%.20g, height=%.20g",
3623 (double) image->page.width,(double) image->page.height);
cristy5d6fc9c2011-12-27 03:10:42 +00003624 (void) SetImageProperty(image,"png:vpAg ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003625 exception);
glennrp07523c72011-03-31 18:12:10 +00003626 }
glennrpcb395ac2011-03-30 19:50:23 +00003627 }
3628
cristy3ed852e2009-09-05 21:47:34 +00003629 /*
3630 Relinquish resources.
3631 */
3632 png_destroy_read_struct(&ping,&ping_info,&end_info);
3633
glennrpcf002022011-01-30 02:38:15 +00003634 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003635#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003636 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003637#endif
3638
3639 if (logging != MagickFalse)
3640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3641 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003642
cristy3ed852e2009-09-05 21:47:34 +00003643 return(image);
3644
3645/* end of reading one PNG image */
3646}
3647
3648static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3649{
3650 Image
3651 *image,
3652 *previous;
3653
3654 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003655 have_mng_structure,
3656 logging,
cristy3ed852e2009-09-05 21:47:34 +00003657 status;
3658
3659 MngInfo
3660 *mng_info;
3661
3662 char
3663 magic_number[MaxTextExtent];
3664
cristy3ed852e2009-09-05 21:47:34 +00003665 ssize_t
3666 count;
3667
3668 /*
3669 Open image file.
3670 */
3671 assert(image_info != (const ImageInfo *) NULL);
3672 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003673
cristy3ed852e2009-09-05 21:47:34 +00003674 if (image_info->debug != MagickFalse)
3675 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3676 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003677
cristy3ed852e2009-09-05 21:47:34 +00003678 assert(exception != (ExceptionInfo *) NULL);
3679 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003680 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy9950d572011-10-01 18:22:35 +00003681 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003682 mng_info=(MngInfo *) NULL;
3683 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003684
cristy3ed852e2009-09-05 21:47:34 +00003685 if (status == MagickFalse)
3686 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003687
cristy3ed852e2009-09-05 21:47:34 +00003688 /*
3689 Verify PNG signature.
3690 */
3691 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003692
glennrpdde35db2011-02-21 12:06:32 +00003693 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003694 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003695
cristy3ed852e2009-09-05 21:47:34 +00003696 /*
3697 Allocate a MngInfo structure.
3698 */
3699 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003700 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003701
cristy3ed852e2009-09-05 21:47:34 +00003702 if (mng_info == (MngInfo *) NULL)
3703 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003704
cristy3ed852e2009-09-05 21:47:34 +00003705 /*
3706 Initialize members of the MngInfo structure.
3707 */
3708 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3709 mng_info->image=image;
3710 have_mng_structure=MagickTrue;
3711
3712 previous=image;
3713 image=ReadOnePNGImage(mng_info,image_info,exception);
3714 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003715
cristy3ed852e2009-09-05 21:47:34 +00003716 if (image == (Image *) NULL)
3717 {
3718 if (previous != (Image *) NULL)
3719 {
3720 if (previous->signature != MagickSignature)
3721 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003722
cristy3ed852e2009-09-05 21:47:34 +00003723 (void) CloseBlob(previous);
3724 (void) DestroyImageList(previous);
3725 }
glennrp0fe50b42010-11-16 03:52:51 +00003726
cristy3ed852e2009-09-05 21:47:34 +00003727 if (logging != MagickFalse)
3728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3729 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003730
cristy3ed852e2009-09-05 21:47:34 +00003731 return((Image *) NULL);
3732 }
glennrp47b9dd52010-11-24 18:12:06 +00003733
cristy3ed852e2009-09-05 21:47:34 +00003734 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003735
cristy3ed852e2009-09-05 21:47:34 +00003736 if ((image->columns == 0) || (image->rows == 0))
3737 {
3738 if (logging != MagickFalse)
3739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3740 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003741
cristy3ed852e2009-09-05 21:47:34 +00003742 ThrowReaderException(CorruptImageError,"CorruptImage");
3743 }
glennrp47b9dd52010-11-24 18:12:06 +00003744
cristy3ed852e2009-09-05 21:47:34 +00003745 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3746 {
cristy018f07f2011-09-04 21:15:19 +00003747 (void) SetImageType(image,TrueColorType,exception);
cristy3ed852e2009-09-05 21:47:34 +00003748 image->matte=MagickFalse;
3749 }
glennrp0fe50b42010-11-16 03:52:51 +00003750
cristy3ed852e2009-09-05 21:47:34 +00003751 if (LocaleCompare(image_info->magick,"PNG32") == 0)
cristy018f07f2011-09-04 21:15:19 +00003752 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003753
cristy3ed852e2009-09-05 21:47:34 +00003754 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3756 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3757 (double) image->page.width,(double) image->page.height,
3758 (double) image->page.x,(double) image->page.y);
3759
3760 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003762
cristy3ed852e2009-09-05 21:47:34 +00003763 return(image);
3764}
3765
3766
3767
3768#if defined(JNG_SUPPORTED)
3769/*
3770%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3771% %
3772% %
3773% %
3774% R e a d O n e J N G I m a g e %
3775% %
3776% %
3777% %
3778%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3779%
3780% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3781% (minus the 8-byte signature) and returns it. It allocates the memory
3782% necessary for the new Image structure and returns a pointer to the new
3783% image.
3784%
3785% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3786%
3787% The format of the ReadOneJNGImage method is:
3788%
3789% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3790% ExceptionInfo *exception)
3791%
3792% A description of each parameter follows:
3793%
3794% o mng_info: Specifies a pointer to a MngInfo structure.
3795%
3796% o image_info: the image info.
3797%
3798% o exception: return any errors or warnings in this structure.
3799%
3800*/
3801static Image *ReadOneJNGImage(MngInfo *mng_info,
3802 const ImageInfo *image_info, ExceptionInfo *exception)
3803{
3804 Image
3805 *alpha_image,
3806 *color_image,
3807 *image,
3808 *jng_image;
3809
3810 ImageInfo
3811 *alpha_image_info,
3812 *color_image_info;
3813
cristy4383ec82011-01-05 15:42:32 +00003814 MagickBooleanType
3815 logging;
3816
cristybb503372010-05-27 20:51:26 +00003817 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003818 y;
3819
3820 MagickBooleanType
3821 status;
3822
3823 png_uint_32
3824 jng_height,
3825 jng_width;
3826
3827 png_byte
3828 jng_color_type,
3829 jng_image_sample_depth,
3830 jng_image_compression_method,
3831 jng_image_interlace_method,
3832 jng_alpha_sample_depth,
3833 jng_alpha_compression_method,
3834 jng_alpha_filter_method,
3835 jng_alpha_interlace_method;
3836
cristy4c08aed2011-07-01 19:47:50 +00003837 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003838 *s;
3839
cristybb503372010-05-27 20:51:26 +00003840 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003841 i,
3842 x;
3843
cristy4c08aed2011-07-01 19:47:50 +00003844 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003845 *q;
3846
3847 register unsigned char
3848 *p;
3849
3850 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003851 read_JSEP,
3852 reading_idat,
3853 skip_to_iend;
3854
cristybb503372010-05-27 20:51:26 +00003855 size_t
cristy3ed852e2009-09-05 21:47:34 +00003856 length;
3857
3858 jng_alpha_compression_method=0;
3859 jng_alpha_sample_depth=8;
3860 jng_color_type=0;
3861 jng_height=0;
3862 jng_width=0;
3863 alpha_image=(Image *) NULL;
3864 color_image=(Image *) NULL;
3865 alpha_image_info=(ImageInfo *) NULL;
3866 color_image_info=(ImageInfo *) NULL;
3867
3868 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003869 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003870
3871 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003872
cristy4c08aed2011-07-01 19:47:50 +00003873 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003874 {
3875 /*
3876 Allocate next image structure.
3877 */
3878 if (logging != MagickFalse)
3879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3880 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003881
cristy9950d572011-10-01 18:22:35 +00003882 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003883
cristy3ed852e2009-09-05 21:47:34 +00003884 if (GetNextImageInList(image) == (Image *) NULL)
3885 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003886
cristy3ed852e2009-09-05 21:47:34 +00003887 image=SyncNextImageInList(image);
3888 }
3889 mng_info->image=image;
3890
3891 /*
3892 Signature bytes have already been read.
3893 */
3894
3895 read_JSEP=MagickFalse;
3896 reading_idat=MagickFalse;
3897 skip_to_iend=MagickFalse;
3898 for (;;)
3899 {
3900 char
3901 type[MaxTextExtent];
3902
3903 unsigned char
3904 *chunk;
3905
3906 unsigned int
3907 count;
3908
3909 /*
3910 Read a new JNG chunk.
3911 */
3912 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3913 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003914
cristy3ed852e2009-09-05 21:47:34 +00003915 if (status == MagickFalse)
3916 break;
glennrp0fe50b42010-11-16 03:52:51 +00003917
cristy3ed852e2009-09-05 21:47:34 +00003918 type[0]='\0';
3919 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3920 length=ReadBlobMSBLong(image);
3921 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3922
3923 if (logging != MagickFalse)
3924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003925 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3926 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003927
3928 if (length > PNG_UINT_31_MAX || count == 0)
3929 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003930
cristy3ed852e2009-09-05 21:47:34 +00003931 p=NULL;
3932 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003933
cristy3ed852e2009-09-05 21:47:34 +00003934 if (length)
3935 {
3936 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003937
cristy3ed852e2009-09-05 21:47:34 +00003938 if (chunk == (unsigned char *) NULL)
3939 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003940
cristybb503372010-05-27 20:51:26 +00003941 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003942 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003943
cristy3ed852e2009-09-05 21:47:34 +00003944 p=chunk;
3945 }
glennrp47b9dd52010-11-24 18:12:06 +00003946
cristy3ed852e2009-09-05 21:47:34 +00003947 (void) ReadBlobMSBLong(image); /* read crc word */
3948
3949 if (skip_to_iend)
3950 {
3951 if (length)
3952 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003953
cristy3ed852e2009-09-05 21:47:34 +00003954 continue;
3955 }
3956
3957 if (memcmp(type,mng_JHDR,4) == 0)
3958 {
3959 if (length == 16)
3960 {
cristybb503372010-05-27 20:51:26 +00003961 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003962 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003963 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003964 (p[6] << 8) | p[7]);
3965 jng_color_type=p[8];
3966 jng_image_sample_depth=p[9];
3967 jng_image_compression_method=p[10];
3968 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003969
cristy3ed852e2009-09-05 21:47:34 +00003970 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3971 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003972
cristy3ed852e2009-09-05 21:47:34 +00003973 jng_alpha_sample_depth=p[12];
3974 jng_alpha_compression_method=p[13];
3975 jng_alpha_filter_method=p[14];
3976 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003977
cristy3ed852e2009-09-05 21:47:34 +00003978 if (logging != MagickFalse)
3979 {
3980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003981 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003982
cristy3ed852e2009-09-05 21:47:34 +00003983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003984 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003985
cristy3ed852e2009-09-05 21:47:34 +00003986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3987 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003988
cristy3ed852e2009-09-05 21:47:34 +00003989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3990 " jng_image_sample_depth: %3d",
3991 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003992
cristy3ed852e2009-09-05 21:47:34 +00003993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3994 " jng_image_compression_method:%3d",
3995 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003996
cristy3ed852e2009-09-05 21:47:34 +00003997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3998 " jng_image_interlace_method: %3d",
3999 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00004000
cristy3ed852e2009-09-05 21:47:34 +00004001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4002 " jng_alpha_sample_depth: %3d",
4003 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00004004
cristy3ed852e2009-09-05 21:47:34 +00004005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4006 " jng_alpha_compression_method:%3d",
4007 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004008
cristy3ed852e2009-09-05 21:47:34 +00004009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4010 " jng_alpha_filter_method: %3d",
4011 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00004012
cristy3ed852e2009-09-05 21:47:34 +00004013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4014 " jng_alpha_interlace_method: %3d",
4015 jng_alpha_interlace_method);
4016 }
4017 }
glennrp47b9dd52010-11-24 18:12:06 +00004018
cristy3ed852e2009-09-05 21:47:34 +00004019 if (length)
4020 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004021
cristy3ed852e2009-09-05 21:47:34 +00004022 continue;
4023 }
4024
4025
4026 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4027 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4028 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4029 {
4030 /*
4031 o create color_image
4032 o open color_blob, attached to color_image
4033 o if (color type has alpha)
4034 open alpha_blob, attached to alpha_image
4035 */
4036
cristy73bd4a52010-10-05 11:24:23 +00004037 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004038
cristy3ed852e2009-09-05 21:47:34 +00004039 if (color_image_info == (ImageInfo *) NULL)
4040 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004041
cristy3ed852e2009-09-05 21:47:34 +00004042 GetImageInfo(color_image_info);
cristy9950d572011-10-01 18:22:35 +00004043 color_image=AcquireImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004044
cristy3ed852e2009-09-05 21:47:34 +00004045 if (color_image == (Image *) NULL)
4046 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4047
4048 if (logging != MagickFalse)
4049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4050 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004051
cristy3ed852e2009-09-05 21:47:34 +00004052 (void) AcquireUniqueFilename(color_image->filename);
4053 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4054 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004055
cristy3ed852e2009-09-05 21:47:34 +00004056 if (status == MagickFalse)
4057 return((Image *) NULL);
4058
4059 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4060 {
4061 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004062 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004063
cristy3ed852e2009-09-05 21:47:34 +00004064 if (alpha_image_info == (ImageInfo *) NULL)
4065 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004066
cristy3ed852e2009-09-05 21:47:34 +00004067 GetImageInfo(alpha_image_info);
cristy9950d572011-10-01 18:22:35 +00004068 alpha_image=AcquireImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004069
cristy3ed852e2009-09-05 21:47:34 +00004070 if (alpha_image == (Image *) NULL)
4071 {
4072 alpha_image=DestroyImage(alpha_image);
4073 ThrowReaderException(ResourceLimitError,
4074 "MemoryAllocationFailed");
4075 }
glennrp0fe50b42010-11-16 03:52:51 +00004076
cristy3ed852e2009-09-05 21:47:34 +00004077 if (logging != MagickFalse)
4078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4079 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004080
cristy3ed852e2009-09-05 21:47:34 +00004081 (void) AcquireUniqueFilename(alpha_image->filename);
4082 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4083 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004084
cristy3ed852e2009-09-05 21:47:34 +00004085 if (status == MagickFalse)
4086 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004087
cristy3ed852e2009-09-05 21:47:34 +00004088 if (jng_alpha_compression_method == 0)
4089 {
4090 unsigned char
4091 data[18];
4092
4093 if (logging != MagickFalse)
4094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4095 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004096
cristy3ed852e2009-09-05 21:47:34 +00004097 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4098 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004099
cristy3ed852e2009-09-05 21:47:34 +00004100 (void) WriteBlobMSBULong(alpha_image,13L);
4101 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004102 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004103 PNGLong(data+4,jng_width);
4104 PNGLong(data+8,jng_height);
4105 data[12]=jng_alpha_sample_depth;
4106 data[13]=0; /* color_type gray */
4107 data[14]=0; /* compression method 0 */
4108 data[15]=0; /* filter_method 0 */
4109 data[16]=0; /* interlace_method 0 */
4110 (void) WriteBlob(alpha_image,17,data);
4111 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4112 }
4113 }
4114 reading_idat=MagickTrue;
4115 }
4116
4117 if (memcmp(type,mng_JDAT,4) == 0)
4118 {
glennrp47b9dd52010-11-24 18:12:06 +00004119 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004120
4121 if (logging != MagickFalse)
4122 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4123 " Copying JDAT chunk data to color_blob.");
4124
4125 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004126
cristy3ed852e2009-09-05 21:47:34 +00004127 if (length)
4128 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004129
cristy3ed852e2009-09-05 21:47:34 +00004130 continue;
4131 }
4132
4133 if (memcmp(type,mng_IDAT,4) == 0)
4134 {
4135 png_byte
4136 data[5];
4137
glennrp47b9dd52010-11-24 18:12:06 +00004138 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004139
4140 if (image_info->ping == MagickFalse)
4141 {
4142 if (logging != MagickFalse)
4143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4144 " Copying IDAT chunk data to alpha_blob.");
4145
cristybb503372010-05-27 20:51:26 +00004146 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004147 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004148 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004149 (void) WriteBlob(alpha_image,4,data);
4150 (void) WriteBlob(alpha_image,length,chunk);
4151 (void) WriteBlobMSBULong(alpha_image,
4152 crc32(crc32(0,data,4),chunk,(uInt) length));
4153 }
glennrp0fe50b42010-11-16 03:52:51 +00004154
cristy3ed852e2009-09-05 21:47:34 +00004155 if (length)
4156 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004157
cristy3ed852e2009-09-05 21:47:34 +00004158 continue;
4159 }
4160
4161 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4162 {
glennrp47b9dd52010-11-24 18:12:06 +00004163 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004164
4165 if (image_info->ping == MagickFalse)
4166 {
4167 if (logging != MagickFalse)
4168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4169 " Copying JDAA chunk data to alpha_blob.");
4170
4171 (void) WriteBlob(alpha_image,length,chunk);
4172 }
glennrp0fe50b42010-11-16 03:52:51 +00004173
cristy3ed852e2009-09-05 21:47:34 +00004174 if (length)
4175 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004176
cristy3ed852e2009-09-05 21:47:34 +00004177 continue;
4178 }
4179
4180 if (memcmp(type,mng_JSEP,4) == 0)
4181 {
4182 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004183
cristy3ed852e2009-09-05 21:47:34 +00004184 if (length)
4185 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004186
cristy3ed852e2009-09-05 21:47:34 +00004187 continue;
4188 }
4189
4190 if (memcmp(type,mng_bKGD,4) == 0)
4191 {
4192 if (length == 2)
4193 {
4194 image->background_color.red=ScaleCharToQuantum(p[1]);
4195 image->background_color.green=image->background_color.red;
4196 image->background_color.blue=image->background_color.red;
4197 }
glennrp0fe50b42010-11-16 03:52:51 +00004198
cristy3ed852e2009-09-05 21:47:34 +00004199 if (length == 6)
4200 {
4201 image->background_color.red=ScaleCharToQuantum(p[1]);
4202 image->background_color.green=ScaleCharToQuantum(p[3]);
4203 image->background_color.blue=ScaleCharToQuantum(p[5]);
4204 }
glennrp0fe50b42010-11-16 03:52:51 +00004205
cristy3ed852e2009-09-05 21:47:34 +00004206 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4207 continue;
4208 }
4209
4210 if (memcmp(type,mng_gAMA,4) == 0)
4211 {
4212 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004213 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004214
cristy3ed852e2009-09-05 21:47:34 +00004215 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4216 continue;
4217 }
4218
4219 if (memcmp(type,mng_cHRM,4) == 0)
4220 {
4221 if (length == 32)
4222 {
cristy8182b072010-05-30 20:10:53 +00004223 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4224 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4225 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4226 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4227 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4228 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4229 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4230 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004231 }
glennrp47b9dd52010-11-24 18:12:06 +00004232
cristy3ed852e2009-09-05 21:47:34 +00004233 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4234 continue;
4235 }
4236
4237 if (memcmp(type,mng_sRGB,4) == 0)
4238 {
4239 if (length == 1)
4240 {
cristy2ea7a8e2012-02-08 01:04:50 +00004241 image->colorspace=sRGBColorspace;
glennrpe610a072010-08-05 17:08:46 +00004242 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004243 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004244 image->gamma=0.45455f;
4245 image->chromaticity.red_primary.x=0.6400f;
4246 image->chromaticity.red_primary.y=0.3300f;
4247 image->chromaticity.green_primary.x=0.3000f;
4248 image->chromaticity.green_primary.y=0.6000f;
4249 image->chromaticity.blue_primary.x=0.1500f;
4250 image->chromaticity.blue_primary.y=0.0600f;
4251 image->chromaticity.white_point.x=0.3127f;
4252 image->chromaticity.white_point.y=0.3290f;
4253 }
glennrp47b9dd52010-11-24 18:12:06 +00004254
cristy3ed852e2009-09-05 21:47:34 +00004255 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4256 continue;
4257 }
4258
4259 if (memcmp(type,mng_oFFs,4) == 0)
4260 {
4261 if (length > 8)
4262 {
glennrp5eae7602011-02-22 15:21:32 +00004263 image->page.x=(ssize_t) mng_get_long(p);
4264 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004265
cristy3ed852e2009-09-05 21:47:34 +00004266 if ((int) p[8] != 0)
4267 {
4268 image->page.x/=10000;
4269 image->page.y/=10000;
4270 }
4271 }
glennrp47b9dd52010-11-24 18:12:06 +00004272
cristy3ed852e2009-09-05 21:47:34 +00004273 if (length)
4274 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004275
cristy3ed852e2009-09-05 21:47:34 +00004276 continue;
4277 }
4278
4279 if (memcmp(type,mng_pHYs,4) == 0)
4280 {
4281 if (length > 8)
4282 {
cristy2a11bef2011-10-28 18:33:11 +00004283 image->resolution.x=(double) mng_get_long(p);
4284 image->resolution.y=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004285 if ((int) p[8] == PNG_RESOLUTION_METER)
4286 {
4287 image->units=PixelsPerCentimeterResolution;
cristy2a11bef2011-10-28 18:33:11 +00004288 image->resolution.x=image->resolution.x/100.0f;
4289 image->resolution.y=image->resolution.y/100.0f;
cristy3ed852e2009-09-05 21:47:34 +00004290 }
4291 }
glennrp0fe50b42010-11-16 03:52:51 +00004292
cristy3ed852e2009-09-05 21:47:34 +00004293 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4294 continue;
4295 }
4296
4297#if 0
4298 if (memcmp(type,mng_iCCP,4) == 0)
4299 {
glennrpfd05d622011-02-25 04:10:33 +00004300 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004301 if (length)
4302 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004303
cristy3ed852e2009-09-05 21:47:34 +00004304 continue;
4305 }
4306#endif
4307
4308 if (length)
4309 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4310
4311 if (memcmp(type,mng_IEND,4))
4312 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004313
cristy3ed852e2009-09-05 21:47:34 +00004314 break;
4315 }
4316
4317
4318 /* IEND found */
4319
4320 /*
4321 Finish up reading image data:
4322
4323 o read main image from color_blob.
4324
4325 o close color_blob.
4326
4327 o if (color_type has alpha)
4328 if alpha_encoding is PNG
4329 read secondary image from alpha_blob via ReadPNG
4330 if alpha_encoding is JPEG
4331 read secondary image from alpha_blob via ReadJPEG
4332
4333 o close alpha_blob.
4334
4335 o copy intensity of secondary image into
cristy4c08aed2011-07-01 19:47:50 +00004336 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004337
4338 o destroy the secondary image.
4339 */
4340
4341 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004342
cristy3ed852e2009-09-05 21:47:34 +00004343 if (logging != MagickFalse)
4344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4345 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004346
cristy3b6fd2e2011-05-20 12:53:50 +00004347 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004348 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004349
cristy3ed852e2009-09-05 21:47:34 +00004350 color_image_info->ping=MagickFalse; /* To do: avoid this */
4351 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004352
cristy3ed852e2009-09-05 21:47:34 +00004353 if (jng_image == (Image *) NULL)
4354 return((Image *) NULL);
4355
4356 (void) RelinquishUniqueFileResource(color_image->filename);
4357 color_image=DestroyImage(color_image);
4358 color_image_info=DestroyImageInfo(color_image_info);
4359
4360 if (jng_image == (Image *) NULL)
4361 return((Image *) NULL);
4362
4363 if (logging != MagickFalse)
4364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4365 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004366
cristy3ed852e2009-09-05 21:47:34 +00004367 image->rows=jng_height;
4368 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004369
cristybb503372010-05-27 20:51:26 +00004370 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004371 {
cristyc82a27b2011-10-21 01:07:16 +00004372 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00004373 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004374 for (x=(ssize_t) image->columns; x != 0; x--)
4375 {
4376 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4377 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4378 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004379 q+=GetPixelChannels(image);
4380 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004381 }
glennrp47b9dd52010-11-24 18:12:06 +00004382
cristy3ed852e2009-09-05 21:47:34 +00004383 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4384 break;
4385 }
glennrp0fe50b42010-11-16 03:52:51 +00004386
cristy3ed852e2009-09-05 21:47:34 +00004387 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004388
cristy3ed852e2009-09-05 21:47:34 +00004389 if (image_info->ping == MagickFalse)
4390 {
4391 if (jng_color_type >= 12)
4392 {
4393 if (jng_alpha_compression_method == 0)
4394 {
4395 png_byte
4396 data[5];
4397 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4398 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004399 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004400 (void) WriteBlob(alpha_image,4,data);
4401 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4402 }
glennrp0fe50b42010-11-16 03:52:51 +00004403
cristy3ed852e2009-09-05 21:47:34 +00004404 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004405
cristy3ed852e2009-09-05 21:47:34 +00004406 if (logging != MagickFalse)
4407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00004408 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004409
cristy3b6fd2e2011-05-20 12:53:50 +00004410 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004411 "%s",alpha_image->filename);
4412
4413 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004414
cristy3ed852e2009-09-05 21:47:34 +00004415 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004416 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004417 {
4418 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristyc82a27b2011-10-21 01:07:16 +00004419 exception);
cristy3ed852e2009-09-05 21:47:34 +00004420 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004421
cristy3ed852e2009-09-05 21:47:34 +00004422 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00004423 for (x=(ssize_t) image->columns; x != 0; x--)
4424 {
4425 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004426 q+=GetPixelChannels(image);
4427 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004428 }
glennrp0fe50b42010-11-16 03:52:51 +00004429
cristy3ed852e2009-09-05 21:47:34 +00004430 else
cristy4c08aed2011-07-01 19:47:50 +00004431 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004432 {
cristy4c08aed2011-07-01 19:47:50 +00004433 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4434 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004435 image->matte=MagickTrue;
cristyed231572011-07-14 02:18:59 +00004436 q+=GetPixelChannels(image);
4437 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004438 }
glennrp0fe50b42010-11-16 03:52:51 +00004439
cristy3ed852e2009-09-05 21:47:34 +00004440 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4441 break;
4442 }
4443 (void) RelinquishUniqueFileResource(alpha_image->filename);
4444 alpha_image=DestroyImage(alpha_image);
4445 alpha_image_info=DestroyImageInfo(alpha_image_info);
4446 if (jng_image != (Image *) NULL)
4447 jng_image=DestroyImage(jng_image);
4448 }
4449 }
4450
glennrp47b9dd52010-11-24 18:12:06 +00004451 /* Read the JNG image. */
4452
cristy3ed852e2009-09-05 21:47:34 +00004453 if (mng_info->mng_type == 0)
4454 {
4455 mng_info->mng_width=jng_width;
4456 mng_info->mng_height=jng_height;
4457 }
glennrp0fe50b42010-11-16 03:52:51 +00004458
cristy3ed852e2009-09-05 21:47:34 +00004459 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004460 {
4461 image->page.width=jng_width;
4462 image->page.height=jng_height;
4463 }
4464
cristy3ed852e2009-09-05 21:47:34 +00004465 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004466 {
4467 image->page.x=mng_info->x_off[mng_info->object_id];
4468 image->page.y=mng_info->y_off[mng_info->object_id];
4469 }
4470
cristy3ed852e2009-09-05 21:47:34 +00004471 else
glennrp0fe50b42010-11-16 03:52:51 +00004472 {
4473 image->page.y=mng_info->y_off[mng_info->object_id];
4474 }
4475
cristy3ed852e2009-09-05 21:47:34 +00004476 mng_info->image_found++;
4477 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4478 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004479
cristy3ed852e2009-09-05 21:47:34 +00004480 if (logging != MagickFalse)
4481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4482 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004483
cristy3ed852e2009-09-05 21:47:34 +00004484 return(image);
4485}
4486
4487/*
4488%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4489% %
4490% %
4491% %
4492% R e a d J N G I m a g e %
4493% %
4494% %
4495% %
4496%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4497%
4498% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4499% (including the 8-byte signature) and returns it. It allocates the memory
4500% necessary for the new Image structure and returns a pointer to the new
4501% image.
4502%
4503% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4504%
4505% The format of the ReadJNGImage method is:
4506%
4507% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4508% *exception)
4509%
4510% A description of each parameter follows:
4511%
4512% o image_info: the image info.
4513%
4514% o exception: return any errors or warnings in this structure.
4515%
4516*/
4517
4518static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4519{
4520 Image
4521 *image,
4522 *previous;
4523
4524 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004525 have_mng_structure,
4526 logging,
cristy3ed852e2009-09-05 21:47:34 +00004527 status;
4528
4529 MngInfo
4530 *mng_info;
4531
4532 char
4533 magic_number[MaxTextExtent];
4534
cristy3ed852e2009-09-05 21:47:34 +00004535 size_t
4536 count;
4537
4538 /*
4539 Open image file.
4540 */
4541 assert(image_info != (const ImageInfo *) NULL);
4542 assert(image_info->signature == MagickSignature);
4543 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4544 assert(exception != (ExceptionInfo *) NULL);
4545 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004546 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy9950d572011-10-01 18:22:35 +00004547 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004548 mng_info=(MngInfo *) NULL;
4549 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004550
cristy3ed852e2009-09-05 21:47:34 +00004551 if (status == MagickFalse)
4552 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004553
cristy3ed852e2009-09-05 21:47:34 +00004554 if (LocaleCompare(image_info->magick,"JNG") != 0)
4555 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004556
glennrp47b9dd52010-11-24 18:12:06 +00004557 /* Verify JNG signature. */
4558
cristy3ed852e2009-09-05 21:47:34 +00004559 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004560
glennrp3b8763e2011-02-21 12:08:18 +00004561 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004562 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004563
glennrp47b9dd52010-11-24 18:12:06 +00004564 /* Allocate a MngInfo structure. */
4565
cristy3ed852e2009-09-05 21:47:34 +00004566 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004567 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004568
cristy3ed852e2009-09-05 21:47:34 +00004569 if (mng_info == (MngInfo *) NULL)
4570 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004571
glennrp47b9dd52010-11-24 18:12:06 +00004572 /* Initialize members of the MngInfo structure. */
4573
cristy3ed852e2009-09-05 21:47:34 +00004574 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4575 have_mng_structure=MagickTrue;
4576
4577 mng_info->image=image;
4578 previous=image;
4579 image=ReadOneJNGImage(mng_info,image_info,exception);
4580 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004581
cristy3ed852e2009-09-05 21:47:34 +00004582 if (image == (Image *) NULL)
4583 {
4584 if (IsImageObject(previous) != MagickFalse)
4585 {
4586 (void) CloseBlob(previous);
4587 (void) DestroyImageList(previous);
4588 }
glennrp0fe50b42010-11-16 03:52:51 +00004589
cristy3ed852e2009-09-05 21:47:34 +00004590 if (logging != MagickFalse)
4591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4592 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004593
cristy3ed852e2009-09-05 21:47:34 +00004594 return((Image *) NULL);
4595 }
4596 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004597
cristy3ed852e2009-09-05 21:47:34 +00004598 if (image->columns == 0 || image->rows == 0)
4599 {
4600 if (logging != MagickFalse)
4601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4602 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004603
cristy3ed852e2009-09-05 21:47:34 +00004604 ThrowReaderException(CorruptImageError,"CorruptImage");
4605 }
glennrp0fe50b42010-11-16 03:52:51 +00004606
cristy3ed852e2009-09-05 21:47:34 +00004607 if (logging != MagickFalse)
4608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004609
cristy3ed852e2009-09-05 21:47:34 +00004610 return(image);
4611}
4612#endif
4613
4614static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4615{
4616 char
4617 page_geometry[MaxTextExtent];
4618
4619 Image
4620 *image,
4621 *previous;
4622
cristy4383ec82011-01-05 15:42:32 +00004623 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004624 logging,
4625 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004626
cristy3ed852e2009-09-05 21:47:34 +00004627 volatile int
4628 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004629 object_id,
4630 term_chunk_found,
4631 skip_to_iend;
4632
cristybb503372010-05-27 20:51:26 +00004633 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004634 image_count=0;
4635
4636 MagickBooleanType
4637 status;
4638
4639 MagickOffsetType
4640 offset;
4641
4642 MngInfo
4643 *mng_info;
4644
4645 MngBox
4646 default_fb,
4647 fb,
4648 previous_fb;
4649
4650#if defined(MNG_INSERT_LAYERS)
cristy101ab702011-10-13 13:06:32 +00004651 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004652 mng_background_color;
4653#endif
4654
4655 register unsigned char
4656 *p;
4657
cristybb503372010-05-27 20:51:26 +00004658 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004659 i;
4660
4661 size_t
4662 count;
4663
cristybb503372010-05-27 20:51:26 +00004664 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004665 loop_level;
4666
4667 volatile short
4668 skipping_loop;
4669
4670#if defined(MNG_INSERT_LAYERS)
4671 unsigned int
4672 mandatory_back=0;
4673#endif
4674
4675 volatile unsigned int
4676#ifdef MNG_OBJECT_BUFFERS
4677 mng_background_object=0,
4678#endif
4679 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4680
cristybb503372010-05-27 20:51:26 +00004681 size_t
cristy3ed852e2009-09-05 21:47:34 +00004682 default_frame_timeout,
4683 frame_timeout,
4684#if defined(MNG_INSERT_LAYERS)
4685 image_height,
4686 image_width,
4687#endif
4688 length;
4689
glennrp38ea0832010-06-02 18:50:28 +00004690 /* These delays are all measured in image ticks_per_second,
4691 * not in MNG ticks_per_second
4692 */
cristybb503372010-05-27 20:51:26 +00004693 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004694 default_frame_delay,
4695 final_delay,
4696 final_image_delay,
4697 frame_delay,
4698#if defined(MNG_INSERT_LAYERS)
4699 insert_layers,
4700#endif
4701 mng_iterations=1,
4702 simplicity=0,
4703 subframe_height=0,
4704 subframe_width=0;
4705
4706 previous_fb.top=0;
4707 previous_fb.bottom=0;
4708 previous_fb.left=0;
4709 previous_fb.right=0;
4710 default_fb.top=0;
4711 default_fb.bottom=0;
4712 default_fb.left=0;
4713 default_fb.right=0;
4714
glennrp47b9dd52010-11-24 18:12:06 +00004715 /* Open image file. */
4716
cristy3ed852e2009-09-05 21:47:34 +00004717 assert(image_info != (const ImageInfo *) NULL);
4718 assert(image_info->signature == MagickSignature);
4719 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4720 assert(exception != (ExceptionInfo *) NULL);
4721 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004722 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy9950d572011-10-01 18:22:35 +00004723 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004724 mng_info=(MngInfo *) NULL;
4725 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004726
cristy3ed852e2009-09-05 21:47:34 +00004727 if (status == MagickFalse)
4728 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004729
cristy3ed852e2009-09-05 21:47:34 +00004730 first_mng_object=MagickFalse;
4731 skipping_loop=(-1);
4732 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004733
4734 /* Allocate a MngInfo structure. */
4735
cristy73bd4a52010-10-05 11:24:23 +00004736 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004737
cristy3ed852e2009-09-05 21:47:34 +00004738 if (mng_info == (MngInfo *) NULL)
4739 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004740
glennrp47b9dd52010-11-24 18:12:06 +00004741 /* Initialize members of the MngInfo structure. */
4742
cristy3ed852e2009-09-05 21:47:34 +00004743 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4744 mng_info->image=image;
4745 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004746
4747 if (LocaleCompare(image_info->magick,"MNG") == 0)
4748 {
4749 char
4750 magic_number[MaxTextExtent];
4751
glennrp47b9dd52010-11-24 18:12:06 +00004752 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004753 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4754 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4755 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004756
4757 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004758 for (i=0; i < MNG_MAX_OBJECTS; i++)
4759 {
cristybb503372010-05-27 20:51:26 +00004760 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4761 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004762 }
4763 mng_info->exists[0]=MagickTrue;
4764 }
glennrp47b9dd52010-11-24 18:12:06 +00004765
cristy3ed852e2009-09-05 21:47:34 +00004766 first_mng_object=MagickTrue;
4767 mng_type=0;
4768#if defined(MNG_INSERT_LAYERS)
4769 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4770#endif
4771 default_frame_delay=0;
4772 default_frame_timeout=0;
4773 frame_delay=0;
4774 final_delay=1;
4775 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4776 object_id=0;
4777 skip_to_iend=MagickFalse;
4778 term_chunk_found=MagickFalse;
4779 mng_info->framing_mode=1;
4780#if defined(MNG_INSERT_LAYERS)
4781 mandatory_back=MagickFalse;
4782#endif
4783#if defined(MNG_INSERT_LAYERS)
4784 mng_background_color=image->background_color;
4785#endif
4786 default_fb=mng_info->frame;
4787 previous_fb=mng_info->frame;
4788 do
4789 {
4790 char
4791 type[MaxTextExtent];
4792
4793 if (LocaleCompare(image_info->magick,"MNG") == 0)
4794 {
4795 unsigned char
4796 *chunk;
4797
4798 /*
4799 Read a new chunk.
4800 */
4801 type[0]='\0';
4802 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4803 length=ReadBlobMSBLong(image);
4804 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4805
4806 if (logging != MagickFalse)
4807 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004808 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4809 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004810
4811 if (length > PNG_UINT_31_MAX)
4812 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004813
cristy3ed852e2009-09-05 21:47:34 +00004814 if (count == 0)
4815 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004816
cristy3ed852e2009-09-05 21:47:34 +00004817 p=NULL;
4818 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004819
cristy3ed852e2009-09-05 21:47:34 +00004820 if (length)
4821 {
4822 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004823
cristy3ed852e2009-09-05 21:47:34 +00004824 if (chunk == (unsigned char *) NULL)
4825 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004826
cristybb503372010-05-27 20:51:26 +00004827 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004828 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004829
cristy3ed852e2009-09-05 21:47:34 +00004830 p=chunk;
4831 }
glennrp0fe50b42010-11-16 03:52:51 +00004832
cristy3ed852e2009-09-05 21:47:34 +00004833 (void) ReadBlobMSBLong(image); /* read crc word */
4834
4835#if !defined(JNG_SUPPORTED)
4836 if (memcmp(type,mng_JHDR,4) == 0)
4837 {
4838 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004839
cristy3ed852e2009-09-05 21:47:34 +00004840 if (mng_info->jhdr_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00004841 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004842 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004843
cristy3ed852e2009-09-05 21:47:34 +00004844 mng_info->jhdr_warning++;
4845 }
4846#endif
4847 if (memcmp(type,mng_DHDR,4) == 0)
4848 {
4849 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004850
cristy3ed852e2009-09-05 21:47:34 +00004851 if (mng_info->dhdr_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00004852 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004853 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004854
cristy3ed852e2009-09-05 21:47:34 +00004855 mng_info->dhdr_warning++;
4856 }
4857 if (memcmp(type,mng_MEND,4) == 0)
4858 break;
glennrp47b9dd52010-11-24 18:12:06 +00004859
cristy3ed852e2009-09-05 21:47:34 +00004860 if (skip_to_iend)
4861 {
4862 if (memcmp(type,mng_IEND,4) == 0)
4863 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004864
cristy3ed852e2009-09-05 21:47:34 +00004865 if (length)
4866 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004867
cristy3ed852e2009-09-05 21:47:34 +00004868 if (logging != MagickFalse)
4869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4870 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004871
cristy3ed852e2009-09-05 21:47:34 +00004872 continue;
4873 }
glennrp0fe50b42010-11-16 03:52:51 +00004874
cristy3ed852e2009-09-05 21:47:34 +00004875 if (memcmp(type,mng_MHDR,4) == 0)
4876 {
cristybb503372010-05-27 20:51:26 +00004877 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004878 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004879
cristybb503372010-05-27 20:51:26 +00004880 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004881 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004882
cristy3ed852e2009-09-05 21:47:34 +00004883 if (logging != MagickFalse)
4884 {
4885 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004886 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004888 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004889 }
glennrp0fe50b42010-11-16 03:52:51 +00004890
cristy3ed852e2009-09-05 21:47:34 +00004891 p+=8;
cristy8182b072010-05-30 20:10:53 +00004892 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004893
cristy3ed852e2009-09-05 21:47:34 +00004894 if (mng_info->ticks_per_second == 0)
4895 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004896
cristy3ed852e2009-09-05 21:47:34 +00004897 else
4898 default_frame_delay=1UL*image->ticks_per_second/
4899 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004900
cristy3ed852e2009-09-05 21:47:34 +00004901 frame_delay=default_frame_delay;
4902 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004903
cristy3ed852e2009-09-05 21:47:34 +00004904 if (length > 16)
4905 {
4906 p+=16;
cristy8182b072010-05-30 20:10:53 +00004907 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004908 }
glennrp0fe50b42010-11-16 03:52:51 +00004909
cristy3ed852e2009-09-05 21:47:34 +00004910 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004911
cristy3ed852e2009-09-05 21:47:34 +00004912 if ((simplicity != 0) && ((simplicity | 11) == 11))
4913 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004914
cristy3ed852e2009-09-05 21:47:34 +00004915 if ((simplicity != 0) && ((simplicity | 9) == 9))
4916 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004917
cristy3ed852e2009-09-05 21:47:34 +00004918#if defined(MNG_INSERT_LAYERS)
4919 if (mng_type != 3)
4920 insert_layers=MagickTrue;
4921#endif
cristy4c08aed2011-07-01 19:47:50 +00004922 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004923 {
glennrp47b9dd52010-11-24 18:12:06 +00004924 /* Allocate next image structure. */
cristy9950d572011-10-01 18:22:35 +00004925 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004926
cristy3ed852e2009-09-05 21:47:34 +00004927 if (GetNextImageInList(image) == (Image *) NULL)
4928 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004929
cristy3ed852e2009-09-05 21:47:34 +00004930 image=SyncNextImageInList(image);
4931 mng_info->image=image;
4932 }
4933
4934 if ((mng_info->mng_width > 65535L) ||
4935 (mng_info->mng_height > 65535L))
4936 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004937
cristy3b6fd2e2011-05-20 12:53:50 +00004938 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004939 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004940 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004941
cristy3ed852e2009-09-05 21:47:34 +00004942 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004943 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004944 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004945 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004946 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004947
cristy3ed852e2009-09-05 21:47:34 +00004948 for (i=0; i < MNG_MAX_OBJECTS; i++)
4949 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004950
cristy3ed852e2009-09-05 21:47:34 +00004951 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4952 continue;
4953 }
4954
4955 if (memcmp(type,mng_TERM,4) == 0)
4956 {
4957 int
4958 repeat=0;
4959
4960
4961 if (length)
4962 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004963
cristy3ed852e2009-09-05 21:47:34 +00004964 if (repeat == 3)
4965 {
cristy8182b072010-05-30 20:10:53 +00004966 final_delay=(png_uint_32) mng_get_long(&p[2]);
4967 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004968
cristy3ed852e2009-09-05 21:47:34 +00004969 if (mng_iterations == PNG_UINT_31_MAX)
4970 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004971
cristy3ed852e2009-09-05 21:47:34 +00004972 image->iterations=mng_iterations;
4973 term_chunk_found=MagickTrue;
4974 }
glennrp0fe50b42010-11-16 03:52:51 +00004975
cristy3ed852e2009-09-05 21:47:34 +00004976 if (logging != MagickFalse)
4977 {
4978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4979 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004980
cristy3ed852e2009-09-05 21:47:34 +00004981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004982 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004983
cristy3ed852e2009-09-05 21:47:34 +00004984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004985 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004986 }
glennrp0fe50b42010-11-16 03:52:51 +00004987
cristy3ed852e2009-09-05 21:47:34 +00004988 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4989 continue;
4990 }
4991 if (memcmp(type,mng_DEFI,4) == 0)
4992 {
4993 if (mng_type == 3)
cristyc82a27b2011-10-21 01:07:16 +00004994 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004995 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4996 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004997
cristy3ed852e2009-09-05 21:47:34 +00004998 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004999
cristy3ed852e2009-09-05 21:47:34 +00005000 if (mng_type == 2 && object_id != 0)
cristyc82a27b2011-10-21 01:07:16 +00005001 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005002 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5003 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005004
cristy3ed852e2009-09-05 21:47:34 +00005005 if (object_id > MNG_MAX_OBJECTS)
5006 {
5007 /*
5008 Instead ofsuing a warning we should allocate a larger
5009 MngInfo structure and continue.
5010 */
cristyc82a27b2011-10-21 01:07:16 +00005011 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005012 CoderError,"object id too large","`%s'",image->filename);
5013 object_id=MNG_MAX_OBJECTS;
5014 }
glennrp0fe50b42010-11-16 03:52:51 +00005015
cristy3ed852e2009-09-05 21:47:34 +00005016 if (mng_info->exists[object_id])
5017 if (mng_info->frozen[object_id])
5018 {
5019 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
cristyc82a27b2011-10-21 01:07:16 +00005020 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005021 GetMagickModule(),CoderError,
5022 "DEFI cannot redefine a frozen MNG object","`%s'",
5023 image->filename);
5024 continue;
5025 }
glennrp0fe50b42010-11-16 03:52:51 +00005026
cristy3ed852e2009-09-05 21:47:34 +00005027 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005028
cristy3ed852e2009-09-05 21:47:34 +00005029 if (length > 2)
5030 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00005031
cristy3ed852e2009-09-05 21:47:34 +00005032 /*
5033 Extract object offset info.
5034 */
5035 if (length > 11)
5036 {
glennrp0fe50b42010-11-16 03:52:51 +00005037 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5038 (p[5] << 16) | (p[6] << 8) | p[7]);
5039
5040 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5041 (p[9] << 16) | (p[10] << 8) | p[11]);
5042
cristy3ed852e2009-09-05 21:47:34 +00005043 if (logging != MagickFalse)
5044 {
5045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005046 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005047 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005048
cristy3ed852e2009-09-05 21:47:34 +00005049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005050 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005051 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005052 }
5053 }
glennrp0fe50b42010-11-16 03:52:51 +00005054
cristy3ed852e2009-09-05 21:47:34 +00005055 /*
5056 Extract object clipping info.
5057 */
5058 if (length > 27)
5059 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5060 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005061
cristy3ed852e2009-09-05 21:47:34 +00005062 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5063 continue;
5064 }
5065 if (memcmp(type,mng_bKGD,4) == 0)
5066 {
5067 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005068
cristy3ed852e2009-09-05 21:47:34 +00005069 if (length > 5)
5070 {
5071 mng_info->mng_global_bkgd.red=
5072 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005073
cristy3ed852e2009-09-05 21:47:34 +00005074 mng_info->mng_global_bkgd.green=
5075 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005076
cristy3ed852e2009-09-05 21:47:34 +00005077 mng_info->mng_global_bkgd.blue=
5078 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005079
cristy3ed852e2009-09-05 21:47:34 +00005080 mng_info->have_global_bkgd=MagickTrue;
5081 }
glennrp0fe50b42010-11-16 03:52:51 +00005082
cristy3ed852e2009-09-05 21:47:34 +00005083 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5084 continue;
5085 }
5086 if (memcmp(type,mng_BACK,4) == 0)
5087 {
5088#if defined(MNG_INSERT_LAYERS)
5089 if (length > 6)
5090 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005091
cristy3ed852e2009-09-05 21:47:34 +00005092 else
5093 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005094
cristy3ed852e2009-09-05 21:47:34 +00005095 if (mandatory_back && length > 5)
5096 {
5097 mng_background_color.red=
5098 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005099
cristy3ed852e2009-09-05 21:47:34 +00005100 mng_background_color.green=
5101 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005102
cristy3ed852e2009-09-05 21:47:34 +00005103 mng_background_color.blue=
5104 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005105
cristy4c08aed2011-07-01 19:47:50 +00005106 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005107 }
glennrp0fe50b42010-11-16 03:52:51 +00005108
cristy3ed852e2009-09-05 21:47:34 +00005109#ifdef MNG_OBJECT_BUFFERS
5110 if (length > 8)
5111 mng_background_object=(p[7] << 8) | p[8];
5112#endif
5113#endif
5114 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5115 continue;
5116 }
glennrp47b9dd52010-11-24 18:12:06 +00005117
cristy3ed852e2009-09-05 21:47:34 +00005118 if (memcmp(type,mng_PLTE,4) == 0)
5119 {
glennrp47b9dd52010-11-24 18:12:06 +00005120 /* Read global PLTE. */
5121
cristy3ed852e2009-09-05 21:47:34 +00005122 if (length && (length < 769))
5123 {
5124 if (mng_info->global_plte == (png_colorp) NULL)
5125 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5126 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005127
cristybb503372010-05-27 20:51:26 +00005128 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005129 {
5130 mng_info->global_plte[i].red=p[3*i];
5131 mng_info->global_plte[i].green=p[3*i+1];
5132 mng_info->global_plte[i].blue=p[3*i+2];
5133 }
glennrp0fe50b42010-11-16 03:52:51 +00005134
cristy35ef8242010-06-03 16:24:13 +00005135 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005136 }
5137#ifdef MNG_LOOSE
5138 for ( ; i < 256; i++)
5139 {
5140 mng_info->global_plte[i].red=i;
5141 mng_info->global_plte[i].green=i;
5142 mng_info->global_plte[i].blue=i;
5143 }
glennrp0fe50b42010-11-16 03:52:51 +00005144
cristy3ed852e2009-09-05 21:47:34 +00005145 if (length)
5146 mng_info->global_plte_length=256;
5147#endif
5148 else
5149 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005150
cristy3ed852e2009-09-05 21:47:34 +00005151 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5152 continue;
5153 }
glennrp47b9dd52010-11-24 18:12:06 +00005154
cristy3ed852e2009-09-05 21:47:34 +00005155 if (memcmp(type,mng_tRNS,4) == 0)
5156 {
5157 /* read global tRNS */
5158
5159 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005160 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005161 mng_info->global_trns[i]=p[i];
5162
5163#ifdef MNG_LOOSE
5164 for ( ; i < 256; i++)
5165 mng_info->global_trns[i]=255;
5166#endif
cristy12560f32010-06-03 16:51:08 +00005167 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005168 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5169 continue;
5170 }
5171 if (memcmp(type,mng_gAMA,4) == 0)
5172 {
5173 if (length == 4)
5174 {
cristybb503372010-05-27 20:51:26 +00005175 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005176 igamma;
5177
cristy8182b072010-05-30 20:10:53 +00005178 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005179 mng_info->global_gamma=((float) igamma)*0.00001;
5180 mng_info->have_global_gama=MagickTrue;
5181 }
glennrp0fe50b42010-11-16 03:52:51 +00005182
cristy3ed852e2009-09-05 21:47:34 +00005183 else
5184 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005185
cristy3ed852e2009-09-05 21:47:34 +00005186 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5187 continue;
5188 }
5189
5190 if (memcmp(type,mng_cHRM,4) == 0)
5191 {
glennrp47b9dd52010-11-24 18:12:06 +00005192 /* Read global cHRM */
5193
cristy3ed852e2009-09-05 21:47:34 +00005194 if (length == 32)
5195 {
cristy8182b072010-05-30 20:10:53 +00005196 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5197 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5198 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005199 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005200 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005201 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005202 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005203 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005204 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005205 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005206 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005207 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005208 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005209 mng_info->have_global_chrm=MagickTrue;
5210 }
5211 else
5212 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005213
cristy3ed852e2009-09-05 21:47:34 +00005214 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5215 continue;
5216 }
glennrp47b9dd52010-11-24 18:12:06 +00005217
cristy3ed852e2009-09-05 21:47:34 +00005218 if (memcmp(type,mng_sRGB,4) == 0)
5219 {
5220 /*
5221 Read global sRGB.
5222 */
5223 if (length)
5224 {
glennrpe610a072010-08-05 17:08:46 +00005225 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005226 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005227 mng_info->have_global_srgb=MagickTrue;
5228 }
5229 else
5230 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005231
cristy3ed852e2009-09-05 21:47:34 +00005232 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5233 continue;
5234 }
glennrp47b9dd52010-11-24 18:12:06 +00005235
cristy3ed852e2009-09-05 21:47:34 +00005236 if (memcmp(type,mng_iCCP,4) == 0)
5237 {
glennrpfd05d622011-02-25 04:10:33 +00005238 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005239
5240 /*
5241 Read global iCCP.
5242 */
5243 if (length)
5244 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005245
cristy3ed852e2009-09-05 21:47:34 +00005246 continue;
5247 }
glennrp47b9dd52010-11-24 18:12:06 +00005248
cristy3ed852e2009-09-05 21:47:34 +00005249 if (memcmp(type,mng_FRAM,4) == 0)
5250 {
5251 if (mng_type == 3)
cristyc82a27b2011-10-21 01:07:16 +00005252 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005253 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5254 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005255
cristy3ed852e2009-09-05 21:47:34 +00005256 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5257 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005258
cristy3ed852e2009-09-05 21:47:34 +00005259 frame_delay=default_frame_delay;
5260 frame_timeout=default_frame_timeout;
5261 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005262
cristy3ed852e2009-09-05 21:47:34 +00005263 if (length)
5264 if (p[0])
5265 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005266
cristy3ed852e2009-09-05 21:47:34 +00005267 if (logging != MagickFalse)
5268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5269 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005270
cristy3ed852e2009-09-05 21:47:34 +00005271 if (length > 6)
5272 {
glennrp47b9dd52010-11-24 18:12:06 +00005273 /* Note the delay and frame clipping boundaries. */
5274
cristy3ed852e2009-09-05 21:47:34 +00005275 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005276
cristybb503372010-05-27 20:51:26 +00005277 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005278 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005279
cristy3ed852e2009-09-05 21:47:34 +00005280 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005281
cristybb503372010-05-27 20:51:26 +00005282 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005283 {
5284 int
5285 change_delay,
5286 change_timeout,
5287 change_clipping;
5288
5289 change_delay=(*p++);
5290 change_timeout=(*p++);
5291 change_clipping=(*p++);
5292 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005293
cristy3ed852e2009-09-05 21:47:34 +00005294 if (change_delay)
5295 {
cristy8182b072010-05-30 20:10:53 +00005296 frame_delay=1UL*image->ticks_per_second*
5297 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005298
cristy8182b072010-05-30 20:10:53 +00005299 if (mng_info->ticks_per_second != 0)
5300 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005301
glennrpbb010dd2010-06-01 13:07:15 +00005302 else
5303 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005304
cristy3ed852e2009-09-05 21:47:34 +00005305 if (change_delay == 2)
5306 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005307
cristy3ed852e2009-09-05 21:47:34 +00005308 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005309
cristy3ed852e2009-09-05 21:47:34 +00005310 if (logging != MagickFalse)
5311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005312 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005313 }
glennrp47b9dd52010-11-24 18:12:06 +00005314
cristy3ed852e2009-09-05 21:47:34 +00005315 if (change_timeout)
5316 {
glennrpbb010dd2010-06-01 13:07:15 +00005317 frame_timeout=1UL*image->ticks_per_second*
5318 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005319
glennrpbb010dd2010-06-01 13:07:15 +00005320 if (mng_info->ticks_per_second != 0)
5321 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005322
glennrpbb010dd2010-06-01 13:07:15 +00005323 else
5324 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005325
cristy3ed852e2009-09-05 21:47:34 +00005326 if (change_delay == 2)
5327 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005328
cristy3ed852e2009-09-05 21:47:34 +00005329 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005330
cristy3ed852e2009-09-05 21:47:34 +00005331 if (logging != MagickFalse)
5332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005333 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005334 }
glennrp47b9dd52010-11-24 18:12:06 +00005335
cristy3ed852e2009-09-05 21:47:34 +00005336 if (change_clipping)
5337 {
5338 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5339 p+=17;
5340 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005341
cristy3ed852e2009-09-05 21:47:34 +00005342 if (logging != MagickFalse)
5343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005344 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005345 (double) fb.left,(double) fb.right,(double) fb.top,
5346 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005347
cristy3ed852e2009-09-05 21:47:34 +00005348 if (change_clipping == 2)
5349 default_fb=fb;
5350 }
5351 }
5352 }
5353 mng_info->clip=fb;
5354 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005355
cristybb503372010-05-27 20:51:26 +00005356 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005357 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005358
cristybb503372010-05-27 20:51:26 +00005359 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005360 -mng_info->clip.top);
5361 /*
5362 Insert a background layer behind the frame if framing_mode is 4.
5363 */
5364#if defined(MNG_INSERT_LAYERS)
5365 if (logging != MagickFalse)
5366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005367 " subframe_width=%.20g, subframe_height=%.20g",(double)
5368 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005369
cristy3ed852e2009-09-05 21:47:34 +00005370 if (insert_layers && (mng_info->framing_mode == 4) &&
5371 (subframe_width) && (subframe_height))
5372 {
glennrp47b9dd52010-11-24 18:12:06 +00005373 /* Allocate next image structure. */
cristy4c08aed2011-07-01 19:47:50 +00005374 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005375 {
cristy9950d572011-10-01 18:22:35 +00005376 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005377
cristy3ed852e2009-09-05 21:47:34 +00005378 if (GetNextImageInList(image) == (Image *) NULL)
5379 {
5380 image=DestroyImageList(image);
5381 MngInfoFreeStruct(mng_info,&have_mng_structure);
5382 return((Image *) NULL);
5383 }
glennrp47b9dd52010-11-24 18:12:06 +00005384
cristy3ed852e2009-09-05 21:47:34 +00005385 image=SyncNextImageInList(image);
5386 }
glennrp0fe50b42010-11-16 03:52:51 +00005387
cristy3ed852e2009-09-05 21:47:34 +00005388 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005389
cristy3ed852e2009-09-05 21:47:34 +00005390 if (term_chunk_found)
5391 {
5392 image->start_loop=MagickTrue;
5393 image->iterations=mng_iterations;
5394 term_chunk_found=MagickFalse;
5395 }
glennrp0fe50b42010-11-16 03:52:51 +00005396
cristy3ed852e2009-09-05 21:47:34 +00005397 else
5398 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005399
cristy3ed852e2009-09-05 21:47:34 +00005400 image->columns=subframe_width;
5401 image->rows=subframe_height;
5402 image->page.width=subframe_width;
5403 image->page.height=subframe_height;
5404 image->page.x=mng_info->clip.left;
5405 image->page.y=mng_info->clip.top;
5406 image->background_color=mng_background_color;
5407 image->matte=MagickFalse;
5408 image->delay=0;
cristyea1a8aa2011-10-20 13:24:06 +00005409 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005410
cristy3ed852e2009-09-05 21:47:34 +00005411 if (logging != MagickFalse)
5412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005413 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005414 (double) mng_info->clip.left,(double) mng_info->clip.right,
5415 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005416 }
5417#endif
5418 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5419 continue;
5420 }
5421 if (memcmp(type,mng_CLIP,4) == 0)
5422 {
5423 unsigned int
5424 first_object,
5425 last_object;
5426
5427 /*
5428 Read CLIP.
5429 */
5430 first_object=(p[0] << 8) | p[1];
5431 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005432
cristy3ed852e2009-09-05 21:47:34 +00005433 for (i=(int) first_object; i <= (int) last_object; i++)
5434 {
5435 if (mng_info->exists[i] && !mng_info->frozen[i])
5436 {
5437 MngBox
5438 box;
5439
5440 box=mng_info->object_clip[i];
5441 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5442 }
5443 }
glennrp47b9dd52010-11-24 18:12:06 +00005444
cristy3ed852e2009-09-05 21:47:34 +00005445 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5446 continue;
5447 }
5448 if (memcmp(type,mng_SAVE,4) == 0)
5449 {
5450 for (i=1; i < MNG_MAX_OBJECTS; i++)
5451 if (mng_info->exists[i])
5452 {
5453 mng_info->frozen[i]=MagickTrue;
5454#ifdef MNG_OBJECT_BUFFERS
5455 if (mng_info->ob[i] != (MngBuffer *) NULL)
5456 mng_info->ob[i]->frozen=MagickTrue;
5457#endif
5458 }
glennrp0fe50b42010-11-16 03:52:51 +00005459
cristy3ed852e2009-09-05 21:47:34 +00005460 if (length)
5461 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005462
cristy3ed852e2009-09-05 21:47:34 +00005463 continue;
5464 }
5465
5466 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5467 {
glennrp47b9dd52010-11-24 18:12:06 +00005468 /* Read DISC or SEEK. */
5469
cristy3ed852e2009-09-05 21:47:34 +00005470 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5471 {
5472 for (i=1; i < MNG_MAX_OBJECTS; i++)
5473 MngInfoDiscardObject(mng_info,i);
5474 }
glennrp0fe50b42010-11-16 03:52:51 +00005475
cristy3ed852e2009-09-05 21:47:34 +00005476 else
5477 {
cristybb503372010-05-27 20:51:26 +00005478 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005479 j;
5480
cristybb503372010-05-27 20:51:26 +00005481 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005482 {
5483 i=p[j] << 8 | p[j+1];
5484 MngInfoDiscardObject(mng_info,i);
5485 }
5486 }
glennrp0fe50b42010-11-16 03:52:51 +00005487
cristy3ed852e2009-09-05 21:47:34 +00005488 if (length)
5489 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005490
cristy3ed852e2009-09-05 21:47:34 +00005491 continue;
5492 }
glennrp47b9dd52010-11-24 18:12:06 +00005493
cristy3ed852e2009-09-05 21:47:34 +00005494 if (memcmp(type,mng_MOVE,4) == 0)
5495 {
cristybb503372010-05-27 20:51:26 +00005496 size_t
cristy3ed852e2009-09-05 21:47:34 +00005497 first_object,
5498 last_object;
5499
glennrp47b9dd52010-11-24 18:12:06 +00005500 /* read MOVE */
5501
cristy3ed852e2009-09-05 21:47:34 +00005502 first_object=(p[0] << 8) | p[1];
5503 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005504 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005505 {
5506 if (mng_info->exists[i] && !mng_info->frozen[i])
5507 {
5508 MngPair
5509 new_pair;
5510
5511 MngPair
5512 old_pair;
5513
5514 old_pair.a=mng_info->x_off[i];
5515 old_pair.b=mng_info->y_off[i];
5516 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5517 mng_info->x_off[i]=new_pair.a;
5518 mng_info->y_off[i]=new_pair.b;
5519 }
5520 }
glennrp47b9dd52010-11-24 18:12:06 +00005521
cristy3ed852e2009-09-05 21:47:34 +00005522 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5523 continue;
5524 }
5525
5526 if (memcmp(type,mng_LOOP,4) == 0)
5527 {
cristybb503372010-05-27 20:51:26 +00005528 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005529 loop_level=chunk[0];
5530 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005531
5532 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005533 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005534
cristy3ed852e2009-09-05 21:47:34 +00005535 if (logging != MagickFalse)
5536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005537 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5538 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005539
cristy3ed852e2009-09-05 21:47:34 +00005540 if (loop_iters == 0)
5541 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005542
cristy3ed852e2009-09-05 21:47:34 +00005543 else
5544 {
5545 mng_info->loop_jump[loop_level]=TellBlob(image);
5546 mng_info->loop_count[loop_level]=loop_iters;
5547 }
glennrp0fe50b42010-11-16 03:52:51 +00005548
cristy3ed852e2009-09-05 21:47:34 +00005549 mng_info->loop_iteration[loop_level]=0;
5550 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5551 continue;
5552 }
glennrp47b9dd52010-11-24 18:12:06 +00005553
cristy3ed852e2009-09-05 21:47:34 +00005554 if (memcmp(type,mng_ENDL,4) == 0)
5555 {
5556 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005557
cristy3ed852e2009-09-05 21:47:34 +00005558 if (skipping_loop > 0)
5559 {
5560 if (skipping_loop == loop_level)
5561 {
5562 /*
5563 Found end of zero-iteration loop.
5564 */
5565 skipping_loop=(-1);
5566 mng_info->loop_active[loop_level]=0;
5567 }
5568 }
glennrp47b9dd52010-11-24 18:12:06 +00005569
cristy3ed852e2009-09-05 21:47:34 +00005570 else
5571 {
5572 if (mng_info->loop_active[loop_level] == 1)
5573 {
5574 mng_info->loop_count[loop_level]--;
5575 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005576
cristy3ed852e2009-09-05 21:47:34 +00005577 if (logging != MagickFalse)
5578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005579 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005580 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005581 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005582
cristy3ed852e2009-09-05 21:47:34 +00005583 if (mng_info->loop_count[loop_level] != 0)
5584 {
5585 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5586 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005587
cristy3ed852e2009-09-05 21:47:34 +00005588 if (offset < 0)
5589 ThrowReaderException(CorruptImageError,
5590 "ImproperImageHeader");
5591 }
glennrp47b9dd52010-11-24 18:12:06 +00005592
cristy3ed852e2009-09-05 21:47:34 +00005593 else
5594 {
5595 short
5596 last_level;
5597
5598 /*
5599 Finished loop.
5600 */
5601 mng_info->loop_active[loop_level]=0;
5602 last_level=(-1);
5603 for (i=0; i < loop_level; i++)
5604 if (mng_info->loop_active[i] == 1)
5605 last_level=(short) i;
5606 loop_level=last_level;
5607 }
5608 }
5609 }
glennrp47b9dd52010-11-24 18:12:06 +00005610
cristy3ed852e2009-09-05 21:47:34 +00005611 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5612 continue;
5613 }
glennrp47b9dd52010-11-24 18:12:06 +00005614
cristy3ed852e2009-09-05 21:47:34 +00005615 if (memcmp(type,mng_CLON,4) == 0)
5616 {
5617 if (mng_info->clon_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005618 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005619 CoderError,"CLON is not implemented yet","`%s'",
5620 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005621
cristy3ed852e2009-09-05 21:47:34 +00005622 mng_info->clon_warning++;
5623 }
glennrp47b9dd52010-11-24 18:12:06 +00005624
cristy3ed852e2009-09-05 21:47:34 +00005625 if (memcmp(type,mng_MAGN,4) == 0)
5626 {
5627 png_uint_16
5628 magn_first,
5629 magn_last,
5630 magn_mb,
5631 magn_ml,
5632 magn_mr,
5633 magn_mt,
5634 magn_mx,
5635 magn_my,
5636 magn_methx,
5637 magn_methy;
5638
5639 if (length > 1)
5640 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005641
cristy3ed852e2009-09-05 21:47:34 +00005642 else
5643 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005644
cristy3ed852e2009-09-05 21:47:34 +00005645 if (length > 3)
5646 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005647
cristy3ed852e2009-09-05 21:47:34 +00005648 else
5649 magn_last=magn_first;
5650#ifndef MNG_OBJECT_BUFFERS
5651 if (magn_first || magn_last)
5652 if (mng_info->magn_warning == 0)
5653 {
cristyc82a27b2011-10-21 01:07:16 +00005654 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005655 GetMagickModule(),CoderError,
5656 "MAGN is not implemented yet for nonzero objects",
5657 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005658
cristy3ed852e2009-09-05 21:47:34 +00005659 mng_info->magn_warning++;
5660 }
5661#endif
5662 if (length > 4)
5663 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005664
cristy3ed852e2009-09-05 21:47:34 +00005665 else
5666 magn_methx=0;
5667
5668 if (length > 6)
5669 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005670
cristy3ed852e2009-09-05 21:47:34 +00005671 else
5672 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005673
cristy3ed852e2009-09-05 21:47:34 +00005674 if (magn_mx == 0)
5675 magn_mx=1;
5676
5677 if (length > 8)
5678 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005679
cristy3ed852e2009-09-05 21:47:34 +00005680 else
5681 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005682
cristy3ed852e2009-09-05 21:47:34 +00005683 if (magn_my == 0)
5684 magn_my=1;
5685
5686 if (length > 10)
5687 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005688
cristy3ed852e2009-09-05 21:47:34 +00005689 else
5690 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005691
cristy3ed852e2009-09-05 21:47:34 +00005692 if (magn_ml == 0)
5693 magn_ml=1;
5694
5695 if (length > 12)
5696 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005697
cristy3ed852e2009-09-05 21:47:34 +00005698 else
5699 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005700
cristy3ed852e2009-09-05 21:47:34 +00005701 if (magn_mr == 0)
5702 magn_mr=1;
5703
5704 if (length > 14)
5705 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005706
cristy3ed852e2009-09-05 21:47:34 +00005707 else
5708 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005709
cristy3ed852e2009-09-05 21:47:34 +00005710 if (magn_mt == 0)
5711 magn_mt=1;
5712
5713 if (length > 16)
5714 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005715
cristy3ed852e2009-09-05 21:47:34 +00005716 else
5717 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005718
cristy3ed852e2009-09-05 21:47:34 +00005719 if (magn_mb == 0)
5720 magn_mb=1;
5721
5722 if (length > 17)
5723 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005724
cristy3ed852e2009-09-05 21:47:34 +00005725 else
5726 magn_methy=magn_methx;
5727
glennrp47b9dd52010-11-24 18:12:06 +00005728
cristy3ed852e2009-09-05 21:47:34 +00005729 if (magn_methx > 5 || magn_methy > 5)
5730 if (mng_info->magn_warning == 0)
5731 {
cristyc82a27b2011-10-21 01:07:16 +00005732 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005733 GetMagickModule(),CoderError,
5734 "Unknown MAGN method in MNG datastream","`%s'",
5735 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005736
cristy3ed852e2009-09-05 21:47:34 +00005737 mng_info->magn_warning++;
5738 }
5739#ifdef MNG_OBJECT_BUFFERS
5740 /* Magnify existing objects in the range magn_first to magn_last */
5741#endif
5742 if (magn_first == 0 || magn_last == 0)
5743 {
5744 /* Save the magnification factors for object 0 */
5745 mng_info->magn_mb=magn_mb;
5746 mng_info->magn_ml=magn_ml;
5747 mng_info->magn_mr=magn_mr;
5748 mng_info->magn_mt=magn_mt;
5749 mng_info->magn_mx=magn_mx;
5750 mng_info->magn_my=magn_my;
5751 mng_info->magn_methx=magn_methx;
5752 mng_info->magn_methy=magn_methy;
5753 }
5754 }
glennrp47b9dd52010-11-24 18:12:06 +00005755
cristy3ed852e2009-09-05 21:47:34 +00005756 if (memcmp(type,mng_PAST,4) == 0)
5757 {
5758 if (mng_info->past_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005759 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005760 CoderError,"PAST is not implemented yet","`%s'",
5761 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005762
cristy3ed852e2009-09-05 21:47:34 +00005763 mng_info->past_warning++;
5764 }
glennrp47b9dd52010-11-24 18:12:06 +00005765
cristy3ed852e2009-09-05 21:47:34 +00005766 if (memcmp(type,mng_SHOW,4) == 0)
5767 {
5768 if (mng_info->show_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005769 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005770 CoderError,"SHOW is not implemented yet","`%s'",
5771 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005772
cristy3ed852e2009-09-05 21:47:34 +00005773 mng_info->show_warning++;
5774 }
glennrp47b9dd52010-11-24 18:12:06 +00005775
cristy3ed852e2009-09-05 21:47:34 +00005776 if (memcmp(type,mng_sBIT,4) == 0)
5777 {
5778 if (length < 4)
5779 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005780
cristy3ed852e2009-09-05 21:47:34 +00005781 else
5782 {
5783 mng_info->global_sbit.gray=p[0];
5784 mng_info->global_sbit.red=p[0];
5785 mng_info->global_sbit.green=p[1];
5786 mng_info->global_sbit.blue=p[2];
5787 mng_info->global_sbit.alpha=p[3];
5788 mng_info->have_global_sbit=MagickTrue;
5789 }
5790 }
5791 if (memcmp(type,mng_pHYs,4) == 0)
5792 {
5793 if (length > 8)
5794 {
5795 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005796 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005797 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005798 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005799 mng_info->global_phys_unit_type=p[8];
5800 mng_info->have_global_phys=MagickTrue;
5801 }
glennrp47b9dd52010-11-24 18:12:06 +00005802
cristy3ed852e2009-09-05 21:47:34 +00005803 else
5804 mng_info->have_global_phys=MagickFalse;
5805 }
5806 if (memcmp(type,mng_pHYg,4) == 0)
5807 {
5808 if (mng_info->phyg_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005809 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005810 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005811
cristy3ed852e2009-09-05 21:47:34 +00005812 mng_info->phyg_warning++;
5813 }
5814 if (memcmp(type,mng_BASI,4) == 0)
5815 {
5816 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005817
cristy3ed852e2009-09-05 21:47:34 +00005818 if (mng_info->basi_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005819 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005820 CoderError,"BASI is not implemented yet","`%s'",
5821 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005822
cristy3ed852e2009-09-05 21:47:34 +00005823 mng_info->basi_warning++;
5824#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005825 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005826 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005827 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005828 (p[6] << 8) | p[7]);
5829 basi_color_type=p[8];
5830 basi_compression_method=p[9];
5831 basi_filter_type=p[10];
5832 basi_interlace_method=p[11];
5833 if (length > 11)
5834 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005835
cristy3ed852e2009-09-05 21:47:34 +00005836 else
5837 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005838
cristy3ed852e2009-09-05 21:47:34 +00005839 if (length > 13)
5840 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005841
cristy3ed852e2009-09-05 21:47:34 +00005842 else
5843 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005844
cristy3ed852e2009-09-05 21:47:34 +00005845 if (length > 15)
5846 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005847
cristy3ed852e2009-09-05 21:47:34 +00005848 else
5849 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005850
cristy3ed852e2009-09-05 21:47:34 +00005851 if (length > 17)
5852 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005853
cristy3ed852e2009-09-05 21:47:34 +00005854 else
5855 {
5856 if (basi_sample_depth == 16)
5857 basi_alpha=65535L;
5858 else
5859 basi_alpha=255;
5860 }
glennrp47b9dd52010-11-24 18:12:06 +00005861
cristy3ed852e2009-09-05 21:47:34 +00005862 if (length > 19)
5863 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005864
cristy3ed852e2009-09-05 21:47:34 +00005865 else
5866 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005867
cristy3ed852e2009-09-05 21:47:34 +00005868#endif
5869 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5870 continue;
5871 }
glennrp47b9dd52010-11-24 18:12:06 +00005872
cristy3ed852e2009-09-05 21:47:34 +00005873 if (memcmp(type,mng_IHDR,4)
5874#if defined(JNG_SUPPORTED)
5875 && memcmp(type,mng_JHDR,4)
5876#endif
5877 )
5878 {
5879 /* Not an IHDR or JHDR chunk */
5880 if (length)
5881 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005882
cristy3ed852e2009-09-05 21:47:34 +00005883 continue;
5884 }
5885/* Process IHDR */
5886 if (logging != MagickFalse)
5887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5888 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005889
cristy3ed852e2009-09-05 21:47:34 +00005890 mng_info->exists[object_id]=MagickTrue;
5891 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005892
cristy3ed852e2009-09-05 21:47:34 +00005893 if (mng_info->invisible[object_id])
5894 {
5895 if (logging != MagickFalse)
5896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5897 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005898
cristy3ed852e2009-09-05 21:47:34 +00005899 skip_to_iend=MagickTrue;
5900 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5901 continue;
5902 }
5903#if defined(MNG_INSERT_LAYERS)
5904 if (length < 8)
5905 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005906
cristy8182b072010-05-30 20:10:53 +00005907 image_width=(size_t) mng_get_long(p);
5908 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005909#endif
5910 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5911
5912 /*
5913 Insert a transparent background layer behind the entire animation
5914 if it is not full screen.
5915 */
5916#if defined(MNG_INSERT_LAYERS)
5917 if (insert_layers && mng_type && first_mng_object)
5918 {
5919 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5920 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005921 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005922 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005923 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005924 {
cristy4c08aed2011-07-01 19:47:50 +00005925 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005926 {
5927 /*
5928 Allocate next image structure.
5929 */
cristy9950d572011-10-01 18:22:35 +00005930 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005931
cristy3ed852e2009-09-05 21:47:34 +00005932 if (GetNextImageInList(image) == (Image *) NULL)
5933 {
5934 image=DestroyImageList(image);
5935 MngInfoFreeStruct(mng_info,&have_mng_structure);
5936 return((Image *) NULL);
5937 }
glennrp47b9dd52010-11-24 18:12:06 +00005938
cristy3ed852e2009-09-05 21:47:34 +00005939 image=SyncNextImageInList(image);
5940 }
5941 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005942
cristy3ed852e2009-09-05 21:47:34 +00005943 if (term_chunk_found)
5944 {
5945 image->start_loop=MagickTrue;
5946 image->iterations=mng_iterations;
5947 term_chunk_found=MagickFalse;
5948 }
glennrp47b9dd52010-11-24 18:12:06 +00005949
cristy3ed852e2009-09-05 21:47:34 +00005950 else
5951 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005952
5953 /* Make a background rectangle. */
5954
cristy3ed852e2009-09-05 21:47:34 +00005955 image->delay=0;
5956 image->columns=mng_info->mng_width;
5957 image->rows=mng_info->mng_height;
5958 image->page.width=mng_info->mng_width;
5959 image->page.height=mng_info->mng_height;
5960 image->page.x=0;
5961 image->page.y=0;
5962 image->background_color=mng_background_color;
cristyea1a8aa2011-10-20 13:24:06 +00005963 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005964 if (logging != MagickFalse)
5965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005966 " Inserted transparent background layer, W=%.20g, H=%.20g",
5967 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005968 }
5969 }
5970 /*
5971 Insert a background layer behind the upcoming image if
5972 framing_mode is 3, and we haven't already inserted one.
5973 */
5974 if (insert_layers && (mng_info->framing_mode == 3) &&
5975 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5976 (simplicity & 0x08)))
5977 {
cristy4c08aed2011-07-01 19:47:50 +00005978 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005979 {
5980 /*
5981 Allocate next image structure.
5982 */
cristy9950d572011-10-01 18:22:35 +00005983 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005984
cristy3ed852e2009-09-05 21:47:34 +00005985 if (GetNextImageInList(image) == (Image *) NULL)
5986 {
5987 image=DestroyImageList(image);
5988 MngInfoFreeStruct(mng_info,&have_mng_structure);
5989 return((Image *) NULL);
5990 }
glennrp47b9dd52010-11-24 18:12:06 +00005991
cristy3ed852e2009-09-05 21:47:34 +00005992 image=SyncNextImageInList(image);
5993 }
glennrp0fe50b42010-11-16 03:52:51 +00005994
cristy3ed852e2009-09-05 21:47:34 +00005995 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005996
cristy3ed852e2009-09-05 21:47:34 +00005997 if (term_chunk_found)
5998 {
5999 image->start_loop=MagickTrue;
6000 image->iterations=mng_iterations;
6001 term_chunk_found=MagickFalse;
6002 }
glennrp0fe50b42010-11-16 03:52:51 +00006003
cristy3ed852e2009-09-05 21:47:34 +00006004 else
6005 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006006
cristy3ed852e2009-09-05 21:47:34 +00006007 image->delay=0;
6008 image->columns=subframe_width;
6009 image->rows=subframe_height;
6010 image->page.width=subframe_width;
6011 image->page.height=subframe_height;
6012 image->page.x=mng_info->clip.left;
6013 image->page.y=mng_info->clip.top;
6014 image->background_color=mng_background_color;
6015 image->matte=MagickFalse;
cristyea1a8aa2011-10-20 13:24:06 +00006016 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006017
cristy3ed852e2009-09-05 21:47:34 +00006018 if (logging != MagickFalse)
6019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00006020 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00006021 (double) mng_info->clip.left,(double) mng_info->clip.right,
6022 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00006023 }
6024#endif /* MNG_INSERT_LAYERS */
6025 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006026
cristy4c08aed2011-07-01 19:47:50 +00006027 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006028 {
6029 /*
6030 Allocate next image structure.
6031 */
cristy9950d572011-10-01 18:22:35 +00006032 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006033
cristy3ed852e2009-09-05 21:47:34 +00006034 if (GetNextImageInList(image) == (Image *) NULL)
6035 {
6036 image=DestroyImageList(image);
6037 MngInfoFreeStruct(mng_info,&have_mng_structure);
6038 return((Image *) NULL);
6039 }
glennrp47b9dd52010-11-24 18:12:06 +00006040
cristy3ed852e2009-09-05 21:47:34 +00006041 image=SyncNextImageInList(image);
6042 }
6043 mng_info->image=image;
6044 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6045 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006046
cristy3ed852e2009-09-05 21:47:34 +00006047 if (status == MagickFalse)
6048 break;
glennrp0fe50b42010-11-16 03:52:51 +00006049
cristy3ed852e2009-09-05 21:47:34 +00006050 if (term_chunk_found)
6051 {
6052 image->start_loop=MagickTrue;
6053 term_chunk_found=MagickFalse;
6054 }
glennrp0fe50b42010-11-16 03:52:51 +00006055
cristy3ed852e2009-09-05 21:47:34 +00006056 else
6057 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006058
cristy3ed852e2009-09-05 21:47:34 +00006059 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6060 {
6061 image->delay=frame_delay;
6062 frame_delay=default_frame_delay;
6063 }
glennrp0fe50b42010-11-16 03:52:51 +00006064
cristy3ed852e2009-09-05 21:47:34 +00006065 else
6066 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006067
cristy3ed852e2009-09-05 21:47:34 +00006068 image->page.width=mng_info->mng_width;
6069 image->page.height=mng_info->mng_height;
6070 image->page.x=mng_info->x_off[object_id];
6071 image->page.y=mng_info->y_off[object_id];
6072 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006073
cristy3ed852e2009-09-05 21:47:34 +00006074 /*
6075 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6076 */
glennrp47b9dd52010-11-24 18:12:06 +00006077
cristy3ed852e2009-09-05 21:47:34 +00006078 if (logging != MagickFalse)
6079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6080 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6081 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006082
cristybb503372010-05-27 20:51:26 +00006083 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006084
cristy3ed852e2009-09-05 21:47:34 +00006085 if (offset < 0)
6086 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6087 }
6088
6089 previous=image;
6090 mng_info->image=image;
6091 mng_info->mng_type=mng_type;
6092 mng_info->object_id=object_id;
6093
6094 if (memcmp(type,mng_IHDR,4) == 0)
6095 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006096
cristy3ed852e2009-09-05 21:47:34 +00006097#if defined(JNG_SUPPORTED)
6098 else
6099 image=ReadOneJNGImage(mng_info,image_info,exception);
6100#endif
6101
6102 if (image == (Image *) NULL)
6103 {
6104 if (IsImageObject(previous) != MagickFalse)
6105 {
6106 (void) DestroyImageList(previous);
6107 (void) CloseBlob(previous);
6108 }
glennrp47b9dd52010-11-24 18:12:06 +00006109
cristy3ed852e2009-09-05 21:47:34 +00006110 MngInfoFreeStruct(mng_info,&have_mng_structure);
6111 return((Image *) NULL);
6112 }
glennrp0fe50b42010-11-16 03:52:51 +00006113
cristy3ed852e2009-09-05 21:47:34 +00006114 if (image->columns == 0 || image->rows == 0)
6115 {
6116 (void) CloseBlob(image);
6117 image=DestroyImageList(image);
6118 MngInfoFreeStruct(mng_info,&have_mng_structure);
6119 return((Image *) NULL);
6120 }
glennrp0fe50b42010-11-16 03:52:51 +00006121
cristy3ed852e2009-09-05 21:47:34 +00006122 mng_info->image=image;
6123
6124 if (mng_type)
6125 {
6126 MngBox
6127 crop_box;
6128
6129 if (mng_info->magn_methx || mng_info->magn_methy)
6130 {
6131 png_uint_32
6132 magnified_height,
6133 magnified_width;
6134
6135 if (logging != MagickFalse)
6136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6137 " Processing MNG MAGN chunk");
6138
6139 if (mng_info->magn_methx == 1)
6140 {
6141 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006142
cristy3ed852e2009-09-05 21:47:34 +00006143 if (image->columns > 1)
6144 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006145
cristy3ed852e2009-09-05 21:47:34 +00006146 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006147 magnified_width += (png_uint_32)
6148 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006149 }
glennrp47b9dd52010-11-24 18:12:06 +00006150
cristy3ed852e2009-09-05 21:47:34 +00006151 else
6152 {
cristy4e5bc842010-06-09 13:56:01 +00006153 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006154
cristy3ed852e2009-09-05 21:47:34 +00006155 if (image->columns > 1)
6156 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006157
cristy3ed852e2009-09-05 21:47:34 +00006158 if (image->columns > 2)
6159 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006160
cristy3ed852e2009-09-05 21:47:34 +00006161 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006162 magnified_width += (png_uint_32)
6163 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006164 }
glennrp47b9dd52010-11-24 18:12:06 +00006165
cristy3ed852e2009-09-05 21:47:34 +00006166 if (mng_info->magn_methy == 1)
6167 {
6168 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006169
cristy3ed852e2009-09-05 21:47:34 +00006170 if (image->rows > 1)
6171 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006172
cristy3ed852e2009-09-05 21:47:34 +00006173 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006174 magnified_height += (png_uint_32)
6175 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006176 }
glennrp47b9dd52010-11-24 18:12:06 +00006177
cristy3ed852e2009-09-05 21:47:34 +00006178 else
6179 {
cristy4e5bc842010-06-09 13:56:01 +00006180 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006181
cristy3ed852e2009-09-05 21:47:34 +00006182 if (image->rows > 1)
6183 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006184
cristy3ed852e2009-09-05 21:47:34 +00006185 if (image->rows > 2)
6186 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006187
cristy3ed852e2009-09-05 21:47:34 +00006188 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006189 magnified_height += (png_uint_32)
6190 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006191 }
glennrp47b9dd52010-11-24 18:12:06 +00006192
cristy3ed852e2009-09-05 21:47:34 +00006193 if (magnified_height > image->rows ||
6194 magnified_width > image->columns)
6195 {
6196 Image
6197 *large_image;
6198
6199 int
6200 yy;
6201
cristy4c08aed2011-07-01 19:47:50 +00006202 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006203 *next,
6204 *prev;
6205
6206 png_uint_16
6207 magn_methx,
6208 magn_methy;
6209
cristy4c08aed2011-07-01 19:47:50 +00006210 ssize_t
6211 m,
6212 y;
6213
6214 register Quantum
6215 *n,
6216 *q;
6217
6218 register ssize_t
6219 x;
6220
glennrp47b9dd52010-11-24 18:12:06 +00006221 /* Allocate next image structure. */
6222
cristy3ed852e2009-09-05 21:47:34 +00006223 if (logging != MagickFalse)
6224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6225 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006226
cristy9950d572011-10-01 18:22:35 +00006227 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006228
cristy3ed852e2009-09-05 21:47:34 +00006229 if (GetNextImageInList(image) == (Image *) NULL)
6230 {
6231 image=DestroyImageList(image);
6232 MngInfoFreeStruct(mng_info,&have_mng_structure);
6233 return((Image *) NULL);
6234 }
6235
6236 large_image=SyncNextImageInList(image);
6237
6238 large_image->columns=magnified_width;
6239 large_image->rows=magnified_height;
6240
6241 magn_methx=mng_info->magn_methx;
6242 magn_methy=mng_info->magn_methy;
6243
glennrp3faa9a32011-04-23 14:00:25 +00006244#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006245#define QM unsigned short
6246 if (magn_methx != 1 || magn_methy != 1)
6247 {
6248 /*
6249 Scale pixels to unsigned shorts to prevent
6250 overflow of intermediate values of interpolations
6251 */
cristybb503372010-05-27 20:51:26 +00006252 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006253 {
6254 q=GetAuthenticPixels(image,0,y,image->columns,1,
6255 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006256
cristybb503372010-05-27 20:51:26 +00006257 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006258 {
cristy4c08aed2011-07-01 19:47:50 +00006259 SetPixelRed(image,ScaleQuantumToShort(
6260 GetPixelRed(image,q)),q);
6261 SetPixelGreen(image,ScaleQuantumToShort(
6262 GetPixelGreen(image,q)),q);
6263 SetPixelBlue(image,ScaleQuantumToShort(
6264 GetPixelBlue(image,q)),q);
6265 SetPixelAlpha(image,ScaleQuantumToShort(
6266 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006267 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006268 }
glennrp47b9dd52010-11-24 18:12:06 +00006269
cristy3ed852e2009-09-05 21:47:34 +00006270 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6271 break;
6272 }
6273 }
6274#else
6275#define QM Quantum
6276#endif
6277
6278 if (image->matte != MagickFalse)
cristyea1a8aa2011-10-20 13:24:06 +00006279 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006280
cristy3ed852e2009-09-05 21:47:34 +00006281 else
6282 {
cristy4c08aed2011-07-01 19:47:50 +00006283 large_image->background_color.alpha=OpaqueAlpha;
cristyea1a8aa2011-10-20 13:24:06 +00006284 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006285
cristy3ed852e2009-09-05 21:47:34 +00006286 if (magn_methx == 4)
6287 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006288
cristy3ed852e2009-09-05 21:47:34 +00006289 if (magn_methx == 5)
6290 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006291
cristy3ed852e2009-09-05 21:47:34 +00006292 if (magn_methy == 4)
6293 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006294
cristy3ed852e2009-09-05 21:47:34 +00006295 if (magn_methy == 5)
6296 magn_methy=3;
6297 }
6298
6299 /* magnify the rows into the right side of the large image */
6300
6301 if (logging != MagickFalse)
6302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006303 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006304 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006305 yy=0;
cristy8a20fa02011-12-27 15:54:31 +00006306 length=(size_t) image->columns*GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00006307 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6308 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006309
cristy4c08aed2011-07-01 19:47:50 +00006310 if ((prev == (Quantum *) NULL) ||
6311 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006312 {
6313 image=DestroyImageList(image);
6314 MngInfoFreeStruct(mng_info,&have_mng_structure);
6315 ThrowReaderException(ResourceLimitError,
6316 "MemoryAllocationFailed");
6317 }
glennrp47b9dd52010-11-24 18:12:06 +00006318
cristy3ed852e2009-09-05 21:47:34 +00006319 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6320 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006321
cristybb503372010-05-27 20:51:26 +00006322 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006323 {
6324 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006325 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006326
cristybb503372010-05-27 20:51:26 +00006327 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6328 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006329
cristybb503372010-05-27 20:51:26 +00006330 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6331 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006332
cristybb503372010-05-27 20:51:26 +00006333 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006334 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006335
cristy3ed852e2009-09-05 21:47:34 +00006336 else
cristybb503372010-05-27 20:51:26 +00006337 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006338
cristy3ed852e2009-09-05 21:47:34 +00006339 n=prev;
6340 prev=next;
6341 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006342
cristybb503372010-05-27 20:51:26 +00006343 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006344 {
6345 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6346 exception);
6347 (void) CopyMagickMemory(next,n,length);
6348 }
glennrp47b9dd52010-11-24 18:12:06 +00006349
cristy3ed852e2009-09-05 21:47:34 +00006350 for (i=0; i < m; i++, yy++)
6351 {
cristy4c08aed2011-07-01 19:47:50 +00006352 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006353 *pixels;
6354
cristybb503372010-05-27 20:51:26 +00006355 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006356 pixels=prev;
6357 n=next;
6358 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006359 1,exception);
cristy97707062011-12-27 18:25:00 +00006360 q+=(large_image->columns-image->columns)*
6361 GetPixelChannels(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006362
cristybb503372010-05-27 20:51:26 +00006363 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006364 {
glennrpfd05d622011-02-25 04:10:33 +00006365 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006366 /*
6367 if (image->storage_class == PseudoClass)
6368 {
6369 }
6370 */
6371
6372 if (magn_methy <= 1)
6373 {
glennrpbb4f99d2011-05-22 11:13:17 +00006374 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006375 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
glennrp847370c2011-07-05 17:37:15 +00006376 SetPixelGreen(large_image,GetPixelGreen(image,
6377 pixels),q);
6378 SetPixelBlue(large_image,GetPixelBlue(image,
6379 pixels),q);
6380 SetPixelAlpha(large_image,GetPixelAlpha(image,
6381 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006382 }
glennrp47b9dd52010-11-24 18:12:06 +00006383
cristy3ed852e2009-09-05 21:47:34 +00006384 else if (magn_methy == 2 || magn_methy == 4)
6385 {
6386 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006387 {
glennrp847370c2011-07-05 17:37:15 +00006388 SetPixelRed(large_image,GetPixelRed(image,
6389 pixels),q);
6390 SetPixelGreen(large_image,GetPixelGreen(image,
6391 pixels),q);
6392 SetPixelBlue(large_image,GetPixelBlue(image,
6393 pixels),q);
6394 SetPixelAlpha(large_image,GetPixelAlpha(image,
6395 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006396 }
glennrp47b9dd52010-11-24 18:12:06 +00006397
cristy3ed852e2009-09-05 21:47:34 +00006398 else
6399 {
6400 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006401 SetPixelRed(large_image,((QM) (((ssize_t)
6402 (2*i*(GetPixelRed(image,n)
6403 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006404 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006405 +GetPixelRed(image,pixels)))),q);
6406 SetPixelGreen(large_image,((QM) (((ssize_t)
6407 (2*i*(GetPixelGreen(image,n)
6408 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006409 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006410 +GetPixelGreen(image,pixels)))),q);
6411 SetPixelBlue(large_image,((QM) (((ssize_t)
6412 (2*i*(GetPixelBlue(image,n)
6413 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006414 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006415 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006416
cristy3ed852e2009-09-05 21:47:34 +00006417 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006418 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6419 (2*i*(GetPixelAlpha(image,n)
6420 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006421 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006422 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006423 }
glennrp47b9dd52010-11-24 18:12:06 +00006424
cristy3ed852e2009-09-05 21:47:34 +00006425 if (magn_methy == 4)
6426 {
6427 /* Replicate nearest */
6428 if (i <= ((m+1) << 1))
glennrp847370c2011-07-05 17:37:15 +00006429 SetPixelAlpha(large_image,GetPixelAlpha(image,
6430 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006431 else
glennrp847370c2011-07-05 17:37:15 +00006432 SetPixelAlpha(large_image,GetPixelAlpha(image,
6433 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006434 }
6435 }
glennrp47b9dd52010-11-24 18:12:06 +00006436
cristy3ed852e2009-09-05 21:47:34 +00006437 else /* if (magn_methy == 3 || magn_methy == 5) */
6438 {
6439 /* Replicate nearest */
6440 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006441 {
glennrp847370c2011-07-05 17:37:15 +00006442 SetPixelRed(large_image,GetPixelRed(image,
6443 pixels),q);
6444 SetPixelGreen(large_image,GetPixelGreen(image,
6445 pixels),q);
6446 SetPixelBlue(large_image,GetPixelBlue(image,
6447 pixels),q);
6448 SetPixelAlpha(large_image,GetPixelAlpha(image,
6449 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006450 }
glennrp47b9dd52010-11-24 18:12:06 +00006451
cristy3ed852e2009-09-05 21:47:34 +00006452 else
glennrpbb4f99d2011-05-22 11:13:17 +00006453 {
cristy4c08aed2011-07-01 19:47:50 +00006454 SetPixelRed(large_image,GetPixelRed(image,n),q);
glennrp847370c2011-07-05 17:37:15 +00006455 SetPixelGreen(large_image,GetPixelGreen(image,n),
6456 q);
6457 SetPixelBlue(large_image,GetPixelBlue(image,n),
6458 q);
6459 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6460 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006461 }
glennrp47b9dd52010-11-24 18:12:06 +00006462
cristy3ed852e2009-09-05 21:47:34 +00006463 if (magn_methy == 5)
6464 {
cristy4c08aed2011-07-01 19:47:50 +00006465 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6466 (GetPixelAlpha(image,n)
6467 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006468 +m))/((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006469 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006470 }
6471 }
cristyed231572011-07-14 02:18:59 +00006472 n+=GetPixelChannels(image);
6473 q+=GetPixelChannels(large_image);
6474 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006475 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006476
cristy3ed852e2009-09-05 21:47:34 +00006477 if (SyncAuthenticPixels(large_image,exception) == 0)
6478 break;
glennrp47b9dd52010-11-24 18:12:06 +00006479
cristy3ed852e2009-09-05 21:47:34 +00006480 } /* i */
6481 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006482
cristy4c08aed2011-07-01 19:47:50 +00006483 prev=(Quantum *) RelinquishMagickMemory(prev);
6484 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006485
6486 length=image->columns;
6487
6488 if (logging != MagickFalse)
6489 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6490 " Delete original image");
6491
6492 DeleteImageFromList(&image);
6493
6494 image=large_image;
6495
6496 mng_info->image=image;
6497
6498 /* magnify the columns */
6499 if (logging != MagickFalse)
6500 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006501 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006502
cristybb503372010-05-27 20:51:26 +00006503 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006504 {
cristy4c08aed2011-07-01 19:47:50 +00006505 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006506 *pixels;
6507
6508 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristyed231572011-07-14 02:18:59 +00006509 pixels=q+(image->columns-length)*GetPixelChannels(image);
6510 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006511
cristybb503372010-05-27 20:51:26 +00006512 for (x=(ssize_t) (image->columns-length);
6513 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006514 {
cristyed231572011-07-14 02:18:59 +00006515 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006516
cristybb503372010-05-27 20:51:26 +00006517 if (x == (ssize_t) (image->columns-length))
6518 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006519
cristybb503372010-05-27 20:51:26 +00006520 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6521 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006522
cristybb503372010-05-27 20:51:26 +00006523 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6524 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006525
cristybb503372010-05-27 20:51:26 +00006526 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006527 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006528
cristy3ed852e2009-09-05 21:47:34 +00006529 else
cristybb503372010-05-27 20:51:26 +00006530 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006531
cristy3ed852e2009-09-05 21:47:34 +00006532 for (i=0; i < m; i++)
6533 {
6534 if (magn_methx <= 1)
6535 {
6536 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006537 SetPixelRed(image,GetPixelRed(image,pixels),q);
6538 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6539 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6540 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006541 }
glennrp47b9dd52010-11-24 18:12:06 +00006542
cristy3ed852e2009-09-05 21:47:34 +00006543 else if (magn_methx == 2 || magn_methx == 4)
6544 {
6545 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006546 {
cristy4c08aed2011-07-01 19:47:50 +00006547 SetPixelRed(image,GetPixelRed(image,pixels),q);
6548 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6549 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6550 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006551 }
glennrp47b9dd52010-11-24 18:12:06 +00006552
cristyed231572011-07-14 02:18:59 +00006553 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006554 else
6555 {
6556 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006557 SetPixelRed(image,(QM) ((2*i*(
6558 GetPixelRed(image,n)
6559 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006560 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006561 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006562
cristy4c08aed2011-07-01 19:47:50 +00006563 SetPixelGreen(image,(QM) ((2*i*(
6564 GetPixelGreen(image,n)
6565 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006566 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006567 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006568
cristy4c08aed2011-07-01 19:47:50 +00006569 SetPixelBlue(image,(QM) ((2*i*(
6570 GetPixelBlue(image,n)
6571 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006572 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006573 GetPixelBlue(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006574 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006575 SetPixelAlpha(image,(QM) ((2*i*(
6576 GetPixelAlpha(image,n)
6577 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006578 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006579 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006580 }
glennrp47b9dd52010-11-24 18:12:06 +00006581
cristy3ed852e2009-09-05 21:47:34 +00006582 if (magn_methx == 4)
6583 {
6584 /* Replicate nearest */
6585 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006586 {
cristy4c08aed2011-07-01 19:47:50 +00006587 SetPixelAlpha(image,
6588 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006589 }
cristy3ed852e2009-09-05 21:47:34 +00006590 else
glennrpbb4f99d2011-05-22 11:13:17 +00006591 {
cristy4c08aed2011-07-01 19:47:50 +00006592 SetPixelAlpha(image,
6593 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006594 }
cristy3ed852e2009-09-05 21:47:34 +00006595 }
6596 }
glennrp47b9dd52010-11-24 18:12:06 +00006597
cristy3ed852e2009-09-05 21:47:34 +00006598 else /* if (magn_methx == 3 || magn_methx == 5) */
6599 {
6600 /* Replicate nearest */
6601 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006602 {
cristy4c08aed2011-07-01 19:47:50 +00006603 SetPixelRed(image,GetPixelRed(image,pixels),q);
6604 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6605 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6606 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006607 }
glennrp47b9dd52010-11-24 18:12:06 +00006608
cristy3ed852e2009-09-05 21:47:34 +00006609 else
glennrpbb4f99d2011-05-22 11:13:17 +00006610 {
cristy4c08aed2011-07-01 19:47:50 +00006611 SetPixelRed(image,GetPixelRed(image,n),q);
6612 SetPixelGreen(image,GetPixelGreen(image,n),q);
6613 SetPixelBlue(image,GetPixelBlue(image,n),q);
6614 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006615 }
glennrp47b9dd52010-11-24 18:12:06 +00006616
cristy3ed852e2009-09-05 21:47:34 +00006617 if (magn_methx == 5)
6618 {
6619 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006620 SetPixelAlpha(image,
6621 (QM) ((2*i*( GetPixelAlpha(image,n)
6622 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006623 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006624 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006625 }
6626 }
cristyed231572011-07-14 02:18:59 +00006627 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006628 }
cristyed231572011-07-14 02:18:59 +00006629 n+=GetPixelChannels(image);
6630 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006631 }
glennrp47b9dd52010-11-24 18:12:06 +00006632
cristy3ed852e2009-09-05 21:47:34 +00006633 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6634 break;
6635 }
glennrp3faa9a32011-04-23 14:00:25 +00006636#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006637 if (magn_methx != 1 || magn_methy != 1)
6638 {
6639 /*
6640 Rescale pixels to Quantum
6641 */
cristybb503372010-05-27 20:51:26 +00006642 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006643 {
6644 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006645
cristybb503372010-05-27 20:51:26 +00006646 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006647 {
cristy4c08aed2011-07-01 19:47:50 +00006648 SetPixelRed(image,ScaleShortToQuantum(
6649 GetPixelRed(image,q)),q);
6650 SetPixelGreen(image,ScaleShortToQuantum(
6651 GetPixelGreen(image,q)),q);
6652 SetPixelBlue(image,ScaleShortToQuantum(
6653 GetPixelBlue(image,q)),q);
6654 SetPixelAlpha(image,ScaleShortToQuantum(
6655 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006656 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006657 }
glennrp47b9dd52010-11-24 18:12:06 +00006658
cristy3ed852e2009-09-05 21:47:34 +00006659 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6660 break;
6661 }
6662 }
6663#endif
6664 if (logging != MagickFalse)
6665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6666 " Finished MAGN processing");
6667 }
6668 }
6669
6670 /*
6671 Crop_box is with respect to the upper left corner of the MNG.
6672 */
6673 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6674 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6675 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6676 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6677 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6678 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6679 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6680 if ((crop_box.left != (mng_info->image_box.left
6681 +mng_info->x_off[object_id])) ||
6682 (crop_box.right != (mng_info->image_box.right
6683 +mng_info->x_off[object_id])) ||
6684 (crop_box.top != (mng_info->image_box.top
6685 +mng_info->y_off[object_id])) ||
6686 (crop_box.bottom != (mng_info->image_box.bottom
6687 +mng_info->y_off[object_id])))
6688 {
6689 if (logging != MagickFalse)
6690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6691 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006692
cristy3ed852e2009-09-05 21:47:34 +00006693 if ((crop_box.left < crop_box.right) &&
6694 (crop_box.top < crop_box.bottom))
6695 {
6696 Image
6697 *im;
6698
6699 RectangleInfo
6700 crop_info;
6701
6702 /*
6703 Crop_info is with respect to the upper left corner of
6704 the image.
6705 */
6706 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6707 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006708 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6709 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006710 image->page.width=image->columns;
6711 image->page.height=image->rows;
6712 image->page.x=0;
6713 image->page.y=0;
6714 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006715
cristy3ed852e2009-09-05 21:47:34 +00006716 if (im != (Image *) NULL)
6717 {
6718 image->columns=im->columns;
6719 image->rows=im->rows;
6720 im=DestroyImage(im);
6721 image->page.width=image->columns;
6722 image->page.height=image->rows;
6723 image->page.x=crop_box.left;
6724 image->page.y=crop_box.top;
6725 }
6726 }
glennrp47b9dd52010-11-24 18:12:06 +00006727
cristy3ed852e2009-09-05 21:47:34 +00006728 else
6729 {
6730 /*
6731 No pixels in crop area. The MNG spec still requires
6732 a layer, though, so make a single transparent pixel in
6733 the top left corner.
6734 */
6735 image->columns=1;
6736 image->rows=1;
6737 image->colors=2;
cristyea1a8aa2011-10-20 13:24:06 +00006738 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006739 image->page.width=1;
6740 image->page.height=1;
6741 image->page.x=0;
6742 image->page.y=0;
6743 }
6744 }
6745#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6746 image=mng_info->image;
6747#endif
6748 }
6749
glennrp2b013e42010-11-24 16:55:50 +00006750#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6751 /* PNG does not handle depths greater than 16 so reduce it even
glennrpcc5d45b2012-01-06 04:06:10 +00006752 * if lossy.
glennrp2b013e42010-11-24 16:55:50 +00006753 */
6754 if (image->depth > 16)
6755 image->depth=16;
6756#endif
6757
glennrp3faa9a32011-04-23 14:00:25 +00006758#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00006759 if (image->depth > 8)
6760 {
6761 /* To do: fill low byte properly */
6762 image->depth=16;
6763 }
6764
cristyc82a27b2011-10-21 01:07:16 +00006765 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00006766 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006767#endif
glennrpd6afd542010-11-19 01:53:05 +00006768
cristy3ed852e2009-09-05 21:47:34 +00006769 if (image_info->number_scenes != 0)
6770 {
6771 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006772 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006773 break;
6774 }
glennrpd6afd542010-11-19 01:53:05 +00006775
cristy3ed852e2009-09-05 21:47:34 +00006776 if (logging != MagickFalse)
6777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6778 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006779
cristy3ed852e2009-09-05 21:47:34 +00006780 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006781
cristy3ed852e2009-09-05 21:47:34 +00006782 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006783
cristy3ed852e2009-09-05 21:47:34 +00006784 if (logging != MagickFalse)
6785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6786 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006787
cristy3ed852e2009-09-05 21:47:34 +00006788#if defined(MNG_INSERT_LAYERS)
6789 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6790 (mng_info->mng_height))
6791 {
6792 /*
6793 Insert a background layer if nothing else was found.
6794 */
6795 if (logging != MagickFalse)
6796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6797 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006798
cristy4c08aed2011-07-01 19:47:50 +00006799 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006800 {
6801 /*
6802 Allocate next image structure.
6803 */
cristy9950d572011-10-01 18:22:35 +00006804 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006805 if (GetNextImageInList(image) == (Image *) NULL)
6806 {
6807 image=DestroyImageList(image);
6808 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006809
cristy3ed852e2009-09-05 21:47:34 +00006810 if (logging != MagickFalse)
6811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6812 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006813
cristy3ed852e2009-09-05 21:47:34 +00006814 return((Image *) NULL);
6815 }
6816 image=SyncNextImageInList(image);
6817 }
6818 image->columns=mng_info->mng_width;
6819 image->rows=mng_info->mng_height;
6820 image->page.width=mng_info->mng_width;
6821 image->page.height=mng_info->mng_height;
6822 image->page.x=0;
6823 image->page.y=0;
6824 image->background_color=mng_background_color;
6825 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006826
cristy3ed852e2009-09-05 21:47:34 +00006827 if (image_info->ping == MagickFalse)
cristyea1a8aa2011-10-20 13:24:06 +00006828 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006829
cristy3ed852e2009-09-05 21:47:34 +00006830 mng_info->image_found++;
6831 }
6832#endif
6833 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006834
cristy3ed852e2009-09-05 21:47:34 +00006835 if (mng_iterations == 1)
6836 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006837
cristy3ed852e2009-09-05 21:47:34 +00006838 while (GetPreviousImageInList(image) != (Image *) NULL)
6839 {
6840 image_count++;
6841 if (image_count > 10*mng_info->image_found)
6842 {
6843 if (logging != MagickFalse)
6844 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006845
cristyc82a27b2011-10-21 01:07:16 +00006846 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006847 CoderError,"Linked list is corrupted, beginning of list not found",
6848 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006849
cristy3ed852e2009-09-05 21:47:34 +00006850 return((Image *) NULL);
6851 }
glennrp0fe50b42010-11-16 03:52:51 +00006852
cristy3ed852e2009-09-05 21:47:34 +00006853 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006854
cristy3ed852e2009-09-05 21:47:34 +00006855 if (GetNextImageInList(image) == (Image *) NULL)
6856 {
6857 if (logging != MagickFalse)
6858 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006859
cristyc82a27b2011-10-21 01:07:16 +00006860 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006861 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6862 image_info->filename);
6863 }
6864 }
glennrp47b9dd52010-11-24 18:12:06 +00006865
cristy3ed852e2009-09-05 21:47:34 +00006866 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6867 GetNextImageInList(image) ==
6868 (Image *) NULL)
6869 {
6870 if (logging != MagickFalse)
6871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6872 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006873
cristyc82a27b2011-10-21 01:07:16 +00006874 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006875 CoderError,"image->next for first image is NULL but shouldn't be.",
6876 "`%s'",image_info->filename);
6877 }
glennrp47b9dd52010-11-24 18:12:06 +00006878
cristy3ed852e2009-09-05 21:47:34 +00006879 if (mng_info->image_found == 0)
6880 {
6881 if (logging != MagickFalse)
6882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6883 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006884
cristyc82a27b2011-10-21 01:07:16 +00006885 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006886 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006887
cristy3ed852e2009-09-05 21:47:34 +00006888 if (image != (Image *) NULL)
6889 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006890
cristy3ed852e2009-09-05 21:47:34 +00006891 MngInfoFreeStruct(mng_info,&have_mng_structure);
6892 return((Image *) NULL);
6893 }
6894
6895 if (mng_info->ticks_per_second)
6896 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6897 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006898
cristy3ed852e2009-09-05 21:47:34 +00006899 else
6900 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006901
cristy3ed852e2009-09-05 21:47:34 +00006902 /* Find final nonzero image delay */
6903 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006904
cristy3ed852e2009-09-05 21:47:34 +00006905 while (GetNextImageInList(image) != (Image *) NULL)
6906 {
6907 if (image->delay)
6908 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006909
cristy3ed852e2009-09-05 21:47:34 +00006910 image=GetNextImageInList(image);
6911 }
glennrp0fe50b42010-11-16 03:52:51 +00006912
cristy3ed852e2009-09-05 21:47:34 +00006913 if (final_delay < final_image_delay)
6914 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006915
cristy3ed852e2009-09-05 21:47:34 +00006916 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006917
cristy3ed852e2009-09-05 21:47:34 +00006918 if (logging != MagickFalse)
6919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006920 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6921 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006922
cristy3ed852e2009-09-05 21:47:34 +00006923 if (logging != MagickFalse)
6924 {
6925 int
6926 scene;
6927
6928 scene=0;
6929 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006930
cristy3ed852e2009-09-05 21:47:34 +00006931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6932 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006933
cristy3ed852e2009-09-05 21:47:34 +00006934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006935 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006936
cristy3ed852e2009-09-05 21:47:34 +00006937 while (GetNextImageInList(image) != (Image *) NULL)
6938 {
6939 image=GetNextImageInList(image);
6940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006941 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006942 }
6943 }
6944
6945 image=GetFirstImageInList(image);
6946#ifdef MNG_COALESCE_LAYERS
6947 if (insert_layers)
6948 {
6949 Image
6950 *next_image,
6951 *next;
6952
cristybb503372010-05-27 20:51:26 +00006953 size_t
cristy3ed852e2009-09-05 21:47:34 +00006954 scene;
6955
6956 if (logging != MagickFalse)
6957 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006958
cristy3ed852e2009-09-05 21:47:34 +00006959 scene=image->scene;
cristyc82a27b2011-10-21 01:07:16 +00006960 next_image=CoalesceImages(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006961
cristy3ed852e2009-09-05 21:47:34 +00006962 if (next_image == (Image *) NULL)
6963 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006964
cristy3ed852e2009-09-05 21:47:34 +00006965 image=DestroyImageList(image);
6966 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006967
cristy3ed852e2009-09-05 21:47:34 +00006968 for (next=image; next != (Image *) NULL; next=next_image)
6969 {
6970 next->page.width=mng_info->mng_width;
6971 next->page.height=mng_info->mng_height;
6972 next->page.x=0;
6973 next->page.y=0;
6974 next->scene=scene++;
6975 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006976
cristy3ed852e2009-09-05 21:47:34 +00006977 if (next_image == (Image *) NULL)
6978 break;
glennrp47b9dd52010-11-24 18:12:06 +00006979
cristy3ed852e2009-09-05 21:47:34 +00006980 if (next->delay == 0)
6981 {
6982 scene--;
6983 next_image->previous=GetPreviousImageInList(next);
6984 if (GetPreviousImageInList(next) == (Image *) NULL)
6985 image=next_image;
6986 else
6987 next->previous->next=next_image;
6988 next=DestroyImage(next);
6989 }
6990 }
6991 }
6992#endif
6993
6994 while (GetNextImageInList(image) != (Image *) NULL)
6995 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006996
cristy3ed852e2009-09-05 21:47:34 +00006997 image->dispose=BackgroundDispose;
6998
6999 if (logging != MagickFalse)
7000 {
7001 int
7002 scene;
7003
7004 scene=0;
7005 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007006
cristy3ed852e2009-09-05 21:47:34 +00007007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7008 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00007009
cristy3ed852e2009-09-05 21:47:34 +00007010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007011 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7012 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00007013
cristy3ed852e2009-09-05 21:47:34 +00007014 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00007015 {
7016 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007017
cristyf2faecf2010-05-28 19:19:36 +00007018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007019 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7020 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00007021 }
7022 }
glennrp47b9dd52010-11-24 18:12:06 +00007023
cristy3ed852e2009-09-05 21:47:34 +00007024 image=GetFirstImageInList(image);
7025 MngInfoFreeStruct(mng_info,&have_mng_structure);
7026 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00007027
cristy3ed852e2009-09-05 21:47:34 +00007028 if (logging != MagickFalse)
7029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00007030
cristy3ed852e2009-09-05 21:47:34 +00007031 return(GetFirstImageInList(image));
7032}
glennrp25c1e2b2010-03-25 01:39:56 +00007033#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007034static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7035{
7036 printf("Your PNG library is too old: You have libpng-%s\n",
7037 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00007038
cristy3ed852e2009-09-05 21:47:34 +00007039 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7040 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007041
cristy3ed852e2009-09-05 21:47:34 +00007042 return(Image *) NULL;
7043}
glennrp47b9dd52010-11-24 18:12:06 +00007044
cristy3ed852e2009-09-05 21:47:34 +00007045static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7046{
7047 return(ReadPNGImage(image_info,exception));
7048}
glennrp25c1e2b2010-03-25 01:39:56 +00007049#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007050#endif
7051
7052/*
7053%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7054% %
7055% %
7056% %
7057% R e g i s t e r P N G I m a g e %
7058% %
7059% %
7060% %
7061%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7062%
7063% RegisterPNGImage() adds properties for the PNG image format to
7064% the list of supported formats. The properties include the image format
7065% tag, a method to read and/or write the format, whether the format
7066% supports the saving of more than one frame to the same file or blob,
7067% whether the format supports native in-memory I/O, and a brief
7068% description of the format.
7069%
7070% The format of the RegisterPNGImage method is:
7071%
cristybb503372010-05-27 20:51:26 +00007072% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007073%
7074*/
cristybb503372010-05-27 20:51:26 +00007075ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007076{
7077 char
7078 version[MaxTextExtent];
7079
7080 MagickInfo
7081 *entry;
7082
7083 static const char
7084 *PNGNote=
7085 {
7086 "See http://www.libpng.org/ for details about the PNG format."
7087 },
glennrp47b9dd52010-11-24 18:12:06 +00007088
cristy3ed852e2009-09-05 21:47:34 +00007089 *JNGNote=
7090 {
7091 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7092 "format."
7093 },
glennrp47b9dd52010-11-24 18:12:06 +00007094
cristy3ed852e2009-09-05 21:47:34 +00007095 *MNGNote=
7096 {
7097 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7098 "format."
7099 };
7100
7101 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007102
cristy3ed852e2009-09-05 21:47:34 +00007103#if defined(PNG_LIBPNG_VER_STRING)
7104 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7105 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007106
cristy3ed852e2009-09-05 21:47:34 +00007107 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7108 {
7109 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7110 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7111 MaxTextExtent);
7112 }
7113#endif
glennrp47b9dd52010-11-24 18:12:06 +00007114
cristy3ed852e2009-09-05 21:47:34 +00007115 entry=SetMagickInfo("MNG");
7116 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007117
cristy3ed852e2009-09-05 21:47:34 +00007118#if defined(MAGICKCORE_PNG_DELEGATE)
7119 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7120 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7121#endif
glennrp47b9dd52010-11-24 18:12:06 +00007122
cristy3ed852e2009-09-05 21:47:34 +00007123 entry->magick=(IsImageFormatHandler *) IsMNG;
7124 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007125
cristy3ed852e2009-09-05 21:47:34 +00007126 if (*version != '\0')
7127 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007128
cristy3ed852e2009-09-05 21:47:34 +00007129 entry->module=ConstantString("PNG");
7130 entry->note=ConstantString(MNGNote);
7131 (void) RegisterMagickInfo(entry);
7132
7133 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007134
cristy3ed852e2009-09-05 21:47:34 +00007135#if defined(MAGICKCORE_PNG_DELEGATE)
7136 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7137 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7138#endif
glennrp47b9dd52010-11-24 18:12:06 +00007139
cristy3ed852e2009-09-05 21:47:34 +00007140 entry->magick=(IsImageFormatHandler *) IsPNG;
7141 entry->adjoin=MagickFalse;
7142 entry->description=ConstantString("Portable Network Graphics");
7143 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007144
cristy3ed852e2009-09-05 21:47:34 +00007145 if (*version != '\0')
7146 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007147
cristy3ed852e2009-09-05 21:47:34 +00007148 entry->note=ConstantString(PNGNote);
7149 (void) RegisterMagickInfo(entry);
7150
7151 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007152
cristy3ed852e2009-09-05 21:47:34 +00007153#if defined(MAGICKCORE_PNG_DELEGATE)
7154 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7155 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7156#endif
glennrp47b9dd52010-11-24 18:12:06 +00007157
cristy3ed852e2009-09-05 21:47:34 +00007158 entry->magick=(IsImageFormatHandler *) IsPNG;
7159 entry->adjoin=MagickFalse;
7160 entry->description=ConstantString(
7161 "8-bit indexed with optional binary transparency");
7162 entry->module=ConstantString("PNG");
7163 (void) RegisterMagickInfo(entry);
7164
7165 entry=SetMagickInfo("PNG24");
7166 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007167
cristy3ed852e2009-09-05 21:47:34 +00007168#if defined(ZLIB_VERSION)
7169 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7170 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007171
cristy3ed852e2009-09-05 21:47:34 +00007172 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7173 {
7174 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7175 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7176 }
7177#endif
glennrp47b9dd52010-11-24 18:12:06 +00007178
cristy3ed852e2009-09-05 21:47:34 +00007179 if (*version != '\0')
7180 entry->version=ConstantString(version);
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 24-bit RGB");
7190 entry->module=ConstantString("PNG");
7191 (void) RegisterMagickInfo(entry);
7192
7193 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007194
cristy3ed852e2009-09-05 21:47:34 +00007195#if defined(MAGICKCORE_PNG_DELEGATE)
7196 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7197 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7198#endif
glennrp47b9dd52010-11-24 18:12:06 +00007199
cristy3ed852e2009-09-05 21:47:34 +00007200 entry->magick=(IsImageFormatHandler *) IsPNG;
7201 entry->adjoin=MagickFalse;
7202 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7203 entry->module=ConstantString("PNG");
7204 (void) RegisterMagickInfo(entry);
7205
7206 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007207
cristy3ed852e2009-09-05 21:47:34 +00007208#if defined(JNG_SUPPORTED)
7209#if defined(MAGICKCORE_PNG_DELEGATE)
7210 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7211 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7212#endif
7213#endif
glennrp47b9dd52010-11-24 18:12:06 +00007214
cristy3ed852e2009-09-05 21:47:34 +00007215 entry->magick=(IsImageFormatHandler *) IsJNG;
7216 entry->adjoin=MagickFalse;
7217 entry->description=ConstantString("JPEG Network Graphics");
7218 entry->module=ConstantString("PNG");
7219 entry->note=ConstantString(JNGNote);
7220 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007221
cristy18b17442009-10-25 18:36:48 +00007222#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007223 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007224#endif
glennrp47b9dd52010-11-24 18:12:06 +00007225
cristy3ed852e2009-09-05 21:47:34 +00007226 return(MagickImageCoderSignature);
7227}
7228
7229/*
7230%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7231% %
7232% %
7233% %
7234% U n r e g i s t e r P N G I m a g e %
7235% %
7236% %
7237% %
7238%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7239%
7240% UnregisterPNGImage() removes format registrations made by the
7241% PNG module from the list of supported formats.
7242%
7243% The format of the UnregisterPNGImage method is:
7244%
7245% UnregisterPNGImage(void)
7246%
7247*/
7248ModuleExport void UnregisterPNGImage(void)
7249{
7250 (void) UnregisterMagickInfo("MNG");
7251 (void) UnregisterMagickInfo("PNG");
7252 (void) UnregisterMagickInfo("PNG8");
7253 (void) UnregisterMagickInfo("PNG24");
7254 (void) UnregisterMagickInfo("PNG32");
7255 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007256
cristy3ed852e2009-09-05 21:47:34 +00007257#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007258 if (ping_semaphore != (SemaphoreInfo *) NULL)
7259 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007260#endif
7261}
7262
7263#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007264#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007265/*
7266%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7267% %
7268% %
7269% %
7270% W r i t e M N G I m a g e %
7271% %
7272% %
7273% %
7274%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7275%
7276% WriteMNGImage() writes an image in the Portable Network Graphics
7277% Group's "Multiple-image Network Graphics" encoded image format.
7278%
7279% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7280%
7281% The format of the WriteMNGImage method is:
7282%
cristy1e178e72011-08-28 19:44:34 +00007283% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7284% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007285%
7286% A description of each parameter follows.
7287%
7288% o image_info: the image info.
7289%
7290% o image: The image.
7291%
cristy1e178e72011-08-28 19:44:34 +00007292% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007293%
7294% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7295% "To do" under ReadPNGImage):
7296%
cristy3ed852e2009-09-05 21:47:34 +00007297% Preserve all unknown and not-yet-handled known chunks found in input
7298% PNG file and copy them into output PNG files according to the PNG
7299% copying rules.
7300%
7301% Write the iCCP chunk at MNG level when (icc profile length > 0)
7302%
7303% Improve selection of color type (use indexed-colour or indexed-colour
7304% with tRNS when 256 or fewer unique RGBA values are present).
7305%
7306% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7307% This will be complicated if we limit ourselves to generating MNG-LC
7308% files. For now we ignore disposal method 3 and simply overlay the next
7309% image on it.
7310%
7311% Check for identical PLTE's or PLTE/tRNS combinations and use a
7312% global MNG PLTE or PLTE/tRNS combination when appropriate.
7313% [mostly done 15 June 1999 but still need to take care of tRNS]
7314%
7315% Check for identical sRGB and replace with a global sRGB (and remove
7316% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7317% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7318% local gAMA/cHRM with local sRGB if appropriate).
7319%
7320% Check for identical sBIT chunks and write global ones.
7321%
7322% Provide option to skip writing the signature tEXt chunks.
7323%
7324% Use signatures to detect identical objects and reuse the first
7325% instance of such objects instead of writing duplicate objects.
7326%
7327% Use a smaller-than-32k value of compression window size when
7328% appropriate.
7329%
7330% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7331% ancillary text chunks and save profiles.
7332%
7333% Provide an option to force LC files (to ensure exact framing rate)
7334% instead of VLC.
7335%
7336% Provide an option to force VLC files instead of LC, even when offsets
7337% are present. This will involve expanding the embedded images with a
7338% transparent region at the top and/or left.
7339*/
7340
cristy3ed852e2009-09-05 21:47:34 +00007341static void
glennrpcf002022011-01-30 02:38:15 +00007342Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007343 png_info *ping_info, unsigned char *profile_type, unsigned char
7344 *profile_description, unsigned char *profile_data, png_uint_32 length)
7345{
cristy3ed852e2009-09-05 21:47:34 +00007346 png_textp
7347 text;
7348
cristybb503372010-05-27 20:51:26 +00007349 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007350 i;
7351
7352 unsigned char
7353 *sp;
7354
7355 png_charp
7356 dp;
7357
7358 png_uint_32
7359 allocated_length,
7360 description_length;
7361
7362 unsigned char
7363 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007364
cristy3ed852e2009-09-05 21:47:34 +00007365 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7366 return;
7367
7368 if (image_info->verbose)
7369 {
glennrp0fe50b42010-11-16 03:52:51 +00007370 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7371 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007372 }
glennrp0fe50b42010-11-16 03:52:51 +00007373
cristy3ed852e2009-09-05 21:47:34 +00007374 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7375 description_length=(png_uint_32) strlen((const char *) profile_description);
7376 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7377 + description_length);
7378 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7379 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7380 text[0].key[0]='\0';
7381 (void) ConcatenateMagickString(text[0].key,
7382 "Raw profile type ",MaxTextExtent);
7383 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7384 sp=profile_data;
7385 dp=text[0].text;
7386 *dp++='\n';
7387 (void) CopyMagickString(dp,(const char *) profile_description,
7388 allocated_length);
7389 dp+=description_length;
7390 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007391 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007392 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007393 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007394
cristybb503372010-05-27 20:51:26 +00007395 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007396 {
7397 if (i%36 == 0)
7398 *dp++='\n';
7399 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7400 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7401 }
glennrp47b9dd52010-11-24 18:12:06 +00007402
cristy3ed852e2009-09-05 21:47:34 +00007403 *dp++='\n';
7404 *dp='\0';
7405 text[0].text_length=(png_size_t) (dp-text[0].text);
7406 text[0].compression=image_info->compression == NoCompression ||
7407 (image_info->compression == UndefinedCompression &&
7408 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007409
cristy3ed852e2009-09-05 21:47:34 +00007410 if (text[0].text_length <= allocated_length)
7411 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007412
cristy3ed852e2009-09-05 21:47:34 +00007413 png_free(ping,text[0].text);
7414 png_free(ping,text[0].key);
7415 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007416}
7417
glennrpcf002022011-01-30 02:38:15 +00007418static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007419 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007420{
7421 char
7422 *name;
7423
7424 const StringInfo
7425 *profile;
7426
7427 unsigned char
7428 *data;
7429
7430 png_uint_32 length;
7431
7432 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007433
7434 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7435 {
cristy3ed852e2009-09-05 21:47:34 +00007436 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007437
cristy3ed852e2009-09-05 21:47:34 +00007438 if (profile != (const StringInfo *) NULL)
7439 {
7440 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007441 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007442
glennrp47b9dd52010-11-24 18:12:06 +00007443 if (LocaleNCompare(name,string,11) == 0)
7444 {
7445 if (logging != MagickFalse)
7446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7447 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007448
glennrpcf002022011-01-30 02:38:15 +00007449 ping_profile=CloneStringInfo(profile);
7450 data=GetStringInfoDatum(ping_profile),
7451 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007452 data[4]=data[3];
7453 data[3]=data[2];
7454 data[2]=data[1];
7455 data[1]=data[0];
7456 (void) WriteBlobMSBULong(image,length-5); /* data length */
7457 (void) WriteBlob(image,length-1,data+1);
7458 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007459 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007460 }
cristy3ed852e2009-09-05 21:47:34 +00007461 }
glennrp47b9dd52010-11-24 18:12:06 +00007462
cristy3ed852e2009-09-05 21:47:34 +00007463 name=GetNextImageProfile(image);
7464 }
glennrp47b9dd52010-11-24 18:12:06 +00007465
cristy3ed852e2009-09-05 21:47:34 +00007466 return(MagickTrue);
7467}
7468
glennrpb9cfe272010-12-21 15:08:06 +00007469
cristy3ed852e2009-09-05 21:47:34 +00007470/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007471static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +00007472 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007473{
7474 Image
7475 *image;
7476
7477 ImageInfo
7478 *image_info;
7479
cristy3ed852e2009-09-05 21:47:34 +00007480 char
7481 s[2];
7482
7483 const char
7484 *name,
7485 *property,
7486 *value;
7487
7488 const StringInfo
7489 *profile;
7490
cristy3ed852e2009-09-05 21:47:34 +00007491 int
cristy3ed852e2009-09-05 21:47:34 +00007492 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007493 pass;
7494
glennrpe9c26dc2010-05-30 01:56:35 +00007495 png_byte
7496 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007497
glennrp39992b42010-11-14 00:03:43 +00007498 png_color
7499 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007500
glennrp5af765f2010-03-30 11:12:18 +00007501 png_color_16
7502 ping_background,
7503 ping_trans_color;
7504
cristy3ed852e2009-09-05 21:47:34 +00007505 png_info
7506 *ping_info;
7507
7508 png_struct
7509 *ping;
7510
glennrp5af765f2010-03-30 11:12:18 +00007511 png_uint_32
7512 ping_height,
7513 ping_width;
7514
cristybb503372010-05-27 20:51:26 +00007515 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007516 y;
7517
7518 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007519 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007520 logging,
glennrp58e01762011-01-07 15:28:54 +00007521 matte,
7522
glennrpda8f3a72011-02-27 23:54:12 +00007523 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007524 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007525 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007526 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007527 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007528 ping_have_bKGD,
7529 ping_have_pHYs,
7530 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007531
7532 ping_exclude_bKGD,
7533 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007534 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007535 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007536 ping_exclude_gAMA,
7537 ping_exclude_iCCP,
7538 /* ping_exclude_iTXt, */
7539 ping_exclude_oFFs,
7540 ping_exclude_pHYs,
7541 ping_exclude_sRGB,
7542 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007543 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007544 ping_exclude_vpAg,
7545 ping_exclude_zCCP, /* hex-encoded iCCP */
7546 ping_exclude_zTXt,
7547
glennrp8d3d6e52011-04-19 04:39:51 +00007548 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007549 ping_need_colortype_warning,
7550
glennrp82b3c532011-03-22 19:20:54 +00007551 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007552 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007553 tried_333,
7554 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007555
7556 QuantumInfo
7557 *quantum_info;
7558
cristyc82a27b2011-10-21 01:07:16 +00007559 PNGErrorInfo
7560 error_info;
7561
cristybb503372010-05-27 20:51:26 +00007562 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007563 i,
7564 x;
7565
7566 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007567 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007568
glennrp5af765f2010-03-30 11:12:18 +00007569 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007570 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007571 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007572 ping_color_type,
7573 ping_interlace_method,
7574 ping_compression_method,
7575 ping_filter_method,
7576 ping_num_trans;
7577
cristybb503372010-05-27 20:51:26 +00007578 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007579 image_depth,
7580 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007581
cristybb503372010-05-27 20:51:26 +00007582 size_t
cristy3ed852e2009-09-05 21:47:34 +00007583 quality,
7584 rowbytes,
7585 save_image_depth;
7586
glennrpdfd70802010-11-14 01:23:35 +00007587 int
glennrpfd05d622011-02-25 04:10:33 +00007588 j,
glennrpf09bded2011-01-08 01:15:59 +00007589 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007590 number_opaque,
7591 number_semitransparent,
7592 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007593 ping_pHYs_unit_type;
7594
7595 png_uint_32
7596 ping_pHYs_x_resolution,
7597 ping_pHYs_y_resolution;
7598
cristy3ed852e2009-09-05 21:47:34 +00007599 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007600 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007601
cristyc82a27b2011-10-21 01:07:16 +00007602 image = CloneImage(IMimage,0,0,MagickFalse,exception);
glennrpb9cfe272010-12-21 15:08:06 +00007603 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007604 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007605 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007606
cristy3ed852e2009-09-05 21:47:34 +00007607#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007608 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007609#endif
7610
glennrp5af765f2010-03-30 11:12:18 +00007611 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007612 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007613 ping_color_type=0,
7614 ping_interlace_method=0,
7615 ping_compression_method=0,
7616 ping_filter_method=0,
7617 ping_num_trans = 0;
7618
7619 ping_background.red = 0;
7620 ping_background.green = 0;
7621 ping_background.blue = 0;
7622 ping_background.gray = 0;
7623 ping_background.index = 0;
7624
7625 ping_trans_color.red=0;
7626 ping_trans_color.green=0;
7627 ping_trans_color.blue=0;
7628 ping_trans_color.gray=0;
7629
glennrpdfd70802010-11-14 01:23:35 +00007630 ping_pHYs_unit_type = 0;
7631 ping_pHYs_x_resolution = 0;
7632 ping_pHYs_y_resolution = 0;
7633
glennrpda8f3a72011-02-27 23:54:12 +00007634 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007635 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007636 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007637 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007638 ping_have_bKGD=MagickFalse;
7639 ping_have_pHYs=MagickFalse;
7640 ping_have_tRNS=MagickFalse;
7641
glennrp0e8ea192010-12-24 18:00:33 +00007642 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7643 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007644 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007645 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007646 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007647 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7648 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7649 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7650 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7651 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7652 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007653 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007654 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7655 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7656 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7657
glennrp8d3d6e52011-04-19 04:39:51 +00007658 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007659 ping_need_colortype_warning = MagickFalse;
7660
cristy0d57eec2011-09-04 22:13:56 +00007661 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7662 * i.e., eliminate the ICC profile and set image->rendering_intent.
7663 * Note that this will not involve any changes to the actual pixels
7664 * but merely passes information to applications that read the resulting
7665 * PNG image.
7666 */
7667 if (ping_exclude_sRGB == MagickFalse)
7668 {
7669 char
7670 *name;
7671
7672 const StringInfo
7673 *profile;
7674
7675 ResetImageProfileIterator(image);
7676 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7677 {
7678 profile=GetImageProfile(image,name);
7679
7680 if (profile != (StringInfo *) NULL)
7681 {
7682 if ((LocaleCompare(name,"ICC") == 0) ||
glennrp29a106e2011-09-06 17:11:42 +00007683 (LocaleCompare(name,"ICM") == 0))
7684 {
glennrpee7b4c02011-10-04 01:21:09 +00007685 int
7686 icheck;
7687
7688 /* 0: not a known sRGB profile
7689 * 1: HP-Microsoft sRGB v2
7690 * 2: ICC sRGB v4 perceptual
7691 * 3: ICC sRGB v2 perceptual no black-compensation
7692 */
7693 png_uint_32
7694 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7695 check_len[4] = {0, 3144, 60960, 3052};
7696
7697 png_uint_32
7698 length,
7699 profile_crc;
7700
cristy0d57eec2011-09-04 22:13:56 +00007701 unsigned char
7702 *data;
7703
glennrp29a106e2011-09-06 17:11:42 +00007704 length=(png_uint_32) GetStringInfoLength(profile);
7705
glennrpee7b4c02011-10-04 01:21:09 +00007706 for (icheck=3; icheck > 0; icheck--)
cristy0d57eec2011-09-04 22:13:56 +00007707 {
glennrpee7b4c02011-10-04 01:21:09 +00007708 if (length == check_len[icheck])
glennrp29a106e2011-09-06 17:11:42 +00007709 {
glennrpee7b4c02011-10-04 01:21:09 +00007710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7711 " Got a %lu-byte ICC profile (potentially sRGB)",
7712 (unsigned long) length);
glennrp29a106e2011-09-06 17:11:42 +00007713
glennrpee7b4c02011-10-04 01:21:09 +00007714 data=GetStringInfoDatum(profile);
7715 profile_crc=crc32(0,data,length);
glennrp29a106e2011-09-06 17:11:42 +00007716
glennrpee7b4c02011-10-04 01:21:09 +00007717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe54cd4b2011-10-04 01:31:35 +00007718 " with crc=%8x",(unsigned int) profile_crc);
glennrpee7b4c02011-10-04 01:21:09 +00007719
7720 if (profile_crc == check_crc[icheck])
7721 {
7722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7723 " It is sRGB.");
7724 if (image->rendering_intent==UndefinedIntent)
7725 image->rendering_intent=PerceptualIntent;
7726 break;
7727 }
glennrp29a106e2011-09-06 17:11:42 +00007728 }
glennrp29a106e2011-09-06 17:11:42 +00007729 }
glennrpee7b4c02011-10-04 01:21:09 +00007730 if (icheck == 0)
glennrp29a106e2011-09-06 17:11:42 +00007731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpee7b4c02011-10-04 01:21:09 +00007732 " Got a %lu-byte ICC profile",
glennrp29a106e2011-09-06 17:11:42 +00007733 (unsigned long) length);
7734 }
cristy0d57eec2011-09-04 22:13:56 +00007735 }
7736 name=GetNextImageProfile(image);
7737 }
7738 }
7739
glennrp8bb3a022010-12-13 20:40:04 +00007740 number_opaque = 0;
7741 number_semitransparent = 0;
7742 number_transparent = 0;
7743
glennrpfd05d622011-02-25 04:10:33 +00007744 if (logging != MagickFalse)
7745 {
7746 if (image->storage_class == UndefinedClass)
7747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7748 " storage_class=UndefinedClass");
7749 if (image->storage_class == DirectClass)
7750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7751 " storage_class=DirectClass");
7752 if (image->storage_class == PseudoClass)
7753 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7754 " storage_class=PseudoClass");
7755 }
glennrp28af3712011-04-06 18:07:30 +00007756
glennrp7e65e932011-08-19 02:31:16 +00007757 if (image->storage_class == PseudoClass &&
7758 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7759 (mng_info->write_png_colortype != 0 &&
7760 mng_info->write_png_colortype != 4)))
7761 {
cristyea1a8aa2011-10-20 13:24:06 +00007762 (void) SyncImage(image,exception);
glennrp7e65e932011-08-19 02:31:16 +00007763 image->storage_class = DirectClass;
7764 }
7765
glennrpc6c391a2011-04-27 02:23:56 +00007766 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007767 {
glennrpc6c391a2011-04-27 02:23:56 +00007768 if (image->storage_class != PseudoClass && image->colormap != NULL)
7769 {
7770 /* Free the bogus colormap; it can cause trouble later */
7771 if (logging != MagickFalse)
7772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7773 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00007774 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00007775 image->colormap=NULL;
7776 }
glennrp28af3712011-04-06 18:07:30 +00007777 }
glennrpbb4f99d2011-05-22 11:13:17 +00007778
cristy510d06a2011-07-06 23:43:54 +00007779 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristye941a752011-10-15 01:52:48 +00007780 (void) TransformImageColorspace(image,RGBColorspace,exception);
glennrp0fe50b42010-11-16 03:52:51 +00007781
glennrp3241bd02010-12-12 04:36:28 +00007782 /*
7783 Sometimes we get PseudoClass images whose RGB values don't match
7784 the colors in the colormap. This code syncs the RGB values.
7785 */
7786 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
cristyea1a8aa2011-10-20 13:24:06 +00007787 (void) SyncImage(image,exception);
glennrp3241bd02010-12-12 04:36:28 +00007788
glennrpa6a06632011-01-19 15:15:34 +00007789#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7790 if (image->depth > 8)
7791 {
7792 if (logging != MagickFalse)
7793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7794 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7795
7796 image->depth=8;
7797 }
7798#endif
7799
glennrp8e58efd2011-05-20 12:16:29 +00007800 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007801 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7802 {
cristy4c08aed2011-07-01 19:47:50 +00007803 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007804 *r;
7805
glennrp8e58efd2011-05-20 12:16:29 +00007806 if (image->depth > 8)
7807 {
7808#if MAGICKCORE_QUANTUM_DEPTH > 16
7809 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007810 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007811
7812 for (y=0; y < (ssize_t) image->rows; y++)
7813 {
cristy8a20fa02011-12-27 15:54:31 +00007814 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007815
cristy4c08aed2011-07-01 19:47:50 +00007816 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007817 break;
7818
7819 for (x=0; x < (ssize_t) image->columns; x++)
7820 {
glennrp54cf7972011-08-06 14:28:09 +00007821 LBR16PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007822 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007823 }
glennrpbb4f99d2011-05-22 11:13:17 +00007824
glennrp8e58efd2011-05-20 12:16:29 +00007825 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7826 break;
7827 }
7828
7829 if (image->storage_class == PseudoClass && image->colormap != NULL)
7830 {
cristy3e08f112011-05-24 13:19:30 +00007831 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007832 {
glennrp91d99252011-06-25 14:30:13 +00007833 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007834 }
7835 }
7836#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7837 }
7838
7839 else if (image->depth > 4)
7840 {
7841#if MAGICKCORE_QUANTUM_DEPTH > 8
7842 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007843 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007844
7845 for (y=0; y < (ssize_t) image->rows; y++)
7846 {
cristyc82a27b2011-10-21 01:07:16 +00007847 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007848
cristy4c08aed2011-07-01 19:47:50 +00007849 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007850 break;
7851
7852 for (x=0; x < (ssize_t) image->columns; x++)
7853 {
glennrp54cf7972011-08-06 14:28:09 +00007854 LBR08PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007855 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007856 }
glennrpbb4f99d2011-05-22 11:13:17 +00007857
glennrp8e58efd2011-05-20 12:16:29 +00007858 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7859 break;
7860 }
7861
7862 if (image->storage_class == PseudoClass && image->colormap != NULL)
7863 {
cristy3e08f112011-05-24 13:19:30 +00007864 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007865 {
glennrp91d99252011-06-25 14:30:13 +00007866 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007867 }
7868 }
7869#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7870 }
7871 else
7872 if (image->depth > 2)
7873 {
7874 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007875 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007876
7877 for (y=0; y < (ssize_t) image->rows; y++)
7878 {
cristy8a20fa02011-12-27 15:54:31 +00007879 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007880
cristy4c08aed2011-07-01 19:47:50 +00007881 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007882 break;
7883
7884 for (x=0; x < (ssize_t) image->columns; x++)
7885 {
glennrp54cf7972011-08-06 14:28:09 +00007886 LBR04PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007887 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007888 }
glennrpbb4f99d2011-05-22 11:13:17 +00007889
glennrp8e58efd2011-05-20 12:16:29 +00007890 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7891 break;
7892 }
7893
7894 if (image->storage_class == PseudoClass && image->colormap != NULL)
7895 {
cristy3e08f112011-05-24 13:19:30 +00007896 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007897 {
glennrp91d99252011-06-25 14:30:13 +00007898 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007899 }
7900 }
7901 }
7902
7903 else if (image->depth > 1)
7904 {
7905 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007906 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007907
7908 for (y=0; y < (ssize_t) image->rows; y++)
7909 {
cristy8a20fa02011-12-27 15:54:31 +00007910 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007911
cristy4c08aed2011-07-01 19:47:50 +00007912 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007913 break;
7914
7915 for (x=0; x < (ssize_t) image->columns; x++)
7916 {
glennrp54cf7972011-08-06 14:28:09 +00007917 LBR02PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007918 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007919 }
glennrpbb4f99d2011-05-22 11:13:17 +00007920
glennrp8e58efd2011-05-20 12:16:29 +00007921 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7922 break;
7923 }
7924
7925 if (image->storage_class == PseudoClass && image->colormap != NULL)
7926 {
cristy3e08f112011-05-24 13:19:30 +00007927 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007928 {
glennrp91d99252011-06-25 14:30:13 +00007929 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007930 }
7931 }
7932 }
7933 else
7934 {
7935 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007936 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007937
7938 for (y=0; y < (ssize_t) image->rows; y++)
7939 {
cristy8a20fa02011-12-27 15:54:31 +00007940 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007941
cristy4c08aed2011-07-01 19:47:50 +00007942 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007943 break;
7944
7945 for (x=0; x < (ssize_t) image->columns; x++)
7946 {
glennrp54cf7972011-08-06 14:28:09 +00007947 LBR01PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007948 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007949 }
glennrpbb4f99d2011-05-22 11:13:17 +00007950
glennrp8e58efd2011-05-20 12:16:29 +00007951 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7952 break;
7953 }
7954
7955 if (image->storage_class == PseudoClass && image->colormap != NULL)
7956 {
cristy3e08f112011-05-24 13:19:30 +00007957 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007958 {
glennrp91d99252011-06-25 14:30:13 +00007959 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007960 }
7961 }
7962 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007963 }
7964
glennrp67b9c1a2011-04-22 18:47:36 +00007965 /* To do: set to next higher multiple of 8 */
7966 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007967 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007968
glennrp2b013e42010-11-24 16:55:50 +00007969#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7970 /* PNG does not handle depths greater than 16 so reduce it even
7971 * if lossy
7972 */
glennrp8e58efd2011-05-20 12:16:29 +00007973 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007974 image->depth=16;
7975#endif
7976
glennrp3faa9a32011-04-23 14:00:25 +00007977#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00007978 if (image->depth > 8)
7979 {
7980 /* To do: fill low byte properly */
7981 image->depth=16;
7982 }
7983
glennrpc722dd82011-02-24 05:13:21 +00007984 if (image->depth == 16 && mng_info->write_png_depth != 16)
cristyc82a27b2011-10-21 01:07:16 +00007985 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007986 image->depth = 8;
7987#endif
7988
glennrpc8c2f062011-02-25 19:00:33 +00007989 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007990 * we reduce the transparency to binary and run again, then if there
7991 * 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 +00007992 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7993 * palette. Then (To do) we take care of a final reduction that is only
7994 * needed if there are still 256 colors present and one of them has both
7995 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007996 */
glennrp82b3c532011-03-22 19:20:54 +00007997
glennrp8ca51ad2011-05-12 21:22:32 +00007998 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007999 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00008000 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00008001
glennrp8ca51ad2011-05-12 21:22:32 +00008002 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00008003 {
8004 /* BUILD_PALETTE
8005 *
8006 * Sometimes we get DirectClass images that have 256 colors or fewer.
8007 * This code will build a colormap.
8008 *
8009 * Also, sometimes we get PseudoClass images with an out-of-date
8010 * colormap. This code will replace the colormap with a new one.
8011 * Sometimes we get PseudoClass images that have more than 256 colors.
8012 * This code will delete the colormap and change the image to
8013 * DirectClass.
8014 *
cristy4c08aed2011-07-01 19:47:50 +00008015 * If image->matte is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00008016 * even though it sometimes contains left-over non-opaque values.
8017 *
8018 * Also we gather some information (number of opaque, transparent,
8019 * and semitransparent pixels, and whether the image has any non-gray
8020 * pixels or only black-and-white pixels) that we might need later.
8021 *
8022 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8023 * we need to check for bogus non-opaque values, at least.
8024 */
glennrp3c218112010-11-27 15:31:26 +00008025
glennrpd71e86a2011-02-24 01:28:37 +00008026 int
8027 n;
glennrp3c218112010-11-27 15:31:26 +00008028
cristy101ab702011-10-13 13:06:32 +00008029 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008030 opaque[260],
8031 semitransparent[260],
8032 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00008033
cristy4c08aed2011-07-01 19:47:50 +00008034 register const Quantum
8035 *s;
glennrp8bb3a022010-12-13 20:40:04 +00008036
cristy4c08aed2011-07-01 19:47:50 +00008037 register Quantum
8038 *q,
glennrpfd05d622011-02-25 04:10:33 +00008039 *r;
8040
glennrpd71e86a2011-02-24 01:28:37 +00008041 if (logging != MagickFalse)
8042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8043 " Enter BUILD_PALETTE:");
8044
8045 if (logging != MagickFalse)
8046 {
glennrp03812ae2010-12-24 01:31:34 +00008047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008048 " image->columns=%.20g",(double) image->columns);
8049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8050 " image->rows=%.20g",(double) image->rows);
8051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8052 " image->matte=%.20g",(double) image->matte);
8053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8054 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008055
glennrpfd05d622011-02-25 04:10:33 +00008056 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008057 {
8058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008059 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008061 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008062
glennrpd71e86a2011-02-24 01:28:37 +00008063 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008064 {
glennrpd71e86a2011-02-24 01:28:37 +00008065 (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);
glennrp7ddcc222010-12-11 05:01:05 +00008072 }
glennrp2cc891a2010-12-24 13:44:32 +00008073
glennrpd71e86a2011-02-24 01:28:37 +00008074 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8075 {
8076 if (i > 255)
8077 {
8078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8079 " %d (%d,%d,%d,%d)",
8080 (int) i,
8081 (int) image->colormap[i].red,
8082 (int) image->colormap[i].green,
8083 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008084 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008085 }
8086 }
glennrp03812ae2010-12-24 01:31:34 +00008087 }
glennrp7ddcc222010-12-11 05:01:05 +00008088
glennrpd71e86a2011-02-24 01:28:37 +00008089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8090 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008091
glennrpd71e86a2011-02-24 01:28:37 +00008092 if (image->colors == 0)
cristyc458f912011-12-27 20:26:40 +00008093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8094 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008095
glennrp8d3d6e52011-04-19 04:39:51 +00008096 if (ping_preserve_colormap == MagickFalse)
8097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8098 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008099 }
8100
glennrpd71e86a2011-02-24 01:28:37 +00008101 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008102 number_opaque = 0;
8103 number_semitransparent = 0;
8104 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008105
8106 for (y=0; y < (ssize_t) image->rows; y++)
8107 {
8108 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8109
cristyacd2ed22011-08-30 01:44:23 +00008110 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008111 break;
8112
8113 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008114 {
glennrp4737d522011-04-29 03:33:42 +00008115 if (image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008116 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008117 {
8118 if (number_opaque < 259)
8119 {
8120 if (number_opaque == 0)
8121 {
cristy101ab702011-10-13 13:06:32 +00008122 GetPixelInfoPixel(image, q, opaque);
cristy4c08aed2011-07-01 19:47:50 +00008123 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008124 number_opaque=1;
8125 }
glennrp2cc891a2010-12-24 13:44:32 +00008126
glennrpd71e86a2011-02-24 01:28:37 +00008127 for (i=0; i< (ssize_t) number_opaque; i++)
8128 {
cristy4c08aed2011-07-01 19:47:50 +00008129 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008130 break;
8131 }
glennrp7ddcc222010-12-11 05:01:05 +00008132
cristyc458f912011-12-27 20:26:40 +00008133 if (i == (ssize_t) number_opaque && number_opaque < 259)
glennrpd71e86a2011-02-24 01:28:37 +00008134 {
8135 number_opaque++;
cristy101ab702011-10-13 13:06:32 +00008136 GetPixelInfoPixel(image, q, opaque+i);
cristy4c08aed2011-07-01 19:47:50 +00008137 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008138 }
8139 }
8140 }
cristy4c08aed2011-07-01 19:47:50 +00008141 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008142 {
8143 if (number_transparent < 259)
8144 {
8145 if (number_transparent == 0)
8146 {
cristy101ab702011-10-13 13:06:32 +00008147 GetPixelInfoPixel(image, q, transparent);
cristy4c08aed2011-07-01 19:47:50 +00008148 ping_trans_color.red=(unsigned short)
8149 GetPixelRed(image,q);
8150 ping_trans_color.green=(unsigned short)
8151 GetPixelGreen(image,q);
8152 ping_trans_color.blue=(unsigned short)
8153 GetPixelBlue(image,q);
8154 ping_trans_color.gray=(unsigned short)
8155 GetPixelRed(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008156 number_transparent = 1;
8157 }
8158
8159 for (i=0; i< (ssize_t) number_transparent; i++)
8160 {
cristy4c08aed2011-07-01 19:47:50 +00008161 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008162 break;
8163 }
8164
8165 if (i == (ssize_t) number_transparent &&
8166 number_transparent < 259)
8167 {
8168 number_transparent++;
cristy101ab702011-10-13 13:06:32 +00008169 GetPixelInfoPixel(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008170 }
8171 }
8172 }
8173 else
8174 {
8175 if (number_semitransparent < 259)
8176 {
8177 if (number_semitransparent == 0)
8178 {
cristy101ab702011-10-13 13:06:32 +00008179 GetPixelInfoPixel(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008180 number_semitransparent = 1;
8181 }
8182
8183 for (i=0; i< (ssize_t) number_semitransparent; i++)
8184 {
cristy4c08aed2011-07-01 19:47:50 +00008185 if (IsPixelEquivalent(image,q, semitransparent+i)
8186 && GetPixelAlpha(image,q) ==
8187 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008188 break;
8189 }
8190
8191 if (i == (ssize_t) number_semitransparent &&
8192 number_semitransparent < 259)
8193 {
8194 number_semitransparent++;
cristy101ab702011-10-13 13:06:32 +00008195 GetPixelInfoPixel(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008196 }
8197 }
8198 }
cristyed231572011-07-14 02:18:59 +00008199 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008200 }
8201 }
8202
cristy4054bfb2011-08-29 23:41:39 +00008203 if (mng_info->write_png8 == MagickFalse &&
8204 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008205 {
8206 /* Add the background color to the palette, if it
8207 * isn't already there.
8208 */
glennrpc6c391a2011-04-27 02:23:56 +00008209 if (logging != MagickFalse)
8210 {
8211 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8212 " Check colormap for background (%d,%d,%d)",
8213 (int) image->background_color.red,
8214 (int) image->background_color.green,
8215 (int) image->background_color.blue);
8216 }
glennrpd71e86a2011-02-24 01:28:37 +00008217 for (i=0; i<number_opaque; i++)
8218 {
glennrpca7ad3a2011-04-26 04:44:54 +00008219 if (opaque[i].red == image->background_color.red &&
8220 opaque[i].green == image->background_color.green &&
8221 opaque[i].blue == image->background_color.blue)
8222 break;
glennrpd71e86a2011-02-24 01:28:37 +00008223 }
glennrpd71e86a2011-02-24 01:28:37 +00008224 if (number_opaque < 259 && i == number_opaque)
8225 {
glennrp8e045c82011-04-27 16:40:27 +00008226 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008227 ping_background.index = i;
8228 if (logging != MagickFalse)
8229 {
8230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8231 " background_color index is %d",(int) i);
8232 }
8233
glennrpd71e86a2011-02-24 01:28:37 +00008234 }
glennrpa080bc32011-03-11 18:03:44 +00008235 else if (logging != MagickFalse)
8236 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8237 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008238 }
8239
8240 image_colors=number_opaque+number_transparent+number_semitransparent;
8241
glennrpa080bc32011-03-11 18:03:44 +00008242 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8243 {
8244 /* No room for the background color; remove it. */
8245 number_opaque--;
8246 image_colors--;
8247 }
8248
glennrpd71e86a2011-02-24 01:28:37 +00008249 if (logging != MagickFalse)
8250 {
8251 if (image_colors > 256)
8252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8253 " image has more than 256 colors");
8254
8255 else
8256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8257 " image has %d colors",image_colors);
8258 }
8259
glennrp8d3d6e52011-04-19 04:39:51 +00008260 if (ping_preserve_colormap != MagickFalse)
8261 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008262
glennrpfd05d622011-02-25 04:10:33 +00008263 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008264 {
8265 ping_have_color=MagickFalse;
8266 ping_have_non_bw=MagickFalse;
8267
8268 if(image_colors > 256)
8269 {
8270 for (y=0; y < (ssize_t) image->rows; y++)
8271 {
8272 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8273
cristyacd2ed22011-08-30 01:44:23 +00008274 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008275 break;
8276
glennrpe5e6b802011-07-20 14:44:40 +00008277 s=q;
8278 for (x=0; x < (ssize_t) image->columns; x++)
8279 {
8280 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8281 GetPixelRed(image,s) != GetPixelBlue(image,s))
8282 {
8283 ping_have_color=MagickTrue;
8284 ping_have_non_bw=MagickTrue;
8285 break;
8286 }
8287 s+=GetPixelChannels(image);
8288 }
8289
8290 if (ping_have_color != MagickFalse)
8291 break;
8292
glennrpd71e86a2011-02-24 01:28:37 +00008293 /* Worst case is black-and-white; we are looking at every
8294 * pixel twice.
8295 */
8296
glennrpd71e86a2011-02-24 01:28:37 +00008297 if (ping_have_non_bw == MagickFalse)
8298 {
8299 s=q;
8300 for (x=0; x < (ssize_t) image->columns; x++)
8301 {
cristy4c08aed2011-07-01 19:47:50 +00008302 if (GetPixelRed(image,s) != 0 &&
8303 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008304 {
8305 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008306 break;
glennrpd71e86a2011-02-24 01:28:37 +00008307 }
cristyed231572011-07-14 02:18:59 +00008308 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008309 }
glennrpe5e6b802011-07-20 14:44:40 +00008310 }
glennrpd71e86a2011-02-24 01:28:37 +00008311 }
glennrpbb4f99d2011-05-22 11:13:17 +00008312 }
8313 }
glennrpd71e86a2011-02-24 01:28:37 +00008314
8315 if (image_colors < 257)
8316 {
cristy101ab702011-10-13 13:06:32 +00008317 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008318 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008319
glennrpd71e86a2011-02-24 01:28:37 +00008320 /*
8321 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008322 */
8323
glennrpd71e86a2011-02-24 01:28:37 +00008324 if (logging != MagickFalse)
8325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8326 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008327
glennrpd71e86a2011-02-24 01:28:37 +00008328 /* Sort palette, transparent first */;
8329
8330 n = 0;
8331
8332 for (i=0; i<number_transparent; i++)
8333 colormap[n++] = transparent[i];
8334
8335 for (i=0; i<number_semitransparent; i++)
8336 colormap[n++] = semitransparent[i];
8337
8338 for (i=0; i<number_opaque; i++)
8339 colormap[n++] = opaque[i];
8340
glennrpc6c391a2011-04-27 02:23:56 +00008341 ping_background.index +=
8342 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008343
glennrpd71e86a2011-02-24 01:28:37 +00008344 /* image_colors < 257; search the colormap instead of the pixels
8345 * to get ping_have_color and ping_have_non_bw
8346 */
8347 for (i=0; i<n; i++)
8348 {
8349 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008350 {
glennrpd71e86a2011-02-24 01:28:37 +00008351 if (colormap[i].red != colormap[i].green ||
8352 colormap[i].red != colormap[i].blue)
8353 {
8354 ping_have_color=MagickTrue;
8355 ping_have_non_bw=MagickTrue;
8356 break;
8357 }
8358 }
8359
8360 if (ping_have_non_bw == MagickFalse)
8361 {
8362 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008363 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008364 }
glennrp8bb3a022010-12-13 20:40:04 +00008365 }
8366
glennrpd71e86a2011-02-24 01:28:37 +00008367 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8368 (number_transparent == 0 && number_semitransparent == 0)) &&
8369 (((mng_info->write_png_colortype-1) ==
8370 PNG_COLOR_TYPE_PALETTE) ||
8371 (mng_info->write_png_colortype == 0)))
8372 {
glennrp6185c532011-01-14 17:58:40 +00008373 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008374 {
glennrpd71e86a2011-02-24 01:28:37 +00008375 if (n != (ssize_t) image_colors)
8376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8377 " image_colors (%d) and n (%d) don't match",
8378 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008379
glennrpd71e86a2011-02-24 01:28:37 +00008380 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8381 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008382 }
glennrp03812ae2010-12-24 01:31:34 +00008383
glennrpd71e86a2011-02-24 01:28:37 +00008384 image->colors = image_colors;
8385
cristy018f07f2011-09-04 21:15:19 +00008386 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008387 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008388 ThrowWriterException(ResourceLimitError,
8389 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008390
8391 for (i=0; i< (ssize_t) image_colors; i++)
8392 image->colormap[i] = colormap[i];
8393
8394 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008395 {
glennrpd71e86a2011-02-24 01:28:37 +00008396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8397 " image->colors=%d (%d)",
8398 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008399
glennrpd71e86a2011-02-24 01:28:37 +00008400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8401 " Update the pixel indexes");
8402 }
glennrp6185c532011-01-14 17:58:40 +00008403
glennrpfd05d622011-02-25 04:10:33 +00008404 /* Sync the pixel indices with the new colormap */
8405
glennrpd71e86a2011-02-24 01:28:37 +00008406 for (y=0; y < (ssize_t) image->rows; y++)
8407 {
cristy97707062011-12-27 18:25:00 +00008408 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp6185c532011-01-14 17:58:40 +00008409
cristyacd2ed22011-08-30 01:44:23 +00008410 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008411 break;
glennrp6185c532011-01-14 17:58:40 +00008412
glennrpd71e86a2011-02-24 01:28:37 +00008413 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008414 {
glennrpd71e86a2011-02-24 01:28:37 +00008415 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008416 {
glennrpd71e86a2011-02-24 01:28:37 +00008417 if ((image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008418 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8419 image->colormap[i].red == GetPixelRed(image,q) &&
8420 image->colormap[i].green == GetPixelGreen(image,q) &&
8421 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008422 {
cristy4c08aed2011-07-01 19:47:50 +00008423 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008424 break;
glennrp6185c532011-01-14 17:58:40 +00008425 }
glennrp6185c532011-01-14 17:58:40 +00008426 }
cristyed231572011-07-14 02:18:59 +00008427 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008428 }
glennrp6185c532011-01-14 17:58:40 +00008429
glennrpd71e86a2011-02-24 01:28:37 +00008430 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8431 break;
8432 }
8433 }
8434 }
8435
8436 if (logging != MagickFalse)
8437 {
8438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8439 " image->colors=%d", (int) image->colors);
8440
8441 if (image->colormap != NULL)
8442 {
8443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008444 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008445
8446 for (i=0; i < (ssize_t) image->colors; i++)
8447 {
cristy72988482011-03-29 16:34:38 +00008448 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008449 {
8450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8451 " %d (%d,%d,%d,%d)",
8452 (int) i,
8453 (int) image->colormap[i].red,
8454 (int) image->colormap[i].green,
8455 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008456 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008457 }
glennrp6185c532011-01-14 17:58:40 +00008458 }
8459 }
glennrp03812ae2010-12-24 01:31:34 +00008460
glennrpd71e86a2011-02-24 01:28:37 +00008461 if (number_transparent < 257)
8462 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8463 " number_transparent = %d",
8464 number_transparent);
8465 else
glennrp03812ae2010-12-24 01:31:34 +00008466
glennrpd71e86a2011-02-24 01:28:37 +00008467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8468 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008469
glennrpd71e86a2011-02-24 01:28:37 +00008470 if (number_opaque < 257)
8471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8472 " number_opaque = %d",
8473 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008474
glennrpd71e86a2011-02-24 01:28:37 +00008475 else
8476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8477 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008478
glennrpd71e86a2011-02-24 01:28:37 +00008479 if (number_semitransparent < 257)
8480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8481 " number_semitransparent = %d",
8482 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008483
glennrpd71e86a2011-02-24 01:28:37 +00008484 else
8485 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8486 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008487
glennrpd71e86a2011-02-24 01:28:37 +00008488 if (ping_have_non_bw == MagickFalse)
8489 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8490 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008491
glennrpd71e86a2011-02-24 01:28:37 +00008492 else if (ping_have_color == MagickFalse)
8493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8494 " All pixels and the background are gray");
8495
8496 else
8497 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8498 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008499
glennrp03812ae2010-12-24 01:31:34 +00008500 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8501 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008502 }
glennrpfd05d622011-02-25 04:10:33 +00008503
glennrpc8c2f062011-02-25 19:00:33 +00008504 if (mng_info->write_png8 == MagickFalse)
8505 break;
glennrpfd05d622011-02-25 04:10:33 +00008506
glennrpc8c2f062011-02-25 19:00:33 +00008507 /* Make any reductions necessary for the PNG8 format */
8508 if (image_colors <= 256 &&
8509 image_colors != 0 && image->colormap != NULL &&
8510 number_semitransparent == 0 &&
8511 number_transparent <= 1)
8512 break;
8513
8514 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008515 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8516 * transparent color so if more than one is transparent we merge
8517 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008518 */
glennrp130fc452011-08-20 03:43:18 +00008519 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008520 {
8521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8522 " Thresholding the alpha channel to binary");
8523
8524 for (y=0; y < (ssize_t) image->rows; y++)
8525 {
cristy8a20fa02011-12-27 15:54:31 +00008526 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008527
cristy4c08aed2011-07-01 19:47:50 +00008528 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008529 break;
8530
8531 for (x=0; x < (ssize_t) image->columns; x++)
8532 {
glennrpf73547f2011-08-20 04:40:26 +00008533 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008534 {
cristy803640d2011-11-17 02:11:32 +00008535 SetPixelInfoPixel(image,&image->background_color,r);
cristy4c08aed2011-07-01 19:47:50 +00008536 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008537 }
8538 else
cristy4c08aed2011-07-01 19:47:50 +00008539 SetPixelAlpha(image,OpaqueAlpha,r);
cristyed231572011-07-14 02:18:59 +00008540 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008541 }
glennrpbb4f99d2011-05-22 11:13:17 +00008542
glennrpc8c2f062011-02-25 19:00:33 +00008543 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8544 break;
8545
8546 if (image_colors != 0 && image_colors <= 256 &&
8547 image->colormap != NULL)
8548 for (i=0; i<image_colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00008549 image->colormap[i].alpha =
8550 (image->colormap[i].alpha > TransparentAlpha/2 ?
8551 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008552 }
8553 continue;
8554 }
8555
8556 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008557 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8558 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8559 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008560 */
glennrpd3371642011-03-22 19:42:23 +00008561 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8562 {
8563 if (logging != MagickFalse)
8564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8565 " Quantizing the background color to 4-4-4");
8566
8567 tried_444 = MagickTrue;
8568
glennrp91d99252011-06-25 14:30:13 +00008569 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008570
8571 if (logging != MagickFalse)
8572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8573 " Quantizing the pixel colors to 4-4-4");
8574
8575 if (image->colormap == NULL)
8576 {
8577 for (y=0; y < (ssize_t) image->rows; y++)
8578 {
cristy8a20fa02011-12-27 15:54:31 +00008579 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpd3371642011-03-22 19:42:23 +00008580
cristy4c08aed2011-07-01 19:47:50 +00008581 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008582 break;
8583
8584 for (x=0; x < (ssize_t) image->columns; x++)
8585 {
cristy4c08aed2011-07-01 19:47:50 +00008586 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008587 LBR04PixelRGB(r);
cristy8a20fa02011-12-27 15:54:31 +00008588 r+=GetPixelChannels(image);
glennrpd3371642011-03-22 19:42:23 +00008589 }
glennrpbb4f99d2011-05-22 11:13:17 +00008590
glennrpd3371642011-03-22 19:42:23 +00008591 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8592 break;
8593 }
8594 }
8595
8596 else /* Should not reach this; colormap already exists and
8597 must be <= 256 */
8598 {
8599 if (logging != MagickFalse)
8600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8601 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008602
glennrpd3371642011-03-22 19:42:23 +00008603 for (i=0; i<image_colors; i++)
8604 {
glennrp91d99252011-06-25 14:30:13 +00008605 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008606 }
8607 }
8608 continue;
8609 }
8610
glennrp82b3c532011-03-22 19:20:54 +00008611 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8612 {
8613 if (logging != MagickFalse)
8614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8615 " Quantizing the background color to 3-3-3");
8616
8617 tried_333 = MagickTrue;
8618
glennrp91d99252011-06-25 14:30:13 +00008619 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008620
8621 if (logging != MagickFalse)
8622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008623 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008624
8625 if (image->colormap == NULL)
8626 {
8627 for (y=0; y < (ssize_t) image->rows; y++)
8628 {
cristy8a20fa02011-12-27 15:54:31 +00008629 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp82b3c532011-03-22 19:20:54 +00008630
cristy4c08aed2011-07-01 19:47:50 +00008631 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008632 break;
8633
8634 for (x=0; x < (ssize_t) image->columns; x++)
8635 {
cristy4c08aed2011-07-01 19:47:50 +00008636 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8637 LBR03RGB(r);
cristy8a20fa02011-12-27 15:54:31 +00008638 r+=GetPixelChannels(image);
glennrp82b3c532011-03-22 19:20:54 +00008639 }
glennrpbb4f99d2011-05-22 11:13:17 +00008640
glennrp82b3c532011-03-22 19:20:54 +00008641 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8642 break;
8643 }
8644 }
8645
8646 else /* Should not reach this; colormap already exists and
8647 must be <= 256 */
8648 {
8649 if (logging != MagickFalse)
8650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008651 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008652 for (i=0; i<image_colors; i++)
8653 {
glennrp91d99252011-06-25 14:30:13 +00008654 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008655 }
glennrpd3371642011-03-22 19:42:23 +00008656 }
8657 continue;
glennrp82b3c532011-03-22 19:20:54 +00008658 }
glennrpc8c2f062011-02-25 19:00:33 +00008659
glennrp8ca51ad2011-05-12 21:22:32 +00008660 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008661 {
8662 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008664 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008665
glennrp8ca51ad2011-05-12 21:22:32 +00008666 tried_332 = MagickTrue;
8667
glennrp3faa9a32011-04-23 14:00:25 +00008668 /* Red and green were already done so we only quantize the blue
8669 * channel
8670 */
8671
glennrp91d99252011-06-25 14:30:13 +00008672 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008673
glennrpc8c2f062011-02-25 19:00:33 +00008674 if (logging != MagickFalse)
8675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008676 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008677
glennrpc8c2f062011-02-25 19:00:33 +00008678 if (image->colormap == NULL)
8679 {
8680 for (y=0; y < (ssize_t) image->rows; y++)
8681 {
cristy8a20fa02011-12-27 15:54:31 +00008682 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008683
cristy4c08aed2011-07-01 19:47:50 +00008684 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008685 break;
8686
8687 for (x=0; x < (ssize_t) image->columns; x++)
8688 {
cristy4c08aed2011-07-01 19:47:50 +00008689 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008690 LBR02PixelBlue(r);
cristy8a20fa02011-12-27 15:54:31 +00008691 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008692 }
glennrpbb4f99d2011-05-22 11:13:17 +00008693
glennrpc8c2f062011-02-25 19:00:33 +00008694 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8695 break;
8696 }
8697 }
glennrpfd05d622011-02-25 04:10:33 +00008698
glennrpc8c2f062011-02-25 19:00:33 +00008699 else /* Should not reach this; colormap already exists and
8700 must be <= 256 */
8701 {
8702 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008704 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008705 for (i=0; i<image_colors; i++)
8706 {
glennrp91d99252011-06-25 14:30:13 +00008707 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008708 }
8709 }
8710 continue;
8711 }
8712 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008713
8714 if (image_colors == 0 || image_colors > 256)
8715 {
8716 /* Take care of special case with 256 colors + 1 transparent
8717 * color. We don't need to quantize to 2-3-2-1; we only need to
8718 * eliminate one color, so we'll merge the two darkest red
8719 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8720 */
8721 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8722 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8723 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8724 {
8725 image->background_color.red=ScaleCharToQuantum(0x24);
8726 }
glennrpbb4f99d2011-05-22 11:13:17 +00008727
glennrp8ca51ad2011-05-12 21:22:32 +00008728 if (image->colormap == NULL)
8729 {
8730 for (y=0; y < (ssize_t) image->rows; y++)
8731 {
cristy8a20fa02011-12-27 15:54:31 +00008732 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008733
cristy4c08aed2011-07-01 19:47:50 +00008734 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008735 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008736
glennrp8ca51ad2011-05-12 21:22:32 +00008737 for (x=0; x < (ssize_t) image->columns; x++)
8738 {
cristy4c08aed2011-07-01 19:47:50 +00008739 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8740 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8741 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8742 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008743 {
cristy4c08aed2011-07-01 19:47:50 +00008744 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008745 }
cristyed231572011-07-14 02:18:59 +00008746 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008747 }
glennrpbb4f99d2011-05-22 11:13:17 +00008748
glennrp8ca51ad2011-05-12 21:22:32 +00008749 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8750 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008751
glennrp8ca51ad2011-05-12 21:22:32 +00008752 }
8753 }
8754
8755 else
8756 {
8757 for (i=0; i<image_colors; i++)
8758 {
8759 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8760 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8761 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8762 {
8763 image->colormap[i].red=ScaleCharToQuantum(0x24);
8764 }
8765 }
8766 }
8767 }
glennrpd71e86a2011-02-24 01:28:37 +00008768 }
glennrpfd05d622011-02-25 04:10:33 +00008769 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008770
glennrpfd05d622011-02-25 04:10:33 +00008771 /* If we are excluding the tRNS chunk and there is transparency,
8772 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8773 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008774 */
glennrp0e8ea192010-12-24 18:00:33 +00008775 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8776 (number_transparent != 0 || number_semitransparent != 0))
8777 {
glennrpd17915c2011-04-29 14:24:22 +00008778 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008779
8780 if (ping_have_color == MagickFalse)
8781 mng_info->write_png_colortype = 5;
8782
8783 else
8784 mng_info->write_png_colortype = 7;
8785
glennrp8d579662011-02-23 02:05:02 +00008786 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008787 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008788 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008789
glennrp0e8ea192010-12-24 18:00:33 +00008790 }
8791
glennrpfd05d622011-02-25 04:10:33 +00008792 /* See if cheap transparency is possible. It is only possible
8793 * when there is a single transparent color, no semitransparent
8794 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008795 * as the transparent color. We only need this information if
8796 * we are writing a PNG with colortype 0 or 2, and we have not
8797 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008798 */
glennrp5a39f372011-02-25 04:52:16 +00008799 if (number_transparent == 1 &&
8800 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008801 {
8802 ping_have_cheap_transparency = MagickTrue;
8803
8804 if (number_semitransparent != 0)
8805 ping_have_cheap_transparency = MagickFalse;
8806
8807 else if (image_colors == 0 || image_colors > 256 ||
8808 image->colormap == NULL)
8809 {
cristy4c08aed2011-07-01 19:47:50 +00008810 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008811 *q;
8812
glennrpfd05d622011-02-25 04:10:33 +00008813 for (y=0; y < (ssize_t) image->rows; y++)
8814 {
8815 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8816
cristyacd2ed22011-08-30 01:44:23 +00008817 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008818 break;
8819
8820 for (x=0; x < (ssize_t) image->columns; x++)
8821 {
cristy4c08aed2011-07-01 19:47:50 +00008822 if (GetPixelAlpha(image,q) != TransparentAlpha &&
glennrp847370c2011-07-05 17:37:15 +00008823 (unsigned short) GetPixelRed(image,q) ==
8824 ping_trans_color.red &&
8825 (unsigned short) GetPixelGreen(image,q) ==
8826 ping_trans_color.green &&
8827 (unsigned short) GetPixelBlue(image,q) ==
8828 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008829 {
8830 ping_have_cheap_transparency = MagickFalse;
8831 break;
8832 }
8833
cristyed231572011-07-14 02:18:59 +00008834 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008835 }
glennrpbb4f99d2011-05-22 11:13:17 +00008836
glennrpfd05d622011-02-25 04:10:33 +00008837 if (ping_have_cheap_transparency == MagickFalse)
8838 break;
8839 }
8840 }
8841 else
8842 {
glennrp67b9c1a2011-04-22 18:47:36 +00008843 /* Assuming that image->colormap[0] is the one transparent color
8844 * and that all others are opaque.
8845 */
glennrpfd05d622011-02-25 04:10:33 +00008846 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008847 for (i=1; i<image_colors; i++)
8848 if (image->colormap[i].red == image->colormap[0].red &&
8849 image->colormap[i].green == image->colormap[0].green &&
8850 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008851 {
glennrp67b9c1a2011-04-22 18:47:36 +00008852 ping_have_cheap_transparency = MagickFalse;
8853 break;
glennrpfd05d622011-02-25 04:10:33 +00008854 }
8855 }
glennrpbb4f99d2011-05-22 11:13:17 +00008856
glennrpfd05d622011-02-25 04:10:33 +00008857 if (logging != MagickFalse)
8858 {
8859 if (ping_have_cheap_transparency == MagickFalse)
8860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8861 " Cheap transparency is not possible.");
8862
8863 else
8864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8865 " Cheap transparency is possible.");
8866 }
8867 }
8868 else
8869 ping_have_cheap_transparency = MagickFalse;
8870
glennrp8640fb52010-11-23 15:48:26 +00008871 image_depth=image->depth;
8872
glennrp26c990a2010-11-23 02:23:20 +00008873 quantum_info = (QuantumInfo *) NULL;
8874 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008875 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008876 image_matte=image->matte;
8877
glennrp0fe50b42010-11-16 03:52:51 +00008878 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008879 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008880
glennrp52a479c2011-02-26 21:14:38 +00008881 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8882 (image->colors == 0 || image->colormap == NULL))
8883 {
glennrp52a479c2011-02-26 21:14:38 +00008884 image_info=DestroyImageInfo(image_info);
8885 image=DestroyImage(image);
cristyc82a27b2011-10-21 01:07:16 +00008886 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
glennrp15e01552011-03-06 23:29:17 +00008887 "Cannot write PNG8 or color-type 3; colormap is NULL",
8888 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008889#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8890 UnlockSemaphoreInfo(ping_semaphore);
8891#endif
8892 return(MagickFalse);
8893 }
8894
cristy3ed852e2009-09-05 21:47:34 +00008895 /*
8896 Allocate the PNG structures
8897 */
8898#ifdef PNG_USER_MEM_SUPPORTED
cristyc82a27b2011-10-21 01:07:16 +00008899 error_info.image=image;
8900 error_info.exception=exception;
8901 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008902 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8903 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008904
cristy3ed852e2009-09-05 21:47:34 +00008905#else
cristyc82a27b2011-10-21 01:07:16 +00008906 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008907 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008908
cristy3ed852e2009-09-05 21:47:34 +00008909#endif
8910 if (ping == (png_struct *) NULL)
8911 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008912
cristy3ed852e2009-09-05 21:47:34 +00008913 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008914
cristy3ed852e2009-09-05 21:47:34 +00008915 if (ping_info == (png_info *) NULL)
8916 {
8917 png_destroy_write_struct(&ping,(png_info **) NULL);
8918 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8919 }
glennrp0fe50b42010-11-16 03:52:51 +00008920
cristy3ed852e2009-09-05 21:47:34 +00008921 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008922 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008923
glennrp5af765f2010-03-30 11:12:18 +00008924 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008925 {
8926 /*
8927 PNG write failed.
8928 */
8929#ifdef PNG_DEBUG
8930 if (image_info->verbose)
8931 (void) printf("PNG write has failed.\n");
8932#endif
8933 png_destroy_write_struct(&ping,&ping_info);
8934#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008935 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008936#endif
glennrpda8f3a72011-02-27 23:54:12 +00008937 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008938 (void) CloseBlob(image);
8939 image_info=DestroyImageInfo(image_info);
8940 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008941 return(MagickFalse);
8942 }
8943 /*
8944 Prepare PNG for writing.
8945 */
8946#if defined(PNG_MNG_FEATURES_SUPPORTED)
8947 if (mng_info->write_mng)
8948 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008949
cristy3ed852e2009-09-05 21:47:34 +00008950#else
8951# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8952 if (mng_info->write_mng)
8953 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008954
cristy3ed852e2009-09-05 21:47:34 +00008955# endif
8956#endif
glennrp2b013e42010-11-24 16:55:50 +00008957
cristy3ed852e2009-09-05 21:47:34 +00008958 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008959
cristy4e5bc842010-06-09 13:56:01 +00008960 ping_width=(png_uint_32) image->columns;
8961 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008962
cristy3ed852e2009-09-05 21:47:34 +00008963 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8964 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008965
cristy3ed852e2009-09-05 21:47:34 +00008966 if (mng_info->write_png_depth != 0)
8967 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008968
cristy3ed852e2009-09-05 21:47:34 +00008969 /* Adjust requested depth to next higher valid depth if necessary */
8970 if (image_depth > 8)
8971 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008972
cristy3ed852e2009-09-05 21:47:34 +00008973 if ((image_depth > 4) && (image_depth < 8))
8974 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008975
cristy3ed852e2009-09-05 21:47:34 +00008976 if (image_depth == 3)
8977 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008978
cristy3ed852e2009-09-05 21:47:34 +00008979 if (logging != MagickFalse)
8980 {
8981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008982 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008984 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008986 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008988 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008990 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008991 }
glennrp8640fb52010-11-23 15:48:26 +00008992
cristy3ed852e2009-09-05 21:47:34 +00008993 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008994 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008995
glennrp26f37912010-12-23 16:22:42 +00008996
cristy3ed852e2009-09-05 21:47:34 +00008997#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008998 if (ping_exclude_pHYs == MagickFalse)
8999 {
cristy2a11bef2011-10-28 18:33:11 +00009000 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00009001 (!mng_info->write_mng || !mng_info->equal_physs))
9002 {
glennrp0fe50b42010-11-16 03:52:51 +00009003 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9005 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00009006
9007 if (image->units == PixelsPerInchResolution)
9008 {
glennrpdfd70802010-11-14 01:23:35 +00009009 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00009010 ping_pHYs_x_resolution=
cristy2a11bef2011-10-28 18:33:11 +00009011 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
glennrp823b55c2011-03-14 18:46:46 +00009012 ping_pHYs_y_resolution=
cristy2a11bef2011-10-28 18:33:11 +00009013 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00009014 }
glennrpdfd70802010-11-14 01:23:35 +00009015
cristy3ed852e2009-09-05 21:47:34 +00009016 else if (image->units == PixelsPerCentimeterResolution)
9017 {
glennrpdfd70802010-11-14 01:23:35 +00009018 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
cristy2a11bef2011-10-28 18:33:11 +00009019 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9020 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00009021 }
glennrp991d11d2010-11-12 21:55:28 +00009022
cristy3ed852e2009-09-05 21:47:34 +00009023 else
9024 {
glennrpdfd70802010-11-14 01:23:35 +00009025 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
cristy2a11bef2011-10-28 18:33:11 +00009026 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9027 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00009028 }
glennrp991d11d2010-11-12 21:55:28 +00009029
glennrp823b55c2011-03-14 18:46:46 +00009030 if (logging != MagickFalse)
9031 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9032 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9033 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9034 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009035 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009036 }
glennrp26f37912010-12-23 16:22:42 +00009037 }
cristy3ed852e2009-09-05 21:47:34 +00009038#endif
glennrpa521b2f2010-10-29 04:11:03 +00009039
glennrp26f37912010-12-23 16:22:42 +00009040 if (ping_exclude_bKGD == MagickFalse)
9041 {
glennrpa521b2f2010-10-29 04:11:03 +00009042 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009043 {
glennrpa521b2f2010-10-29 04:11:03 +00009044 unsigned int
9045 mask;
cristy3ed852e2009-09-05 21:47:34 +00009046
glennrpa521b2f2010-10-29 04:11:03 +00009047 mask=0xffff;
9048 if (ping_bit_depth == 8)
9049 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009050
glennrpa521b2f2010-10-29 04:11:03 +00009051 if (ping_bit_depth == 4)
9052 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009053
glennrpa521b2f2010-10-29 04:11:03 +00009054 if (ping_bit_depth == 2)
9055 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009056
glennrpa521b2f2010-10-29 04:11:03 +00009057 if (ping_bit_depth == 1)
9058 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009059
glennrpa521b2f2010-10-29 04:11:03 +00009060 ping_background.red=(png_uint_16)
9061 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009062
glennrpa521b2f2010-10-29 04:11:03 +00009063 ping_background.green=(png_uint_16)
9064 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009065
glennrpa521b2f2010-10-29 04:11:03 +00009066 ping_background.blue=(png_uint_16)
9067 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009068
9069 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009070 }
cristy3ed852e2009-09-05 21:47:34 +00009071
glennrp0fe50b42010-11-16 03:52:51 +00009072 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009073 {
9074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9075 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9077 " background_color index is %d",
9078 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009079
9080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9081 " ping_bit_depth=%d",ping_bit_depth);
9082 }
glennrp0fe50b42010-11-16 03:52:51 +00009083
9084 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009085 }
glennrp0fe50b42010-11-16 03:52:51 +00009086
cristy3ed852e2009-09-05 21:47:34 +00009087 /*
9088 Select the color type.
9089 */
9090 matte=image_matte;
9091 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009092
glennrp1273f7b2011-02-24 03:20:30 +00009093 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009094 {
glennrp0fe50b42010-11-16 03:52:51 +00009095
glennrpfd05d622011-02-25 04:10:33 +00009096 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009097 for reducing the sample depth from 8. */
9098
glennrp0fe50b42010-11-16 03:52:51 +00009099 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009100
glennrp8bb3a022010-12-13 20:40:04 +00009101 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009102
9103 /*
9104 Set image palette.
9105 */
9106 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9107
glennrp0fe50b42010-11-16 03:52:51 +00009108 if (logging != MagickFalse)
9109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9110 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009111 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009112
9113 for (i=0; i < (ssize_t) number_colors; i++)
9114 {
9115 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9116 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9117 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9118 if (logging != MagickFalse)
9119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009120#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009121 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009122#else
9123 " %5ld (%5d,%5d,%5d)",
9124#endif
glennrp0fe50b42010-11-16 03:52:51 +00009125 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9126
9127 }
glennrp2b013e42010-11-24 16:55:50 +00009128
glennrp8bb3a022010-12-13 20:40:04 +00009129 ping_have_PLTE=MagickTrue;
9130 image_depth=ping_bit_depth;
9131 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009132
glennrp58e01762011-01-07 15:28:54 +00009133 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009134 {
glennrp0fe50b42010-11-16 03:52:51 +00009135 /*
9136 Identify which colormap entry is transparent.
9137 */
9138 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009139 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009140
glennrp8bb3a022010-12-13 20:40:04 +00009141 for (i=0; i < (ssize_t) number_transparent; i++)
9142 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009143
glennrp0fe50b42010-11-16 03:52:51 +00009144
glennrp2cc891a2010-12-24 13:44:32 +00009145 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009146 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009147
9148 if (ping_num_trans == 0)
9149 ping_have_tRNS=MagickFalse;
9150
glennrp8bb3a022010-12-13 20:40:04 +00009151 else
9152 ping_have_tRNS=MagickTrue;
9153 }
glennrp0fe50b42010-11-16 03:52:51 +00009154
glennrp1273f7b2011-02-24 03:20:30 +00009155 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009156 {
glennrp1273f7b2011-02-24 03:20:30 +00009157 /*
9158 * Identify which colormap entry is the background color.
9159 */
9160
glennrp4f25bd02011-01-01 18:51:28 +00009161 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9162 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9163 break;
glennrp0fe50b42010-11-16 03:52:51 +00009164
glennrp4f25bd02011-01-01 18:51:28 +00009165 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009166
9167 if (logging != MagickFalse)
9168 {
9169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9170 " background_color index is %d",
9171 (int) ping_background.index);
9172 }
glennrp4f25bd02011-01-01 18:51:28 +00009173 }
cristy3ed852e2009-09-05 21:47:34 +00009174 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009175
glennrp7e65e932011-08-19 02:31:16 +00009176 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009177 {
9178 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009179 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009180 }
glennrp0fe50b42010-11-16 03:52:51 +00009181
glennrp7e65e932011-08-19 02:31:16 +00009182 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009183 {
9184 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009185 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009186 }
glennrp0fe50b42010-11-16 03:52:51 +00009187
glennrp8bb3a022010-12-13 20:40:04 +00009188 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009189 {
glennrp5af765f2010-03-30 11:12:18 +00009190 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009191
glennrp8bb3a022010-12-13 20:40:04 +00009192 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009193 {
glennrp5af765f2010-03-30 11:12:18 +00009194 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009195
glennrp5af765f2010-03-30 11:12:18 +00009196 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9197 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009198 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009199
glennrp8bb3a022010-12-13 20:40:04 +00009200 else
9201 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009202
9203 if (logging != MagickFalse)
9204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9205 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009206 }
glennrp0fe50b42010-11-16 03:52:51 +00009207
glennrp7c4c9e62011-03-21 20:23:32 +00009208 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009209 {
9210 if (logging != MagickFalse)
9211 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009212 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009213
glennrpd6bf1612010-12-17 17:28:54 +00009214 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009215 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009216
glennrpd6bf1612010-12-17 17:28:54 +00009217 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009218 {
glennrp5af765f2010-03-30 11:12:18 +00009219 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009220 image_matte=MagickFalse;
9221 }
glennrp0fe50b42010-11-16 03:52:51 +00009222
glennrpd6bf1612010-12-17 17:28:54 +00009223 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009224 {
glennrp5af765f2010-03-30 11:12:18 +00009225 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009226 image_matte=MagickTrue;
9227 }
glennrp0fe50b42010-11-16 03:52:51 +00009228
glennrp5aa37f62011-01-02 03:07:57 +00009229 if (image_info->type == PaletteType ||
9230 image_info->type == PaletteMatteType)
9231 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9232
glennrp7c4c9e62011-03-21 20:23:32 +00009233 if (mng_info->write_png_colortype == 0 &&
9234 (image_info->type == UndefinedType ||
9235 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009236 {
glennrp5aa37f62011-01-02 03:07:57 +00009237 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009238 {
glennrp5aa37f62011-01-02 03:07:57 +00009239 if (image_matte == MagickFalse)
9240 {
9241 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9242 image_matte=MagickFalse;
9243 }
glennrp0fe50b42010-11-16 03:52:51 +00009244
glennrp0b206f52011-01-07 04:55:32 +00009245 else
glennrp5aa37f62011-01-02 03:07:57 +00009246 {
9247 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9248 image_matte=MagickTrue;
9249 }
9250 }
9251 else
glennrp8bb3a022010-12-13 20:40:04 +00009252 {
glennrp5aa37f62011-01-02 03:07:57 +00009253 if (image_matte == MagickFalse)
9254 {
9255 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9256 image_matte=MagickFalse;
9257 }
glennrp8bb3a022010-12-13 20:40:04 +00009258
glennrp0b206f52011-01-07 04:55:32 +00009259 else
glennrp5aa37f62011-01-02 03:07:57 +00009260 {
9261 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9262 image_matte=MagickTrue;
9263 }
9264 }
glennrp0fe50b42010-11-16 03:52:51 +00009265 }
glennrp5aa37f62011-01-02 03:07:57 +00009266
cristy3ed852e2009-09-05 21:47:34 +00009267 }
glennrp0fe50b42010-11-16 03:52:51 +00009268
cristy3ed852e2009-09-05 21:47:34 +00009269 if (logging != MagickFalse)
9270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009271 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009272
glennrp5af765f2010-03-30 11:12:18 +00009273 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009274 {
9275 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9276 ping_color_type == PNG_COLOR_TYPE_RGB ||
9277 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9278 ping_bit_depth=8;
9279 }
cristy3ed852e2009-09-05 21:47:34 +00009280
glennrpd6bf1612010-12-17 17:28:54 +00009281 old_bit_depth=ping_bit_depth;
9282
glennrp5af765f2010-03-30 11:12:18 +00009283 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009284 {
glennrp8d579662011-02-23 02:05:02 +00009285 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9286 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009287 }
glennrp8640fb52010-11-23 15:48:26 +00009288
glennrp5af765f2010-03-30 11:12:18 +00009289 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009290 {
cristy35ef8242010-06-03 16:24:13 +00009291 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009292 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009293
9294 if (image->colors == 0)
9295 {
glennrp0fe50b42010-11-16 03:52:51 +00009296 /* DO SOMETHING */
cristyc82a27b2011-10-21 01:07:16 +00009297 (void) ThrowMagickException(exception,
glennrp0f111982010-07-07 20:18:33 +00009298 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009299 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009300 }
9301
cristy35ef8242010-06-03 16:24:13 +00009302 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009303 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009304 }
glennrp2b013e42010-11-24 16:55:50 +00009305
glennrpd6bf1612010-12-17 17:28:54 +00009306 if (logging != MagickFalse)
9307 {
9308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9309 " Number of colors: %.20g",(double) image_colors);
9310
9311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9312 " Tentative PNG bit depth: %d",ping_bit_depth);
9313 }
9314
9315 if (ping_bit_depth < (int) mng_info->write_png_depth)
9316 ping_bit_depth = mng_info->write_png_depth;
9317 }
glennrp2cc891a2010-12-24 13:44:32 +00009318
glennrp5af765f2010-03-30 11:12:18 +00009319 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009320
cristy3ed852e2009-09-05 21:47:34 +00009321 if (logging != MagickFalse)
9322 {
9323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009324 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009325
cristy3ed852e2009-09-05 21:47:34 +00009326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009327 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009328
cristy3ed852e2009-09-05 21:47:34 +00009329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009330 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009331
cristy3ed852e2009-09-05 21:47:34 +00009332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009333
glennrp8640fb52010-11-23 15:48:26 +00009334 " image->depth: %.20g",(double) image->depth);
9335
9336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009337 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009338 }
9339
glennrp58e01762011-01-07 15:28:54 +00009340 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009341 {
glennrp4f25bd02011-01-01 18:51:28 +00009342 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009343 {
glennrp7c4c9e62011-03-21 20:23:32 +00009344 if (mng_info->write_png_colortype == 0)
9345 {
9346 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009347
glennrp7c4c9e62011-03-21 20:23:32 +00009348 if (ping_have_color != MagickFalse)
9349 ping_color_type=PNG_COLOR_TYPE_RGBA;
9350 }
glennrp4f25bd02011-01-01 18:51:28 +00009351
9352 /*
9353 * Determine if there is any transparent color.
9354 */
9355 if (number_transparent + number_semitransparent == 0)
9356 {
9357 /*
9358 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9359 */
glennrpa6a06632011-01-19 15:15:34 +00009360
glennrp4f25bd02011-01-01 18:51:28 +00009361 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009362
9363 if (mng_info->write_png_colortype == 0)
9364 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009365 }
9366
9367 else
9368 {
9369 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009370 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009371
9372 mask=0xffff;
9373
9374 if (ping_bit_depth == 8)
9375 mask=0x00ff;
9376
9377 if (ping_bit_depth == 4)
9378 mask=0x000f;
9379
9380 if (ping_bit_depth == 2)
9381 mask=0x0003;
9382
9383 if (ping_bit_depth == 1)
9384 mask=0x0001;
9385
9386 ping_trans_color.red=(png_uint_16)
9387 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9388
9389 ping_trans_color.green=(png_uint_16)
9390 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9391
9392 ping_trans_color.blue=(png_uint_16)
9393 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9394
9395 ping_trans_color.gray=(png_uint_16)
cristy101ab702011-10-13 13:06:32 +00009396 (ScaleQuantumToShort(GetPixelInfoIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009397 image->colormap)) & mask);
9398
9399 ping_trans_color.index=(png_byte) 0;
9400
9401 ping_have_tRNS=MagickTrue;
9402 }
9403
9404 if (ping_have_tRNS != MagickFalse)
9405 {
9406 /*
glennrpfd05d622011-02-25 04:10:33 +00009407 * Determine if there is one and only one transparent color
9408 * and if so if it is fully transparent.
9409 */
9410 if (ping_have_cheap_transparency == MagickFalse)
9411 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009412 }
9413
9414 if (ping_have_tRNS != MagickFalse)
9415 {
glennrp7c4c9e62011-03-21 20:23:32 +00009416 if (mng_info->write_png_colortype == 0)
9417 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009418
9419 if (image_depth == 8)
9420 {
9421 ping_trans_color.red&=0xff;
9422 ping_trans_color.green&=0xff;
9423 ping_trans_color.blue&=0xff;
9424 ping_trans_color.gray&=0xff;
9425 }
9426 }
9427 }
cristy3ed852e2009-09-05 21:47:34 +00009428 else
9429 {
cristy3ed852e2009-09-05 21:47:34 +00009430 if (image_depth == 8)
9431 {
glennrp5af765f2010-03-30 11:12:18 +00009432 ping_trans_color.red&=0xff;
9433 ping_trans_color.green&=0xff;
9434 ping_trans_color.blue&=0xff;
9435 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009436 }
9437 }
9438 }
glennrp8640fb52010-11-23 15:48:26 +00009439
cristy3ed852e2009-09-05 21:47:34 +00009440 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009441
glennrp2e09f552010-11-14 00:38:48 +00009442 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009443 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009444
glennrp39992b42010-11-14 00:03:43 +00009445 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009446 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009447 ping_have_color == MagickFalse &&
9448 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009449 {
cristy35ef8242010-06-03 16:24:13 +00009450 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009451
cristy3ed852e2009-09-05 21:47:34 +00009452 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009453 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009454
glennrp7c4c9e62011-03-21 20:23:32 +00009455 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009456 {
glennrp5af765f2010-03-30 11:12:18 +00009457 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009458
cristy3ed852e2009-09-05 21:47:34 +00009459 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009460 {
9461 if (logging != MagickFalse)
9462 {
9463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9464 " Scaling ping_trans_color (0)");
9465 }
9466 ping_trans_color.gray*=0x0101;
9467 }
cristy3ed852e2009-09-05 21:47:34 +00009468 }
glennrp0fe50b42010-11-16 03:52:51 +00009469
cristy3ed852e2009-09-05 21:47:34 +00009470 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9471 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009472
glennrp136ee3a2011-04-27 15:47:45 +00009473 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009474 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009475 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009476
cristy3ed852e2009-09-05 21:47:34 +00009477 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009478 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009479
cristy3ed852e2009-09-05 21:47:34 +00009480 else
9481 {
glennrp5af765f2010-03-30 11:12:18 +00009482 ping_bit_depth=8;
9483 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009484 {
9485 if(!mng_info->write_png_depth)
9486 {
glennrp5af765f2010-03-30 11:12:18 +00009487 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009488
cristy35ef8242010-06-03 16:24:13 +00009489 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009490 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009491 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009492 }
9493 }
glennrp2b013e42010-11-24 16:55:50 +00009494
glennrp0fe50b42010-11-16 03:52:51 +00009495 else if (ping_color_type ==
9496 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009497 mng_info->IsPalette)
9498 {
cristy3ed852e2009-09-05 21:47:34 +00009499 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009500
cristy3ed852e2009-09-05 21:47:34 +00009501 int
9502 depth_4_ok=MagickTrue,
9503 depth_2_ok=MagickTrue,
9504 depth_1_ok=MagickTrue;
9505
cristybb503372010-05-27 20:51:26 +00009506 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009507 {
9508 unsigned char
9509 intensity;
9510
9511 intensity=ScaleQuantumToChar(image->colormap[i].red);
9512
9513 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9514 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9515 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9516 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009517 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009518 depth_1_ok=MagickFalse;
9519 }
glennrp2b013e42010-11-24 16:55:50 +00009520
cristy3ed852e2009-09-05 21:47:34 +00009521 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009522 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009523
cristy3ed852e2009-09-05 21:47:34 +00009524 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009525 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009526
cristy3ed852e2009-09-05 21:47:34 +00009527 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009528 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009529 }
9530 }
glennrp2b013e42010-11-24 16:55:50 +00009531
glennrp5af765f2010-03-30 11:12:18 +00009532 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009533 }
glennrp0fe50b42010-11-16 03:52:51 +00009534
cristy3ed852e2009-09-05 21:47:34 +00009535 else
glennrp0fe50b42010-11-16 03:52:51 +00009536
cristy3ed852e2009-09-05 21:47:34 +00009537 if (mng_info->IsPalette)
9538 {
glennrp17a14852010-05-10 03:01:59 +00009539 number_colors=image_colors;
9540
cristy3ed852e2009-09-05 21:47:34 +00009541 if (image_depth <= 8)
9542 {
cristy3ed852e2009-09-05 21:47:34 +00009543 /*
9544 Set image palette.
9545 */
glennrp5af765f2010-03-30 11:12:18 +00009546 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009547
glennrp58e01762011-01-07 15:28:54 +00009548 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009549 {
glennrp9c1eb072010-06-06 22:19:15 +00009550 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009551
glennrp3b51f0e2010-11-27 18:14:08 +00009552 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9554 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009555 }
glennrp0fe50b42010-11-16 03:52:51 +00009556
cristy3ed852e2009-09-05 21:47:34 +00009557 else
9558 {
cristybb503372010-05-27 20:51:26 +00009559 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009560 {
9561 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9562 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9563 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9564 }
glennrp0fe50b42010-11-16 03:52:51 +00009565
glennrp3b51f0e2010-11-27 18:14:08 +00009566 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009568 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009569 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009570
glennrp39992b42010-11-14 00:03:43 +00009571 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009572 }
glennrp0fe50b42010-11-16 03:52:51 +00009573
cristy3ed852e2009-09-05 21:47:34 +00009574 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009575 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009576 {
cristybefe4d22010-06-07 01:18:58 +00009577 size_t
9578 one;
9579
glennrp5af765f2010-03-30 11:12:18 +00009580 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009581 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009582
cristy94b11832011-09-08 19:46:03 +00009583 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009584 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009585 }
glennrp0fe50b42010-11-16 03:52:51 +00009586
glennrp5af765f2010-03-30 11:12:18 +00009587 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009588
glennrp58e01762011-01-07 15:28:54 +00009589 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009590 {
glennrp0fe50b42010-11-16 03:52:51 +00009591 /*
glennrpd6bf1612010-12-17 17:28:54 +00009592 * Set up trans_colors array.
9593 */
glennrp0fe50b42010-11-16 03:52:51 +00009594 assert(number_colors <= 256);
9595
glennrpd6bf1612010-12-17 17:28:54 +00009596 ping_num_trans=(unsigned short) (number_transparent +
9597 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009598
9599 if (ping_num_trans == 0)
9600 ping_have_tRNS=MagickFalse;
9601
glennrpd6bf1612010-12-17 17:28:54 +00009602 else
glennrp0fe50b42010-11-16 03:52:51 +00009603 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009604 if (logging != MagickFalse)
9605 {
9606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9607 " Scaling ping_trans_color (1)");
9608 }
glennrpd6bf1612010-12-17 17:28:54 +00009609 ping_have_tRNS=MagickTrue;
9610
9611 for (i=0; i < ping_num_trans; i++)
9612 {
cristy4c08aed2011-07-01 19:47:50 +00009613 ping_trans_alpha[i]= (png_byte)
9614 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009615 }
glennrp0fe50b42010-11-16 03:52:51 +00009616 }
9617 }
cristy3ed852e2009-09-05 21:47:34 +00009618 }
9619 }
glennrp0fe50b42010-11-16 03:52:51 +00009620
cristy3ed852e2009-09-05 21:47:34 +00009621 else
9622 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009623
cristy3ed852e2009-09-05 21:47:34 +00009624 if (image_depth < 8)
9625 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009626
cristy3ed852e2009-09-05 21:47:34 +00009627 if ((save_image_depth == 16) && (image_depth == 8))
9628 {
glennrp4f25bd02011-01-01 18:51:28 +00009629 if (logging != MagickFalse)
9630 {
9631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9632 " Scaling ping_trans_color from (%d,%d,%d)",
9633 (int) ping_trans_color.red,
9634 (int) ping_trans_color.green,
9635 (int) ping_trans_color.blue);
9636 }
9637
glennrp5af765f2010-03-30 11:12:18 +00009638 ping_trans_color.red*=0x0101;
9639 ping_trans_color.green*=0x0101;
9640 ping_trans_color.blue*=0x0101;
9641 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009642
9643 if (logging != MagickFalse)
9644 {
9645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9646 " to (%d,%d,%d)",
9647 (int) ping_trans_color.red,
9648 (int) ping_trans_color.green,
9649 (int) ping_trans_color.blue);
9650 }
cristy3ed852e2009-09-05 21:47:34 +00009651 }
9652 }
9653
cristy4383ec82011-01-05 15:42:32 +00009654 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9655 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009656
cristy3ed852e2009-09-05 21:47:34 +00009657 /*
9658 Adjust background and transparency samples in sub-8-bit grayscale files.
9659 */
glennrp5af765f2010-03-30 11:12:18 +00009660 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009661 PNG_COLOR_TYPE_GRAY)
9662 {
9663 png_uint_16
9664 maxval;
9665
cristy35ef8242010-06-03 16:24:13 +00009666 size_t
9667 one=1;
9668
cristy22ffd972010-06-03 16:51:47 +00009669 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009670
glennrp4f25bd02011-01-01 18:51:28 +00009671 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009672 {
cristy3ed852e2009-09-05 21:47:34 +00009673
glennrp9f0fa852011-12-15 12:20:50 +00009674 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9675 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9676 &image->background_color))) +.5)));
cristy3ed852e2009-09-05 21:47:34 +00009677
9678 if (logging != MagickFalse)
9679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009680 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9682 " background_color index is %d",
9683 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009684
glennrp991d11d2010-11-12 21:55:28 +00009685 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009686 }
cristy3ed852e2009-09-05 21:47:34 +00009687
glennrp3e3e20f2011-06-09 04:21:43 +00009688 if (logging != MagickFalse)
9689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9690 " Scaling ping_trans_color.gray from %d",
9691 (int)ping_trans_color.gray);
9692
glennrp9be9b1c2011-06-09 12:21:45 +00009693 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009694 ping_trans_color.gray)+.5);
9695
9696 if (logging != MagickFalse)
9697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9698 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009699 }
glennrp17a14852010-05-10 03:01:59 +00009700
glennrp26f37912010-12-23 16:22:42 +00009701 if (ping_exclude_bKGD == MagickFalse)
9702 {
glennrp1273f7b2011-02-24 03:20:30 +00009703 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009704 {
9705 /*
9706 Identify which colormap entry is the background color.
9707 */
9708
glennrp17a14852010-05-10 03:01:59 +00009709 number_colors=image_colors;
9710
glennrpa521b2f2010-10-29 04:11:03 +00009711 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9712 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009713 break;
9714
9715 ping_background.index=(png_byte) i;
9716
glennrp3b51f0e2010-11-27 18:14:08 +00009717 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009718 {
9719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009720 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009721 }
glennrp0fe50b42010-11-16 03:52:51 +00009722
cristy13d07042010-11-21 20:56:18 +00009723 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009724 {
9725 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009726
9727 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009728 {
9729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9730 " background =(%d,%d,%d)",
9731 (int) ping_background.red,
9732 (int) ping_background.green,
9733 (int) ping_background.blue);
9734 }
9735 }
glennrpa521b2f2010-10-29 04:11:03 +00009736
glennrpd6bf1612010-12-17 17:28:54 +00009737 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009738 {
glennrp3b51f0e2010-11-27 18:14:08 +00009739 if (logging != MagickFalse)
9740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9741 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009742 ping_have_bKGD = MagickFalse;
9743 }
glennrp17a14852010-05-10 03:01:59 +00009744 }
glennrp26f37912010-12-23 16:22:42 +00009745 }
glennrp17a14852010-05-10 03:01:59 +00009746
cristy3ed852e2009-09-05 21:47:34 +00009747 if (logging != MagickFalse)
9748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009749 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009750 /*
9751 Initialize compression level and filtering.
9752 */
9753 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009754 {
9755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9756 " Setting up deflate compression");
9757
9758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9759 " Compression buffer size: 32768");
9760 }
9761
cristy3ed852e2009-09-05 21:47:34 +00009762 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009763
cristy3ed852e2009-09-05 21:47:34 +00009764 if (logging != MagickFalse)
9765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9766 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009767
cristy4054bfb2011-08-29 23:41:39 +00009768 png_set_compression_mem_level(ping, 9);
9769
glennrp10d739e2011-06-29 18:00:52 +00009770 /* Untangle the "-quality" setting:
9771
9772 Undefined is 0; the default is used.
9773 Default is 75
9774
9775 10's digit:
9776
9777 0: Use Z_HUFFMAN_ONLY strategy with the
9778 zlib default compression level
9779
9780 1-9: the zlib compression level
9781
9782 1's digit:
9783
9784 0-4: the PNG filter method
9785
9786 5: libpng adaptive filtering if compression level > 5
9787 libpng filter type "none" if compression level <= 5
9788 or if image is grayscale or palette
9789
9790 6: libpng adaptive filtering
9791
9792 7: "LOCO" filtering (intrapixel differing) if writing
9793 a MNG, othewise "none". Did not work in IM-6.7.0-9
9794 and earlier because of a missing "else".
9795
9796 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009797 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009798
9799 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009800 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009801
9802 Note that using the -quality option, not all combinations of
9803 PNG filter type, zlib compression level, and zlib compression
cristy5e6be1e2011-07-16 01:23:39 +00009804 strategy are possible. This will be addressed soon in a
cristy5d6fc9c2011-12-27 03:10:42 +00009805 release that accomodates "-define png:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +00009806
9807 */
9808
cristy3ed852e2009-09-05 21:47:34 +00009809 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9810 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009811
glennrp18682582011-06-30 18:11:47 +00009812 if (quality <= 9)
9813 {
9814 if (mng_info->write_png_compression_strategy == 0)
9815 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9816 }
9817
9818 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009819 {
9820 int
9821 level;
9822
cristybb503372010-05-27 20:51:26 +00009823 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009824
glennrp18682582011-06-30 18:11:47 +00009825 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009826 }
glennrp0fe50b42010-11-16 03:52:51 +00009827
glennrp18682582011-06-30 18:11:47 +00009828 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009829 {
glennrp18682582011-06-30 18:11:47 +00009830 if ((quality %10) == 8 || (quality %10) == 9)
9831 mng_info->write_png_compression_strategy=Z_RLE;
cristy3ed852e2009-09-05 21:47:34 +00009832 }
glennrp0fe50b42010-11-16 03:52:51 +00009833
glennrp18682582011-06-30 18:11:47 +00009834 if (mng_info->write_png_compression_filter == 0)
9835 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9836
cristy3ed852e2009-09-05 21:47:34 +00009837 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009838 {
glennrp18682582011-06-30 18:11:47 +00009839 if (mng_info->write_png_compression_level)
9840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9841 " Compression level: %d",
9842 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009843
glennrp18682582011-06-30 18:11:47 +00009844 if (mng_info->write_png_compression_strategy)
9845 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9846 " Compression strategy: %d",
9847 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009848
glennrp18682582011-06-30 18:11:47 +00009849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9850 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009851
cristy4054bfb2011-08-29 23:41:39 +00009852 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +00009853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9854 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +00009855 else if (mng_info->write_png_compression_filter == 0 ||
9856 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +00009857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9858 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009859 else
9860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9861 " Base filter method: %d",
9862 (int) mng_info->write_png_compression_filter-1);
9863 }
glennrp2b013e42010-11-24 16:55:50 +00009864
glennrp18682582011-06-30 18:11:47 +00009865 if (mng_info->write_png_compression_level != 0)
9866 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9867
9868 if (mng_info->write_png_compression_filter == 6)
9869 {
9870 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9871 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9872 (quality < 50))
9873 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9874 else
9875 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9876 }
cristy4054bfb2011-08-29 23:41:39 +00009877 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +00009878 mng_info->write_png_compression_filter == 10)
9879 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9880
9881 else if (mng_info->write_png_compression_filter == 8)
9882 {
9883#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9884 if (mng_info->write_mng)
9885 {
9886 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9887 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9888 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9889 }
9890#endif
cristy4054bfb2011-08-29 23:41:39 +00009891 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +00009892 }
9893
9894 else if (mng_info->write_png_compression_filter == 9)
9895 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9896
9897 else if (mng_info->write_png_compression_filter != 0)
9898 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9899 mng_info->write_png_compression_filter-1);
9900
9901 if (mng_info->write_png_compression_strategy != 0)
9902 png_set_compression_strategy(ping,
9903 mng_info->write_png_compression_strategy-1);
9904
cristy0d57eec2011-09-04 22:13:56 +00009905 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9906 if (ping_exclude_sRGB != MagickFalse ||
9907 (image->rendering_intent == UndefinedIntent))
9908 {
9909 if ((ping_exclude_tEXt == MagickFalse ||
9910 ping_exclude_zTXt == MagickFalse) &&
9911 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009912 {
9913 ResetImageProfileIterator(image);
9914 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009915 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009916 profile=GetImageProfile(image,name);
9917
9918 if (profile != (StringInfo *) NULL)
9919 {
glennrp5af765f2010-03-30 11:12:18 +00009920#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009921 if ((LocaleCompare(name,"ICC") == 0) ||
9922 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009923 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009924
9925 if (ping_exclude_iCCP == MagickFalse)
9926 {
cristy9f027d12011-09-21 01:17:17 +00009927 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009928#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009929 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009930#else
9931 (png_const_bytep) GetStringInfoDatum(profile),
9932#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009933 (png_uint_32) GetStringInfoLength(profile));
9934 }
glennrp26f37912010-12-23 16:22:42 +00009935 }
glennrp0fe50b42010-11-16 03:52:51 +00009936
glennrpc8cbc5d2011-01-01 00:12:34 +00009937 else
cristy3ed852e2009-09-05 21:47:34 +00009938#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009939 if (ping_exclude_zCCP == MagickFalse)
9940 {
glennrpcf002022011-01-30 02:38:15 +00009941 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009942 (unsigned char *) name,(unsigned char *) name,
9943 GetStringInfoDatum(profile),
9944 (png_uint_32) GetStringInfoLength(profile));
9945 }
9946 }
glennrp0b206f52011-01-07 04:55:32 +00009947
glennrpc8cbc5d2011-01-01 00:12:34 +00009948 if (logging != MagickFalse)
9949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9950 " Setting up text chunk with %s profile",name);
9951
9952 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009953 }
cristy0d57eec2011-09-04 22:13:56 +00009954 }
cristy3ed852e2009-09-05 21:47:34 +00009955 }
9956
9957#if defined(PNG_WRITE_sRGB_SUPPORTED)
9958 if ((mng_info->have_write_global_srgb == 0) &&
9959 ((image->rendering_intent != UndefinedIntent) ||
9960 (image->colorspace == sRGBColorspace)))
9961 {
glennrp26f37912010-12-23 16:22:42 +00009962 if (ping_exclude_sRGB == MagickFalse)
9963 {
9964 /*
9965 Note image rendering intent.
9966 */
9967 if (logging != MagickFalse)
9968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9969 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009970
glennrp26f37912010-12-23 16:22:42 +00009971 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009972 Magick_RenderingIntent_to_PNG_RenderingIntent(
9973 image->rendering_intent)));
glennrp26f37912010-12-23 16:22:42 +00009974 }
cristy3ed852e2009-09-05 21:47:34 +00009975 }
glennrp26f37912010-12-23 16:22:42 +00009976
glennrp5af765f2010-03-30 11:12:18 +00009977 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009978#endif
9979 {
glennrp2cc891a2010-12-24 13:44:32 +00009980 if (ping_exclude_gAMA == MagickFalse &&
9981 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009982 (image->gamma < .45 || image->gamma > .46)))
9983 {
cristy3ed852e2009-09-05 21:47:34 +00009984 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9985 {
9986 /*
9987 Note image gamma.
9988 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9989 */
9990 if (logging != MagickFalse)
9991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9992 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009993
cristy3ed852e2009-09-05 21:47:34 +00009994 png_set_gAMA(ping,ping_info,image->gamma);
9995 }
glennrp26f37912010-12-23 16:22:42 +00009996 }
glennrp2b013e42010-11-24 16:55:50 +00009997
glennrp26f37912010-12-23 16:22:42 +00009998 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009999 {
glennrp26f37912010-12-23 16:22:42 +000010000 if ((mng_info->have_write_global_chrm == 0) &&
10001 (image->chromaticity.red_primary.x != 0.0))
10002 {
10003 /*
10004 Note image chromaticity.
10005 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10006 */
10007 PrimaryInfo
10008 bp,
10009 gp,
10010 rp,
10011 wp;
cristy3ed852e2009-09-05 21:47:34 +000010012
glennrp26f37912010-12-23 16:22:42 +000010013 wp=image->chromaticity.white_point;
10014 rp=image->chromaticity.red_primary;
10015 gp=image->chromaticity.green_primary;
10016 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +000010017
glennrp26f37912010-12-23 16:22:42 +000010018 if (logging != MagickFalse)
10019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10020 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010021
glennrp26f37912010-12-23 16:22:42 +000010022 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10023 bp.x,bp.y);
10024 }
10025 }
cristy3ed852e2009-09-05 21:47:34 +000010026 }
glennrpdfd70802010-11-14 01:23:35 +000010027
glennrp5af765f2010-03-30 11:12:18 +000010028 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +000010029
10030 if (mng_info->write_mng)
10031 png_set_sig_bytes(ping,8);
10032
cristy5d6fc9c2011-12-27 03:10:42 +000010033 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
cristy3ed852e2009-09-05 21:47:34 +000010034
glennrpd6bf1612010-12-17 17:28:54 +000010035 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +000010036 {
10037 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +000010038 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010039 {
glennrp5af765f2010-03-30 11:12:18 +000010040 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +000010041
glennrp5af765f2010-03-30 11:12:18 +000010042 if (ping_bit_depth < 8)
10043 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +000010044 }
glennrp0fe50b42010-11-16 03:52:51 +000010045
cristy3ed852e2009-09-05 21:47:34 +000010046 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +000010047 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +000010048 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +000010049 }
10050
glennrp0e8ea192010-12-24 18:00:33 +000010051 if (ping_need_colortype_warning != MagickFalse ||
10052 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +000010053 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +000010054 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +000010055 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +000010056 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +000010057 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +000010058 {
10059 if (logging != MagickFalse)
10060 {
glennrp0e8ea192010-12-24 18:00:33 +000010061 if (ping_need_colortype_warning != MagickFalse)
10062 {
10063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10064 " Image has transparency but tRNS chunk was excluded");
10065 }
10066
cristy3ed852e2009-09-05 21:47:34 +000010067 if (mng_info->write_png_depth)
10068 {
10069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010070 " Defined png:bit-depth=%u, Computed depth=%u",
cristy3ed852e2009-09-05 21:47:34 +000010071 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +000010072 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +000010073 }
glennrp0e8ea192010-12-24 18:00:33 +000010074
cristy3ed852e2009-09-05 21:47:34 +000010075 if (mng_info->write_png_colortype)
10076 {
10077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010078 " Defined png:color-type=%u, Computed color type=%u",
cristy3ed852e2009-09-05 21:47:34 +000010079 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +000010080 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010081 }
10082 }
glennrp0e8ea192010-12-24 18:00:33 +000010083
glennrp3bd2e412010-08-10 13:34:52 +000010084 png_warning(ping,
cristy5d6fc9c2011-12-27 03:10:42 +000010085 "Cannot write image with defined png:bit-depth or png:color-type.");
cristy3ed852e2009-09-05 21:47:34 +000010086 }
10087
glennrp58e01762011-01-07 15:28:54 +000010088 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010089 {
10090 /* Add an opaque matte channel */
10091 image->matte = MagickTrue;
cristye941a752011-10-15 01:52:48 +000010092 (void) SetImageAlpha(image,OpaqueAlpha,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010093
glennrpb4a13412010-05-05 12:47:19 +000010094 if (logging != MagickFalse)
10095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10096 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +000010097 }
10098
glennrp0e319732011-01-25 21:53:13 +000010099 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +000010100 {
glennrp991d11d2010-11-12 21:55:28 +000010101 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +000010102 {
glennrp991d11d2010-11-12 21:55:28 +000010103 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010104 if (logging != MagickFalse)
10105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10106 " Setting ping_have_tRNS=MagickTrue.");
10107 }
glennrpe9c26dc2010-05-30 01:56:35 +000010108 }
10109
cristy3ed852e2009-09-05 21:47:34 +000010110 if (logging != MagickFalse)
10111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10112 " Writing PNG header chunks");
10113
glennrp5af765f2010-03-30 11:12:18 +000010114 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10115 ping_bit_depth,ping_color_type,
10116 ping_interlace_method,ping_compression_method,
10117 ping_filter_method);
10118
glennrp39992b42010-11-14 00:03:43 +000010119 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10120 {
glennrpf09bded2011-01-08 01:15:59 +000010121 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010122
glennrp3b51f0e2010-11-27 18:14:08 +000010123 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +000010124 {
glennrp8640fb52010-11-23 15:48:26 +000010125 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +000010126 {
glennrpd6bf1612010-12-17 17:28:54 +000010127 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +000010128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010129 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10130 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010131 (int) palette[i].red,
10132 (int) palette[i].green,
10133 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +000010134 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010135 (int) ping_trans_alpha[i]);
10136 else
10137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010138 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010139 (int) i,
10140 (int) palette[i].red,
10141 (int) palette[i].green,
10142 (int) palette[i].blue);
10143 }
glennrp39992b42010-11-14 00:03:43 +000010144 }
glennrp39992b42010-11-14 00:03:43 +000010145 }
10146
glennrp26f37912010-12-23 16:22:42 +000010147 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010148 {
glennrp26f37912010-12-23 16:22:42 +000010149 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010150 {
glennrp26f37912010-12-23 16:22:42 +000010151 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010152 if (logging)
10153 {
10154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10155 " Setting up bKGD chunk");
10156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10157 " background color = (%d,%d,%d)",
10158 (int) ping_background.red,
10159 (int) ping_background.green,
10160 (int) ping_background.blue);
10161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10162 " index = %d, gray=%d",
10163 (int) ping_background.index,
10164 (int) ping_background.gray);
10165 }
10166 }
glennrp26f37912010-12-23 16:22:42 +000010167 }
10168
10169 if (ping_exclude_pHYs == MagickFalse)
10170 {
10171 if (ping_have_pHYs != MagickFalse)
10172 {
10173 png_set_pHYs(ping,ping_info,
10174 ping_pHYs_x_resolution,
10175 ping_pHYs_y_resolution,
10176 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010177
10178 if (logging)
10179 {
10180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10181 " Setting up pHYs chunk");
10182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10183 " x_resolution=%lu",
10184 (unsigned long) ping_pHYs_x_resolution);
10185 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10186 " y_resolution=%lu",
10187 (unsigned long) ping_pHYs_y_resolution);
10188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10189 " unit_type=%lu",
10190 (unsigned long) ping_pHYs_unit_type);
10191 }
glennrp26f37912010-12-23 16:22:42 +000010192 }
glennrpdfd70802010-11-14 01:23:35 +000010193 }
10194
10195#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010196 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010197 {
glennrp26f37912010-12-23 16:22:42 +000010198 if (image->page.x || image->page.y)
10199 {
10200 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10201 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010202
glennrp26f37912010-12-23 16:22:42 +000010203 if (logging != MagickFalse)
10204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10205 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10206 (int) image->page.x, (int) image->page.y);
10207 }
glennrpdfd70802010-11-14 01:23:35 +000010208 }
10209#endif
10210
glennrpda8f3a72011-02-27 23:54:12 +000010211 if (mng_info->need_blob != MagickFalse)
10212 {
cristyc82a27b2011-10-21 01:07:16 +000010213 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
glennrpda8f3a72011-02-27 23:54:12 +000010214 MagickFalse)
10215 png_error(ping,"WriteBlob Failed");
10216
10217 ping_have_blob=MagickTrue;
10218 }
10219
cristy3ed852e2009-09-05 21:47:34 +000010220 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010221
glennrp39992b42010-11-14 00:03:43 +000010222 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010223 {
glennrp3b51f0e2010-11-27 18:14:08 +000010224 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010225 {
10226 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10227 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10228 }
10229
10230 if (ping_color_type == 3)
10231 (void) png_set_tRNS(ping, ping_info,
10232 ping_trans_alpha,
10233 ping_num_trans,
10234 NULL);
10235
10236 else
10237 {
10238 (void) png_set_tRNS(ping, ping_info,
10239 NULL,
10240 0,
10241 &ping_trans_color);
10242
glennrp3b51f0e2010-11-27 18:14:08 +000010243 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010244 {
10245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010246 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010247 (int) ping_trans_color.red,
10248 (int) ping_trans_color.green,
10249 (int) ping_trans_color.blue);
10250 }
10251 }
glennrp991d11d2010-11-12 21:55:28 +000010252 }
10253
cristy3ed852e2009-09-05 21:47:34 +000010254 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010255 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010256
cristy3ed852e2009-09-05 21:47:34 +000010257 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010258
cristy3ed852e2009-09-05 21:47:34 +000010259 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010260 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010261
glennrp26f37912010-12-23 16:22:42 +000010262 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010263 {
glennrp4f25bd02011-01-01 18:51:28 +000010264 if ((image->page.width != 0 && image->page.width != image->columns) ||
10265 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010266 {
10267 unsigned char
10268 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010269
glennrp26f37912010-12-23 16:22:42 +000010270 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10271 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010272 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010273 PNGLong(chunk+4,(png_uint_32) image->page.width);
10274 PNGLong(chunk+8,(png_uint_32) image->page.height);
10275 chunk[12]=0; /* unit = pixels */
10276 (void) WriteBlob(image,13,chunk);
10277 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10278 }
cristy3ed852e2009-09-05 21:47:34 +000010279 }
10280
10281#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010282 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010283#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010284 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010285#undef PNG_HAVE_IDAT
10286#endif
10287
10288 png_set_packing(ping);
10289 /*
10290 Allocate memory.
10291 */
10292 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010293 if (image_depth > 8)
10294 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010295 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010296 {
glennrpb4a13412010-05-05 12:47:19 +000010297 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010298 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010299 break;
glennrp0fe50b42010-11-16 03:52:51 +000010300
glennrpb4a13412010-05-05 12:47:19 +000010301 case PNG_COLOR_TYPE_GRAY_ALPHA:
10302 rowbytes*=2;
10303 break;
glennrp0fe50b42010-11-16 03:52:51 +000010304
glennrpb4a13412010-05-05 12:47:19 +000010305 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010306 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010307 break;
glennrp0fe50b42010-11-16 03:52:51 +000010308
glennrpb4a13412010-05-05 12:47:19 +000010309 default:
10310 break;
cristy3ed852e2009-09-05 21:47:34 +000010311 }
glennrp3b51f0e2010-11-27 18:14:08 +000010312
10313 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010314 {
10315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10316 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010317
glennrpb4a13412010-05-05 12:47:19 +000010318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010319 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010320 }
glennrpcf002022011-01-30 02:38:15 +000010321 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10322 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010323
glennrpcf002022011-01-30 02:38:15 +000010324 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010325 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010326
cristy3ed852e2009-09-05 21:47:34 +000010327 /*
10328 Initialize image scanlines.
10329 */
glennrp5af765f2010-03-30 11:12:18 +000010330 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010331 {
10332 /*
10333 PNG write failed.
10334 */
10335#ifdef PNG_DEBUG
10336 if (image_info->verbose)
10337 (void) printf("PNG write has failed.\n");
10338#endif
10339 png_destroy_write_struct(&ping,&ping_info);
10340 if (quantum_info != (QuantumInfo *) NULL)
10341 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010342 if (ping_pixels != (unsigned char *) NULL)
10343 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010344#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010345 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010346#endif
glennrpda8f3a72011-02-27 23:54:12 +000010347 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010348 (void) CloseBlob(image);
10349 image_info=DestroyImageInfo(image_info);
10350 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010351 return(MagickFalse);
10352 }
cristyed552522009-10-16 14:04:35 +000010353 quantum_info=AcquireQuantumInfo(image_info,image);
10354 if (quantum_info == (QuantumInfo *) NULL)
10355 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010356 quantum_info->format=UndefinedQuantumFormat;
10357 quantum_info->depth=image_depth;
10358 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010359
cristy3ed852e2009-09-05 21:47:34 +000010360 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010361 !mng_info->write_png32) &&
10362 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010363 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010364 image_matte == MagickFalse &&
10365 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010366 {
glennrp8bb3a022010-12-13 20:40:04 +000010367 /* Palette, Bilevel, or Opaque Monochrome */
cristy4c08aed2011-07-01 19:47:50 +000010368 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010369 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010370
cristy3ed852e2009-09-05 21:47:34 +000010371 quantum_info->depth=8;
10372 for (pass=0; pass < num_passes; pass++)
10373 {
10374 /*
10375 Convert PseudoClass image to a PNG monochrome image.
10376 */
cristybb503372010-05-27 20:51:26 +000010377 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010378 {
glennrpd71e86a2011-02-24 01:28:37 +000010379 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010380 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10381 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010382
cristyc82a27b2011-10-21 01:07:16 +000010383 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010384
cristy4c08aed2011-07-01 19:47:50 +000010385 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010386 break;
glennrp0fe50b42010-11-16 03:52:51 +000010387
cristy3ed852e2009-09-05 21:47:34 +000010388 if (mng_info->IsPalette)
10389 {
cristy4c08aed2011-07-01 19:47:50 +000010390 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010391 quantum_info,GrayQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010392 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10393 mng_info->write_png_depth &&
10394 mng_info->write_png_depth != old_bit_depth)
10395 {
10396 /* Undo pixel scaling */
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)
cristy3ed852e2009-09-05 21:47:34 +000010399 >> (8-old_bit_depth));
10400 }
10401 }
glennrp0fe50b42010-11-16 03:52:51 +000010402
cristy3ed852e2009-09-05 21:47:34 +000010403 else
10404 {
cristy4c08aed2011-07-01 19:47:50 +000010405 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010406 quantum_info,RedQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010407 }
glennrp0fe50b42010-11-16 03:52:51 +000010408
cristy3ed852e2009-09-05 21:47:34 +000010409 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010410 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010411 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010412 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010413
glennrp3b51f0e2010-11-27 18:14:08 +000010414 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10416 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010417
glennrpcf002022011-01-30 02:38:15 +000010418 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010419 }
10420 if (image->previous == (Image *) NULL)
10421 {
10422 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10423 if (status == MagickFalse)
10424 break;
10425 }
10426 }
10427 }
glennrp0fe50b42010-11-16 03:52:51 +000010428
glennrp8bb3a022010-12-13 20:40:04 +000010429 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010430 {
glennrp0fe50b42010-11-16 03:52:51 +000010431 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010432 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010433 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010434 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010435 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010436 {
cristy4c08aed2011-07-01 19:47:50 +000010437 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010438 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010439
glennrp8bb3a022010-12-13 20:40:04 +000010440 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010441 {
glennrp8bb3a022010-12-13 20:40:04 +000010442
cristybb503372010-05-27 20:51:26 +000010443 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010444 {
cristyc82a27b2011-10-21 01:07:16 +000010445 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010446
cristy4c08aed2011-07-01 19:47:50 +000010447 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010448 break;
glennrp2cc891a2010-12-24 13:44:32 +000010449
glennrp5af765f2010-03-30 11:12:18 +000010450 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010451 {
glennrp8bb3a022010-12-13 20:40:04 +000010452 if (mng_info->IsPalette)
cristy4c08aed2011-07-01 19:47:50 +000010453 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010454 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010455
glennrp8bb3a022010-12-13 20:40:04 +000010456 else
cristy4c08aed2011-07-01 19:47:50 +000010457 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010458 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010459
glennrp3b51f0e2010-11-27 18:14:08 +000010460 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010462 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010463 }
glennrp2cc891a2010-12-24 13:44:32 +000010464
glennrp8bb3a022010-12-13 20:40:04 +000010465 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10466 {
10467 if (logging != MagickFalse && y == 0)
10468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10469 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010470
cristy4c08aed2011-07-01 19:47:50 +000010471 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010472 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010473 }
glennrp2cc891a2010-12-24 13:44:32 +000010474
glennrp3b51f0e2010-11-27 18:14:08 +000010475 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010477 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010478
glennrpcf002022011-01-30 02:38:15 +000010479 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010480 }
glennrp2cc891a2010-12-24 13:44:32 +000010481
glennrp8bb3a022010-12-13 20:40:04 +000010482 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010483 {
glennrp8bb3a022010-12-13 20:40:04 +000010484 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10485 if (status == MagickFalse)
10486 break;
cristy3ed852e2009-09-05 21:47:34 +000010487 }
cristy3ed852e2009-09-05 21:47:34 +000010488 }
10489 }
glennrp8bb3a022010-12-13 20:40:04 +000010490
10491 else
10492 {
cristy4c08aed2011-07-01 19:47:50 +000010493 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010494 *p;
10495
10496 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010497 {
glennrp8bb3a022010-12-13 20:40:04 +000010498 if ((image_depth > 8) || (mng_info->write_png24 ||
10499 mng_info->write_png32 ||
10500 (!mng_info->write_png8 && !mng_info->IsPalette)))
10501 {
10502 for (y=0; y < (ssize_t) image->rows; y++)
10503 {
10504 p=GetVirtualPixels(image,0,y,image->columns,1,
cristyc82a27b2011-10-21 01:07:16 +000010505 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010506
cristy4c08aed2011-07-01 19:47:50 +000010507 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010508 break;
glennrp2cc891a2010-12-24 13:44:32 +000010509
glennrp8bb3a022010-12-13 20:40:04 +000010510 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10511 {
10512 if (image->storage_class == DirectClass)
cristy4c08aed2011-07-01 19:47:50 +000010513 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010514 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010515
glennrp8bb3a022010-12-13 20:40:04 +000010516 else
cristy4c08aed2011-07-01 19:47:50 +000010517 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010518 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010519 }
glennrp2cc891a2010-12-24 13:44:32 +000010520
glennrp8bb3a022010-12-13 20:40:04 +000010521 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10522 {
cristy4c08aed2011-07-01 19:47:50 +000010523 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010524 quantum_info,GrayAlphaQuantum,ping_pixels,
cristyc82a27b2011-10-21 01:07:16 +000010525 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 GRAY_ALPHA PNG pixels (3)");
10530 }
glennrp2cc891a2010-12-24 13:44:32 +000010531
glennrp8bb3a022010-12-13 20:40:04 +000010532 else if (image_matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +000010533 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010534 quantum_info,RGBAQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010535
glennrp8bb3a022010-12-13 20:40:04 +000010536 else
cristy4c08aed2011-07-01 19:47:50 +000010537 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010538 quantum_info,RGBQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010539
glennrp8bb3a022010-12-13 20:40:04 +000010540 if (logging != MagickFalse && y == 0)
10541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10542 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010543
glennrpcf002022011-01-30 02:38:15 +000010544 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010545 }
10546 }
glennrp2cc891a2010-12-24 13:44:32 +000010547
glennrp8bb3a022010-12-13 20:40:04 +000010548 else
10549 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10550 mng_info->write_png32 ||
10551 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10552 {
10553 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10554 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10555 {
10556 if (logging != MagickFalse)
10557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10558 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010559
glennrp8bb3a022010-12-13 20:40:04 +000010560 quantum_info->depth=8;
10561 image_depth=8;
10562 }
glennrp2cc891a2010-12-24 13:44:32 +000010563
glennrp8bb3a022010-12-13 20:40:04 +000010564 for (y=0; y < (ssize_t) image->rows; y++)
10565 {
10566 if (logging != MagickFalse && y == 0)
10567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10568 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010569
cristy97707062011-12-27 18:25:00 +000010570 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000010571
cristy4c08aed2011-07-01 19:47:50 +000010572 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010573 break;
glennrp2cc891a2010-12-24 13:44:32 +000010574
glennrp8bb3a022010-12-13 20:40:04 +000010575 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010576 {
glennrp4bf89732011-03-21 13:48:28 +000010577 quantum_info->depth=image->depth;
10578
cristy4c08aed2011-07-01 19:47:50 +000010579 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010580 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp44757ab2011-03-17 12:57:03 +000010581 }
glennrp2cc891a2010-12-24 13:44:32 +000010582
glennrp8bb3a022010-12-13 20:40:04 +000010583 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10584 {
10585 if (logging != MagickFalse && y == 0)
10586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10587 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010588
cristy4c08aed2011-07-01 19:47:50 +000010589 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010590 quantum_info,GrayAlphaQuantum,ping_pixels,
cristyc82a27b2011-10-21 01:07:16 +000010591 exception);
glennrp8bb3a022010-12-13 20:40:04 +000010592 }
glennrp2cc891a2010-12-24 13:44:32 +000010593
glennrp8bb3a022010-12-13 20:40:04 +000010594 else
glennrp8bb3a022010-12-13 20:40:04 +000010595 {
cristy4c08aed2011-07-01 19:47:50 +000010596 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010597 quantum_info,IndexQuantum,ping_pixels,exception);
glennrp5eae7602011-02-22 15:21:32 +000010598
10599 if (logging != MagickFalse && y <= 2)
10600 {
10601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010602 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010603
10604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10605 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10606 (int)ping_pixels[0],(int)ping_pixels[1]);
10607 }
glennrp8bb3a022010-12-13 20:40:04 +000010608 }
glennrpcf002022011-01-30 02:38:15 +000010609 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010610 }
10611 }
glennrp2cc891a2010-12-24 13:44:32 +000010612
glennrp8bb3a022010-12-13 20:40:04 +000010613 if (image->previous == (Image *) NULL)
10614 {
10615 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10616 if (status == MagickFalse)
10617 break;
10618 }
cristy3ed852e2009-09-05 21:47:34 +000010619 }
glennrp8bb3a022010-12-13 20:40:04 +000010620 }
10621 }
10622
cristyb32b90a2009-09-07 21:45:48 +000010623 if (quantum_info != (QuantumInfo *) NULL)
10624 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010625
10626 if (logging != MagickFalse)
10627 {
10628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010629 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010630
cristy3ed852e2009-09-05 21:47:34 +000010631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010632 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010633
cristy3ed852e2009-09-05 21:47:34 +000010634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010635 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010636
cristy3ed852e2009-09-05 21:47:34 +000010637 if (mng_info->write_png_depth)
10638 {
10639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010640 " Defined png:bit-depth: %d",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010641 }
glennrp0fe50b42010-11-16 03:52:51 +000010642
cristy3ed852e2009-09-05 21:47:34 +000010643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010644 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010645
cristy3ed852e2009-09-05 21:47:34 +000010646 if (mng_info->write_png_colortype)
10647 {
10648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010649 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010650 }
glennrp0fe50b42010-11-16 03:52:51 +000010651
cristy3ed852e2009-09-05 21:47:34 +000010652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010653 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010654
cristy3ed852e2009-09-05 21:47:34 +000010655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010656 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010657 }
10658 /*
glennrpa0ed0092011-04-18 16:36:29 +000010659 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010660 */
glennrp823b55c2011-03-14 18:46:46 +000010661 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010662 {
glennrp26f37912010-12-23 16:22:42 +000010663 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010664 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010665 while (property != (const char *) NULL)
10666 {
10667 png_textp
10668 text;
glennrp2cc891a2010-12-24 13:44:32 +000010669
cristyd15e6592011-10-15 00:13:06 +000010670 value=GetImageProperty(image,property,exception);
glennrpa0ed0092011-04-18 16:36:29 +000010671
10672 /* Don't write any "png:" properties; those are just for "identify" */
10673 if (LocaleNCompare(property,"png:",4) != 0 &&
10674
10675 /* Suppress density and units if we wrote a pHYs chunk */
10676 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010677 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010678 LocaleCompare(property,"units") != 0) &&
10679
10680 /* Suppress the IM-generated Date:create and Date:modify */
10681 (ping_exclude_date == MagickFalse ||
10682 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010683 {
glennrpc70af4a2011-03-07 00:08:23 +000010684 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010685 {
glennrpc70af4a2011-03-07 00:08:23 +000010686 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10687 text[0].key=(char *) property;
10688 text[0].text=(char *) value;
10689 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010690
glennrpc70af4a2011-03-07 00:08:23 +000010691 if (ping_exclude_tEXt != MagickFalse)
10692 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10693
10694 else if (ping_exclude_zTXt != MagickFalse)
10695 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10696
10697 else
glennrp26f37912010-12-23 16:22:42 +000010698 {
glennrpc70af4a2011-03-07 00:08:23 +000010699 text[0].compression=image_info->compression == NoCompression ||
10700 (image_info->compression == UndefinedCompression &&
10701 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10702 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010703 }
glennrp2cc891a2010-12-24 13:44:32 +000010704
glennrpc70af4a2011-03-07 00:08:23 +000010705 if (logging != MagickFalse)
10706 {
10707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10708 " Setting up text chunk");
10709
10710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10711 " keyword: %s",text[0].key);
10712 }
10713
10714 png_set_text(ping,ping_info,text,1);
10715 png_free(ping,text);
10716 }
glennrp26f37912010-12-23 16:22:42 +000010717 }
10718 property=GetNextImageProperty(image);
10719 }
cristy3ed852e2009-09-05 21:47:34 +000010720 }
10721
10722 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010723 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010724
10725 if (logging != MagickFalse)
10726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10727 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010728
cristy3ed852e2009-09-05 21:47:34 +000010729 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010730
cristy3ed852e2009-09-05 21:47:34 +000010731 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10732 {
10733 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010734 (ping_width != mng_info->page.width) ||
10735 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010736 {
10737 unsigned char
10738 chunk[32];
10739
10740 /*
10741 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10742 */
10743 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10744 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010745 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010746 chunk[4]=4;
10747 chunk[5]=0; /* frame name separator (no name) */
10748 chunk[6]=1; /* flag for changing delay, for next frame only */
10749 chunk[7]=0; /* flag for changing frame timeout */
10750 chunk[8]=1; /* flag for changing frame clipping for next frame */
10751 chunk[9]=0; /* flag for changing frame sync_id */
10752 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10753 chunk[14]=0; /* clipping boundaries delta type */
10754 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10755 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010756 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010757 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10758 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010759 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010760 (void) WriteBlob(image,31,chunk);
10761 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10762 mng_info->old_framing_mode=4;
10763 mng_info->framing_mode=1;
10764 }
glennrp0fe50b42010-11-16 03:52:51 +000010765
cristy3ed852e2009-09-05 21:47:34 +000010766 else
10767 mng_info->framing_mode=3;
10768 }
10769 if (mng_info->write_mng && !mng_info->need_fram &&
10770 ((int) image->dispose == 3))
cristyc82a27b2011-10-21 01:07:16 +000010771 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010772 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010773 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010774
cristy3ed852e2009-09-05 21:47:34 +000010775 /*
10776 Free PNG resources.
10777 */
glennrp5af765f2010-03-30 11:12:18 +000010778
cristy3ed852e2009-09-05 21:47:34 +000010779 png_destroy_write_struct(&ping,&ping_info);
10780
glennrpcf002022011-01-30 02:38:15 +000010781 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010782
10783#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010784 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010785#endif
10786
glennrpda8f3a72011-02-27 23:54:12 +000010787 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010788 (void) CloseBlob(image);
10789
10790 image_info=DestroyImageInfo(image_info);
10791 image=DestroyImage(image);
10792
10793 /* Store bit depth actually written */
10794 s[0]=(char) ping_bit_depth;
10795 s[1]='\0';
10796
cristyd15e6592011-10-15 00:13:06 +000010797 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
glennrpb9cfe272010-12-21 15:08:06 +000010798
cristy3ed852e2009-09-05 21:47:34 +000010799 if (logging != MagickFalse)
10800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10801 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010802
cristy3ed852e2009-09-05 21:47:34 +000010803 return(MagickTrue);
10804/* End write one PNG image */
10805}
10806
10807/*
10808%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10809% %
10810% %
10811% %
10812% W r i t e P N G I m a g e %
10813% %
10814% %
10815% %
10816%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10817%
10818% WritePNGImage() writes a Portable Network Graphics (PNG) or
10819% Multiple-image Network Graphics (MNG) image file.
10820%
10821% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10822%
10823% The format of the WritePNGImage method is:
10824%
cristy1e178e72011-08-28 19:44:34 +000010825% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10826% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010827%
10828% A description of each parameter follows:
10829%
10830% o image_info: the image info.
10831%
10832% o image: The image.
10833%
cristy1e178e72011-08-28 19:44:34 +000010834% o exception: return any errors or warnings in this structure.
10835%
cristy3ed852e2009-09-05 21:47:34 +000010836% Returns MagickTrue on success, MagickFalse on failure.
10837%
10838% Communicating with the PNG encoder:
10839%
10840% While the datastream written is always in PNG format and normally would
10841% be given the "png" file extension, this method also writes the following
cristy5d6fc9c2011-12-27 03:10:42 +000010842% pseudo-formats which are subsets of png:
cristy3ed852e2009-09-05 21:47:34 +000010843%
glennrp5a39f372011-02-25 04:52:16 +000010844% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10845% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010846% is present, the tRNS chunk must only have values 0 and 255
10847% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010848% transparent). If other values are present they will be
10849% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010850% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000010851% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10852% of any resulting fully-transparent pixels is changed to
10853% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000010854%
10855% If you want better quantization or dithering of the colors
10856% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010857% PNG encoder. The pixels contain 8-bit indices even if
10858% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010859% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010860% PNG grayscale type might be slightly more efficient. Please
10861% note that writing to the PNG8 format may result in loss
10862% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010863%
10864% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10865% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010866% one of the colors as transparent. The only loss incurred
10867% is reduction of sample depth to 8. If the image has more
10868% than one transparent color, has semitransparent pixels, or
10869% has an opaque pixel with the same RGB components as the
10870% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010871%
10872% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10873% transparency is permitted, i.e., the alpha sample for
10874% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010875% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010876% The only loss in data is the reduction of the sample depth
10877% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010878%
10879% o -define: For more precise control of the PNG output, you can use the
10880% Image options "png:bit-depth" and "png:color-type". These
10881% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010882% from the application programming interfaces. The options
10883% are case-independent and are converted to lowercase before
10884% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010885%
10886% png:color-type can be 0, 2, 3, 4, or 6.
10887%
10888% When png:color-type is 0 (Grayscale), png:bit-depth can
10889% be 1, 2, 4, 8, or 16.
10890%
10891% When png:color-type is 2 (RGB), png:bit-depth can
10892% be 8 or 16.
10893%
10894% When png:color-type is 3 (Indexed), png:bit-depth can
10895% be 1, 2, 4, or 8. This refers to the number of bits
10896% used to store the index. The color samples always have
10897% bit-depth 8 in indexed PNG files.
10898%
10899% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10900% png:bit-depth can be 8 or 16.
10901%
glennrp5a39f372011-02-25 04:52:16 +000010902% If the image cannot be written without loss with the requested bit-depth
10903% and color-type, a PNG file will not be written, and the encoder will
10904% return MagickFalse.
10905%
cristy3ed852e2009-09-05 21:47:34 +000010906% Since image encoders should not be responsible for the "heavy lifting",
10907% the user should make sure that ImageMagick has already reduced the
10908% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010909% transparency prior to attempting to write the image with depth, color,
glennrp54dc0692011-07-01 19:02:13 +000010910% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010911%
cristy3ed852e2009-09-05 21:47:34 +000010912% Note that another definition, "png:bit-depth-written" exists, but it
10913% is not intended for external use. It is only used internally by the
10914% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10915%
10916% It is possible to request that the PNG encoder write previously-formatted
10917% ancillary chunks in the output PNG file, using the "-profile" commandline
10918% option as shown below or by setting the profile via a programming
10919% interface:
10920%
10921% -profile PNG-chunk-x:<file>
10922%
10923% where x is a location flag and <file> is a file containing the chunk
10924% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010925% This encoder will compute the chunk length and CRC, so those must not
10926% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010927%
10928% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10929% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10930% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010931% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010932%
glennrpbb8a7332010-11-13 15:17:35 +000010933% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010934%
glennrp3241bd02010-12-12 04:36:28 +000010935% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010936%
glennrpd6afd542010-11-19 01:53:05 +000010937% o 32-bit depth is reduced to 16.
10938% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10939% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010940% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010941% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010942% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010943% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10944% this can be done without loss and a larger bit depth N was not
cristy5d6fc9c2011-12-27 03:10:42 +000010945% requested via the "-define png:bit-depth=N" option.
glennrpd6afd542010-11-19 01:53:05 +000010946% o If matte channel is present but only one transparent color is
10947% present, RGB+tRNS is written instead of RGBA
10948% o Opaque matte channel is removed (or added, if color-type 4 or 6
10949% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010950%
cristy3ed852e2009-09-05 21:47:34 +000010951%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10952*/
10953static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +000010954 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010955{
10956 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010957 excluding,
10958 logging,
10959 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010960 status;
10961
10962 MngInfo
10963 *mng_info;
10964
10965 const char
10966 *value;
10967
10968 int
glennrp21f0e622011-01-07 16:20:57 +000010969 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010970 source;
10971
cristy3ed852e2009-09-05 21:47:34 +000010972 /*
10973 Open image file.
10974 */
10975 assert(image_info != (const ImageInfo *) NULL);
10976 assert(image_info->signature == MagickSignature);
10977 assert(image != (Image *) NULL);
10978 assert(image->signature == MagickSignature);
10979 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010980 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010981 /*
10982 Allocate a MngInfo structure.
10983 */
10984 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010985 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010986
cristy3ed852e2009-09-05 21:47:34 +000010987 if (mng_info == (MngInfo *) NULL)
10988 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010989
cristy3ed852e2009-09-05 21:47:34 +000010990 /*
10991 Initialize members of the MngInfo structure.
10992 */
10993 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10994 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010995 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010996 have_mng_structure=MagickTrue;
10997
10998 /* See if user has requested a specific PNG subformat */
10999
11000 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11001 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11002 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11003
11004 if (mng_info->write_png8)
11005 {
glennrp9c1eb072010-06-06 22:19:15 +000011006 mng_info->write_png_colortype = /* 3 */ 4;
11007 mng_info->write_png_depth = 8;
11008 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000011009 }
11010
11011 if (mng_info->write_png24)
11012 {
glennrp9c1eb072010-06-06 22:19:15 +000011013 mng_info->write_png_colortype = /* 2 */ 3;
11014 mng_info->write_png_depth = 8;
11015 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011016
glennrp9c1eb072010-06-06 22:19:15 +000011017 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011018 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011019
glennrp9c1eb072010-06-06 22:19:15 +000011020 else
cristy018f07f2011-09-04 21:15:19 +000011021 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011022
cristyea1a8aa2011-10-20 13:24:06 +000011023 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011024 }
11025
11026 if (mng_info->write_png32)
11027 {
glennrp9c1eb072010-06-06 22:19:15 +000011028 mng_info->write_png_colortype = /* 6 */ 7;
11029 mng_info->write_png_depth = 8;
11030 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011031
glennrp9c1eb072010-06-06 22:19:15 +000011032 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011033 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011034
glennrp9c1eb072010-06-06 22:19:15 +000011035 else
cristy018f07f2011-09-04 21:15:19 +000011036 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011037
cristyea1a8aa2011-10-20 13:24:06 +000011038 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011039 }
11040
11041 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011042
cristy3ed852e2009-09-05 21:47:34 +000011043 if (value != (char *) NULL)
11044 {
11045 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011046 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011047
cristy3ed852e2009-09-05 21:47:34 +000011048 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011049 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011050
cristy3ed852e2009-09-05 21:47:34 +000011051 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011052 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011053
cristy3ed852e2009-09-05 21:47:34 +000011054 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011055 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011056
cristy3ed852e2009-09-05 21:47:34 +000011057 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011058 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011059
glennrpbb8a7332010-11-13 15:17:35 +000011060 else
cristyc82a27b2011-10-21 01:07:16 +000011061 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011062 GetMagickModule(),CoderWarning,
11063 "ignoring invalid defined png:bit-depth",
11064 "=%s",value);
11065
cristy3ed852e2009-09-05 21:47:34 +000011066 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011068 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011069 }
glennrp0fe50b42010-11-16 03:52:51 +000011070
cristy3ed852e2009-09-05 21:47:34 +000011071 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011072
cristy3ed852e2009-09-05 21:47:34 +000011073 if (value != (char *) NULL)
11074 {
11075 /* We must store colortype+1 because 0 is a valid colortype */
11076 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011077 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011078
glennrpaa9c3c72012-01-30 21:14:50 +000011079 else if (LocaleCompare(value,"1") == 0)
11080 mng_info->write_png_colortype = 2;
11081
cristy3ed852e2009-09-05 21:47:34 +000011082 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011083 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011084
cristy3ed852e2009-09-05 21:47:34 +000011085 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011086 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011087
cristy3ed852e2009-09-05 21:47:34 +000011088 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011089 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011090
cristy3ed852e2009-09-05 21:47:34 +000011091 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011092 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011093
glennrpbb8a7332010-11-13 15:17:35 +000011094 else
cristyc82a27b2011-10-21 01:07:16 +000011095 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011096 GetMagickModule(),CoderWarning,
11097 "ignoring invalid defined png:color-type",
11098 "=%s",value);
11099
cristy3ed852e2009-09-05 21:47:34 +000011100 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011102 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011103 }
11104
glennrp0e8ea192010-12-24 18:00:33 +000011105 /* Check for chunks to be excluded:
11106 *
glennrp0dff56c2011-01-29 19:10:02 +000011107 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011108 * listed in the "unused_chunks" array, above.
11109 *
cristy5d6fc9c2011-12-27 03:10:42 +000011110 * Chunks can be listed for exclusion via a "png:exclude-chunk"
glennrp0e8ea192010-12-24 18:00:33 +000011111 * define (in the image properties or in the image artifacts)
11112 * or via a mng_info member. For convenience, in addition
11113 * to or instead of a comma-separated list of chunks, the
11114 * "exclude-chunk" string can be simply "all" or "none".
11115 *
11116 * The exclude-chunk define takes priority over the mng_info.
11117 *
cristy5d6fc9c2011-12-27 03:10:42 +000011118 * A "png:include-chunk" define takes priority over both the
11119 * mng_info and the "png:exclude-chunk" define. Like the
glennrp0e8ea192010-12-24 18:00:33 +000011120 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011121 * well as a comma-separated list. Chunks that are unknown to
11122 * ImageMagick are always excluded, regardless of their "copy-safe"
11123 * status according to the PNG specification, and even if they
glennrpaa192b12012-01-17 21:35:21 +000011124 * appear in the "include-chunk" list. Such defines appearing among
11125 * the image options take priority over those found among the image
11126 * artifacts.
glennrp0e8ea192010-12-24 18:00:33 +000011127 *
11128 * Finally, all chunks listed in the "unused_chunks" array are
11129 * automatically excluded, regardless of the other instructions
11130 * or lack thereof.
11131 *
11132 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11133 * will not be written and the gAMA chunk will only be written if it
11134 * is not between .45 and .46, or approximately (1.0/2.2).
11135 *
11136 * If you exclude tRNS and the image has transparency, the colortype
11137 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11138 *
11139 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011140 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011141 */
11142
glennrp26f37912010-12-23 16:22:42 +000011143 mng_info->ping_exclude_bKGD=MagickFalse;
11144 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011145 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011146 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11147 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011148 mng_info->ping_exclude_iCCP=MagickFalse;
11149 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11150 mng_info->ping_exclude_oFFs=MagickFalse;
11151 mng_info->ping_exclude_pHYs=MagickFalse;
11152 mng_info->ping_exclude_sRGB=MagickFalse;
11153 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011154 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011155 mng_info->ping_exclude_vpAg=MagickFalse;
11156 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11157 mng_info->ping_exclude_zTXt=MagickFalse;
11158
glennrp8d3d6e52011-04-19 04:39:51 +000011159 mng_info->ping_preserve_colormap=MagickFalse;
11160
11161 value=GetImageArtifact(image,"png:preserve-colormap");
11162 if (value == NULL)
11163 value=GetImageOption(image_info,"png:preserve-colormap");
11164 if (value != NULL)
11165 mng_info->ping_preserve_colormap=MagickTrue;
11166
glennrp18682582011-06-30 18:11:47 +000011167 /* Thes compression-level, compression-strategy, and compression-filter
11168 * defines take precedence over values from the -quality option.
11169 */
11170 value=GetImageArtifact(image,"png:compression-level");
11171 if (value == NULL)
11172 value=GetImageOption(image_info,"png:compression-level");
11173 if (value != NULL)
11174 {
glennrp18682582011-06-30 18:11:47 +000011175 /* We have to add 1 to everything because 0 is a valid input,
11176 * and we want to use 0 (the default) to mean undefined.
11177 */
11178 if (LocaleCompare(value,"0") == 0)
11179 mng_info->write_png_compression_level = 1;
11180
glennrp0ffb95c2012-01-30 21:16:22 +000011181 else if (LocaleCompare(value,"1") == 0)
glennrp18682582011-06-30 18:11:47 +000011182 mng_info->write_png_compression_level = 2;
11183
11184 else if (LocaleCompare(value,"2") == 0)
11185 mng_info->write_png_compression_level = 3;
11186
11187 else if (LocaleCompare(value,"3") == 0)
11188 mng_info->write_png_compression_level = 4;
11189
11190 else if (LocaleCompare(value,"4") == 0)
11191 mng_info->write_png_compression_level = 5;
11192
11193 else if (LocaleCompare(value,"5") == 0)
11194 mng_info->write_png_compression_level = 6;
11195
11196 else if (LocaleCompare(value,"6") == 0)
11197 mng_info->write_png_compression_level = 7;
11198
11199 else if (LocaleCompare(value,"7") == 0)
11200 mng_info->write_png_compression_level = 8;
11201
11202 else if (LocaleCompare(value,"8") == 0)
11203 mng_info->write_png_compression_level = 9;
11204
11205 else if (LocaleCompare(value,"9") == 0)
11206 mng_info->write_png_compression_level = 10;
11207
11208 else
cristyc82a27b2011-10-21 01:07:16 +000011209 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011210 GetMagickModule(),CoderWarning,
11211 "ignoring invalid defined png:compression-level",
11212 "=%s",value);
11213 }
11214
11215 value=GetImageArtifact(image,"png:compression-strategy");
11216 if (value == NULL)
11217 value=GetImageOption(image_info,"png:compression-strategy");
11218 if (value != NULL)
11219 {
11220
11221 if (LocaleCompare(value,"0") == 0)
11222 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11223
11224 else if (LocaleCompare(value,"1") == 0)
11225 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11226
11227 else if (LocaleCompare(value,"2") == 0)
11228 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11229
11230 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011231#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011232 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011233#else
11234 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11235#endif
glennrp18682582011-06-30 18:11:47 +000011236
11237 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011238#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011239 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011240#else
11241 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11242#endif
glennrp18682582011-06-30 18:11:47 +000011243
11244 else
cristyc82a27b2011-10-21 01:07:16 +000011245 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011246 GetMagickModule(),CoderWarning,
11247 "ignoring invalid defined png:compression-strategy",
11248 "=%s",value);
11249 }
11250
11251 value=GetImageArtifact(image,"png:compression-filter");
11252 if (value == NULL)
11253 value=GetImageOption(image_info,"png:compression-filter");
11254 if (value != NULL)
11255 {
11256
11257 /* To do: combinations of filters allowed by libpng
11258 * masks 0x08 through 0xf8
11259 *
11260 * Implement this as a comma-separated list of 0,1,2,3,4,5
11261 * where 5 is a special case meaning PNG_ALL_FILTERS.
11262 */
11263
11264 if (LocaleCompare(value,"0") == 0)
11265 mng_info->write_png_compression_filter = 1;
11266
11267 if (LocaleCompare(value,"1") == 0)
11268 mng_info->write_png_compression_filter = 2;
11269
11270 else if (LocaleCompare(value,"2") == 0)
11271 mng_info->write_png_compression_filter = 3;
11272
11273 else if (LocaleCompare(value,"3") == 0)
11274 mng_info->write_png_compression_filter = 4;
11275
11276 else if (LocaleCompare(value,"4") == 0)
11277 mng_info->write_png_compression_filter = 5;
11278
11279 else if (LocaleCompare(value,"5") == 0)
11280 mng_info->write_png_compression_filter = 6;
11281
glennrp18682582011-06-30 18:11:47 +000011282 else
cristyc82a27b2011-10-21 01:07:16 +000011283 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011284 GetMagickModule(),CoderWarning,
11285 "ignoring invalid defined png:compression-filter",
11286 "=%s",value);
11287 }
11288
glennrp03812ae2010-12-24 01:31:34 +000011289 excluding=MagickFalse;
11290
glennrp5c7cf4e2010-12-24 00:30:00 +000011291 for (source=0; source<1; source++)
11292 {
11293 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011294 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011295 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011296
11297 if (value == NULL)
11298 value=GetImageArtifact(image,"png:exclude-chunks");
11299 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011300 else
glennrpacba0042010-12-24 14:27:26 +000011301 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011302 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011303
glennrpacba0042010-12-24 14:27:26 +000011304 if (value == NULL)
11305 value=GetImageOption(image_info,"png:exclude-chunks");
11306 }
11307
glennrp03812ae2010-12-24 01:31:34 +000011308 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011309 {
glennrp03812ae2010-12-24 01:31:34 +000011310
11311 size_t
11312 last;
11313
11314 excluding=MagickTrue;
11315
11316 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011317 {
11318 if (source == 0)
11319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11320 " png:exclude-chunk=%s found in image artifacts.\n", value);
11321 else
11322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11323 " png:exclude-chunk=%s found in image properties.\n", value);
11324 }
glennrp03812ae2010-12-24 01:31:34 +000011325
11326 last=strlen(value);
11327
11328 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011329 {
glennrp03812ae2010-12-24 01:31:34 +000011330
11331 if (LocaleNCompare(value+i,"all",3) == 0)
11332 {
11333 mng_info->ping_exclude_bKGD=MagickTrue;
11334 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011335 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011336 mng_info->ping_exclude_EXIF=MagickTrue;
11337 mng_info->ping_exclude_gAMA=MagickTrue;
11338 mng_info->ping_exclude_iCCP=MagickTrue;
11339 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11340 mng_info->ping_exclude_oFFs=MagickTrue;
11341 mng_info->ping_exclude_pHYs=MagickTrue;
11342 mng_info->ping_exclude_sRGB=MagickTrue;
11343 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011344 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011345 mng_info->ping_exclude_vpAg=MagickTrue;
11346 mng_info->ping_exclude_zCCP=MagickTrue;
11347 mng_info->ping_exclude_zTXt=MagickTrue;
11348 i--;
11349 }
glennrp2cc891a2010-12-24 13:44:32 +000011350
glennrp03812ae2010-12-24 01:31:34 +000011351 if (LocaleNCompare(value+i,"none",4) == 0)
11352 {
11353 mng_info->ping_exclude_bKGD=MagickFalse;
11354 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011355 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011356 mng_info->ping_exclude_EXIF=MagickFalse;
11357 mng_info->ping_exclude_gAMA=MagickFalse;
11358 mng_info->ping_exclude_iCCP=MagickFalse;
11359 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11360 mng_info->ping_exclude_oFFs=MagickFalse;
11361 mng_info->ping_exclude_pHYs=MagickFalse;
11362 mng_info->ping_exclude_sRGB=MagickFalse;
11363 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011364 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011365 mng_info->ping_exclude_vpAg=MagickFalse;
11366 mng_info->ping_exclude_zCCP=MagickFalse;
11367 mng_info->ping_exclude_zTXt=MagickFalse;
11368 }
glennrp2cc891a2010-12-24 13:44:32 +000011369
glennrp03812ae2010-12-24 01:31:34 +000011370 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11371 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011372
glennrp03812ae2010-12-24 01:31:34 +000011373 if (LocaleNCompare(value+i,"chrm",4) == 0)
11374 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011375
glennrpa0ed0092011-04-18 16:36:29 +000011376 if (LocaleNCompare(value+i,"date",4) == 0)
11377 mng_info->ping_exclude_date=MagickTrue;
11378
glennrp03812ae2010-12-24 01:31:34 +000011379 if (LocaleNCompare(value+i,"exif",4) == 0)
11380 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011381
glennrp03812ae2010-12-24 01:31:34 +000011382 if (LocaleNCompare(value+i,"gama",4) == 0)
11383 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011384
glennrp03812ae2010-12-24 01:31:34 +000011385 if (LocaleNCompare(value+i,"iccp",4) == 0)
11386 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011387
glennrp03812ae2010-12-24 01:31:34 +000011388 /*
11389 if (LocaleNCompare(value+i,"itxt",4) == 0)
11390 mng_info->ping_exclude_iTXt=MagickTrue;
11391 */
glennrp2cc891a2010-12-24 13:44:32 +000011392
glennrp03812ae2010-12-24 01:31:34 +000011393 if (LocaleNCompare(value+i,"gama",4) == 0)
11394 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011395
glennrp03812ae2010-12-24 01:31:34 +000011396 if (LocaleNCompare(value+i,"offs",4) == 0)
11397 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011398
glennrp03812ae2010-12-24 01:31:34 +000011399 if (LocaleNCompare(value+i,"phys",4) == 0)
11400 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011401
glennrpa1e3b7b2010-12-24 16:37:33 +000011402 if (LocaleNCompare(value+i,"srgb",4) == 0)
11403 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011404
glennrp03812ae2010-12-24 01:31:34 +000011405 if (LocaleNCompare(value+i,"text",4) == 0)
11406 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011407
glennrpa1e3b7b2010-12-24 16:37:33 +000011408 if (LocaleNCompare(value+i,"trns",4) == 0)
11409 mng_info->ping_exclude_tRNS=MagickTrue;
11410
glennrp03812ae2010-12-24 01:31:34 +000011411 if (LocaleNCompare(value+i,"vpag",4) == 0)
11412 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011413
glennrp03812ae2010-12-24 01:31:34 +000011414 if (LocaleNCompare(value+i,"zccp",4) == 0)
11415 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011416
glennrp03812ae2010-12-24 01:31:34 +000011417 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11418 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011419
glennrp03812ae2010-12-24 01:31:34 +000011420 }
glennrpce91ed52010-12-23 22:37:49 +000011421 }
glennrp26f37912010-12-23 16:22:42 +000011422 }
11423
glennrp5c7cf4e2010-12-24 00:30:00 +000011424 for (source=0; source<1; source++)
11425 {
11426 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011427 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011428 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011429
11430 if (value == NULL)
11431 value=GetImageArtifact(image,"png:include-chunks");
11432 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011433 else
glennrpacba0042010-12-24 14:27:26 +000011434 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011435 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011436
glennrpacba0042010-12-24 14:27:26 +000011437 if (value == NULL)
11438 value=GetImageOption(image_info,"png:include-chunks");
11439 }
11440
glennrp03812ae2010-12-24 01:31:34 +000011441 if (value != NULL)
11442 {
11443 size_t
11444 last;
glennrp26f37912010-12-23 16:22:42 +000011445
glennrp03812ae2010-12-24 01:31:34 +000011446 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011447
glennrp03812ae2010-12-24 01:31:34 +000011448 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011449 {
11450 if (source == 0)
11451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11452 " png:include-chunk=%s found in image artifacts.\n", value);
11453 else
11454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11455 " png:include-chunk=%s found in image properties.\n", value);
11456 }
glennrp03812ae2010-12-24 01:31:34 +000011457
11458 last=strlen(value);
11459
11460 for (i=0; i<(int) last; i+=5)
11461 {
11462 if (LocaleNCompare(value+i,"all",3) == 0)
11463 {
11464 mng_info->ping_exclude_bKGD=MagickFalse;
11465 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011466 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011467 mng_info->ping_exclude_EXIF=MagickFalse;
11468 mng_info->ping_exclude_gAMA=MagickFalse;
11469 mng_info->ping_exclude_iCCP=MagickFalse;
11470 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11471 mng_info->ping_exclude_oFFs=MagickFalse;
11472 mng_info->ping_exclude_pHYs=MagickFalse;
11473 mng_info->ping_exclude_sRGB=MagickFalse;
11474 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011475 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011476 mng_info->ping_exclude_vpAg=MagickFalse;
11477 mng_info->ping_exclude_zCCP=MagickFalse;
11478 mng_info->ping_exclude_zTXt=MagickFalse;
11479 i--;
11480 }
glennrp2cc891a2010-12-24 13:44:32 +000011481
glennrp03812ae2010-12-24 01:31:34 +000011482 if (LocaleNCompare(value+i,"none",4) == 0)
11483 {
11484 mng_info->ping_exclude_bKGD=MagickTrue;
11485 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011486 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011487 mng_info->ping_exclude_EXIF=MagickTrue;
11488 mng_info->ping_exclude_gAMA=MagickTrue;
11489 mng_info->ping_exclude_iCCP=MagickTrue;
11490 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11491 mng_info->ping_exclude_oFFs=MagickTrue;
11492 mng_info->ping_exclude_pHYs=MagickTrue;
11493 mng_info->ping_exclude_sRGB=MagickTrue;
11494 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011495 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011496 mng_info->ping_exclude_vpAg=MagickTrue;
11497 mng_info->ping_exclude_zCCP=MagickTrue;
11498 mng_info->ping_exclude_zTXt=MagickTrue;
11499 }
glennrp2cc891a2010-12-24 13:44:32 +000011500
glennrp03812ae2010-12-24 01:31:34 +000011501 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11502 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011503
glennrp03812ae2010-12-24 01:31:34 +000011504 if (LocaleNCompare(value+i,"chrm",4) == 0)
11505 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011506
glennrpa0ed0092011-04-18 16:36:29 +000011507 if (LocaleNCompare(value+i,"date",4) == 0)
11508 mng_info->ping_exclude_date=MagickFalse;
11509
glennrp03812ae2010-12-24 01:31:34 +000011510 if (LocaleNCompare(value+i,"exif",4) == 0)
11511 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011512
glennrp03812ae2010-12-24 01:31:34 +000011513 if (LocaleNCompare(value+i,"gama",4) == 0)
11514 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011515
glennrp03812ae2010-12-24 01:31:34 +000011516 if (LocaleNCompare(value+i,"iccp",4) == 0)
11517 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011518
glennrp03812ae2010-12-24 01:31:34 +000011519 /*
11520 if (LocaleNCompare(value+i,"itxt",4) == 0)
11521 mng_info->ping_exclude_iTXt=MagickFalse;
11522 */
glennrp2cc891a2010-12-24 13:44:32 +000011523
glennrp03812ae2010-12-24 01:31:34 +000011524 if (LocaleNCompare(value+i,"gama",4) == 0)
11525 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011526
glennrp03812ae2010-12-24 01:31:34 +000011527 if (LocaleNCompare(value+i,"offs",4) == 0)
11528 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011529
glennrp03812ae2010-12-24 01:31:34 +000011530 if (LocaleNCompare(value+i,"phys",4) == 0)
11531 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011532
glennrpa1e3b7b2010-12-24 16:37:33 +000011533 if (LocaleNCompare(value+i,"srgb",4) == 0)
11534 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011535
glennrp03812ae2010-12-24 01:31:34 +000011536 if (LocaleNCompare(value+i,"text",4) == 0)
11537 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011538
glennrpa1e3b7b2010-12-24 16:37:33 +000011539 if (LocaleNCompare(value+i,"trns",4) == 0)
11540 mng_info->ping_exclude_tRNS=MagickFalse;
11541
glennrp03812ae2010-12-24 01:31:34 +000011542 if (LocaleNCompare(value+i,"vpag",4) == 0)
11543 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011544
glennrp03812ae2010-12-24 01:31:34 +000011545 if (LocaleNCompare(value+i,"zccp",4) == 0)
11546 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011547
glennrp03812ae2010-12-24 01:31:34 +000011548 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11549 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011550
glennrp03812ae2010-12-24 01:31:34 +000011551 }
glennrpce91ed52010-12-23 22:37:49 +000011552 }
glennrp26f37912010-12-23 16:22:42 +000011553 }
11554
glennrp03812ae2010-12-24 01:31:34 +000011555 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011556 {
11557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011558 " Chunks to be excluded from the output png:");
glennrp26f37912010-12-23 16:22:42 +000011559 if (mng_info->ping_exclude_bKGD != MagickFalse)
11560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11561 " bKGD");
11562 if (mng_info->ping_exclude_cHRM != MagickFalse)
11563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11564 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011565 if (mng_info->ping_exclude_date != MagickFalse)
11566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11567 " date");
glennrp26f37912010-12-23 16:22:42 +000011568 if (mng_info->ping_exclude_EXIF != MagickFalse)
11569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11570 " EXIF");
11571 if (mng_info->ping_exclude_gAMA != MagickFalse)
11572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11573 " gAMA");
11574 if (mng_info->ping_exclude_iCCP != MagickFalse)
11575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11576 " iCCP");
11577/*
11578 if (mng_info->ping_exclude_iTXt != MagickFalse)
11579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11580 " iTXt");
11581*/
11582 if (mng_info->ping_exclude_oFFs != MagickFalse)
11583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11584 " oFFs");
11585 if (mng_info->ping_exclude_pHYs != MagickFalse)
11586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11587 " pHYs");
11588 if (mng_info->ping_exclude_sRGB != MagickFalse)
11589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11590 " sRGB");
11591 if (mng_info->ping_exclude_tEXt != MagickFalse)
11592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11593 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011594 if (mng_info->ping_exclude_tRNS != MagickFalse)
11595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11596 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011597 if (mng_info->ping_exclude_vpAg != MagickFalse)
11598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11599 " vpAg");
11600 if (mng_info->ping_exclude_zCCP != MagickFalse)
11601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11602 " zCCP");
11603 if (mng_info->ping_exclude_zTXt != MagickFalse)
11604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11605 " zTXt");
11606 }
11607
glennrpb9cfe272010-12-21 15:08:06 +000011608 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011609
cristy018f07f2011-09-04 21:15:19 +000011610 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011611
11612 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011613
cristy3ed852e2009-09-05 21:47:34 +000011614 if (logging != MagickFalse)
11615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011616
cristy3ed852e2009-09-05 21:47:34 +000011617 return(status);
11618}
11619
11620#if defined(JNG_SUPPORTED)
11621
11622/* Write one JNG image */
11623static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +000011624 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011625{
11626 Image
11627 *jpeg_image;
11628
11629 ImageInfo
11630 *jpeg_image_info;
11631
11632 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011633 logging,
cristy3ed852e2009-09-05 21:47:34 +000011634 status;
11635
11636 size_t
11637 length;
11638
11639 unsigned char
11640 *blob,
11641 chunk[80],
11642 *p;
11643
11644 unsigned int
11645 jng_alpha_compression_method,
11646 jng_alpha_sample_depth,
11647 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011648 transparent;
11649
cristybb503372010-05-27 20:51:26 +000011650 size_t
glennrp59575fa2011-12-31 21:31:39 +000011651 jng_alpha_quality,
cristy3ed852e2009-09-05 21:47:34 +000011652 jng_quality;
11653
11654 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011655 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011656
11657 blob=(unsigned char *) NULL;
11658 jpeg_image=(Image *) NULL;
11659 jpeg_image_info=(ImageInfo *) NULL;
11660
11661 status=MagickTrue;
11662 transparent=image_info->type==GrayscaleMatteType ||
glennrp59575fa2011-12-31 21:31:39 +000011663 image_info->type==TrueColorMatteType || image->matte != MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +000011664
glennrp59575fa2011-12-31 21:31:39 +000011665 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
11666
11667 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
11668
11669 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
11670 image_info->quality;
11671
11672 if (jng_alpha_quality >= 1000)
11673 jng_alpha_quality /= 1000;
cristy3ed852e2009-09-05 21:47:34 +000011674
11675 if (transparent)
11676 {
11677 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011678
cristy3ed852e2009-09-05 21:47:34 +000011679 /* Create JPEG blob, image, and image_info */
11680 if (logging != MagickFalse)
11681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +000011682 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011683
cristy3ed852e2009-09-05 21:47:34 +000011684 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011685
cristy3ed852e2009-09-05 21:47:34 +000011686 if (jpeg_image_info == (ImageInfo *) NULL)
11687 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011688
cristy3ed852e2009-09-05 21:47:34 +000011689 if (logging != MagickFalse)
11690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11691 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011692
cristy262346f2012-01-11 19:34:10 +000011693 jpeg_image=SeparateImage(image,AlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +000011694 if (jpeg_image == (Image *) NULL)
11695 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11696 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +000011697 jpeg_image->matte=MagickFalse;
glennrp59575fa2011-12-31 21:31:39 +000011698 jpeg_image->quality=jng_alpha_quality;
cristy3ed852e2009-09-05 21:47:34 +000011699 jpeg_image_info->type=GrayscaleType;
cristy018f07f2011-09-04 21:15:19 +000011700 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000011701 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011702 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011703 "%s",jpeg_image->filename);
11704 }
glennrp59575fa2011-12-31 21:31:39 +000011705 else
11706 {
11707 jng_alpha_compression_method=0;
11708 jng_color_type=10;
11709 jng_alpha_sample_depth=0;
11710 }
cristy3ed852e2009-09-05 21:47:34 +000011711
11712 /* To do: check bit depth of PNG alpha channel */
11713
11714 /* Check if image is grayscale. */
11715 if (image_info->type != TrueColorMatteType && image_info->type !=
cristyc82a27b2011-10-21 01:07:16 +000011716 TrueColorType && ImageIsGray(image,exception))
cristy3ed852e2009-09-05 21:47:34 +000011717 jng_color_type-=2;
11718
glennrp59575fa2011-12-31 21:31:39 +000011719 if (logging != MagickFalse)
11720 {
11721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11722 " JNG Quality = %d",(int) jng_quality);
11723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11724 " JNG Color Type = %d",jng_color_type);
11725 if (transparent)
11726 {
11727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11728 " JNG Alpha Compression = %d",jng_alpha_compression_method);
11729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11730 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
11731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11732 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
11733 }
11734 }
11735
cristy3ed852e2009-09-05 21:47:34 +000011736 if (transparent)
11737 {
11738 if (jng_alpha_compression_method==0)
11739 {
11740 const char
11741 *value;
11742
cristy4c08aed2011-07-01 19:47:50 +000011743 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011744 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000011745 exception);
cristy3ed852e2009-09-05 21:47:34 +000011746 if (logging != MagickFalse)
11747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11748 " Creating PNG blob.");
11749 length=0;
11750
11751 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11752 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11753 jpeg_image_info->interlace=NoInterlace;
11754
glennrpcc5d45b2012-01-06 04:06:10 +000011755 /* Exclude all ancillary chunks */
11756 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
11757
cristy3ed852e2009-09-05 21:47:34 +000011758 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyc82a27b2011-10-21 01:07:16 +000011759 exception);
cristy3ed852e2009-09-05 21:47:34 +000011760
11761 /* Retrieve sample depth used */
cristyd15e6592011-10-15 00:13:06 +000011762 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
cristy3ed852e2009-09-05 21:47:34 +000011763 if (value != (char *) NULL)
11764 jng_alpha_sample_depth= (unsigned int) value[0];
11765 }
11766 else
11767 {
cristy4c08aed2011-07-01 19:47:50 +000011768 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011769
11770 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000011771 exception);
cristy3ed852e2009-09-05 21:47:34 +000011772
11773 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11774 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11775 jpeg_image_info->interlace=NoInterlace;
11776 if (logging != MagickFalse)
11777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11778 " Creating blob.");
11779 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyc82a27b2011-10-21 01:07:16 +000011780 exception);
cristy3ed852e2009-09-05 21:47:34 +000011781 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011782
cristy3ed852e2009-09-05 21:47:34 +000011783 if (logging != MagickFalse)
11784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011785 " Successfully read jpeg_image into a blob, length=%.20g.",
11786 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011787
11788 }
11789 /* Destroy JPEG image and image_info */
11790 jpeg_image=DestroyImage(jpeg_image);
11791 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11792 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11793 }
11794
11795 /* Write JHDR chunk */
11796 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11797 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011798 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011799 PNGLong(chunk+4,(png_uint_32) image->columns);
11800 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011801 chunk[12]=jng_color_type;
11802 chunk[13]=8; /* sample depth */
11803 chunk[14]=8; /*jng_image_compression_method */
11804 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11805 chunk[16]=jng_alpha_sample_depth;
11806 chunk[17]=jng_alpha_compression_method;
11807 chunk[18]=0; /*jng_alpha_filter_method */
11808 chunk[19]=0; /*jng_alpha_interlace_method */
11809 (void) WriteBlob(image,20,chunk);
11810 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11811 if (logging != MagickFalse)
11812 {
11813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011814 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011815
cristy3ed852e2009-09-05 21:47:34 +000011816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011817 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011818
cristy3ed852e2009-09-05 21:47:34 +000011819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11820 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011821
cristy3ed852e2009-09-05 21:47:34 +000011822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11823 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011824
cristy3ed852e2009-09-05 21:47:34 +000011825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11826 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011827
cristy3ed852e2009-09-05 21:47:34 +000011828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11829 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011830
cristy3ed852e2009-09-05 21:47:34 +000011831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11832 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011833
cristy3ed852e2009-09-05 21:47:34 +000011834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11835 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011836
cristy3ed852e2009-09-05 21:47:34 +000011837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11838 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011839
cristy3ed852e2009-09-05 21:47:34 +000011840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11841 " JNG alpha interlace:%5d",0);
11842 }
11843
glennrp0fe50b42010-11-16 03:52:51 +000011844 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011845 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011846
11847 /*
11848 Write leading ancillary chunks
11849 */
11850
11851 if (transparent)
11852 {
11853 /*
11854 Write JNG bKGD chunk
11855 */
11856
11857 unsigned char
11858 blue,
11859 green,
11860 red;
11861
cristybb503372010-05-27 20:51:26 +000011862 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011863 num_bytes;
11864
11865 if (jng_color_type == 8 || jng_color_type == 12)
11866 num_bytes=6L;
11867 else
11868 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011869 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011870 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011871 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011872 red=ScaleQuantumToChar(image->background_color.red);
11873 green=ScaleQuantumToChar(image->background_color.green);
11874 blue=ScaleQuantumToChar(image->background_color.blue);
11875 *(chunk+4)=0;
11876 *(chunk+5)=red;
11877 *(chunk+6)=0;
11878 *(chunk+7)=green;
11879 *(chunk+8)=0;
11880 *(chunk+9)=blue;
11881 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11882 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11883 }
11884
11885 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11886 {
11887 /*
11888 Write JNG sRGB chunk
11889 */
11890 (void) WriteBlobMSBULong(image,1L);
11891 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011892 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011893
cristy3ed852e2009-09-05 21:47:34 +000011894 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011895 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011896 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011897 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011898
cristy3ed852e2009-09-05 21:47:34 +000011899 else
glennrpe610a072010-08-05 17:08:46 +000011900 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011901 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011902 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011903
cristy3ed852e2009-09-05 21:47:34 +000011904 (void) WriteBlob(image,5,chunk);
11905 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11906 }
11907 else
11908 {
11909 if (image->gamma != 0.0)
11910 {
11911 /*
11912 Write JNG gAMA chunk
11913 */
11914 (void) WriteBlobMSBULong(image,4L);
11915 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011916 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011917 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011918 (void) WriteBlob(image,8,chunk);
11919 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11920 }
glennrp0fe50b42010-11-16 03:52:51 +000011921
cristy3ed852e2009-09-05 21:47:34 +000011922 if ((mng_info->equal_chrms == MagickFalse) &&
11923 (image->chromaticity.red_primary.x != 0.0))
11924 {
11925 PrimaryInfo
11926 primary;
11927
11928 /*
11929 Write JNG cHRM chunk
11930 */
11931 (void) WriteBlobMSBULong(image,32L);
11932 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011933 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011934 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011935 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11936 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011937 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011938 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11939 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011940 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011941 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11942 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011943 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011944 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11945 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011946 (void) WriteBlob(image,36,chunk);
11947 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11948 }
11949 }
glennrp0fe50b42010-11-16 03:52:51 +000011950
cristy2a11bef2011-10-28 18:33:11 +000011951 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000011952 {
11953 /*
11954 Write JNG pHYs chunk
11955 */
11956 (void) WriteBlobMSBULong(image,9L);
11957 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011958 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011959 if (image->units == PixelsPerInchResolution)
11960 {
cristy35ef8242010-06-03 16:24:13 +000011961 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011962 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011963
cristy35ef8242010-06-03 16:24:13 +000011964 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011965 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011966
cristy3ed852e2009-09-05 21:47:34 +000011967 chunk[12]=1;
11968 }
glennrp0fe50b42010-11-16 03:52:51 +000011969
cristy3ed852e2009-09-05 21:47:34 +000011970 else
11971 {
11972 if (image->units == PixelsPerCentimeterResolution)
11973 {
cristy35ef8242010-06-03 16:24:13 +000011974 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011975 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011976
cristy35ef8242010-06-03 16:24:13 +000011977 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011978 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011979
cristy3ed852e2009-09-05 21:47:34 +000011980 chunk[12]=1;
11981 }
glennrp0fe50b42010-11-16 03:52:51 +000011982
cristy3ed852e2009-09-05 21:47:34 +000011983 else
11984 {
cristy2a11bef2011-10-28 18:33:11 +000011985 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
11986 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011987 chunk[12]=0;
11988 }
11989 }
11990 (void) WriteBlob(image,13,chunk);
11991 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11992 }
11993
11994 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11995 {
11996 /*
11997 Write JNG oFFs chunk
11998 */
11999 (void) WriteBlobMSBULong(image,9L);
12000 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000012001 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000012002 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12003 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000012004 chunk[12]=0;
12005 (void) WriteBlob(image,13,chunk);
12006 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12007 }
12008 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12009 {
12010 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12011 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000012012 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000012013 PNGLong(chunk+4,(png_uint_32) image->page.width);
12014 PNGLong(chunk+8,(png_uint_32) image->page.height);
12015 chunk[12]=0; /* unit = pixels */
12016 (void) WriteBlob(image,13,chunk);
12017 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12018 }
12019
12020
12021 if (transparent)
12022 {
12023 if (jng_alpha_compression_method==0)
12024 {
cristybb503372010-05-27 20:51:26 +000012025 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012026 i;
12027
cristybb503372010-05-27 20:51:26 +000012028 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012029 len;
12030
12031 /* Write IDAT chunk header */
12032 if (logging != MagickFalse)
12033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012034 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000012035 length);
cristy3ed852e2009-09-05 21:47:34 +000012036
12037 /* Copy IDAT chunks */
12038 len=0;
12039 p=blob+8;
cristybb503372010-05-27 20:51:26 +000012040 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000012041 {
12042 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12043 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012044
cristy3ed852e2009-09-05 21:47:34 +000012045 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12046 {
12047 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000012048 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000012049 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000012050 (void) WriteBlob(image,(size_t) len+4,p);
12051 (void) WriteBlobMSBULong(image,
12052 crc32(0,p,(uInt) len+4));
12053 }
glennrp0fe50b42010-11-16 03:52:51 +000012054
cristy3ed852e2009-09-05 21:47:34 +000012055 else
12056 {
12057 if (logging != MagickFalse)
12058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012059 " Skipping %c%c%c%c chunk, length=%.20g.",
12060 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012061 }
12062 p+=(8+len);
12063 }
12064 }
12065 else
12066 {
12067 /* Write JDAA chunk header */
12068 if (logging != MagickFalse)
12069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012070 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012071 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012072 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012073 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012074 /* Write JDAT chunk(s) data */
12075 (void) WriteBlob(image,4,chunk);
12076 (void) WriteBlob(image,length,blob);
12077 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12078 (uInt) length));
12079 }
12080 blob=(unsigned char *) RelinquishMagickMemory(blob);
12081 }
12082
12083 /* Encode image as a JPEG blob */
12084 if (logging != MagickFalse)
12085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12086 " Creating jpeg_image_info.");
12087 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12088 if (jpeg_image_info == (ImageInfo *) NULL)
12089 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12090
12091 if (logging != MagickFalse)
12092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12093 " Creating jpeg_image.");
12094
cristyc82a27b2011-10-21 01:07:16 +000012095 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012096 if (jpeg_image == (Image *) NULL)
12097 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12098 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12099
12100 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012101 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012102 jpeg_image->filename);
12103
12104 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000012105 exception);
cristy3ed852e2009-09-05 21:47:34 +000012106
12107 if (logging != MagickFalse)
12108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012109 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12110 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012111
12112 if (jng_color_type == 8 || jng_color_type == 12)
12113 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012114
glennrp59575fa2011-12-31 21:31:39 +000012115 jpeg_image_info->quality=jng_quality;
12116 jpeg_image->quality=jng_quality;
cristy3ed852e2009-09-05 21:47:34 +000012117 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12118 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012119
cristy3ed852e2009-09-05 21:47:34 +000012120 if (logging != MagickFalse)
12121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12122 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012123
cristyc82a27b2011-10-21 01:07:16 +000012124 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
glennrp0fe50b42010-11-16 03:52:51 +000012125
cristy3ed852e2009-09-05 21:47:34 +000012126 if (logging != MagickFalse)
12127 {
12128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012129 " Successfully read jpeg_image into a blob, length=%.20g.",
12130 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012131
12132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012133 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012134 }
glennrp0fe50b42010-11-16 03:52:51 +000012135
cristy3ed852e2009-09-05 21:47:34 +000012136 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012137 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012138 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012139 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012140 (void) WriteBlob(image,4,chunk);
12141 (void) WriteBlob(image,length,blob);
12142 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12143
12144 jpeg_image=DestroyImage(jpeg_image);
12145 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12146 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12147 blob=(unsigned char *) RelinquishMagickMemory(blob);
12148
12149 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012150 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012151
12152 /* Write IEND chunk */
12153 (void) WriteBlobMSBULong(image,0L);
12154 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012155 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012156 (void) WriteBlob(image,4,chunk);
12157 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12158
12159 if (logging != MagickFalse)
12160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12161 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012162
cristy3ed852e2009-09-05 21:47:34 +000012163 return(status);
12164}
12165
12166
12167/*
12168%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12169% %
12170% %
12171% %
12172% W r i t e J N G I m a g e %
12173% %
12174% %
12175% %
12176%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12177%
12178% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12179%
12180% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12181%
12182% The format of the WriteJNGImage method is:
12183%
cristy1e178e72011-08-28 19:44:34 +000012184% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12185% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012186%
12187% A description of each parameter follows:
12188%
12189% o image_info: the image info.
12190%
12191% o image: The image.
12192%
cristy1e178e72011-08-28 19:44:34 +000012193% o exception: return any errors or warnings in this structure.
12194%
cristy3ed852e2009-09-05 21:47:34 +000012195%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12196*/
cristy1e178e72011-08-28 19:44:34 +000012197static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12198 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012199{
12200 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012201 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012202 logging,
cristy3ed852e2009-09-05 21:47:34 +000012203 status;
12204
12205 MngInfo
12206 *mng_info;
12207
cristy3ed852e2009-09-05 21:47:34 +000012208 /*
12209 Open image file.
12210 */
12211 assert(image_info != (const ImageInfo *) NULL);
12212 assert(image_info->signature == MagickSignature);
12213 assert(image != (Image *) NULL);
12214 assert(image->signature == MagickSignature);
12215 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012216 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristyc82a27b2011-10-21 01:07:16 +000012217 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012218 if (status == MagickFalse)
12219 return(status);
12220
12221 /*
12222 Allocate a MngInfo structure.
12223 */
12224 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012225 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012226 if (mng_info == (MngInfo *) NULL)
12227 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12228 /*
12229 Initialize members of the MngInfo structure.
12230 */
12231 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12232 mng_info->image=image;
12233 have_mng_structure=MagickTrue;
12234
12235 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12236
cristy018f07f2011-09-04 21:15:19 +000012237 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012238 (void) CloseBlob(image);
12239
12240 (void) CatchImageException(image);
12241 MngInfoFreeStruct(mng_info,&have_mng_structure);
12242 if (logging != MagickFalse)
12243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12244 return(status);
12245}
12246#endif
12247
cristy1e178e72011-08-28 19:44:34 +000012248static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12249 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012250{
12251 const char
12252 *option;
12253
12254 Image
12255 *next_image;
12256
12257 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012258 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012259 status;
12260
glennrp03812ae2010-12-24 01:31:34 +000012261 volatile MagickBooleanType
12262 logging;
12263
cristy3ed852e2009-09-05 21:47:34 +000012264 MngInfo
12265 *mng_info;
12266
12267 int
cristy3ed852e2009-09-05 21:47:34 +000012268 image_count,
12269 need_iterations,
12270 need_matte;
12271
12272 volatile int
12273#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12274 defined(PNG_MNG_FEATURES_SUPPORTED)
12275 need_local_plte,
12276#endif
12277 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012278 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012279 use_global_plte;
12280
cristybb503372010-05-27 20:51:26 +000012281 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012282 i;
12283
12284 unsigned char
12285 chunk[800];
12286
12287 volatile unsigned int
12288 write_jng,
12289 write_mng;
12290
cristybb503372010-05-27 20:51:26 +000012291 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012292 scene;
12293
cristybb503372010-05-27 20:51:26 +000012294 size_t
cristy3ed852e2009-09-05 21:47:34 +000012295 final_delay=0,
12296 initial_delay;
12297
glennrpd5045b42010-03-24 12:40:35 +000012298#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012299 if (image_info->verbose)
12300 printf("Your PNG library (libpng-%s) is rather old.\n",
12301 PNG_LIBPNG_VER_STRING);
12302#endif
12303
12304 /*
12305 Open image file.
12306 */
12307 assert(image_info != (const ImageInfo *) NULL);
12308 assert(image_info->signature == MagickSignature);
12309 assert(image != (Image *) NULL);
12310 assert(image->signature == MagickSignature);
12311 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012312 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristyc82a27b2011-10-21 01:07:16 +000012313 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012314 if (status == MagickFalse)
12315 return(status);
12316
12317 /*
12318 Allocate a MngInfo structure.
12319 */
12320 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012321 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012322 if (mng_info == (MngInfo *) NULL)
12323 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12324 /*
12325 Initialize members of the MngInfo structure.
12326 */
12327 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12328 mng_info->image=image;
12329 have_mng_structure=MagickTrue;
12330 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12331
12332 /*
12333 * See if user has requested a specific PNG subformat to be used
12334 * for all of the PNGs in the MNG being written, e.g.,
12335 *
12336 * convert *.png png8:animation.mng
12337 *
12338 * To do: check -define png:bit_depth and png:color_type as well,
12339 * or perhaps use mng:bit_depth and mng:color_type instead for
12340 * global settings.
12341 */
12342
12343 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12344 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12345 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12346
12347 write_jng=MagickFalse;
12348 if (image_info->compression == JPEGCompression)
12349 write_jng=MagickTrue;
12350
12351 mng_info->adjoin=image_info->adjoin &&
12352 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12353
cristy3ed852e2009-09-05 21:47:34 +000012354 if (logging != MagickFalse)
12355 {
12356 /* Log some info about the input */
12357 Image
12358 *p;
12359
12360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12361 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012362
cristy3ed852e2009-09-05 21:47:34 +000012363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012364 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012365
cristy3ed852e2009-09-05 21:47:34 +000012366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12367 " Type: %d",image_info->type);
12368
12369 scene=0;
12370 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12371 {
12372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012373 " Scene: %.20g",(double) scene++);
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 depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012377
cristy3ed852e2009-09-05 21:47:34 +000012378 if (p->matte)
12379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12380 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012381
cristy3ed852e2009-09-05 21:47:34 +000012382 else
12383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12384 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012385
cristy3ed852e2009-09-05 21:47:34 +000012386 if (p->storage_class == PseudoClass)
12387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12388 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012389
cristy3ed852e2009-09-05 21:47:34 +000012390 else
12391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12392 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012393
cristy3ed852e2009-09-05 21:47:34 +000012394 if (p->colors)
12395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012396 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012397
cristy3ed852e2009-09-05 21:47:34 +000012398 else
12399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12400 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012401
cristy3ed852e2009-09-05 21:47:34 +000012402 if (mng_info->adjoin == MagickFalse)
12403 break;
12404 }
12405 }
12406
cristy3ed852e2009-09-05 21:47:34 +000012407 use_global_plte=MagickFalse;
12408 all_images_are_gray=MagickFalse;
12409#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12410 need_local_plte=MagickTrue;
12411#endif
12412 need_defi=MagickFalse;
12413 need_matte=MagickFalse;
12414 mng_info->framing_mode=1;
12415 mng_info->old_framing_mode=1;
12416
12417 if (write_mng)
12418 if (image_info->page != (char *) NULL)
12419 {
12420 /*
12421 Determine image bounding box.
12422 */
12423 SetGeometry(image,&mng_info->page);
12424 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12425 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12426 }
12427 if (write_mng)
12428 {
12429 unsigned int
12430 need_geom;
12431
12432 unsigned short
12433 red,
12434 green,
12435 blue;
12436
12437 mng_info->page=image->page;
12438 need_geom=MagickTrue;
12439 if (mng_info->page.width || mng_info->page.height)
12440 need_geom=MagickFalse;
12441 /*
12442 Check all the scenes.
12443 */
12444 initial_delay=image->delay;
12445 need_iterations=MagickFalse;
12446 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12447 mng_info->equal_physs=MagickTrue,
12448 mng_info->equal_gammas=MagickTrue;
12449 mng_info->equal_srgbs=MagickTrue;
12450 mng_info->equal_backgrounds=MagickTrue;
12451 image_count=0;
12452#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12453 defined(PNG_MNG_FEATURES_SUPPORTED)
12454 all_images_are_gray=MagickTrue;
12455 mng_info->equal_palettes=MagickFalse;
12456 need_local_plte=MagickFalse;
12457#endif
12458 for (next_image=image; next_image != (Image *) NULL; )
12459 {
12460 if (need_geom)
12461 {
12462 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12463 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012464
cristy3ed852e2009-09-05 21:47:34 +000012465 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12466 mng_info->page.height=next_image->rows+next_image->page.y;
12467 }
glennrp0fe50b42010-11-16 03:52:51 +000012468
cristy3ed852e2009-09-05 21:47:34 +000012469 if (next_image->page.x || next_image->page.y)
12470 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012471
cristy3ed852e2009-09-05 21:47:34 +000012472 if (next_image->matte)
12473 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012474
cristy3ed852e2009-09-05 21:47:34 +000012475 if ((int) next_image->dispose >= BackgroundDispose)
12476 if (next_image->matte || next_image->page.x || next_image->page.y ||
12477 ((next_image->columns < mng_info->page.width) &&
12478 (next_image->rows < mng_info->page.height)))
12479 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012480
cristy3ed852e2009-09-05 21:47:34 +000012481 if (next_image->iterations)
12482 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012483
cristy3ed852e2009-09-05 21:47:34 +000012484 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012485
cristy3ed852e2009-09-05 21:47:34 +000012486 if (final_delay != initial_delay || final_delay > 1UL*
12487 next_image->ticks_per_second)
12488 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012489
cristy3ed852e2009-09-05 21:47:34 +000012490#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12491 defined(PNG_MNG_FEATURES_SUPPORTED)
12492 /*
12493 check for global palette possibility.
12494 */
12495 if (image->matte != MagickFalse)
12496 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012497
cristy3ed852e2009-09-05 21:47:34 +000012498 if (need_local_plte == 0)
12499 {
cristyc82a27b2011-10-21 01:07:16 +000012500 if (ImageIsGray(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012501 all_images_are_gray=MagickFalse;
12502 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12503 if (use_global_plte == 0)
12504 use_global_plte=mng_info->equal_palettes;
12505 need_local_plte=!mng_info->equal_palettes;
12506 }
12507#endif
12508 if (GetNextImageInList(next_image) != (Image *) NULL)
12509 {
12510 if (next_image->background_color.red !=
12511 next_image->next->background_color.red ||
12512 next_image->background_color.green !=
12513 next_image->next->background_color.green ||
12514 next_image->background_color.blue !=
12515 next_image->next->background_color.blue)
12516 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012517
cristy3ed852e2009-09-05 21:47:34 +000012518 if (next_image->gamma != next_image->next->gamma)
12519 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012520
cristy3ed852e2009-09-05 21:47:34 +000012521 if (next_image->rendering_intent !=
12522 next_image->next->rendering_intent)
12523 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012524
cristy3ed852e2009-09-05 21:47:34 +000012525 if ((next_image->units != next_image->next->units) ||
cristy2a11bef2011-10-28 18:33:11 +000012526 (next_image->resolution.x != next_image->next->resolution.x) ||
12527 (next_image->resolution.y != next_image->next->resolution.y))
cristy3ed852e2009-09-05 21:47:34 +000012528 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012529
cristy3ed852e2009-09-05 21:47:34 +000012530 if (mng_info->equal_chrms)
12531 {
12532 if (next_image->chromaticity.red_primary.x !=
12533 next_image->next->chromaticity.red_primary.x ||
12534 next_image->chromaticity.red_primary.y !=
12535 next_image->next->chromaticity.red_primary.y ||
12536 next_image->chromaticity.green_primary.x !=
12537 next_image->next->chromaticity.green_primary.x ||
12538 next_image->chromaticity.green_primary.y !=
12539 next_image->next->chromaticity.green_primary.y ||
12540 next_image->chromaticity.blue_primary.x !=
12541 next_image->next->chromaticity.blue_primary.x ||
12542 next_image->chromaticity.blue_primary.y !=
12543 next_image->next->chromaticity.blue_primary.y ||
12544 next_image->chromaticity.white_point.x !=
12545 next_image->next->chromaticity.white_point.x ||
12546 next_image->chromaticity.white_point.y !=
12547 next_image->next->chromaticity.white_point.y)
12548 mng_info->equal_chrms=MagickFalse;
12549 }
12550 }
12551 image_count++;
12552 next_image=GetNextImageInList(next_image);
12553 }
12554 if (image_count < 2)
12555 {
12556 mng_info->equal_backgrounds=MagickFalse;
12557 mng_info->equal_chrms=MagickFalse;
12558 mng_info->equal_gammas=MagickFalse;
12559 mng_info->equal_srgbs=MagickFalse;
12560 mng_info->equal_physs=MagickFalse;
12561 use_global_plte=MagickFalse;
12562#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12563 need_local_plte=MagickTrue;
12564#endif
12565 need_iterations=MagickFalse;
12566 }
glennrp0fe50b42010-11-16 03:52:51 +000012567
cristy3ed852e2009-09-05 21:47:34 +000012568 if (mng_info->need_fram == MagickFalse)
12569 {
12570 /*
12571 Only certain framing rates 100/n are exactly representable without
12572 the FRAM chunk but we'll allow some slop in VLC files
12573 */
12574 if (final_delay == 0)
12575 {
12576 if (need_iterations != MagickFalse)
12577 {
12578 /*
12579 It's probably a GIF with loop; don't run it *too* fast.
12580 */
glennrp02617122010-07-28 13:07:35 +000012581 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012582 {
12583 final_delay=10;
cristyc82a27b2011-10-21 01:07:16 +000012584 (void) ThrowMagickException(exception,GetMagickModule(),
12585 CoderWarning,
glennrpd908de42010-07-28 13:28:27 +000012586 "input has zero delay between all frames; assuming",
12587 " 10 cs `%s'","");
12588 }
cristy3ed852e2009-09-05 21:47:34 +000012589 }
12590 else
12591 mng_info->ticks_per_second=0;
12592 }
12593 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012594 mng_info->ticks_per_second=(png_uint_32)
12595 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012596 if (final_delay > 50)
12597 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012598
cristy3ed852e2009-09-05 21:47:34 +000012599 if (final_delay > 75)
12600 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012601
cristy3ed852e2009-09-05 21:47:34 +000012602 if (final_delay > 125)
12603 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012604
cristy3ed852e2009-09-05 21:47:34 +000012605 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12606 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12607 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12608 1UL*image->ticks_per_second))
12609 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12610 }
glennrp0fe50b42010-11-16 03:52:51 +000012611
cristy3ed852e2009-09-05 21:47:34 +000012612 if (mng_info->need_fram != MagickFalse)
12613 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12614 /*
12615 If pseudocolor, we should also check to see if all the
12616 palettes are identical and write a global PLTE if they are.
12617 ../glennrp Feb 99.
12618 */
12619 /*
12620 Write the MNG version 1.0 signature and MHDR chunk.
12621 */
12622 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12623 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12624 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012625 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012626 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12627 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012628 PNGLong(chunk+12,mng_info->ticks_per_second);
12629 PNGLong(chunk+16,0L); /* layer count=unknown */
12630 PNGLong(chunk+20,0L); /* frame count=unknown */
12631 PNGLong(chunk+24,0L); /* play time=unknown */
12632 if (write_jng)
12633 {
12634 if (need_matte)
12635 {
12636 if (need_defi || mng_info->need_fram || use_global_plte)
12637 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012638
cristy3ed852e2009-09-05 21:47:34 +000012639 else
12640 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12641 }
glennrp0fe50b42010-11-16 03:52:51 +000012642
cristy3ed852e2009-09-05 21:47:34 +000012643 else
12644 {
12645 if (need_defi || mng_info->need_fram || use_global_plte)
12646 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012647
cristy3ed852e2009-09-05 21:47:34 +000012648 else
12649 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12650 }
12651 }
glennrp0fe50b42010-11-16 03:52:51 +000012652
cristy3ed852e2009-09-05 21:47:34 +000012653 else
12654 {
12655 if (need_matte)
12656 {
12657 if (need_defi || mng_info->need_fram || use_global_plte)
12658 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012659
cristy3ed852e2009-09-05 21:47:34 +000012660 else
12661 PNGLong(chunk+28,9L); /* simplicity=VLC */
12662 }
glennrp0fe50b42010-11-16 03:52:51 +000012663
cristy3ed852e2009-09-05 21:47:34 +000012664 else
12665 {
12666 if (need_defi || mng_info->need_fram || use_global_plte)
12667 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012668
cristy3ed852e2009-09-05 21:47:34 +000012669 else
12670 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12671 }
12672 }
12673 (void) WriteBlob(image,32,chunk);
12674 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12675 option=GetImageOption(image_info,"mng:need-cacheoff");
12676 if (option != (const char *) NULL)
12677 {
12678 size_t
12679 length;
12680
12681 /*
12682 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12683 */
12684 PNGType(chunk,mng_nEED);
12685 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012686 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012687 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012688 length+=4;
12689 (void) WriteBlob(image,length,chunk);
12690 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12691 }
12692 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12693 (GetNextImageInList(image) != (Image *) NULL) &&
12694 (image->iterations != 1))
12695 {
12696 /*
12697 Write MNG TERM chunk
12698 */
12699 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12700 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012701 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012702 chunk[4]=3; /* repeat animation */
12703 chunk[5]=0; /* show last frame when done */
12704 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12705 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012706
cristy3ed852e2009-09-05 21:47:34 +000012707 if (image->iterations == 0)
12708 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012709
cristy3ed852e2009-09-05 21:47:34 +000012710 else
12711 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012712
cristy3ed852e2009-09-05 21:47:34 +000012713 if (logging != MagickFalse)
12714 {
12715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012716 " TERM delay: %.20g",(double) (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 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012721 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012722
cristy3ed852e2009-09-05 21:47:34 +000012723 else
12724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012725 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012726 }
12727 (void) WriteBlob(image,14,chunk);
12728 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12729 }
12730 /*
12731 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12732 */
12733 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12734 mng_info->equal_srgbs)
12735 {
12736 /*
12737 Write MNG sRGB chunk
12738 */
12739 (void) WriteBlobMSBULong(image,1L);
12740 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012741 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012742
cristy3ed852e2009-09-05 21:47:34 +000012743 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012744 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012745 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012746 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012747
cristy3ed852e2009-09-05 21:47:34 +000012748 else
glennrpe610a072010-08-05 17:08:46 +000012749 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012750 Magick_RenderingIntent_to_PNG_RenderingIntent(
12751 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012752
cristy3ed852e2009-09-05 21:47:34 +000012753 (void) WriteBlob(image,5,chunk);
12754 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12755 mng_info->have_write_global_srgb=MagickTrue;
12756 }
glennrp0fe50b42010-11-16 03:52:51 +000012757
cristy3ed852e2009-09-05 21:47:34 +000012758 else
12759 {
12760 if (image->gamma && mng_info->equal_gammas)
12761 {
12762 /*
12763 Write MNG gAMA chunk
12764 */
12765 (void) WriteBlobMSBULong(image,4L);
12766 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012767 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012768 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012769 (void) WriteBlob(image,8,chunk);
12770 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12771 mng_info->have_write_global_gama=MagickTrue;
12772 }
12773 if (mng_info->equal_chrms)
12774 {
12775 PrimaryInfo
12776 primary;
12777
12778 /*
12779 Write MNG cHRM chunk
12780 */
12781 (void) WriteBlobMSBULong(image,32L);
12782 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012783 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012784 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012785 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12786 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012787 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012788 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12789 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012790 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012791 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12792 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012793 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012794 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12795 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012796 (void) WriteBlob(image,36,chunk);
12797 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12798 mng_info->have_write_global_chrm=MagickTrue;
12799 }
12800 }
cristy2a11bef2011-10-28 18:33:11 +000012801 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012802 {
12803 /*
12804 Write MNG pHYs chunk
12805 */
12806 (void) WriteBlobMSBULong(image,9L);
12807 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012808 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012809
cristy3ed852e2009-09-05 21:47:34 +000012810 if (image->units == PixelsPerInchResolution)
12811 {
cristy35ef8242010-06-03 16:24:13 +000012812 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012813 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012814
cristy35ef8242010-06-03 16:24:13 +000012815 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012816 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012817
cristy3ed852e2009-09-05 21:47:34 +000012818 chunk[12]=1;
12819 }
glennrp0fe50b42010-11-16 03:52:51 +000012820
cristy3ed852e2009-09-05 21:47:34 +000012821 else
12822 {
12823 if (image->units == PixelsPerCentimeterResolution)
12824 {
cristy35ef8242010-06-03 16:24:13 +000012825 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012826 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012827
cristy35ef8242010-06-03 16:24:13 +000012828 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012829 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012830
cristy3ed852e2009-09-05 21:47:34 +000012831 chunk[12]=1;
12832 }
glennrp0fe50b42010-11-16 03:52:51 +000012833
cristy3ed852e2009-09-05 21:47:34 +000012834 else
12835 {
cristy2a11bef2011-10-28 18:33:11 +000012836 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12837 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012838 chunk[12]=0;
12839 }
12840 }
12841 (void) WriteBlob(image,13,chunk);
12842 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12843 }
12844 /*
12845 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12846 or does not cover the entire frame.
12847 */
12848 if (write_mng && (image->matte || image->page.x > 0 ||
12849 image->page.y > 0 || (image->page.width &&
12850 (image->page.width+image->page.x < mng_info->page.width))
12851 || (image->page.height && (image->page.height+image->page.y
12852 < mng_info->page.height))))
12853 {
12854 (void) WriteBlobMSBULong(image,6L);
12855 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012856 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012857 red=ScaleQuantumToShort(image->background_color.red);
12858 green=ScaleQuantumToShort(image->background_color.green);
12859 blue=ScaleQuantumToShort(image->background_color.blue);
12860 PNGShort(chunk+4,red);
12861 PNGShort(chunk+6,green);
12862 PNGShort(chunk+8,blue);
12863 (void) WriteBlob(image,10,chunk);
12864 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12865 if (mng_info->equal_backgrounds)
12866 {
12867 (void) WriteBlobMSBULong(image,6L);
12868 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012869 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012870 (void) WriteBlob(image,10,chunk);
12871 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12872 }
12873 }
12874
12875#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12876 if ((need_local_plte == MagickFalse) &&
12877 (image->storage_class == PseudoClass) &&
12878 (all_images_are_gray == MagickFalse))
12879 {
cristybb503372010-05-27 20:51:26 +000012880 size_t
cristy3ed852e2009-09-05 21:47:34 +000012881 data_length;
12882
12883 /*
12884 Write MNG PLTE chunk
12885 */
12886 data_length=3*image->colors;
12887 (void) WriteBlobMSBULong(image,data_length);
12888 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012889 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012890
cristybb503372010-05-27 20:51:26 +000012891 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012892 {
cristy5f07f702011-09-26 17:29:10 +000012893 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12894 image->colormap[i].red) & 0xff);
12895 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12896 image->colormap[i].green) & 0xff);
12897 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12898 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000012899 }
glennrp0fe50b42010-11-16 03:52:51 +000012900
cristy3ed852e2009-09-05 21:47:34 +000012901 (void) WriteBlob(image,data_length+4,chunk);
12902 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12903 mng_info->have_write_global_plte=MagickTrue;
12904 }
12905#endif
12906 }
12907 scene=0;
12908 mng_info->delay=0;
12909#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12910 defined(PNG_MNG_FEATURES_SUPPORTED)
12911 mng_info->equal_palettes=MagickFalse;
12912#endif
12913 do
12914 {
12915 if (mng_info->adjoin)
12916 {
12917#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12918 defined(PNG_MNG_FEATURES_SUPPORTED)
12919 /*
12920 If we aren't using a global palette for the entire MNG, check to
12921 see if we can use one for two or more consecutive images.
12922 */
12923 if (need_local_plte && use_global_plte && !all_images_are_gray)
12924 {
12925 if (mng_info->IsPalette)
12926 {
12927 /*
12928 When equal_palettes is true, this image has the same palette
12929 as the previous PseudoClass image
12930 */
12931 mng_info->have_write_global_plte=mng_info->equal_palettes;
12932 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12933 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12934 {
12935 /*
12936 Write MNG PLTE chunk
12937 */
cristybb503372010-05-27 20:51:26 +000012938 size_t
cristy3ed852e2009-09-05 21:47:34 +000012939 data_length;
12940
12941 data_length=3*image->colors;
12942 (void) WriteBlobMSBULong(image,data_length);
12943 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012944 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012945
cristybb503372010-05-27 20:51:26 +000012946 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012947 {
12948 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12949 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12950 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12951 }
glennrp0fe50b42010-11-16 03:52:51 +000012952
cristy3ed852e2009-09-05 21:47:34 +000012953 (void) WriteBlob(image,data_length+4,chunk);
12954 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12955 (uInt) (data_length+4)));
12956 mng_info->have_write_global_plte=MagickTrue;
12957 }
12958 }
12959 else
12960 mng_info->have_write_global_plte=MagickFalse;
12961 }
12962#endif
12963 if (need_defi)
12964 {
cristybb503372010-05-27 20:51:26 +000012965 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012966 previous_x,
12967 previous_y;
12968
12969 if (scene)
12970 {
12971 previous_x=mng_info->page.x;
12972 previous_y=mng_info->page.y;
12973 }
12974 else
12975 {
12976 previous_x=0;
12977 previous_y=0;
12978 }
12979 mng_info->page=image->page;
12980 if ((mng_info->page.x != previous_x) ||
12981 (mng_info->page.y != previous_y))
12982 {
12983 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12984 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012985 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012986 chunk[4]=0; /* object 0 MSB */
12987 chunk[5]=0; /* object 0 LSB */
12988 chunk[6]=0; /* visible */
12989 chunk[7]=0; /* abstract */
12990 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12991 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12992 (void) WriteBlob(image,16,chunk);
12993 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12994 }
12995 }
12996 }
12997
12998 mng_info->write_mng=write_mng;
12999
13000 if ((int) image->dispose >= 3)
13001 mng_info->framing_mode=3;
13002
13003 if (mng_info->need_fram && mng_info->adjoin &&
13004 ((image->delay != mng_info->delay) ||
13005 (mng_info->framing_mode != mng_info->old_framing_mode)))
13006 {
13007 if (image->delay == mng_info->delay)
13008 {
13009 /*
13010 Write a MNG FRAM chunk with the new framing mode.
13011 */
13012 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13013 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013014 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000013015 chunk[4]=(unsigned char) mng_info->framing_mode;
13016 (void) WriteBlob(image,5,chunk);
13017 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13018 }
13019 else
13020 {
13021 /*
13022 Write a MNG FRAM chunk with the delay.
13023 */
13024 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13025 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013026 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013027 chunk[4]=(unsigned char) mng_info->framing_mode;
13028 chunk[5]=0; /* frame name separator (no name) */
13029 chunk[6]=2; /* flag for changing default delay */
13030 chunk[7]=0; /* flag for changing frame timeout */
13031 chunk[8]=0; /* flag for changing frame clipping */
13032 chunk[9]=0; /* flag for changing frame sync_id */
13033 PNGLong(chunk+10,(png_uint_32)
13034 ((mng_info->ticks_per_second*
13035 image->delay)/MagickMax(image->ticks_per_second,1)));
13036 (void) WriteBlob(image,14,chunk);
13037 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000013038 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000013039 }
13040 mng_info->old_framing_mode=mng_info->framing_mode;
13041 }
13042
13043#if defined(JNG_SUPPORTED)
13044 if (image_info->compression == JPEGCompression)
13045 {
13046 ImageInfo
13047 *write_info;
13048
13049 if (logging != MagickFalse)
13050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13051 " Writing JNG object.");
13052 /* To do: specify the desired alpha compression method. */
13053 write_info=CloneImageInfo(image_info);
13054 write_info->compression=UndefinedCompression;
cristy018f07f2011-09-04 21:15:19 +000013055 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013056 write_info=DestroyImageInfo(write_info);
13057 }
13058 else
13059#endif
13060 {
13061 if (logging != MagickFalse)
13062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13063 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013064
glennrpb9cfe272010-12-21 15:08:06 +000013065 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013066 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013067
13068 /* We don't want any ancillary chunks written */
13069 mng_info->ping_exclude_bKGD=MagickTrue;
13070 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013071 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013072 mng_info->ping_exclude_EXIF=MagickTrue;
13073 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013074 mng_info->ping_exclude_iCCP=MagickTrue;
13075 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13076 mng_info->ping_exclude_oFFs=MagickTrue;
13077 mng_info->ping_exclude_pHYs=MagickTrue;
13078 mng_info->ping_exclude_sRGB=MagickTrue;
13079 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013080 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013081 mng_info->ping_exclude_vpAg=MagickTrue;
13082 mng_info->ping_exclude_zCCP=MagickTrue;
13083 mng_info->ping_exclude_zTXt=MagickTrue;
13084
cristy018f07f2011-09-04 21:15:19 +000013085 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013086 }
13087
13088 if (status == MagickFalse)
13089 {
13090 MngInfoFreeStruct(mng_info,&have_mng_structure);
13091 (void) CloseBlob(image);
13092 return(MagickFalse);
13093 }
13094 (void) CatchImageException(image);
13095 if (GetNextImageInList(image) == (Image *) NULL)
13096 break;
13097 image=SyncNextImageInList(image);
13098 status=SetImageProgress(image,SaveImagesTag,scene++,
13099 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013100
cristy3ed852e2009-09-05 21:47:34 +000013101 if (status == MagickFalse)
13102 break;
glennrp0fe50b42010-11-16 03:52:51 +000013103
cristy3ed852e2009-09-05 21:47:34 +000013104 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013105
cristy3ed852e2009-09-05 21:47:34 +000013106 if (write_mng)
13107 {
13108 while (GetPreviousImageInList(image) != (Image *) NULL)
13109 image=GetPreviousImageInList(image);
13110 /*
13111 Write the MEND chunk.
13112 */
13113 (void) WriteBlobMSBULong(image,0x00000000L);
13114 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013115 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013116 (void) WriteBlob(image,4,chunk);
13117 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13118 }
13119 /*
13120 Relinquish resources.
13121 */
13122 (void) CloseBlob(image);
13123 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013124
cristy3ed852e2009-09-05 21:47:34 +000013125 if (logging != MagickFalse)
13126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013127
cristy3ed852e2009-09-05 21:47:34 +000013128 return(MagickTrue);
13129}
glennrpd5045b42010-03-24 12:40:35 +000013130#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013131
cristy3ed852e2009-09-05 21:47:34 +000013132static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13133{
glennrp3bd393f2011-12-21 18:54:53 +000013134 (void) image;
cristy3ed852e2009-09-05 21:47:34 +000013135 printf("Your PNG library is too old: You have libpng-%s\n",
13136 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013137
cristy3ed852e2009-09-05 21:47:34 +000013138 ThrowBinaryException(CoderError,"PNG library is too old",
13139 image_info->filename);
13140}
glennrp39992b42010-11-14 00:03:43 +000013141
cristy3ed852e2009-09-05 21:47:34 +000013142static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13143{
13144 return(WritePNGImage(image_info,image));
13145}
glennrpd5045b42010-03-24 12:40:35 +000013146#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013147#endif