blob: cf926929c5cbd74e5f3047f91322f94672764599 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP N N GGGG %
7% P P NN N G %
8% PPPP N N N G GG %
9% P N NN G G %
10% P N N GGG %
11% %
12% %
13% Read/Write Portable Network Graphics Image Format %
14% %
15% Software Design %
16% John Cristy %
17% Glenn Randers-Pehrson %
18% November 1997 %
19% %
20% %
cristy1454be72011-12-19 01:52:48 +000021% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000022% dedicated to making software imaging solutions freely available. %
23% %
24% You may not use this file except in compliance with the License. You may %
25% obtain a copy of the License at %
26% %
27% http://www.imagemagick.org/script/license.php %
28% %
29% Unless required by applicable law or agreed to in writing, software %
30% distributed under the License is distributed on an "AS IS" BASIS, %
31% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32% See the License for the specific language governing permissions and %
33% limitations under the License. %
34% %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38*/
39
40
41/*
42 Include declarations.
43*/
cristy4c08aed2011-07-01 19:47:50 +000044#include "MagickCore/studio.h"
45#include "MagickCore/artifact.h"
46#include "MagickCore/attribute.h"
47#include "MagickCore/blob.h"
48#include "MagickCore/blob-private.h"
49#include "MagickCore/cache.h"
50#include "MagickCore/color.h"
51#include "MagickCore/color-private.h"
52#include "MagickCore/colormap.h"
53#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000054#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000055#include "MagickCore/constitute.h"
56#include "MagickCore/enhance.h"
57#include "MagickCore/exception.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/geometry.h"
60#include "MagickCore/histogram.h"
61#include "MagickCore/image.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/layer.h"
64#include "MagickCore/list.h"
65#include "MagickCore/log.h"
66#include "MagickCore/MagickCore.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/module.h"
69#include "MagickCore/monitor.h"
70#include "MagickCore/monitor-private.h"
71#include "MagickCore/option.h"
72#include "MagickCore/pixel.h"
73#include "MagickCore/pixel-accessor.h"
74#include "MagickCore/profile.h"
75#include "MagickCore/property.h"
76#include "MagickCore/quantum-private.h"
77#include "MagickCore/resource_.h"
78#include "MagickCore/semaphore.h"
79#include "MagickCore/quantum-private.h"
80#include "MagickCore/static.h"
81#include "MagickCore/statistic.h"
82#include "MagickCore/string_.h"
83#include "MagickCore/string-private.h"
84#include "MagickCore/transform.h"
85#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000086#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp286a6352009-11-09 02:58:50 +000087
glennrp7ef138c2009-11-10 13:50:20 +000088/* Suppress libpng pedantic warnings that were added in
89 * libpng-1.2.41 and libpng-1.4.0. If you are working on
glennrpfaa852b2010-03-30 12:17:00 +000090 * migration to libpng-1.5, remove these defines and then
glennrp7ef138c2009-11-10 13:50:20 +000091 * fix any code that generates warnings.
92 */
glennrp991e92a2010-01-28 03:09:00 +000093/* #define PNG_DEPRECATED Use of this function is deprecated */
glennrpfaa852b2010-03-30 12:17:00 +000094/* #define PNG_USE_RESULT The result of this function must be checked */
95/* #define PNG_NORETURN This function does not return */
96/* #define PNG_ALLOCATED The result of the function is new memory */
glennrp8371ecc2011-02-19 19:15:38 +000097/* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
glennrp5b927342011-04-14 14:31:48 +000098
99/* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
cristy75cfe702011-04-14 14:27:17 +0000100#define PNG_PTR_NORETURN
glennrp286a6352009-11-09 02:58:50 +0000101
cristy3ed852e2009-09-05 21:47:34 +0000102#include "png.h"
103#include "zlib.h"
104
105/* ImageMagick differences */
106#define first_scene scene
107
glennrpd5045b42010-03-24 12:40:35 +0000108#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000109/*
110 Optional declarations. Define or undefine them as you like.
111*/
112/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
113
114/*
115 Features under construction. Define these to work on them.
116*/
117#undef MNG_OBJECT_BUFFERS
118#undef MNG_BASI_SUPPORTED
119#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
120#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
cristy3ed852e2009-09-05 21:47:34 +0000121#if defined(MAGICKCORE_JPEG_DELEGATE)
122# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
123#endif
124#if !defined(RGBColorMatchExact)
125#define IsPNGColorEqual(color,target) \
glennrp8e045c82011-04-27 16:40:27 +0000126 (((color).red == (target).red) && \
127 ((color).green == (target).green) && \
128 ((color).blue == (target).blue))
cristy3ed852e2009-09-05 21:47:34 +0000129#endif
130
glennrp8e58efd2011-05-20 12:16:29 +0000131/* Macros for left-bit-replication to ensure that pixels
glennrp1a2061f2011-10-18 12:30:45 +0000132 * and PixelInfos all have the same image->depth, and for use
glennrp8e58efd2011-05-20 12:16:29 +0000133 * in PNG8 quantization.
134 */
135
136
137/* LBR01: Replicate top bit */
138
glennrp05001c32011-08-06 13:04:16 +0000139#define LBR01PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000140 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
141 0 : QuantumRange);
142
glennrp91d99252011-06-25 14:30:13 +0000143#define LBR01PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000144 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
145 0 : QuantumRange);
146
glennrp91d99252011-06-25 14:30:13 +0000147#define LBR01PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000148 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
149 0 : QuantumRange);
150
cristy4c08aed2011-07-01 19:47:50 +0000151#define LBR01PacketAlpha(pixelpacket) \
152 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000153 0 : QuantumRange);
154
glennrp91d99252011-06-25 14:30:13 +0000155#define LBR01PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000156 { \
glennrp05001c32011-08-06 13:04:16 +0000157 LBR01PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000158 LBR01PacketGreen((pixelpacket)); \
159 LBR01PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000160 }
glennrp8e58efd2011-05-20 12:16:29 +0000161
glennrp91d99252011-06-25 14:30:13 +0000162#define LBR01PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000163 { \
glennrp91d99252011-06-25 14:30:13 +0000164 LBR01PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000165 LBR01PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000166 }
glennrp8e58efd2011-05-20 12:16:29 +0000167
cristyef618312011-06-25 12:26:44 +0000168#define LBR01PixelRed(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000169 (ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000170 0 : QuantumRange);
171
glennrp54cf7972011-08-06 14:28:09 +0000172#define LBR01PixelGreen(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000173 (ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000174 0 : QuantumRange);
175
glennrp54cf7972011-08-06 14:28:09 +0000176#define LBR01PixelBlue(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000177 (ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000178 0 : QuantumRange);
179
glennrp54cf7972011-08-06 14:28:09 +0000180#define LBR01PixelAlpha(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000181 (ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000182 0 : QuantumRange);
183
glennrp54cf7972011-08-06 14:28:09 +0000184#define LBR01PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000185 { \
cristyef618312011-06-25 12:26:44 +0000186 LBR01PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000187 LBR01PixelGreen((pixel)); \
188 LBR01PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000189 }
glennrp8e58efd2011-05-20 12:16:29 +0000190
glennrp54cf7972011-08-06 14:28:09 +0000191#define LBR01PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000192 { \
glennrp54cf7972011-08-06 14:28:09 +0000193 LBR01PixelRGB((pixel)); \
194 LBR01PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000195 }
glennrp8e58efd2011-05-20 12:16:29 +0000196
197/* LBR02: Replicate top 2 bits */
198
glennrp05001c32011-08-06 13:04:16 +0000199#define LBR02PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000200 { \
201 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
202 (pixelpacket).red=ScaleCharToQuantum( \
203 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
204 }
glennrp91d99252011-06-25 14:30:13 +0000205#define LBR02PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000206 { \
207 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
208 (pixelpacket).green=ScaleCharToQuantum( \
209 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
210 }
glennrp91d99252011-06-25 14:30:13 +0000211#define LBR02PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000212 { \
213 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
214 (pixelpacket).blue=ScaleCharToQuantum( \
215 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
216 }
cristy4c08aed2011-07-01 19:47:50 +0000217#define LBR02PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000218 { \
cristy4c08aed2011-07-01 19:47:50 +0000219 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
220 (pixelpacket).alpha=ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000221 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
222 }
223
glennrp91d99252011-06-25 14:30:13 +0000224#define LBR02PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000225 { \
glennrp05001c32011-08-06 13:04:16 +0000226 LBR02PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000227 LBR02PacketGreen((pixelpacket)); \
228 LBR02PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000229 }
glennrp8e58efd2011-05-20 12:16:29 +0000230
glennrp91d99252011-06-25 14:30:13 +0000231#define LBR02PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000232 { \
glennrp91d99252011-06-25 14:30:13 +0000233 LBR02PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000234 LBR02PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000235 }
glennrp8e58efd2011-05-20 12:16:29 +0000236
cristyef618312011-06-25 12:26:44 +0000237#define LBR02PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000238 { \
cristy4c08aed2011-07-01 19:47:50 +0000239 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000240 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000241 SetPixelRed(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000242 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
243 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000244 }
glennrp54cf7972011-08-06 14:28:09 +0000245#define LBR02PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000246 { \
cristy4c08aed2011-07-01 19:47:50 +0000247 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000248 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000249 SetPixelGreen(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000250 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
251 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000252 }
glennrp54cf7972011-08-06 14:28:09 +0000253#define LBR02PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000254 { \
255 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000256 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
257 SetPixelBlue(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000258 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
259 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000260 }
glennrp54cf7972011-08-06 14:28:09 +0000261#define LBR02PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000262 { \
263 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000264 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
265 SetPixelAlpha(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000266 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
267 (pixel) ); \
glennrp8e58efd2011-05-20 12:16:29 +0000268 }
269
glennrp54cf7972011-08-06 14:28:09 +0000270#define LBR02PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000271 { \
cristyef618312011-06-25 12:26:44 +0000272 LBR02PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000273 LBR02PixelGreen((pixel)); \
274 LBR02PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000275 }
glennrp8e58efd2011-05-20 12:16:29 +0000276
glennrp54cf7972011-08-06 14:28:09 +0000277#define LBR02PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000278 { \
glennrp54cf7972011-08-06 14:28:09 +0000279 LBR02PixelRGB((pixel)); \
280 LBR02PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000281 }
glennrp8e58efd2011-05-20 12:16:29 +0000282
283/* LBR03: Replicate top 3 bits (only used with opaque pixels during
284 PNG8 quantization) */
285
glennrp05001c32011-08-06 13:04:16 +0000286#define LBR03PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000287 { \
288 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
289 (pixelpacket).red=ScaleCharToQuantum( \
290 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
291 }
glennrp91d99252011-06-25 14:30:13 +0000292#define LBR03PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000293 { \
294 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
295 (pixelpacket).green=ScaleCharToQuantum( \
296 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
297 }
glennrp91d99252011-06-25 14:30:13 +0000298#define LBR03PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000299 { \
300 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
301 (pixelpacket).blue=ScaleCharToQuantum( \
302 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
303 }
304
glennrp91d99252011-06-25 14:30:13 +0000305#define LBR03PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000306 { \
glennrp05001c32011-08-06 13:04:16 +0000307 LBR03PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000308 LBR03PacketGreen((pixelpacket)); \
309 LBR03PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000310 }
glennrp8e58efd2011-05-20 12:16:29 +0000311
cristyef618312011-06-25 12:26:44 +0000312#define LBR03PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000313 { \
cristy4c08aed2011-07-01 19:47:50 +0000314 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000315 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000316 SetPixelRed(image, ScaleCharToQuantum( \
317 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000318 }
cristy4c08aed2011-07-01 19:47:50 +0000319#define LBR03Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000320 { \
cristy4c08aed2011-07-01 19:47:50 +0000321 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000322 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000323 SetPixelGreen(image, ScaleCharToQuantum( \
324 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000325 }
cristy4c08aed2011-07-01 19:47:50 +0000326#define LBR03Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000327 { \
cristy4c08aed2011-07-01 19:47:50 +0000328 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000329 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000330 SetPixelBlue(image, ScaleCharToQuantum( \
331 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000332 }
333
cristy4c08aed2011-07-01 19:47:50 +0000334#define LBR03RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000335 { \
cristyef618312011-06-25 12:26:44 +0000336 LBR03PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000337 LBR03Green((pixel)); \
338 LBR03Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000339 }
glennrp8e58efd2011-05-20 12:16:29 +0000340
341/* LBR04: Replicate top 4 bits */
342
glennrp05001c32011-08-06 13:04:16 +0000343#define LBR04PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000344 { \
345 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
346 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
347 }
glennrp91d99252011-06-25 14:30:13 +0000348#define LBR04PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000349 { \
350 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
351 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
352 }
glennrp91d99252011-06-25 14:30:13 +0000353#define LBR04PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000354 { \
355 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
356 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
357 }
cristy4c08aed2011-07-01 19:47:50 +0000358#define LBR04PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000359 { \
cristy4c08aed2011-07-01 19:47:50 +0000360 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
361 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
glennrp8e58efd2011-05-20 12:16:29 +0000362 }
363
glennrp91d99252011-06-25 14:30:13 +0000364#define LBR04PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000365 { \
glennrp05001c32011-08-06 13:04:16 +0000366 LBR04PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000367 LBR04PacketGreen((pixelpacket)); \
368 LBR04PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000369 }
glennrp8e58efd2011-05-20 12:16:29 +0000370
glennrp91d99252011-06-25 14:30:13 +0000371#define LBR04PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000372 { \
glennrp91d99252011-06-25 14:30:13 +0000373 LBR04PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000374 LBR04PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000375 }
glennrp8e58efd2011-05-20 12:16:29 +0000376
cristyef618312011-06-25 12:26:44 +0000377#define LBR04PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000378 { \
cristy4c08aed2011-07-01 19:47:50 +0000379 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000380 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000381 SetPixelRed(image,\
382 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000383 }
glennrp54cf7972011-08-06 14:28:09 +0000384#define LBR04PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000385 { \
cristy4c08aed2011-07-01 19:47:50 +0000386 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000387 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000388 SetPixelGreen(image,\
389 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000390 }
glennrp54cf7972011-08-06 14:28:09 +0000391#define LBR04PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000392 { \
393 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000394 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
395 SetPixelBlue(image,\
396 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000397 }
glennrp54cf7972011-08-06 14:28:09 +0000398#define LBR04PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000399 { \
400 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000401 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
402 SetPixelAlpha(image,\
403 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000404 }
405
glennrp54cf7972011-08-06 14:28:09 +0000406#define LBR04PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000407 { \
cristyef618312011-06-25 12:26:44 +0000408 LBR04PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000409 LBR04PixelGreen((pixel)); \
410 LBR04PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000411 }
glennrp8e58efd2011-05-20 12:16:29 +0000412
glennrp54cf7972011-08-06 14:28:09 +0000413#define LBR04PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000414 { \
glennrp54cf7972011-08-06 14:28:09 +0000415 LBR04PixelRGB((pixel)); \
416 LBR04PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000417 }
glennrp8e58efd2011-05-20 12:16:29 +0000418
419
420/* LBR08: Replicate top 8 bits */
421
glennrp05001c32011-08-06 13:04:16 +0000422#define LBR08PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000423 { \
424 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
425 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
426 }
glennrp91d99252011-06-25 14:30:13 +0000427#define LBR08PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000428 { \
429 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
430 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
431 }
glennrp91d99252011-06-25 14:30:13 +0000432#define LBR08PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000433 { \
434 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
435 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
436 }
cristy4c08aed2011-07-01 19:47:50 +0000437#define LBR08PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000438 { \
cristy4c08aed2011-07-01 19:47:50 +0000439 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
440 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000441 }
442
glennrp91d99252011-06-25 14:30:13 +0000443#define LBR08PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000444 { \
glennrp05001c32011-08-06 13:04:16 +0000445 LBR08PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000446 LBR08PacketGreen((pixelpacket)); \
447 LBR08PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000448 }
glennrp8e58efd2011-05-20 12:16:29 +0000449
glennrp91d99252011-06-25 14:30:13 +0000450#define LBR08PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000451 { \
glennrp91d99252011-06-25 14:30:13 +0000452 LBR08PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000453 LBR08PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000454 }
glennrp8e58efd2011-05-20 12:16:29 +0000455
cristyef618312011-06-25 12:26:44 +0000456#define LBR08PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000457 { \
458 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000459 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
460 SetPixelRed(image,\
461 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000462 }
glennrp54cf7972011-08-06 14:28:09 +0000463#define LBR08PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000464 { \
465 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000466 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
467 SetPixelGreen(image,\
468 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000469 }
glennrp54cf7972011-08-06 14:28:09 +0000470#define LBR08PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000471 { \
472 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000473 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
474 SetPixelBlue(image,\
475 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000476 }
glennrp54cf7972011-08-06 14:28:09 +0000477#define LBR08PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000478 { \
479 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000480 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
481 SetPixelAlpha(image,\
482 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000483 }
484
glennrp54cf7972011-08-06 14:28:09 +0000485#define LBR08PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000486 { \
cristyef618312011-06-25 12:26:44 +0000487 LBR08PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000488 LBR08PixelGreen((pixel)); \
489 LBR08PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000490 }
glennrp8e58efd2011-05-20 12:16:29 +0000491
glennrp54cf7972011-08-06 14:28:09 +0000492#define LBR08PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000493 { \
glennrp54cf7972011-08-06 14:28:09 +0000494 LBR08PixelRGB((pixel)); \
495 LBR08PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000496 }
glennrp8e58efd2011-05-20 12:16:29 +0000497
498
499/* LBR16: Replicate top 16 bits */
500
glennrp05001c32011-08-06 13:04:16 +0000501#define LBR16PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000502 { \
503 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
504 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
505 }
glennrp91d99252011-06-25 14:30:13 +0000506#define LBR16PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000507 { \
508 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
509 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
510 }
glennrp91d99252011-06-25 14:30:13 +0000511#define LBR16PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000512 { \
513 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
514 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
515 }
cristy4c08aed2011-07-01 19:47:50 +0000516#define LBR16PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000517 { \
cristy4c08aed2011-07-01 19:47:50 +0000518 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
519 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000520 }
521
glennrp91d99252011-06-25 14:30:13 +0000522#define LBR16PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000523 { \
glennrp05001c32011-08-06 13:04:16 +0000524 LBR16PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000525 LBR16PacketGreen((pixelpacket)); \
526 LBR16PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000527 }
glennrp8e58efd2011-05-20 12:16:29 +0000528
glennrp91d99252011-06-25 14:30:13 +0000529#define LBR16PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000530 { \
glennrp91d99252011-06-25 14:30:13 +0000531 LBR16PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000532 LBR16PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000533 }
glennrp8e58efd2011-05-20 12:16:29 +0000534
cristyef618312011-06-25 12:26:44 +0000535#define LBR16PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000536 { \
537 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000538 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
539 SetPixelRed(image,\
540 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000541 }
glennrp54cf7972011-08-06 14:28:09 +0000542#define LBR16PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000543 { \
544 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000545 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
546 SetPixelGreen(image,\
547 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000548 }
glennrp54cf7972011-08-06 14:28:09 +0000549#define LBR16PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000550 { \
551 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000552 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
553 SetPixelBlue(image,\
554 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000555 }
glennrp54cf7972011-08-06 14:28:09 +0000556#define LBR16PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000557 { \
558 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000559 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
560 SetPixelAlpha(image,\
561 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000562 }
563
glennrp54cf7972011-08-06 14:28:09 +0000564#define LBR16PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000565 { \
cristyef618312011-06-25 12:26:44 +0000566 LBR16PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000567 LBR16PixelGreen((pixel)); \
568 LBR16PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000569 }
glennrp8e58efd2011-05-20 12:16:29 +0000570
glennrp54cf7972011-08-06 14:28:09 +0000571#define LBR16PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000572 { \
glennrp54cf7972011-08-06 14:28:09 +0000573 LBR16PixelRGB((pixel)); \
574 LBR16PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000575 }
glennrp8e58efd2011-05-20 12:16:29 +0000576
cristy3ed852e2009-09-05 21:47:34 +0000577/*
578 Establish thread safety.
579 setjmp/longjmp is claimed to be safe on these platforms:
580 setjmp/longjmp is alleged to be unsafe on these platforms:
581*/
582#ifndef SETJMP_IS_THREAD_SAFE
583#define PNG_SETJMP_NOT_THREAD_SAFE
584#endif
585
586#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
587static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000588 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000589#endif
590
591/*
592 This temporary until I set up malloc'ed object attributes array.
593 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
594 waste more memory.
595*/
596#define MNG_MAX_OBJECTS 256
597
598/*
599 If this not defined, spec is interpreted strictly. If it is
600 defined, an attempt will be made to recover from some errors,
601 including
602 o global PLTE too short
603*/
604#undef MNG_LOOSE
605
606/*
607 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
608 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
609 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
610 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
611 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
612 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
613 will be enabled by default in libpng-1.2.0.
614*/
cristy3ed852e2009-09-05 21:47:34 +0000615#ifdef PNG_MNG_FEATURES_SUPPORTED
616# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
617# define PNG_READ_EMPTY_PLTE_SUPPORTED
618# endif
619# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
620# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
621# endif
622#endif
623
624/*
cristybb503372010-05-27 20:51:26 +0000625 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000626 This macro is only defined in libpng-1.0.3 and later.
627 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
628*/
629#ifndef PNG_UINT_31_MAX
630#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
631#endif
632
633/*
634 Constant strings for known chunk types. If you need to add a chunk,
635 add a string holding the name here. To make the code more
636 portable, we use ASCII numbers like this, not characters.
637*/
638
glennrp85dcf872011-12-07 02:51:47 +0000639static png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
640static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
641static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
642static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
643static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
644static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
645static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
646static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
647static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
648static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
649static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
650static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
651static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
652static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
653static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
654static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
655static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
656static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
657static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
658static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
659static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
660static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
661static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
662static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
663static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
664static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
665static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
666static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
667static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
668static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
669static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
670static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
671static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
672static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000673
674#if defined(JNG_SUPPORTED)
glennrp85dcf872011-12-07 02:51:47 +0000675static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
676static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
677static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
678static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
679static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
680static png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000681#endif
682
683/*
684Other known chunks that are not yet supported by ImageMagick:
glennrp85dcf872011-12-07 02:51:47 +0000685static png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
686static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
687static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
688static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
689static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
690static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
691static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
692static png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000693*/
694
695typedef struct _MngBox
696{
cristy8182b072010-05-30 20:10:53 +0000697 long
cristy3ed852e2009-09-05 21:47:34 +0000698 left,
699 right,
700 top,
701 bottom;
702} MngBox;
703
704typedef struct _MngPair
705{
cristy8182b072010-05-30 20:10:53 +0000706 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000707 a,
708 b;
709} MngPair;
710
711#ifdef MNG_OBJECT_BUFFERS
712typedef struct _MngBuffer
713{
714
cristybb503372010-05-27 20:51:26 +0000715 size_t
cristy3ed852e2009-09-05 21:47:34 +0000716 height,
717 width;
718
719 Image
720 *image;
721
722 png_color
723 plte[256];
724
725 int
726 reference_count;
727
728 unsigned char
729 alpha_sample_depth,
730 compression_method,
731 color_type,
732 concrete,
733 filter_method,
734 frozen,
735 image_type,
736 interlace_method,
737 pixel_sample_depth,
738 plte_length,
739 sample_depth,
740 viewable;
741} MngBuffer;
742#endif
743
744typedef struct _MngInfo
745{
746
747#ifdef MNG_OBJECT_BUFFERS
748 MngBuffer
749 *ob[MNG_MAX_OBJECTS];
750#endif
751
752 Image *
753 image;
754
755 RectangleInfo
756 page;
757
758 int
759 adjoin,
760#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
761 bytes_in_read_buffer,
762 found_empty_plte,
763#endif
764 equal_backgrounds,
765 equal_chrms,
766 equal_gammas,
767#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
768 defined(PNG_MNG_FEATURES_SUPPORTED)
769 equal_palettes,
770#endif
771 equal_physs,
772 equal_srgbs,
773 framing_mode,
774 have_global_bkgd,
775 have_global_chrm,
776 have_global_gama,
777 have_global_phys,
778 have_global_sbit,
779 have_global_srgb,
780 have_saved_bkgd_index,
781 have_write_global_chrm,
782 have_write_global_gama,
783 have_write_global_plte,
784 have_write_global_srgb,
785 need_fram,
786 object_id,
787 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000788 saved_bkgd_index;
789
790 int
791 new_number_colors;
792
cristybb503372010-05-27 20:51:26 +0000793 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000794 image_found,
795 loop_count[256],
796 loop_iteration[256],
797 scenes_found,
798 x_off[MNG_MAX_OBJECTS],
799 y_off[MNG_MAX_OBJECTS];
800
801 MngBox
802 clip,
803 frame,
804 image_box,
805 object_clip[MNG_MAX_OBJECTS];
806
807 unsigned char
808 /* These flags could be combined into one byte */
809 exists[MNG_MAX_OBJECTS],
810 frozen[MNG_MAX_OBJECTS],
811 loop_active[256],
812 invisible[MNG_MAX_OBJECTS],
813 viewable[MNG_MAX_OBJECTS];
814
815 MagickOffsetType
816 loop_jump[256];
817
818 png_colorp
819 global_plte;
820
821 png_color_8
822 global_sbit;
823
824 png_byte
825#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
826 read_buffer[8],
827#endif
828 global_trns[256];
829
830 float
831 global_gamma;
832
833 ChromaticityInfo
834 global_chrm;
835
836 RenderingIntent
837 global_srgb_intent;
838
cristy35ef8242010-06-03 16:24:13 +0000839 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000840 delay,
841 global_plte_length,
842 global_trns_length,
843 global_x_pixels_per_unit,
844 global_y_pixels_per_unit,
845 mng_width,
846 mng_height,
847 ticks_per_second;
848
glennrpb9cfe272010-12-21 15:08:06 +0000849 MagickBooleanType
850 need_blob;
851
cristy3ed852e2009-09-05 21:47:34 +0000852 unsigned int
853 IsPalette,
854 global_phys_unit_type,
855 basi_warning,
856 clon_warning,
857 dhdr_warning,
858 jhdr_warning,
859 magn_warning,
860 past_warning,
861 phyg_warning,
862 phys_warning,
863 sbit_warning,
864 show_warning,
865 mng_type,
866 write_mng,
867 write_png_colortype,
868 write_png_depth,
glennrp18682582011-06-30 18:11:47 +0000869 write_png_compression_level,
870 write_png_compression_strategy,
871 write_png_compression_filter,
cristy3ed852e2009-09-05 21:47:34 +0000872 write_png8,
873 write_png24,
874 write_png32;
875
876#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000877 size_t
cristy3ed852e2009-09-05 21:47:34 +0000878 basi_width,
879 basi_height;
880
881 unsigned int
882 basi_depth,
883 basi_color_type,
884 basi_compression_method,
885 basi_filter_type,
886 basi_interlace_method,
887 basi_red,
888 basi_green,
889 basi_blue,
890 basi_alpha,
891 basi_viewable;
892#endif
893
894 png_uint_16
895 magn_first,
896 magn_last,
897 magn_mb,
898 magn_ml,
899 magn_mr,
900 magn_mt,
901 magn_mx,
902 magn_my,
903 magn_methx,
904 magn_methy;
905
cristy101ab702011-10-13 13:06:32 +0000906 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000907 mng_global_bkgd;
908
glennrp26f37912010-12-23 16:22:42 +0000909 /* Added at version 6.6.6-7 */
910 MagickBooleanType
911 ping_exclude_bKGD,
912 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000913 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000914 ping_exclude_EXIF,
915 ping_exclude_gAMA,
916 ping_exclude_iCCP,
917 /* ping_exclude_iTXt, */
918 ping_exclude_oFFs,
919 ping_exclude_pHYs,
920 ping_exclude_sRGB,
921 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000922 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000923 ping_exclude_vpAg,
924 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000925 ping_exclude_zTXt,
926 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000927
cristy3ed852e2009-09-05 21:47:34 +0000928} MngInfo;
929#endif /* VER */
930
931/*
932 Forward declarations.
933*/
934static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000935 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000936
cristy3ed852e2009-09-05 21:47:34 +0000937static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000938 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000939
cristy3ed852e2009-09-05 21:47:34 +0000940#if defined(JNG_SUPPORTED)
941static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000942 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000943#endif
944
glennrp0c3e06b2010-11-19 13:45:02 +0000945#if PNG_LIBPNG_VER > 10011
946
glennrpfd05d622011-02-25 04:10:33 +0000947
glennrp0c3e06b2010-11-19 13:45:02 +0000948#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
949static MagickBooleanType
cristyc82a27b2011-10-21 01:07:16 +0000950LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
glennrp0c3e06b2010-11-19 13:45:02 +0000951{
glennrp67b9c1a2011-04-22 18:47:36 +0000952 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
953 *
954 * This is true if the high byte and the next highest byte of
955 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000956 * are equal to each other. We check this by seeing if the samples
957 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000958 *
959 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000960 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000961 */
962
glennrp3faa9a32011-04-23 14:00:25 +0000963#define QuantumToCharToQuantumEqQuantum(quantum) \
964 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
965
glennrp0c3e06b2010-11-19 13:45:02 +0000966 MagickBooleanType
967 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000968
glennrp03e11f62011-04-22 13:30:16 +0000969 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000970 {
971
cristy4c08aed2011-07-01 19:47:50 +0000972 const Quantum
glennrp0c3e06b2010-11-19 13:45:02 +0000973 *p;
974
975 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000976 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
977 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
978 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
979 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000980
981 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
982 {
983 int indx;
984
985 for (indx=0; indx < (ssize_t) image->colors; indx++)
986 {
glennrp3faa9a32011-04-23 14:00:25 +0000987 ok_to_reduce=(
988 QuantumToCharToQuantumEqQuantum(
989 image->colormap[indx].red) &&
990 QuantumToCharToQuantumEqQuantum(
991 image->colormap[indx].green) &&
992 QuantumToCharToQuantumEqQuantum(
993 image->colormap[indx].blue)) ?
994 MagickTrue : MagickFalse;
995
glennrp0c3e06b2010-11-19 13:45:02 +0000996 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000997 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000998 }
999 }
1000
1001 if ((ok_to_reduce != MagickFalse) &&
1002 (image->storage_class != PseudoClass))
1003 {
1004 ssize_t
1005 y;
1006
1007 register ssize_t
1008 x;
1009
1010 for (y=0; y < (ssize_t) image->rows; y++)
1011 {
cristyc82a27b2011-10-21 01:07:16 +00001012 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0c3e06b2010-11-19 13:45:02 +00001013
cristy4c08aed2011-07-01 19:47:50 +00001014 if (p == (const Quantum *) NULL)
glennrp0c3e06b2010-11-19 13:45:02 +00001015 {
1016 ok_to_reduce = MagickFalse;
1017 break;
1018 }
1019
1020 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1021 {
glennrp3faa9a32011-04-23 14:00:25 +00001022 ok_to_reduce=
cristy4c08aed2011-07-01 19:47:50 +00001023 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1024 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1025 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
glennrp3faa9a32011-04-23 14:00:25 +00001026 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001027
1028 if (ok_to_reduce == MagickFalse)
1029 break;
1030
cristyed231572011-07-14 02:18:59 +00001031 p+=GetPixelChannels(image);
glennrp0c3e06b2010-11-19 13:45:02 +00001032 }
glennrp8640fb52010-11-23 15:48:26 +00001033 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +00001034 break;
1035 }
1036 }
1037
1038 if (ok_to_reduce != MagickFalse)
1039 {
glennrp0c3e06b2010-11-19 13:45:02 +00001040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001041 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +00001042 }
glennrpa6a06632011-01-19 15:15:34 +00001043 else
1044 {
1045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001046 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +00001047 }
glennrp0c3e06b2010-11-19 13:45:02 +00001048 }
1049
1050 return ok_to_reduce;
1051}
1052#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1053
glennrpe610a072010-08-05 17:08:46 +00001054static int
glennrpcf002022011-01-30 02:38:15 +00001055Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +00001056{
glennrpe610a072010-08-05 17:08:46 +00001057 switch (intent)
1058 {
1059 case PerceptualIntent:
1060 return 0;
glennrp0fe50b42010-11-16 03:52:51 +00001061
glennrpe610a072010-08-05 17:08:46 +00001062 case RelativeIntent:
1063 return 1;
glennrp0fe50b42010-11-16 03:52:51 +00001064
glennrpe610a072010-08-05 17:08:46 +00001065 case SaturationIntent:
1066 return 2;
glennrp0fe50b42010-11-16 03:52:51 +00001067
glennrpe610a072010-08-05 17:08:46 +00001068 case AbsoluteIntent:
1069 return 3;
glennrp0fe50b42010-11-16 03:52:51 +00001070
glennrpe610a072010-08-05 17:08:46 +00001071 default:
1072 return -1;
1073 }
1074}
1075
1076static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +00001077Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +00001078{
glennrpcf002022011-01-30 02:38:15 +00001079 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001080 {
1081 case 0:
1082 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001083
glennrpe610a072010-08-05 17:08:46 +00001084 case 1:
1085 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001086
glennrpe610a072010-08-05 17:08:46 +00001087 case 2:
1088 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001089
glennrpe610a072010-08-05 17:08:46 +00001090 case 3:
1091 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001092
glennrpe610a072010-08-05 17:08:46 +00001093 default:
1094 return UndefinedIntent;
1095 }
1096}
1097
cristybb503372010-05-27 20:51:26 +00001098static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001099{
1100 if (x > y)
1101 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001102
cristy3ed852e2009-09-05 21:47:34 +00001103 return(y);
1104}
glennrp0c3e06b2010-11-19 13:45:02 +00001105
cristybb503372010-05-27 20:51:26 +00001106static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001107{
1108 if (x < y)
1109 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001110
cristy3ed852e2009-09-05 21:47:34 +00001111 return(y);
1112}
glennrp0c3e06b2010-11-19 13:45:02 +00001113
cristy3ed852e2009-09-05 21:47:34 +00001114
1115/*
1116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1117% %
1118% %
1119% %
1120% I m a g e I s G r a y %
1121% %
1122% %
1123% %
1124%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1125% %
cristy4c08aed2011-07-01 19:47:50 +00001126% Like IsImageGray except does not change DirectClass to PseudoClass %
cristy3ed852e2009-09-05 21:47:34 +00001127% %
1128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1129*/
cristyc82a27b2011-10-21 01:07:16 +00001130static MagickBooleanType ImageIsGray(Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001131{
cristy4c08aed2011-07-01 19:47:50 +00001132 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001133 *p;
1134
cristybb503372010-05-27 20:51:26 +00001135 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001136 i,
1137 x,
1138 y;
1139
1140 assert(image != (Image *) NULL);
1141 assert(image->signature == MagickSignature);
1142 if (image->debug != MagickFalse)
1143 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1144
1145 if (image->storage_class == PseudoClass)
1146 {
cristybb503372010-05-27 20:51:26 +00001147 for (i=0; i < (ssize_t) image->colors; i++)
cristy101ab702011-10-13 13:06:32 +00001148 if (IsPixelInfoGray(image->colormap+i) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001149 return(MagickFalse);
1150 return(MagickTrue);
1151 }
cristybb503372010-05-27 20:51:26 +00001152 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001153 {
cristyc82a27b2011-10-21 01:07:16 +00001154 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001155 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001156 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +00001157 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001158 {
cristy4c08aed2011-07-01 19:47:50 +00001159 if (IsPixelGray(image,p) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001160 return(MagickFalse);
cristyed231572011-07-14 02:18:59 +00001161 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001162 }
1163 }
1164 return(MagickTrue);
1165}
glennrpd5045b42010-03-24 12:40:35 +00001166#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001167#endif /* MAGICKCORE_PNG_DELEGATE */
1168
1169/*
1170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1171% %
1172% %
1173% %
1174% I s M N G %
1175% %
1176% %
1177% %
1178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1179%
1180% IsMNG() returns MagickTrue if the image format type, identified by the
1181% magick string, is MNG.
1182%
1183% The format of the IsMNG method is:
1184%
1185% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1186%
1187% A description of each parameter follows:
1188%
1189% o magick: compare image format pattern against these bytes.
1190%
1191% o length: Specifies the length of the magick string.
1192%
1193%
1194*/
1195static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1196{
1197 if (length < 8)
1198 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001199
cristy3ed852e2009-09-05 21:47:34 +00001200 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1201 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001202
cristy3ed852e2009-09-05 21:47:34 +00001203 return(MagickFalse);
1204}
1205
1206/*
1207%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1208% %
1209% %
1210% %
1211% I s J N G %
1212% %
1213% %
1214% %
1215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1216%
1217% IsJNG() returns MagickTrue if the image format type, identified by the
1218% magick string, is JNG.
1219%
1220% The format of the IsJNG method is:
1221%
1222% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1223%
1224% A description of each parameter follows:
1225%
1226% o magick: compare image format pattern against these bytes.
1227%
1228% o length: Specifies the length of the magick string.
1229%
1230%
1231*/
1232static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1233{
1234 if (length < 8)
1235 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001236
cristy3ed852e2009-09-05 21:47:34 +00001237 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1238 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001239
cristy3ed852e2009-09-05 21:47:34 +00001240 return(MagickFalse);
1241}
1242
1243/*
1244%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245% %
1246% %
1247% %
1248% I s P N G %
1249% %
1250% %
1251% %
1252%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1253%
1254% IsPNG() returns MagickTrue if the image format type, identified by the
1255% magick string, is PNG.
1256%
1257% The format of the IsPNG method is:
1258%
1259% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1260%
1261% A description of each parameter follows:
1262%
1263% o magick: compare image format pattern against these bytes.
1264%
1265% o length: Specifies the length of the magick string.
1266%
1267*/
1268static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1269{
1270 if (length < 8)
1271 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001272
cristy3ed852e2009-09-05 21:47:34 +00001273 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1274 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001275
cristy3ed852e2009-09-05 21:47:34 +00001276 return(MagickFalse);
1277}
1278
1279#if defined(MAGICKCORE_PNG_DELEGATE)
1280#if defined(__cplusplus) || defined(c_plusplus)
1281extern "C" {
1282#endif
1283
glennrpd5045b42010-03-24 12:40:35 +00001284#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001285static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001286{
1287 unsigned char
1288 buffer[4];
1289
1290 assert(image != (Image *) NULL);
1291 assert(image->signature == MagickSignature);
1292 buffer[0]=(unsigned char) (value >> 24);
1293 buffer[1]=(unsigned char) (value >> 16);
1294 buffer[2]=(unsigned char) (value >> 8);
1295 buffer[3]=(unsigned char) value;
1296 return((size_t) WriteBlob(image,4,buffer));
1297}
1298
1299static void PNGLong(png_bytep p,png_uint_32 value)
1300{
1301 *p++=(png_byte) ((value >> 24) & 0xff);
1302 *p++=(png_byte) ((value >> 16) & 0xff);
1303 *p++=(png_byte) ((value >> 8) & 0xff);
1304 *p++=(png_byte) (value & 0xff);
1305}
1306
glennrpa521b2f2010-10-29 04:11:03 +00001307#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001308static void PNGsLong(png_bytep p,png_int_32 value)
1309{
1310 *p++=(png_byte) ((value >> 24) & 0xff);
1311 *p++=(png_byte) ((value >> 16) & 0xff);
1312 *p++=(png_byte) ((value >> 8) & 0xff);
1313 *p++=(png_byte) (value & 0xff);
1314}
glennrpa521b2f2010-10-29 04:11:03 +00001315#endif
cristy3ed852e2009-09-05 21:47:34 +00001316
1317static void PNGShort(png_bytep p,png_uint_16 value)
1318{
1319 *p++=(png_byte) ((value >> 8) & 0xff);
1320 *p++=(png_byte) (value & 0xff);
1321}
1322
1323static void PNGType(png_bytep p,png_bytep type)
1324{
1325 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1326}
1327
glennrp03812ae2010-12-24 01:31:34 +00001328static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1329 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001330{
1331 if (logging != MagickFalse)
1332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001333 " Writing %c%c%c%c chunk, length: %.20g",
1334 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001335}
glennrpd5045b42010-03-24 12:40:35 +00001336#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001337
1338#if defined(__cplusplus) || defined(c_plusplus)
1339}
1340#endif
1341
glennrpd5045b42010-03-24 12:40:35 +00001342#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001343/*
1344%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1345% %
1346% %
1347% %
1348% R e a d P N G I m a g e %
1349% %
1350% %
1351% %
1352%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1353%
1354% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1355% Multiple-image Network Graphics (MNG) image file and returns it. It
1356% allocates the memory necessary for the new Image structure and returns a
1357% pointer to the new image or set of images.
1358%
1359% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1360%
1361% The format of the ReadPNGImage method is:
1362%
1363% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1364%
1365% A description of each parameter follows:
1366%
1367% o image_info: the image info.
1368%
1369% o exception: return any errors or warnings in this structure.
1370%
1371% To do, more or less in chronological order (as of version 5.5.2,
1372% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1373%
1374% Get 16-bit cheap transparency working.
1375%
1376% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1377%
1378% Preserve all unknown and not-yet-handled known chunks found in input
1379% PNG file and copy them into output PNG files according to the PNG
1380% copying rules.
1381%
1382% (At this point, PNG encoding should be in full MNG compliance)
1383%
1384% Provide options for choice of background to use when the MNG BACK
1385% chunk is not present or is not mandatory (i.e., leave transparent,
1386% user specified, MNG BACK, PNG bKGD)
1387%
1388% Implement LOOP/ENDL [done, but could do discretionary loops more
1389% efficiently by linking in the duplicate frames.].
1390%
1391% Decode and act on the MHDR simplicity profile (offer option to reject
1392% files or attempt to process them anyway when the profile isn't LC or VLC).
1393%
1394% Upgrade to full MNG without Delta-PNG.
1395%
1396% o BACK [done a while ago except for background image ID]
1397% o MOVE [done 15 May 1999]
1398% o CLIP [done 15 May 1999]
1399% o DISC [done 19 May 1999]
1400% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1401% o SEEK [partially done 19 May 1999 (discard function only)]
1402% o SHOW
1403% o PAST
1404% o BASI
1405% o MNG-level tEXt/iTXt/zTXt
1406% o pHYg
1407% o pHYs
1408% o sBIT
1409% o bKGD
1410% o iTXt (wait for libpng implementation).
1411%
1412% Use the scene signature to discover when an identical scene is
1413% being reused, and just point to the original image->exception instead
1414% of storing another set of pixels. This not specific to MNG
1415% but could be applied generally.
1416%
1417% Upgrade to full MNG with Delta-PNG.
1418%
1419% JNG tEXt/iTXt/zTXt
1420%
1421% We will not attempt to read files containing the CgBI chunk.
1422% They are really Xcode files meant for display on the iPhone.
1423% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001424% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001425% since irretrievable loss of color data has occurred due to the
1426% use of premultiplied alpha.
1427*/
1428
1429#if defined(__cplusplus) || defined(c_plusplus)
1430extern "C" {
1431#endif
1432
1433/*
1434 This the function that does the actual reading of data. It is
1435 the same as the one supplied in libpng, except that it receives the
1436 datastream from the ReadBlob() function instead of standard input.
1437*/
1438static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1439{
1440 Image
1441 *image;
1442
1443 image=(Image *) png_get_io_ptr(png_ptr);
1444 if (length)
1445 {
1446 png_size_t
1447 check;
1448
1449 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1450 if (check != length)
1451 {
1452 char
1453 msg[MaxTextExtent];
1454
cristy3b6fd2e2011-05-20 12:53:50 +00001455 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001456 "Expected %.20g bytes; found %.20g bytes",(double) length,
1457 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001458 png_warning(png_ptr,msg);
1459 png_error(png_ptr,"Read Exception");
1460 }
1461 }
1462}
1463
1464#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1465 !defined(PNG_MNG_FEATURES_SUPPORTED)
1466/* We use mng_get_data() instead of png_get_data() if we have a libpng
1467 * older than libpng-1.0.3a, which was the first to allow the empty
1468 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1469 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1470 * encountered after an empty PLTE, so we have to look ahead for bKGD
1471 * chunks and remove them from the datastream that is passed to libpng,
1472 * and store their contents for later use.
1473 */
1474static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1475{
1476 MngInfo
1477 *mng_info;
1478
1479 Image
1480 *image;
1481
1482 png_size_t
1483 check;
1484
cristybb503372010-05-27 20:51:26 +00001485 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001486 i;
1487
1488 i=0;
1489 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1490 image=(Image *) mng_info->image;
1491 while (mng_info->bytes_in_read_buffer && length)
1492 {
1493 data[i]=mng_info->read_buffer[i];
1494 mng_info->bytes_in_read_buffer--;
1495 length--;
1496 i++;
1497 }
1498 if (length)
1499 {
1500 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001501
cristy3ed852e2009-09-05 21:47:34 +00001502 if (check != length)
1503 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001504
cristy3ed852e2009-09-05 21:47:34 +00001505 if (length == 4)
1506 {
1507 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1508 (data[3] == 0))
1509 {
1510 check=(png_size_t) ReadBlob(image,(size_t) length,
1511 (char *) mng_info->read_buffer);
1512 mng_info->read_buffer[4]=0;
1513 mng_info->bytes_in_read_buffer=4;
1514 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1515 mng_info->found_empty_plte=MagickTrue;
1516 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1517 {
1518 mng_info->found_empty_plte=MagickFalse;
1519 mng_info->have_saved_bkgd_index=MagickFalse;
1520 }
1521 }
glennrp0fe50b42010-11-16 03:52:51 +00001522
cristy3ed852e2009-09-05 21:47:34 +00001523 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1524 (data[3] == 1))
1525 {
1526 check=(png_size_t) ReadBlob(image,(size_t) length,
1527 (char *) mng_info->read_buffer);
1528 mng_info->read_buffer[4]=0;
1529 mng_info->bytes_in_read_buffer=4;
1530 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1531 if (mng_info->found_empty_plte)
1532 {
1533 /*
1534 Skip the bKGD data byte and CRC.
1535 */
1536 check=(png_size_t)
1537 ReadBlob(image,5,(char *) mng_info->read_buffer);
1538 check=(png_size_t) ReadBlob(image,(size_t) length,
1539 (char *) mng_info->read_buffer);
1540 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1541 mng_info->have_saved_bkgd_index=MagickTrue;
1542 mng_info->bytes_in_read_buffer=0;
1543 }
1544 }
1545 }
1546 }
1547}
1548#endif
1549
1550static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1551{
1552 Image
1553 *image;
1554
1555 image=(Image *) png_get_io_ptr(png_ptr);
1556 if (length)
1557 {
1558 png_size_t
1559 check;
1560
cristybb503372010-05-27 20:51:26 +00001561 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001562
cristy3ed852e2009-09-05 21:47:34 +00001563 if (check != length)
1564 png_error(png_ptr,"WriteBlob Failed");
1565 }
1566}
1567
1568static void png_flush_data(png_structp png_ptr)
1569{
1570 (void) png_ptr;
1571}
1572
1573#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1574static int PalettesAreEqual(Image *a,Image *b)
1575{
cristybb503372010-05-27 20:51:26 +00001576 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001577 i;
1578
1579 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1580 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001581
cristy3ed852e2009-09-05 21:47:34 +00001582 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1583 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001584
cristy3ed852e2009-09-05 21:47:34 +00001585 if (a->colors != b->colors)
1586 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001587
cristybb503372010-05-27 20:51:26 +00001588 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001589 {
1590 if ((a->colormap[i].red != b->colormap[i].red) ||
1591 (a->colormap[i].green != b->colormap[i].green) ||
1592 (a->colormap[i].blue != b->colormap[i].blue))
1593 return((int) MagickFalse);
1594 }
glennrp0fe50b42010-11-16 03:52:51 +00001595
cristy3ed852e2009-09-05 21:47:34 +00001596 return((int) MagickTrue);
1597}
1598#endif
1599
1600static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1601{
1602 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1603 mng_info->exists[i] && !mng_info->frozen[i])
1604 {
1605#ifdef MNG_OBJECT_BUFFERS
1606 if (mng_info->ob[i] != (MngBuffer *) NULL)
1607 {
1608 if (mng_info->ob[i]->reference_count > 0)
1609 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001610
cristy3ed852e2009-09-05 21:47:34 +00001611 if (mng_info->ob[i]->reference_count == 0)
1612 {
1613 if (mng_info->ob[i]->image != (Image *) NULL)
1614 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001615
cristy3ed852e2009-09-05 21:47:34 +00001616 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1617 }
1618 }
1619 mng_info->ob[i]=(MngBuffer *) NULL;
1620#endif
1621 mng_info->exists[i]=MagickFalse;
1622 mng_info->invisible[i]=MagickFalse;
1623 mng_info->viewable[i]=MagickFalse;
1624 mng_info->frozen[i]=MagickFalse;
1625 mng_info->x_off[i]=0;
1626 mng_info->y_off[i]=0;
1627 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001628 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001629 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001630 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001631 }
1632}
1633
glennrp21f0e622011-01-07 16:20:57 +00001634static void MngInfoFreeStruct(MngInfo *mng_info,
1635 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001636{
glennrp21f0e622011-01-07 16:20:57 +00001637 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001638 {
cristybb503372010-05-27 20:51:26 +00001639 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001640 i;
1641
1642 for (i=1; i < MNG_MAX_OBJECTS; i++)
1643 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001644
cristy3ed852e2009-09-05 21:47:34 +00001645 if (mng_info->global_plte != (png_colorp) NULL)
1646 mng_info->global_plte=(png_colorp)
1647 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001648
cristy3ed852e2009-09-05 21:47:34 +00001649 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1650 *have_mng_structure=MagickFalse;
1651 }
1652}
1653
1654static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1655{
1656 MngBox
1657 box;
1658
1659 box=box1;
1660 if (box.left < box2.left)
1661 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001662
cristy3ed852e2009-09-05 21:47:34 +00001663 if (box.top < box2.top)
1664 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001665
cristy3ed852e2009-09-05 21:47:34 +00001666 if (box.right > box2.right)
1667 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001668
cristy3ed852e2009-09-05 21:47:34 +00001669 if (box.bottom > box2.bottom)
1670 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001671
cristy3ed852e2009-09-05 21:47:34 +00001672 return box;
1673}
1674
1675static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1676{
1677 MngBox
1678 box;
1679
1680 /*
1681 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1682 */
cristybb503372010-05-27 20:51:26 +00001683 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1684 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1685 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1686 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001687 if (delta_type != 0)
1688 {
1689 box.left+=previous_box.left;
1690 box.right+=previous_box.right;
1691 box.top+=previous_box.top;
1692 box.bottom+=previous_box.bottom;
1693 }
glennrp0fe50b42010-11-16 03:52:51 +00001694
cristy3ed852e2009-09-05 21:47:34 +00001695 return(box);
1696}
1697
1698static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1699 unsigned char *p)
1700{
1701 MngPair
1702 pair;
1703 /*
cristybb503372010-05-27 20:51:26 +00001704 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001705 */
cristy8182b072010-05-30 20:10:53 +00001706 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1707 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001708
cristy3ed852e2009-09-05 21:47:34 +00001709 if (delta_type != 0)
1710 {
1711 pair.a+=previous_pair.a;
1712 pair.b+=previous_pair.b;
1713 }
glennrp0fe50b42010-11-16 03:52:51 +00001714
cristy3ed852e2009-09-05 21:47:34 +00001715 return(pair);
1716}
1717
cristy8182b072010-05-30 20:10:53 +00001718static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001719{
cristy8182b072010-05-30 20:10:53 +00001720 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001721}
1722
cristyc82a27b2011-10-21 01:07:16 +00001723typedef struct _PNGErrorInfo
cristy3ed852e2009-09-05 21:47:34 +00001724{
1725 Image
1726 *image;
1727
cristyc82a27b2011-10-21 01:07:16 +00001728 ExceptionInfo
1729 *exception;
1730} PNGErrorInfo;
glennrp0fe50b42010-11-16 03:52:51 +00001731
cristyc82a27b2011-10-21 01:07:16 +00001732static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1733{
1734 ExceptionInfo
1735 *exception;
glennrp0fe50b42010-11-16 03:52:51 +00001736
cristyc82a27b2011-10-21 01:07:16 +00001737 Image
1738 *image;
1739
1740 PNGErrorInfo
1741 *error_info;
1742
1743 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1744 image=error_info->image;
1745 exception=error_info->exception;
1746
1747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1748 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1749
1750 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1751 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001752
glennrpe4017e32011-01-08 17:16:09 +00001753#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001754 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1755 * are building with libpng-1.4.x and can be ignored.
1756 */
cristy3ed852e2009-09-05 21:47:34 +00001757 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001758#else
1759 png_longjmp(ping,1);
1760#endif
cristy3ed852e2009-09-05 21:47:34 +00001761}
1762
glennrpcf002022011-01-30 02:38:15 +00001763static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001764{
cristyc82a27b2011-10-21 01:07:16 +00001765 ExceptionInfo
1766 *exception;
1767
cristy3ed852e2009-09-05 21:47:34 +00001768 Image
1769 *image;
1770
cristyc82a27b2011-10-21 01:07:16 +00001771 PNGErrorInfo
1772 *error_info;
1773
cristy3ed852e2009-09-05 21:47:34 +00001774 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1775 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001776
cristyc82a27b2011-10-21 01:07:16 +00001777 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1778 image=error_info->image;
1779 exception=error_info->exception;
1780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1781 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001782
cristyc82a27b2011-10-21 01:07:16 +00001783 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
cristy3ed852e2009-09-05 21:47:34 +00001784 message,"`%s'",image->filename);
1785}
1786
1787#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001788static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001789{
1790#if (PNG_LIBPNG_VER < 10011)
1791 png_voidp
1792 ret;
1793
cristydf0d90e2011-12-12 01:03:55 +00001794 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001795 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001796
cristy3ed852e2009-09-05 21:47:34 +00001797 if (ret == NULL)
1798 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001799
cristy3ed852e2009-09-05 21:47:34 +00001800 return(ret);
1801#else
cristydf0d90e2011-12-12 01:03:55 +00001802 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001803 return((png_voidp) AcquireMagickMemory((size_t) size));
1804#endif
1805}
1806
1807/*
1808 Free a pointer. It is removed from the list at the same time.
1809*/
glennrpcf002022011-01-30 02:38:15 +00001810static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001811{
glennrp3bd393f2011-12-21 18:54:53 +00001812 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001813 ptr=RelinquishMagickMemory(ptr);
1814 return((png_free_ptr) NULL);
1815}
1816#endif
1817
1818#if defined(__cplusplus) || defined(c_plusplus)
1819}
1820#endif
1821
1822static int
glennrpcf002022011-01-30 02:38:15 +00001823Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristyd15e6592011-10-15 00:13:06 +00001824 png_textp text,int ii,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001825{
cristybb503372010-05-27 20:51:26 +00001826 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001827 i;
1828
1829 register unsigned char
1830 *dp;
1831
1832 register png_charp
1833 sp;
1834
1835 png_uint_32
1836 length,
1837 nibbles;
1838
1839 StringInfo
1840 *profile;
1841
glennrp0c3e06b2010-11-19 13:45:02 +00001842 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001843 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1844 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1845 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1846 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1847 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1848 13,14,15};
1849
1850 sp=text[ii].text+1;
1851 /* look for newline */
1852 while (*sp != '\n')
1853 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001854
cristy3ed852e2009-09-05 21:47:34 +00001855 /* look for length */
1856 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1857 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001858
cristyf2f27272009-12-17 14:48:46 +00001859 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001860
glennrp97f90e22011-02-22 05:47:58 +00001861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1862 " length: %lu",(unsigned long) length);
1863
cristy3ed852e2009-09-05 21:47:34 +00001864 while (*sp != ' ' && *sp != '\n')
1865 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001866
cristy3ed852e2009-09-05 21:47:34 +00001867 /* allocate space */
1868 if (length == 0)
1869 {
cristyc82a27b2011-10-21 01:07:16 +00001870 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001871 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1872 return(MagickFalse);
1873 }
glennrp0fe50b42010-11-16 03:52:51 +00001874
cristy8723e4b2011-09-01 13:11:19 +00001875 profile=BlobToStringInfo((const void *) NULL,length);
glennrp0fe50b42010-11-16 03:52:51 +00001876
cristy3ed852e2009-09-05 21:47:34 +00001877 if (profile == (StringInfo *) NULL)
1878 {
cristyc82a27b2011-10-21 01:07:16 +00001879 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001880 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1881 "unable to copy profile");
1882 return(MagickFalse);
1883 }
glennrp0fe50b42010-11-16 03:52:51 +00001884
cristy3ed852e2009-09-05 21:47:34 +00001885 /* copy profile, skipping white space and column 1 "=" signs */
1886 dp=GetStringInfoDatum(profile);
1887 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001888
cristybb503372010-05-27 20:51:26 +00001889 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001890 {
1891 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1892 {
1893 if (*sp == '\0')
1894 {
cristyc82a27b2011-10-21 01:07:16 +00001895 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001896 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1897 profile=DestroyStringInfo(profile);
1898 return(MagickFalse);
1899 }
1900 sp++;
1901 }
glennrp0fe50b42010-11-16 03:52:51 +00001902
cristy3ed852e2009-09-05 21:47:34 +00001903 if (i%2 == 0)
1904 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001905
cristy3ed852e2009-09-05 21:47:34 +00001906 else
1907 (*dp++)+=unhex[(int) *sp++];
1908 }
1909 /*
1910 We have already read "Raw profile type.
1911 */
cristyd15e6592011-10-15 00:13:06 +00001912 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001913 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001914
cristy3ed852e2009-09-05 21:47:34 +00001915 if (image_info->verbose)
1916 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001917
cristy3ed852e2009-09-05 21:47:34 +00001918 return MagickTrue;
1919}
1920
1921#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1922static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1923{
1924 Image
1925 *image;
1926
1927
1928 /* The unknown chunk structure contains the chunk data:
1929 png_byte name[5];
1930 png_byte *data;
1931 png_size_t size;
1932
1933 Note that libpng has already taken care of the CRC handling.
1934 */
1935
1936
1937 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1938 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1939 return(0); /* Did not recognize */
1940
1941 /* recognized vpAg */
1942
1943 if (chunk->size != 9)
1944 return(-1); /* Error return */
1945
1946 if (chunk->data[8] != 0)
1947 return(0); /* ImageMagick requires pixel units */
1948
1949 image=(Image *) png_get_user_chunk_ptr(ping);
1950
cristybb503372010-05-27 20:51:26 +00001951 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001952 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001953
cristybb503372010-05-27 20:51:26 +00001954 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001955 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1956
1957 /* Return one of the following: */
1958 /* return(-n); chunk had an error */
1959 /* return(0); did not recognize */
1960 /* return(n); success */
1961
1962 return(1);
1963
1964}
1965#endif
1966
1967/*
1968%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1969% %
1970% %
1971% %
1972% R e a d O n e P N G I m a g e %
1973% %
1974% %
1975% %
1976%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1977%
1978% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1979% (minus the 8-byte signature) and returns it. It allocates the memory
1980% necessary for the new Image structure and returns a pointer to the new
1981% image.
1982%
1983% The format of the ReadOnePNGImage method is:
1984%
1985% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1986% ExceptionInfo *exception)
1987%
1988% A description of each parameter follows:
1989%
1990% o mng_info: Specifies a pointer to a MngInfo structure.
1991%
1992% o image_info: the image info.
1993%
1994% o exception: return any errors or warnings in this structure.
1995%
1996*/
1997static Image *ReadOnePNGImage(MngInfo *mng_info,
1998 const ImageInfo *image_info, ExceptionInfo *exception)
1999{
2000 /* Read one PNG image */
2001
glennrpcc95c3f2011-04-18 16:46:48 +00002002 /* To do: Read the tIME chunk into the date:modify property */
2003 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2004
cristy3ed852e2009-09-05 21:47:34 +00002005 Image
2006 *image;
2007
2008 int
glennrp4eb39312011-03-30 21:34:55 +00002009 intent,
glennrpcb395ac2011-03-30 19:50:23 +00002010 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00002011 num_text,
glennrp4eb39312011-03-30 21:34:55 +00002012 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00002013 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00002014 pass,
2015 ping_bit_depth,
2016 ping_color_type,
2017 ping_interlace_method,
2018 ping_compression_method,
2019 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00002020 ping_num_trans,
2021 unit_type;
2022
2023 double
2024 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002025
2026 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002027 logging,
cristy3ed852e2009-09-05 21:47:34 +00002028 status;
2029
cristyc82a27b2011-10-21 01:07:16 +00002030 PixelInfo
2031 transparent_color;
2032
2033 PNGErrorInfo
2034 error_info;
2035
glennrpfaa852b2010-03-30 12:17:00 +00002036 png_bytep
2037 ping_trans_alpha;
2038
2039 png_color_16p
2040 ping_background,
2041 ping_trans_color;
2042
cristy3ed852e2009-09-05 21:47:34 +00002043 png_info
2044 *end_info,
2045 *ping_info;
2046
2047 png_struct
2048 *ping;
2049
2050 png_textp
2051 text;
2052
glennrpfaa852b2010-03-30 12:17:00 +00002053 png_uint_32
2054 ping_height,
2055 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002056 x_resolution,
2057 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002058
cristy3ed852e2009-09-05 21:47:34 +00002059 QuantumInfo
2060 *quantum_info;
2061
2062 unsigned char
glennrpcf002022011-01-30 02:38:15 +00002063 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002064
cristybb503372010-05-27 20:51:26 +00002065 ssize_t
cristy756ae432011-11-19 02:18:25 +00002066 ping_rowbytes,
cristy3ed852e2009-09-05 21:47:34 +00002067 y;
2068
2069 register unsigned char
2070 *p;
2071
cristybb503372010-05-27 20:51:26 +00002072 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002073 i,
2074 x;
2075
cristy4c08aed2011-07-01 19:47:50 +00002076 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002077 *q;
2078
2079 size_t
glennrp39992b42010-11-14 00:03:43 +00002080 length,
cristy3ed852e2009-09-05 21:47:34 +00002081 row_offset;
2082
cristyeb3b22a2011-03-31 20:16:11 +00002083 ssize_t
2084 j;
2085
cristy3ed852e2009-09-05 21:47:34 +00002086#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2087 png_byte unused_chunks[]=
2088 {
2089 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2090 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2091 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2092 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2093 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2094 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2095 };
2096#endif
2097
2098 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002099 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002100
2101#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002102 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002103#endif
2104
glennrp25c1e2b2010-03-25 01:39:56 +00002105#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002106 if (image_info->verbose)
2107 printf("Your PNG library (libpng-%s) is rather old.\n",
2108 PNG_LIBPNG_VER_STRING);
2109#endif
2110
glennrp61b4c952009-11-10 20:40:41 +00002111#if (PNG_LIBPNG_VER >= 10400)
2112# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2113 if (image_info->verbose)
2114 {
2115 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2116 PNG_LIBPNG_VER_STRING);
2117 printf("Please update it.\n");
2118 }
2119# endif
2120#endif
2121
2122
cristyed552522009-10-16 14:04:35 +00002123 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002124 image=mng_info->image;
2125
glennrpa6a06632011-01-19 15:15:34 +00002126 if (logging != MagickFalse)
2127 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2128 " image->matte=%d",(int) image->matte);
2129
glennrp0e319732011-01-25 21:53:13 +00002130 /* Set to an out-of-range color unless tRNS chunk is present */
2131 transparent_color.red=65537;
2132 transparent_color.green=65537;
2133 transparent_color.blue=65537;
cristy4c08aed2011-07-01 19:47:50 +00002134 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002135
glennrpcb395ac2011-03-30 19:50:23 +00002136 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002137 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002138 num_raw_profiles = 0;
2139
cristy3ed852e2009-09-05 21:47:34 +00002140 /*
2141 Allocate the PNG structures
2142 */
2143#ifdef PNG_USER_MEM_SUPPORTED
cristyc82a27b2011-10-21 01:07:16 +00002144 error_info.image=image;
2145 error_info.exception=exception;
2146 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002147 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2148 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002149#else
cristyc82a27b2011-10-21 01:07:16 +00002150 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002151 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002152#endif
2153 if (ping == (png_struct *) NULL)
2154 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002155
cristy3ed852e2009-09-05 21:47:34 +00002156 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002157
cristy3ed852e2009-09-05 21:47:34 +00002158 if (ping_info == (png_info *) NULL)
2159 {
2160 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2161 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2162 }
glennrp0fe50b42010-11-16 03:52:51 +00002163
cristy3ed852e2009-09-05 21:47:34 +00002164 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002165
cristy3ed852e2009-09-05 21:47:34 +00002166 if (end_info == (png_info *) NULL)
2167 {
2168 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2169 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2170 }
glennrp0fe50b42010-11-16 03:52:51 +00002171
glennrpcf002022011-01-30 02:38:15 +00002172 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002173
glennrpfaa852b2010-03-30 12:17:00 +00002174 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002175 {
2176 /*
2177 PNG image is corrupt.
2178 */
2179 png_destroy_read_struct(&ping,&ping_info,&end_info);
2180#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002181 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002182#endif
2183 if (logging != MagickFalse)
2184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2185 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002186
cristy3ed852e2009-09-05 21:47:34 +00002187 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002188 {
cristyc82a27b2011-10-21 01:07:16 +00002189 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002190 image->columns=0;
2191 }
glennrp0fe50b42010-11-16 03:52:51 +00002192
cristy3ed852e2009-09-05 21:47:34 +00002193 return(GetFirstImageInList(image));
2194 }
2195 /*
2196 Prepare PNG for reading.
2197 */
glennrpfaa852b2010-03-30 12:17:00 +00002198
cristy3ed852e2009-09-05 21:47:34 +00002199 mng_info->image_found++;
2200 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002201
cristy3ed852e2009-09-05 21:47:34 +00002202 if (LocaleCompare(image_info->magick,"MNG") == 0)
2203 {
2204#if defined(PNG_MNG_FEATURES_SUPPORTED)
2205 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2206 png_set_read_fn(ping,image,png_get_data);
2207#else
2208#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2209 png_permit_empty_plte(ping,MagickTrue);
2210 png_set_read_fn(ping,image,png_get_data);
2211#else
2212 mng_info->image=image;
2213 mng_info->bytes_in_read_buffer=0;
2214 mng_info->found_empty_plte=MagickFalse;
2215 mng_info->have_saved_bkgd_index=MagickFalse;
2216 png_set_read_fn(ping,mng_info,mng_get_data);
2217#endif
2218#endif
2219 }
glennrp0fe50b42010-11-16 03:52:51 +00002220
cristy3ed852e2009-09-05 21:47:34 +00002221 else
2222 png_set_read_fn(ping,image,png_get_data);
2223
2224#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2225 /* Ignore unused chunks and all unknown chunks except for vpAg */
2226 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2227 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2228 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2229 (int)sizeof(unused_chunks)/5);
2230 /* Callback for other unknown chunks */
2231 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2232#endif
2233
glennrp991e92a2010-01-28 03:09:00 +00002234#if (PNG_LIBPNG_VER < 10400)
2235# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2236 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002237 /* Disable thread-unsafe features of pnggccrd */
2238 if (png_access_version_number() >= 10200)
2239 {
2240 png_uint_32 mmx_disable_mask=0;
2241 png_uint_32 asm_flags;
2242
2243 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2244 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2245 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2246 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2247 asm_flags=png_get_asm_flags(ping);
2248 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2249 }
glennrp991e92a2010-01-28 03:09:00 +00002250# endif
cristy3ed852e2009-09-05 21:47:34 +00002251#endif
2252
2253 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002254
2255 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2256 &ping_bit_depth,&ping_color_type,
2257 &ping_interlace_method,&ping_compression_method,
2258 &ping_filter_method);
2259
2260 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2261 &ping_trans_color);
2262
2263 (void) png_get_bKGD(ping, ping_info, &ping_background);
2264
2265 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002266 {
glennrpfaa852b2010-03-30 12:17:00 +00002267 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2268 {
2269 png_set_packing(ping);
2270 ping_bit_depth = 8;
2271 }
cristy3ed852e2009-09-05 21:47:34 +00002272 }
glennrpfaa852b2010-03-30 12:17:00 +00002273
2274 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002275 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002276 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00002277 if (logging != MagickFalse)
2278 {
2279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002280 " PNG width: %.20g, height: %.20g",
2281 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002282
cristy3ed852e2009-09-05 21:47:34 +00002283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2284 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002285 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002286
cristy3ed852e2009-09-05 21:47:34 +00002287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2288 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002289 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002290
cristy3ed852e2009-09-05 21:47:34 +00002291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2292 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002293 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002294 }
2295
glennrpfaa852b2010-03-30 12:17:00 +00002296#ifdef PNG_READ_iCCP_SUPPORTED
2297 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002298 {
2299 int
2300 compression;
2301
glennrpe4017e32011-01-08 17:16:09 +00002302#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002303 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002304 info;
2305#else
2306 png_bytep
2307 info;
2308#endif
2309
2310 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002311 name;
2312
2313 png_uint_32
2314 profile_length;
2315
2316 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2317 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002318
cristy3ed852e2009-09-05 21:47:34 +00002319 if (profile_length != 0)
2320 {
2321 StringInfo
2322 *profile;
2323
2324 if (logging != MagickFalse)
2325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2326 " Reading PNG iCCP chunk.");
cristye8f8f382011-09-01 13:32:37 +00002327 profile=BlobToStringInfo(info,profile_length);
2328 if (profile == (StringInfo *) NULL)
2329 {
cristyc82a27b2011-10-21 01:07:16 +00002330 (void) ThrowMagickException(exception,GetMagickModule(),
cristye8f8f382011-09-01 13:32:37 +00002331 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2332 "unable to copy profile");
cristyad5b0852011-09-08 02:01:21 +00002333 return((Image *) NULL);
cristye8f8f382011-09-01 13:32:37 +00002334 }
cristy3ed852e2009-09-05 21:47:34 +00002335 SetStringInfoDatum(profile,(const unsigned char *) info);
cristyd15e6592011-10-15 00:13:06 +00002336 (void) SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00002337 profile=DestroyStringInfo(profile);
2338 }
2339 }
2340#endif
2341#if defined(PNG_READ_sRGB_SUPPORTED)
2342 {
cristy3ed852e2009-09-05 21:47:34 +00002343 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00002344 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2345 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00002346
cristy3ed852e2009-09-05 21:47:34 +00002347 if (png_get_sRGB(ping,ping_info,&intent))
2348 {
glennrpcf002022011-01-30 02:38:15 +00002349 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2350 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002351
cristy3ed852e2009-09-05 21:47:34 +00002352 if (logging != MagickFalse)
2353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002354 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002355 }
2356 }
2357#endif
2358 {
glennrpfaa852b2010-03-30 12:17:00 +00002359 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2360 if (mng_info->have_global_gama)
2361 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002362
cristy3ed852e2009-09-05 21:47:34 +00002363 if (png_get_gAMA(ping,ping_info,&file_gamma))
2364 {
2365 image->gamma=(float) file_gamma;
2366 if (logging != MagickFalse)
2367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2368 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2369 }
2370 }
glennrpfaa852b2010-03-30 12:17:00 +00002371 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2372 {
2373 if (mng_info->have_global_chrm != MagickFalse)
2374 {
2375 (void) png_set_cHRM(ping,ping_info,
2376 mng_info->global_chrm.white_point.x,
2377 mng_info->global_chrm.white_point.y,
2378 mng_info->global_chrm.red_primary.x,
2379 mng_info->global_chrm.red_primary.y,
2380 mng_info->global_chrm.green_primary.x,
2381 mng_info->global_chrm.green_primary.y,
2382 mng_info->global_chrm.blue_primary.x,
2383 mng_info->global_chrm.blue_primary.y);
2384 }
2385 }
glennrp0fe50b42010-11-16 03:52:51 +00002386
glennrpfaa852b2010-03-30 12:17:00 +00002387 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002388 {
2389 (void) png_get_cHRM(ping,ping_info,
2390 &image->chromaticity.white_point.x,
2391 &image->chromaticity.white_point.y,
2392 &image->chromaticity.red_primary.x,
2393 &image->chromaticity.red_primary.y,
2394 &image->chromaticity.green_primary.x,
2395 &image->chromaticity.green_primary.y,
2396 &image->chromaticity.blue_primary.x,
2397 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002398
cristy3ed852e2009-09-05 21:47:34 +00002399 if (logging != MagickFalse)
2400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2401 " Reading PNG cHRM chunk.");
2402 }
glennrp0fe50b42010-11-16 03:52:51 +00002403
glennrpe610a072010-08-05 17:08:46 +00002404 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002405 {
glennrpe610a072010-08-05 17:08:46 +00002406 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002407 Magick_RenderingIntent_to_PNG_RenderingIntent
2408 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00002409 png_set_gAMA(ping,ping_info,0.45455f);
2410 png_set_cHRM(ping,ping_info,
2411 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2412 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002413 }
cristy3ed852e2009-09-05 21:47:34 +00002414#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002415 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002416 {
cristy905ef802011-02-23 00:29:18 +00002417 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2418 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002419
cristy3ed852e2009-09-05 21:47:34 +00002420 if (logging != MagickFalse)
2421 if (image->page.x || image->page.y)
2422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002423 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2424 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002425 }
2426#endif
2427#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002428 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2429 {
2430 if (mng_info->have_global_phys)
2431 {
2432 png_set_pHYs(ping,ping_info,
2433 mng_info->global_x_pixels_per_unit,
2434 mng_info->global_y_pixels_per_unit,
2435 mng_info->global_phys_unit_type);
2436 }
2437 }
2438
2439 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002440 {
cristy3ed852e2009-09-05 21:47:34 +00002441 /*
2442 Set image resolution.
2443 */
2444 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002445 &unit_type);
cristy2a11bef2011-10-28 18:33:11 +00002446 image->resolution.x=(double) x_resolution;
2447 image->resolution.y=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002448
cristy3ed852e2009-09-05 21:47:34 +00002449 if (unit_type == PNG_RESOLUTION_METER)
2450 {
2451 image->units=PixelsPerCentimeterResolution;
cristy2a11bef2011-10-28 18:33:11 +00002452 image->resolution.x=(double) x_resolution/100.0;
2453 image->resolution.y=(double) y_resolution/100.0;
cristy3ed852e2009-09-05 21:47:34 +00002454 }
glennrp0fe50b42010-11-16 03:52:51 +00002455
cristy3ed852e2009-09-05 21:47:34 +00002456 if (logging != MagickFalse)
2457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002458 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2459 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002460 }
cristy3ed852e2009-09-05 21:47:34 +00002461#endif
glennrp823b55c2011-03-14 18:46:46 +00002462
glennrpfaa852b2010-03-30 12:17:00 +00002463 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002464 {
2465 int
2466 number_colors;
2467
2468 png_colorp
2469 palette;
2470
2471 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002472
cristy3ed852e2009-09-05 21:47:34 +00002473 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002474 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002475 {
2476 if (mng_info->global_plte_length)
2477 {
2478 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2479 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002480
glennrpfaa852b2010-03-30 12:17:00 +00002481 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002482 if (mng_info->global_trns_length)
2483 {
2484 if (mng_info->global_trns_length >
2485 mng_info->global_plte_length)
cristyc82a27b2011-10-21 01:07:16 +00002486 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00002487 GetMagickModule(),CoderError,
2488 "global tRNS has more entries than global PLTE",
2489 "`%s'",image_info->filename);
2490 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2491 (int) mng_info->global_trns_length,NULL);
2492 }
glennrpbfd9e612011-04-22 14:02:20 +00002493#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002494 if (
2495#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2496 mng_info->have_saved_bkgd_index ||
2497#endif
glennrpfaa852b2010-03-30 12:17:00 +00002498 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002499 {
2500 png_color_16
2501 background;
2502
2503#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2504 if (mng_info->have_saved_bkgd_index)
2505 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002506#endif
glennrpfaa852b2010-03-30 12:17:00 +00002507 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2508 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002509
cristy3ed852e2009-09-05 21:47:34 +00002510 background.red=(png_uint_16)
2511 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002512
cristy3ed852e2009-09-05 21:47:34 +00002513 background.green=(png_uint_16)
2514 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002515
cristy3ed852e2009-09-05 21:47:34 +00002516 background.blue=(png_uint_16)
2517 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002518
glennrpc6c391a2011-04-27 02:23:56 +00002519 background.gray=(png_uint_16)
2520 mng_info->global_plte[background.index].green;
2521
cristy3ed852e2009-09-05 21:47:34 +00002522 png_set_bKGD(ping,ping_info,&background);
2523 }
2524#endif
2525 }
2526 else
cristyc82a27b2011-10-21 01:07:16 +00002527 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00002528 CoderError,"No global PLTE in file","`%s'",
2529 image_info->filename);
2530 }
2531 }
2532
glennrpbfd9e612011-04-22 14:02:20 +00002533#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002534 if (mng_info->have_global_bkgd &&
2535 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002536 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002537
glennrpfaa852b2010-03-30 12:17:00 +00002538 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002539 {
glennrpbfd9e612011-04-22 14:02:20 +00002540 unsigned int
2541 bkgd_scale;
2542
cristy3ed852e2009-09-05 21:47:34 +00002543 /*
2544 Set image background color.
2545 */
2546 if (logging != MagickFalse)
2547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2548 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002549
glennrpbfd9e612011-04-22 14:02:20 +00002550 /* Scale background components to 16-bit, then scale
2551 * to quantum depth
2552 */
2553 if (logging != MagickFalse)
2554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2555 " raw ping_background=(%d,%d,%d).",ping_background->red,
2556 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002557
glennrpbfd9e612011-04-22 14:02:20 +00002558 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002559
glennrpbfd9e612011-04-22 14:02:20 +00002560 if (ping_bit_depth == 1)
2561 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002562
glennrpbfd9e612011-04-22 14:02:20 +00002563 else if (ping_bit_depth == 2)
2564 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002565
glennrpbfd9e612011-04-22 14:02:20 +00002566 else if (ping_bit_depth == 4)
2567 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002568
glennrpbfd9e612011-04-22 14:02:20 +00002569 if (ping_bit_depth <= 8)
2570 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002571
glennrpbfd9e612011-04-22 14:02:20 +00002572 ping_background->red *= bkgd_scale;
2573 ping_background->green *= bkgd_scale;
2574 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002575
glennrpbfd9e612011-04-22 14:02:20 +00002576 if (logging != MagickFalse)
2577 {
glennrp2cbb4482010-06-02 04:37:24 +00002578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2579 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002580
glennrp2cbb4482010-06-02 04:37:24 +00002581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2582 " ping_background=(%d,%d,%d).",ping_background->red,
2583 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002584 }
glennrp2cbb4482010-06-02 04:37:24 +00002585
glennrpbfd9e612011-04-22 14:02:20 +00002586 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002587 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002588
glennrpbfd9e612011-04-22 14:02:20 +00002589 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002590 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002591
glennrpbfd9e612011-04-22 14:02:20 +00002592 image->background_color.blue=
2593 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002594
cristy4c08aed2011-07-01 19:47:50 +00002595 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002596
glennrpbfd9e612011-04-22 14:02:20 +00002597 if (logging != MagickFalse)
2598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2599 " image->background_color=(%.20g,%.20g,%.20g).",
2600 (double) image->background_color.red,
2601 (double) image->background_color.green,
2602 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002603 }
glennrpbfd9e612011-04-22 14:02:20 +00002604#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002605
glennrpfaa852b2010-03-30 12:17:00 +00002606 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002607 {
2608 /*
glennrpa6a06632011-01-19 15:15:34 +00002609 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002610 */
2611 int
2612 max_sample;
2613
cristy35ef8242010-06-03 16:24:13 +00002614 size_t
2615 one=1;
2616
cristy3ed852e2009-09-05 21:47:34 +00002617 if (logging != MagickFalse)
2618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2619 " Reading PNG tRNS chunk.");
2620
cristyf9cca6a2010-06-04 23:49:28 +00002621 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002622
glennrpfaa852b2010-03-30 12:17:00 +00002623 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2624 (int)ping_trans_color->gray > max_sample) ||
2625 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2626 ((int)ping_trans_color->red > max_sample ||
2627 (int)ping_trans_color->green > max_sample ||
2628 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002629 {
2630 if (logging != MagickFalse)
2631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2632 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002633 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002634 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002635 image->matte=MagickFalse;
2636 }
2637 else
2638 {
glennrpa6a06632011-01-19 15:15:34 +00002639 int
2640 scale_to_short;
2641
2642 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2643
2644 /* Scale transparent_color to short */
2645 transparent_color.red= scale_to_short*ping_trans_color->red;
2646 transparent_color.green= scale_to_short*ping_trans_color->green;
2647 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy4c08aed2011-07-01 19:47:50 +00002648 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002649
glennrpfaa852b2010-03-30 12:17:00 +00002650 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002651 {
glennrp0f111982010-07-07 20:18:33 +00002652 if (logging != MagickFalse)
2653 {
2654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2655 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002656
glennrp0f111982010-07-07 20:18:33 +00002657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy101ab702011-10-13 13:06:32 +00002658 " scaled graylevel is %.20g.",transparent_color.alpha);
glennrp0f111982010-07-07 20:18:33 +00002659 }
cristy4c08aed2011-07-01 19:47:50 +00002660 transparent_color.red=transparent_color.alpha;
2661 transparent_color.green=transparent_color.alpha;
2662 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002663 }
2664 }
2665 }
2666#if defined(PNG_READ_sBIT_SUPPORTED)
2667 if (mng_info->have_global_sbit)
2668 {
glennrpfaa852b2010-03-30 12:17:00 +00002669 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002670 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2671 }
2672#endif
2673 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002674
cristy3ed852e2009-09-05 21:47:34 +00002675 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002676
2677 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2678
cristy3ed852e2009-09-05 21:47:34 +00002679 /*
2680 Initialize image structure.
2681 */
2682 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002683 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002684 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002685 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002686 if (mng_info->mng_type == 0)
2687 {
glennrpfaa852b2010-03-30 12:17:00 +00002688 mng_info->mng_width=ping_width;
2689 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002690 mng_info->frame=mng_info->image_box;
2691 mng_info->clip=mng_info->image_box;
2692 }
glennrp0fe50b42010-11-16 03:52:51 +00002693
cristy3ed852e2009-09-05 21:47:34 +00002694 else
2695 {
2696 image->page.y=mng_info->y_off[mng_info->object_id];
2697 }
glennrp0fe50b42010-11-16 03:52:51 +00002698
cristy3ed852e2009-09-05 21:47:34 +00002699 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002700 image->columns=ping_width;
2701 image->rows=ping_height;
2702 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002703 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002704 {
cristybefe4d22010-06-07 01:18:58 +00002705 size_t
2706 one;
2707
cristy3ed852e2009-09-05 21:47:34 +00002708 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002709 one=1;
2710 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002711#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2712 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002713 image->colors=256;
2714#else
2715 if (image->colors > 65536L)
2716 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002717#endif
glennrpfaa852b2010-03-30 12:17:00 +00002718 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002719 {
2720 int
2721 number_colors;
2722
2723 png_colorp
2724 palette;
2725
2726 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002727 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002728
cristy3ed852e2009-09-05 21:47:34 +00002729 if (logging != MagickFalse)
2730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2731 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2732 }
2733 }
2734
2735 if (image->storage_class == PseudoClass)
2736 {
2737 /*
2738 Initialize image colormap.
2739 */
cristy018f07f2011-09-04 21:15:19 +00002740 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002741 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002742
glennrpfaa852b2010-03-30 12:17:00 +00002743 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002744 {
2745 int
2746 number_colors;
2747
2748 png_colorp
2749 palette;
2750
2751 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002752
glennrp6af6cf12011-04-22 13:05:16 +00002753 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002754 {
2755 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2756 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2757 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2758 }
glennrp6af6cf12011-04-22 13:05:16 +00002759
glennrp67b9c1a2011-04-22 18:47:36 +00002760 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002761 {
2762 image->colormap[i].red=0;
2763 image->colormap[i].green=0;
2764 image->colormap[i].blue=0;
2765 }
cristy3ed852e2009-09-05 21:47:34 +00002766 }
glennrp0fe50b42010-11-16 03:52:51 +00002767
cristy3ed852e2009-09-05 21:47:34 +00002768 else
2769 {
cristybb503372010-05-27 20:51:26 +00002770 size_t
cristy3ed852e2009-09-05 21:47:34 +00002771 scale;
2772
glennrpfaa852b2010-03-30 12:17:00 +00002773 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002774
cristy3ed852e2009-09-05 21:47:34 +00002775 if (scale < 1)
2776 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002777
cristybb503372010-05-27 20:51:26 +00002778 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002779 {
2780 image->colormap[i].red=(Quantum) (i*scale);
2781 image->colormap[i].green=(Quantum) (i*scale);
2782 image->colormap[i].blue=(Quantum) (i*scale);
2783 }
2784 }
2785 }
glennrp147bc912011-03-30 18:47:21 +00002786
glennrpcb395ac2011-03-30 19:50:23 +00002787 /* Set some properties for reporting by "identify" */
2788 {
glennrp147bc912011-03-30 18:47:21 +00002789 char
2790 msg[MaxTextExtent];
2791
2792 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2793 ping_interlace_method in value */
2794
cristy3b6fd2e2011-05-20 12:53:50 +00002795 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002796 "%d, %d",(int) ping_width, (int) ping_height);
cristy5d6fc9c2011-12-27 03:10:42 +00002797 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002798
cristy3b6fd2e2011-05-20 12:53:50 +00002799 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
cristy5d6fc9c2011-12-27 03:10:42 +00002800 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002801
cristy3b6fd2e2011-05-20 12:53:50 +00002802 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
cristy5d6fc9c2011-12-27 03:10:42 +00002803 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002804
cristy3b6fd2e2011-05-20 12:53:50 +00002805 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002806 (int) ping_interlace_method);
cristy5d6fc9c2011-12-27 03:10:42 +00002807 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
glennrpcb395ac2011-03-30 19:50:23 +00002808 }
glennrp147bc912011-03-30 18:47:21 +00002809
cristy3ed852e2009-09-05 21:47:34 +00002810 /*
2811 Read image scanlines.
2812 */
2813 if (image->delay != 0)
2814 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002815
glennrp0ca69b12010-07-26 01:57:52 +00002816 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002817 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2818 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002819 {
2820 if (logging != MagickFalse)
2821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002822 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002823 mng_info->scenes_found-1);
2824 png_destroy_read_struct(&ping,&ping_info,&end_info);
2825#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002826 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002827#endif
2828 if (logging != MagickFalse)
2829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2830 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002831
cristy3ed852e2009-09-05 21:47:34 +00002832 return(image);
2833 }
glennrp0fe50b42010-11-16 03:52:51 +00002834
cristy3ed852e2009-09-05 21:47:34 +00002835 if (logging != MagickFalse)
2836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2837 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002838
cristy3ed852e2009-09-05 21:47:34 +00002839 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002840 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2841 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002842
cristy3ed852e2009-09-05 21:47:34 +00002843 else
glennrpcf002022011-01-30 02:38:15 +00002844 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2845 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002846
glennrpcf002022011-01-30 02:38:15 +00002847 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002848 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002849
cristy3ed852e2009-09-05 21:47:34 +00002850 if (logging != MagickFalse)
2851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2852 " Converting PNG pixels to pixel packets");
2853 /*
2854 Convert PNG pixels to pixel packets.
2855 */
glennrpfaa852b2010-03-30 12:17:00 +00002856 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002857 {
2858 /*
2859 PNG image is corrupt.
2860 */
2861 png_destroy_read_struct(&ping,&ping_info,&end_info);
2862#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002863 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002864#endif
2865 if (quantum_info != (QuantumInfo *) NULL)
2866 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002867
glennrpcf002022011-01-30 02:38:15 +00002868 if (ping_pixels != (unsigned char *) NULL)
2869 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002870
cristy3ed852e2009-09-05 21:47:34 +00002871 if (logging != MagickFalse)
2872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2873 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002874
cristy3ed852e2009-09-05 21:47:34 +00002875 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002876 {
cristyc82a27b2011-10-21 01:07:16 +00002877 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002878 image->columns=0;
2879 }
glennrp0fe50b42010-11-16 03:52:51 +00002880
cristy3ed852e2009-09-05 21:47:34 +00002881 return(GetFirstImageInList(image));
2882 }
glennrp0fe50b42010-11-16 03:52:51 +00002883
cristyed552522009-10-16 14:04:35 +00002884 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002885
cristyed552522009-10-16 14:04:35 +00002886 if (quantum_info == (QuantumInfo *) NULL)
2887 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002888
glennrpc8cbc5d2011-01-01 00:12:34 +00002889 {
2890
2891 MagickBooleanType
2892 found_transparent_pixel;
2893
2894 found_transparent_pixel=MagickFalse;
2895
cristy3ed852e2009-09-05 21:47:34 +00002896 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002897 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002898 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002899 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002900 /*
2901 Convert image to DirectClass pixel packets.
2902 */
glennrp67b9c1a2011-04-22 18:47:36 +00002903#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2904 int
2905 depth;
2906
2907 depth=(ssize_t) ping_bit_depth;
2908#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002909 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2910 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2911 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2912 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002913
glennrpc8cbc5d2011-01-01 00:12:34 +00002914 for (y=0; y < (ssize_t) image->rows; y++)
2915 {
2916 if (num_passes > 1)
2917 row_offset=ping_rowbytes*y;
2918
2919 else
2920 row_offset=0;
2921
glennrpcf002022011-01-30 02:38:15 +00002922 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002923 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2924
cristyacd2ed22011-08-30 01:44:23 +00002925 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00002926 break;
2927
glennrpc8cbc5d2011-01-01 00:12:34 +00002928 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2929 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002930 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002931
2932 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2933 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002934 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002935
2936 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2937 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002938 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002939
2940 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2941 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002942 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002943
2944 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2945 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002946 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002947
glennrpc8cbc5d2011-01-01 00:12:34 +00002948 if (found_transparent_pixel == MagickFalse)
2949 {
2950 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002951 if (y== 0 && logging != MagickFalse)
2952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2953 " Looking for cheap transparent pixel");
2954
glennrpc8cbc5d2011-01-01 00:12:34 +00002955 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2956 {
glennrp5aa37f62011-01-02 03:07:57 +00002957 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2958 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy4c08aed2011-07-01 19:47:50 +00002959 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00002960 {
glennrpa6a06632011-01-19 15:15:34 +00002961 if (logging != MagickFalse)
2962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2963 " ...got one.");
2964
glennrpc8cbc5d2011-01-01 00:12:34 +00002965 found_transparent_pixel = MagickTrue;
2966 break;
2967 }
glennrp4f25bd02011-01-01 18:51:28 +00002968 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2969 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp847370c2011-07-05 17:37:15 +00002970 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2971 transparent_color.red &&
2972 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2973 transparent_color.green &&
2974 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2975 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002976 {
glennrpa6a06632011-01-19 15:15:34 +00002977 if (logging != MagickFalse)
2978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2979 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002980 found_transparent_pixel = MagickTrue;
2981 break;
2982 }
cristyed231572011-07-14 02:18:59 +00002983 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00002984 }
2985 }
2986
2987 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2988 {
2989 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2990 image->rows);
2991
2992 if (status == MagickFalse)
2993 break;
2994 }
2995 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2996 break;
2997 }
2998
2999 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3000 {
3001 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00003002 if (status == MagickFalse)
3003 break;
3004 }
cristy3ed852e2009-09-05 21:47:34 +00003005 }
cristy3ed852e2009-09-05 21:47:34 +00003006 }
glennrp0fe50b42010-11-16 03:52:51 +00003007
cristy3ed852e2009-09-05 21:47:34 +00003008 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00003009
cristy3ed852e2009-09-05 21:47:34 +00003010 for (pass=0; pass < num_passes; pass++)
3011 {
3012 Quantum
3013 *quantum_scanline;
3014
3015 register Quantum
3016 *r;
3017
3018 /*
3019 Convert grayscale image to PseudoClass pixel packets.
3020 */
glennrpc17d96f2011-06-27 01:20:11 +00003021 if (logging != MagickFalse)
3022 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3023 " Converting grayscale pixels to pixel packets");
cristy4c08aed2011-07-01 19:47:50 +00003024
glennrpfaa852b2010-03-30 12:17:00 +00003025 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00003026 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00003027
cristy3ed852e2009-09-05 21:47:34 +00003028 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3029 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003030
cristy3ed852e2009-09-05 21:47:34 +00003031 if (quantum_scanline == (Quantum *) NULL)
3032 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003033
cristybb503372010-05-27 20:51:26 +00003034 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003035 {
3036 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003037 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003038
cristy3ed852e2009-09-05 21:47:34 +00003039 else
3040 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003041
glennrpcf002022011-01-30 02:38:15 +00003042 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00003043 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003044
cristyacd2ed22011-08-30 01:44:23 +00003045 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003046 break;
glennrp0fe50b42010-11-16 03:52:51 +00003047
glennrpcf002022011-01-30 02:38:15 +00003048 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003049 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003050
glennrpfaa852b2010-03-30 12:17:00 +00003051 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003052 {
3053 case 1:
3054 {
cristybb503372010-05-27 20:51:26 +00003055 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003056 bit;
3057
cristybb503372010-05-27 20:51:26 +00003058 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003059 {
3060 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003061 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003062 p++;
3063 }
glennrp0fe50b42010-11-16 03:52:51 +00003064
cristy3ed852e2009-09-05 21:47:34 +00003065 if ((image->columns % 8) != 0)
3066 {
cristybb503372010-05-27 20:51:26 +00003067 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003068 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003069 }
glennrp0fe50b42010-11-16 03:52:51 +00003070
cristy3ed852e2009-09-05 21:47:34 +00003071 break;
3072 }
glennrp47b9dd52010-11-24 18:12:06 +00003073
cristy3ed852e2009-09-05 21:47:34 +00003074 case 2:
3075 {
cristybb503372010-05-27 20:51:26 +00003076 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003077 {
glennrpa18d5bc2011-04-23 14:51:34 +00003078 *r++=(*p >> 6) & 0x03;
3079 *r++=(*p >> 4) & 0x03;
3080 *r++=(*p >> 2) & 0x03;
3081 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003082 }
glennrp0fe50b42010-11-16 03:52:51 +00003083
cristy3ed852e2009-09-05 21:47:34 +00003084 if ((image->columns % 4) != 0)
3085 {
cristybb503372010-05-27 20:51:26 +00003086 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003087 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003088 }
glennrp0fe50b42010-11-16 03:52:51 +00003089
cristy3ed852e2009-09-05 21:47:34 +00003090 break;
3091 }
glennrp47b9dd52010-11-24 18:12:06 +00003092
cristy3ed852e2009-09-05 21:47:34 +00003093 case 4:
3094 {
cristybb503372010-05-27 20:51:26 +00003095 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003096 {
glennrpa18d5bc2011-04-23 14:51:34 +00003097 *r++=(*p >> 4) & 0x0f;
3098 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003099 }
glennrp0fe50b42010-11-16 03:52:51 +00003100
cristy3ed852e2009-09-05 21:47:34 +00003101 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003102 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003103
cristy3ed852e2009-09-05 21:47:34 +00003104 break;
3105 }
glennrp47b9dd52010-11-24 18:12:06 +00003106
cristy3ed852e2009-09-05 21:47:34 +00003107 case 8:
3108 {
glennrpfaa852b2010-03-30 12:17:00 +00003109 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003110 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003111 {
glennrpa18d5bc2011-04-23 14:51:34 +00003112 *r++=*p++;
cristy4c08aed2011-07-01 19:47:50 +00003113 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3114 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003115 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003116 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003117 }
glennrp0fe50b42010-11-16 03:52:51 +00003118
cristy3ed852e2009-09-05 21:47:34 +00003119 else
cristybb503372010-05-27 20:51:26 +00003120 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003121 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003122
cristy3ed852e2009-09-05 21:47:34 +00003123 break;
3124 }
glennrp47b9dd52010-11-24 18:12:06 +00003125
cristy3ed852e2009-09-05 21:47:34 +00003126 case 16:
3127 {
cristybb503372010-05-27 20:51:26 +00003128 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003129 {
glennrpc17d96f2011-06-27 01:20:11 +00003130#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003131 size_t
3132 quantum;
3133
3134 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003135 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003136
3137 else
glennrpc17d96f2011-06-27 01:20:11 +00003138 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003139
glennrp58f77c72011-04-23 14:09:09 +00003140 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003141 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003142 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003143
3144 if (ping_color_type == 4)
3145 {
glennrpc17d96f2011-06-27 01:20:11 +00003146 if (image->colors > 256)
3147 quantum=((*p++) << 8);
3148 else
3149 quantum=0;
3150
glennrp58f77c72011-04-23 14:09:09 +00003151 quantum|=(*p++);
cristy4c08aed2011-07-01 19:47:50 +00003152 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3153 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003154 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003155 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003156 }
glennrp58f77c72011-04-23 14:09:09 +00003157
3158#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3159 *r++=(*p++);
3160 p++; /* strip low byte */
3161
3162 if (ping_color_type == 4)
3163 {
cristy4c08aed2011-07-01 19:47:50 +00003164 SetPixelAlpha(image,*p++,q);
3165 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003166 found_transparent_pixel = MagickTrue;
3167 p++;
cristyed231572011-07-14 02:18:59 +00003168 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003169 }
cristy3ed852e2009-09-05 21:47:34 +00003170#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003171 }
glennrp47b9dd52010-11-24 18:12:06 +00003172
cristy3ed852e2009-09-05 21:47:34 +00003173 break;
3174 }
glennrp47b9dd52010-11-24 18:12:06 +00003175
cristy3ed852e2009-09-05 21:47:34 +00003176 default:
3177 break;
3178 }
glennrp3faa9a32011-04-23 14:00:25 +00003179
cristy3ed852e2009-09-05 21:47:34 +00003180 /*
3181 Transfer image scanline.
3182 */
3183 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003184
cristy4c08aed2011-07-01 19:47:50 +00003185 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3186
cristyacd2ed22011-08-30 01:44:23 +00003187 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00003188 break;
cristybb503372010-05-27 20:51:26 +00003189 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00003190 {
3191 SetPixelIndex(image,*r++,q);
cristyed231572011-07-14 02:18:59 +00003192 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00003193 }
glennrp0fe50b42010-11-16 03:52:51 +00003194
cristy3ed852e2009-09-05 21:47:34 +00003195 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3196 break;
glennrp0fe50b42010-11-16 03:52:51 +00003197
cristy7a287bf2010-02-14 02:18:09 +00003198 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3199 {
cristycee97112010-05-28 00:44:52 +00003200 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003201 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003202
cristy7a287bf2010-02-14 02:18:09 +00003203 if (status == MagickFalse)
3204 break;
3205 }
cristy3ed852e2009-09-05 21:47:34 +00003206 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003207
cristy7a287bf2010-02-14 02:18:09 +00003208 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003209 {
3210 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003211
cristy3ed852e2009-09-05 21:47:34 +00003212 if (status == MagickFalse)
3213 break;
3214 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003215
cristy3ed852e2009-09-05 21:47:34 +00003216 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3217 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003218
3219 image->matte=found_transparent_pixel;
3220
3221 if (logging != MagickFalse)
3222 {
3223 if (found_transparent_pixel != MagickFalse)
3224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3225 " Found transparent pixel");
3226 else
glennrp5aa37f62011-01-02 03:07:57 +00003227 {
3228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3229 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003230
glennrp5aa37f62011-01-02 03:07:57 +00003231 ping_color_type&=0x03;
3232 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003233 }
3234 }
3235
cristyb32b90a2009-09-07 21:45:48 +00003236 if (quantum_info != (QuantumInfo *) NULL)
3237 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00003238
cristy5c6f7892010-05-05 22:53:29 +00003239 if (image->storage_class == PseudoClass)
3240 {
cristyaeb2cbc2010-05-07 13:28:58 +00003241 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003242 matte;
3243
3244 matte=image->matte;
3245 image->matte=MagickFalse;
cristyea1a8aa2011-10-20 13:24:06 +00003246 (void) SyncImage(image,exception);
cristyaeb2cbc2010-05-07 13:28:58 +00003247 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003248 }
glennrp47b9dd52010-11-24 18:12:06 +00003249
glennrp4eb39312011-03-30 21:34:55 +00003250 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003251
3252 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003253 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003254 {
3255 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003256 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003257 image->colors=2;
cristyea1a8aa2011-10-20 13:24:06 +00003258 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003259#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003260 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003261#endif
3262 if (logging != MagickFalse)
3263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3264 " exit ReadOnePNGImage() early.");
3265 return(image);
3266 }
glennrp47b9dd52010-11-24 18:12:06 +00003267
glennrpfaa852b2010-03-30 12:17:00 +00003268 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003269 {
3270 ClassType
3271 storage_class;
3272
3273 /*
3274 Image has a transparent background.
3275 */
3276 storage_class=image->storage_class;
3277 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003278
glennrp3c218112010-11-27 15:31:26 +00003279/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003280
glennrp0fe50b42010-11-16 03:52:51 +00003281 if (storage_class == PseudoClass)
3282 {
3283 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003284 {
glennrp0fe50b42010-11-16 03:52:51 +00003285 for (x=0; x < ping_num_trans; x++)
3286 {
cristy6b7677c2012-01-01 20:59:57 +00003287 image->colormap[x].matte=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00003288 image->colormap[x].alpha =
3289 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003290 }
glennrpc11cf6a2010-03-20 16:46:19 +00003291 }
glennrp47b9dd52010-11-24 18:12:06 +00003292
glennrp0fe50b42010-11-16 03:52:51 +00003293 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3294 {
3295 for (x=0; x < (int) image->colors; x++)
3296 {
3297 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy4c08aed2011-07-01 19:47:50 +00003298 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003299 {
cristy6b7677c2012-01-01 20:59:57 +00003300 image->colormap[x].matte=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00003301 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003302 }
3303 }
3304 }
cristyea1a8aa2011-10-20 13:24:06 +00003305 (void) SyncImage(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003306 }
glennrp47b9dd52010-11-24 18:12:06 +00003307
glennrpa6a06632011-01-19 15:15:34 +00003308#if 1 /* Should have already been done above, but glennrp problem P10
3309 * needs this.
3310 */
glennrp0fe50b42010-11-16 03:52:51 +00003311 else
3312 {
3313 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003314 {
glennrp0fe50b42010-11-16 03:52:51 +00003315 image->storage_class=storage_class;
3316 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3317
cristyacd2ed22011-08-30 01:44:23 +00003318 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003319 break;
3320
glennrp0fe50b42010-11-16 03:52:51 +00003321
glennrpa6a06632011-01-19 15:15:34 +00003322 /* Caution: on a Q8 build, this does not distinguish between
3323 * 16-bit colors that differ only in the low byte
3324 */
glennrp0fe50b42010-11-16 03:52:51 +00003325 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3326 {
glennrp847370c2011-07-05 17:37:15 +00003327 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3328 transparent_color.red &&
3329 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3330 transparent_color.green &&
3331 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3332 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003333 {
cristy4c08aed2011-07-01 19:47:50 +00003334 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003335 }
glennrp0fe50b42010-11-16 03:52:51 +00003336
glennrp67b9c1a2011-04-22 18:47:36 +00003337#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003338 else
glennrp4f25bd02011-01-01 18:51:28 +00003339 {
cristy4c08aed2011-07-01 19:47:50 +00003340 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003341 }
glennrpa6a06632011-01-19 15:15:34 +00003342#endif
glennrp0fe50b42010-11-16 03:52:51 +00003343
cristyed231572011-07-14 02:18:59 +00003344 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003345 }
3346
3347 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3348 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003349 }
glennrp0fe50b42010-11-16 03:52:51 +00003350 }
glennrpa6a06632011-01-19 15:15:34 +00003351#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003352
cristy3ed852e2009-09-05 21:47:34 +00003353 image->storage_class=DirectClass;
3354 }
glennrp3c218112010-11-27 15:31:26 +00003355
cristyb40fc462010-08-08 00:49:49 +00003356 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3357 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3358 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00003359
cristyeb3b22a2011-03-31 20:16:11 +00003360 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003361 {
3362 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003363 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3364 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003365 else
glennrpa0ed0092011-04-18 16:36:29 +00003366 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3367 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003368
glennrp4eb39312011-03-30 21:34:55 +00003369 if (status != MagickFalse)
3370 for (i=0; i < (ssize_t) num_text; i++)
3371 {
3372 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003373
glennrp4eb39312011-03-30 21:34:55 +00003374 if (logging != MagickFalse)
3375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3376 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003377
glennrp4eb39312011-03-30 21:34:55 +00003378 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003379 {
cristyd15e6592011-10-15 00:13:06 +00003380 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i,
3381 exception);
glennrp4eb39312011-03-30 21:34:55 +00003382 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003383 }
glennrp0fe50b42010-11-16 03:52:51 +00003384
glennrp4eb39312011-03-30 21:34:55 +00003385 else
3386 {
3387 char
3388 *value;
3389
3390 length=text[i].text_length;
3391 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3392 sizeof(*value));
3393 if (value == (char *) NULL)
3394 {
cristyc82a27b2011-10-21 01:07:16 +00003395 (void) ThrowMagickException(exception,GetMagickModule(),
glennrp4eb39312011-03-30 21:34:55 +00003396 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3397 image->filename);
3398 break;
3399 }
3400 *value='\0';
3401 (void) ConcatenateMagickString(value,text[i].text,length+2);
3402
3403 /* Don't save "density" or "units" property if we have a pHYs
3404 * chunk
3405 */
3406 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3407 (LocaleCompare(text[i].key,"density") != 0 &&
3408 LocaleCompare(text[i].key,"units") != 0))
cristyd15e6592011-10-15 00:13:06 +00003409 (void) SetImageProperty(image,text[i].key,value,exception);
glennrp4eb39312011-03-30 21:34:55 +00003410
3411 if (logging != MagickFalse)
3412 {
3413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3414 " length: %lu",(unsigned long) length);
3415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3416 " Keyword: %s",text[i].key);
3417 }
3418
3419 value=DestroyString(value);
3420 }
3421 }
3422 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003423 }
glennrp3c218112010-11-27 15:31:26 +00003424
cristy3ed852e2009-09-05 21:47:34 +00003425#ifdef MNG_OBJECT_BUFFERS
3426 /*
3427 Store the object if necessary.
3428 */
3429 if (object_id && !mng_info->frozen[object_id])
3430 {
3431 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3432 {
3433 /*
3434 create a new object buffer.
3435 */
3436 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003437 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003438
cristy3ed852e2009-09-05 21:47:34 +00003439 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3440 {
3441 mng_info->ob[object_id]->image=(Image *) NULL;
3442 mng_info->ob[object_id]->reference_count=1;
3443 }
3444 }
glennrp47b9dd52010-11-24 18:12:06 +00003445
cristy3ed852e2009-09-05 21:47:34 +00003446 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3447 mng_info->ob[object_id]->frozen)
3448 {
3449 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
cristyc82a27b2011-10-21 01:07:16 +00003450 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003451 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3452 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003453
cristy3ed852e2009-09-05 21:47:34 +00003454 if (mng_info->ob[object_id]->frozen)
cristyc82a27b2011-10-21 01:07:16 +00003455 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003456 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3457 "`%s'",image->filename);
3458 }
glennrp0fe50b42010-11-16 03:52:51 +00003459
cristy3ed852e2009-09-05 21:47:34 +00003460 else
3461 {
cristy3ed852e2009-09-05 21:47:34 +00003462
3463 if (mng_info->ob[object_id]->image != (Image *) NULL)
3464 mng_info->ob[object_id]->image=DestroyImage
3465 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003466
cristy3ed852e2009-09-05 21:47:34 +00003467 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
cristyc82a27b2011-10-21 01:07:16 +00003468 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003469
cristy3ed852e2009-09-05 21:47:34 +00003470 if (mng_info->ob[object_id]->image != (Image *) NULL)
3471 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003472
cristy3ed852e2009-09-05 21:47:34 +00003473 else
cristyc82a27b2011-10-21 01:07:16 +00003474 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003475 ResourceLimitError,"Cloning image for object buffer failed",
3476 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003477
glennrpfaa852b2010-03-30 12:17:00 +00003478 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003479 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003480
glennrpfaa852b2010-03-30 12:17:00 +00003481 mng_info->ob[object_id]->width=ping_width;
3482 mng_info->ob[object_id]->height=ping_height;
3483 mng_info->ob[object_id]->color_type=ping_color_type;
3484 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3485 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3486 mng_info->ob[object_id]->compression_method=
3487 ping_compression_method;
3488 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003489
glennrpfaa852b2010-03-30 12:17:00 +00003490 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003491 {
3492 int
3493 number_colors;
3494
3495 png_colorp
3496 plte;
3497
3498 /*
3499 Copy the PLTE to the object buffer.
3500 */
3501 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3502 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003503
cristy3ed852e2009-09-05 21:47:34 +00003504 for (i=0; i < number_colors; i++)
3505 {
3506 mng_info->ob[object_id]->plte[i]=plte[i];
3507 }
3508 }
glennrp47b9dd52010-11-24 18:12:06 +00003509
cristy3ed852e2009-09-05 21:47:34 +00003510 else
3511 mng_info->ob[object_id]->plte_length=0;
3512 }
3513 }
3514#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003515
3516 /* Set image->matte to MagickTrue if the input colortype supports
3517 * alpha or if a valid tRNS chunk is present, no matter whether there
3518 * is actual transparency present.
3519 */
3520 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3521 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3522 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3523 MagickTrue : MagickFalse;
3524
glennrpcb395ac2011-03-30 19:50:23 +00003525 /* Set more properties for identify to retrieve */
3526 {
3527 char
3528 msg[MaxTextExtent];
3529
glennrp4eb39312011-03-30 21:34:55 +00003530 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003531 {
3532 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003533 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003534 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
cristy5d6fc9c2011-12-27 03:10:42 +00003535 (void) SetImageProperty(image,"png:text ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003536 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003537 }
3538
3539 if (num_raw_profiles != 0)
3540 {
cristy3b6fd2e2011-05-20 12:53:50 +00003541 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003542 "%d were found", num_raw_profiles);
cristy5d6fc9c2011-12-27 03:10:42 +00003543 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003544 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003545 }
3546
glennrpcb395ac2011-03-30 19:50:23 +00003547 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003548 {
cristy3b6fd2e2011-05-20 12:53:50 +00003549 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003550 "chunk was found (see Chromaticity, above)");
cristy5d6fc9c2011-12-27 03:10:42 +00003551 (void) SetImageProperty(image,"png:cHRM ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003552 exception);
glennrp59612252011-03-30 21:45:21 +00003553 }
glennrpcb395ac2011-03-30 19:50:23 +00003554
3555 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003556 {
cristy3b6fd2e2011-05-20 12:53:50 +00003557 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003558 "chunk was found (see Background color, above)");
cristy5d6fc9c2011-12-27 03:10:42 +00003559 (void) SetImageProperty(image,"png:bKGD ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003560 exception);
glennrp59612252011-03-30 21:45:21 +00003561 }
3562
cristy3b6fd2e2011-05-20 12:53:50 +00003563 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003564 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003565
3566 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy5d6fc9c2011-12-27 03:10:42 +00003567 (void) SetImageProperty(image,"png:iCCP ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003568 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003569
glennrpcb395ac2011-03-30 19:50:23 +00003570 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy5d6fc9c2011-12-27 03:10:42 +00003571 (void) SetImageProperty(image,"png:tRNS ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003572 exception);
glennrp4eb39312011-03-30 21:34:55 +00003573
3574#if defined(PNG_sRGB_SUPPORTED)
3575 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3576 {
cristy3b6fd2e2011-05-20 12:53:50 +00003577 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003578 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003579 (int) intent);
cristy5d6fc9c2011-12-27 03:10:42 +00003580 (void) SetImageProperty(image,"png:sRGB ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003581 exception);
glennrp4eb39312011-03-30 21:34:55 +00003582 }
3583#endif
3584
3585 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3586 {
cristy3b6fd2e2011-05-20 12:53:50 +00003587 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003588 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003589 file_gamma);
cristy5d6fc9c2011-12-27 03:10:42 +00003590 (void) SetImageProperty(image,"png:gAMA ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003591 exception);
glennrp4eb39312011-03-30 21:34:55 +00003592 }
3593
3594#if defined(PNG_pHYs_SUPPORTED)
3595 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3596 {
cristy3b6fd2e2011-05-20 12:53:50 +00003597 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003598 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003599 (double) x_resolution,(double) y_resolution, unit_type);
cristy5d6fc9c2011-12-27 03:10:42 +00003600 (void) SetImageProperty(image,"png:pHYs ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003601 exception);
glennrp4eb39312011-03-30 21:34:55 +00003602 }
3603#endif
3604
3605#if defined(PNG_oFFs_SUPPORTED)
3606 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3607 {
cristy3b6fd2e2011-05-20 12:53:50 +00003608 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003609 (double) image->page.x,(double) image->page.y);
cristy5d6fc9c2011-12-27 03:10:42 +00003610 (void) SetImageProperty(image,"png:oFFs ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003611 exception);
glennrp4eb39312011-03-30 21:34:55 +00003612 }
3613#endif
3614
glennrp07523c72011-03-31 18:12:10 +00003615 if ((image->page.width != 0 && image->page.width != image->columns) ||
3616 (image->page.height != 0 && image->page.height != image->rows))
3617 {
cristy3b6fd2e2011-05-20 12:53:50 +00003618 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003619 "width=%.20g, height=%.20g",
3620 (double) image->page.width,(double) image->page.height);
cristy5d6fc9c2011-12-27 03:10:42 +00003621 (void) SetImageProperty(image,"png:vpAg ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003622 exception);
glennrp07523c72011-03-31 18:12:10 +00003623 }
glennrpcb395ac2011-03-30 19:50:23 +00003624 }
3625
cristy3ed852e2009-09-05 21:47:34 +00003626 /*
3627 Relinquish resources.
3628 */
3629 png_destroy_read_struct(&ping,&ping_info,&end_info);
3630
glennrpcf002022011-01-30 02:38:15 +00003631 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003632#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003633 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003634#endif
3635
3636 if (logging != MagickFalse)
3637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3638 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003639
cristy3ed852e2009-09-05 21:47:34 +00003640 return(image);
3641
3642/* end of reading one PNG image */
3643}
3644
3645static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3646{
3647 Image
3648 *image,
3649 *previous;
3650
3651 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003652 have_mng_structure,
3653 logging,
cristy3ed852e2009-09-05 21:47:34 +00003654 status;
3655
3656 MngInfo
3657 *mng_info;
3658
3659 char
3660 magic_number[MaxTextExtent];
3661
cristy3ed852e2009-09-05 21:47:34 +00003662 ssize_t
3663 count;
3664
3665 /*
3666 Open image file.
3667 */
3668 assert(image_info != (const ImageInfo *) NULL);
3669 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003670
cristy3ed852e2009-09-05 21:47:34 +00003671 if (image_info->debug != MagickFalse)
3672 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3673 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003674
cristy3ed852e2009-09-05 21:47:34 +00003675 assert(exception != (ExceptionInfo *) NULL);
3676 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003677 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy9950d572011-10-01 18:22:35 +00003678 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003679 mng_info=(MngInfo *) NULL;
3680 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003681
cristy3ed852e2009-09-05 21:47:34 +00003682 if (status == MagickFalse)
3683 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003684
cristy3ed852e2009-09-05 21:47:34 +00003685 /*
3686 Verify PNG signature.
3687 */
3688 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003689
glennrpdde35db2011-02-21 12:06:32 +00003690 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003691 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003692
cristy3ed852e2009-09-05 21:47:34 +00003693 /*
3694 Allocate a MngInfo structure.
3695 */
3696 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003697 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003698
cristy3ed852e2009-09-05 21:47:34 +00003699 if (mng_info == (MngInfo *) NULL)
3700 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003701
cristy3ed852e2009-09-05 21:47:34 +00003702 /*
3703 Initialize members of the MngInfo structure.
3704 */
3705 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3706 mng_info->image=image;
3707 have_mng_structure=MagickTrue;
3708
3709 previous=image;
3710 image=ReadOnePNGImage(mng_info,image_info,exception);
3711 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003712
cristy3ed852e2009-09-05 21:47:34 +00003713 if (image == (Image *) NULL)
3714 {
3715 if (previous != (Image *) NULL)
3716 {
3717 if (previous->signature != MagickSignature)
3718 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003719
cristy3ed852e2009-09-05 21:47:34 +00003720 (void) CloseBlob(previous);
3721 (void) DestroyImageList(previous);
3722 }
glennrp0fe50b42010-11-16 03:52:51 +00003723
cristy3ed852e2009-09-05 21:47:34 +00003724 if (logging != MagickFalse)
3725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3726 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003727
cristy3ed852e2009-09-05 21:47:34 +00003728 return((Image *) NULL);
3729 }
glennrp47b9dd52010-11-24 18:12:06 +00003730
cristy3ed852e2009-09-05 21:47:34 +00003731 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003732
cristy3ed852e2009-09-05 21:47:34 +00003733 if ((image->columns == 0) || (image->rows == 0))
3734 {
3735 if (logging != MagickFalse)
3736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3737 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003738
cristy3ed852e2009-09-05 21:47:34 +00003739 ThrowReaderException(CorruptImageError,"CorruptImage");
3740 }
glennrp47b9dd52010-11-24 18:12:06 +00003741
cristy3ed852e2009-09-05 21:47:34 +00003742 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3743 {
cristy018f07f2011-09-04 21:15:19 +00003744 (void) SetImageType(image,TrueColorType,exception);
cristy3ed852e2009-09-05 21:47:34 +00003745 image->matte=MagickFalse;
3746 }
glennrp0fe50b42010-11-16 03:52:51 +00003747
cristy3ed852e2009-09-05 21:47:34 +00003748 if (LocaleCompare(image_info->magick,"PNG32") == 0)
cristy018f07f2011-09-04 21:15:19 +00003749 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003750
cristy3ed852e2009-09-05 21:47:34 +00003751 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3753 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3754 (double) image->page.width,(double) image->page.height,
3755 (double) image->page.x,(double) image->page.y);
3756
3757 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003759
cristy3ed852e2009-09-05 21:47:34 +00003760 return(image);
3761}
3762
3763
3764
3765#if defined(JNG_SUPPORTED)
3766/*
3767%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3768% %
3769% %
3770% %
3771% R e a d O n e J N G I m a g e %
3772% %
3773% %
3774% %
3775%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3776%
3777% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3778% (minus the 8-byte signature) and returns it. It allocates the memory
3779% necessary for the new Image structure and returns a pointer to the new
3780% image.
3781%
3782% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3783%
3784% The format of the ReadOneJNGImage method is:
3785%
3786% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3787% ExceptionInfo *exception)
3788%
3789% A description of each parameter follows:
3790%
3791% o mng_info: Specifies a pointer to a MngInfo structure.
3792%
3793% o image_info: the image info.
3794%
3795% o exception: return any errors or warnings in this structure.
3796%
3797*/
3798static Image *ReadOneJNGImage(MngInfo *mng_info,
3799 const ImageInfo *image_info, ExceptionInfo *exception)
3800{
3801 Image
3802 *alpha_image,
3803 *color_image,
3804 *image,
3805 *jng_image;
3806
3807 ImageInfo
3808 *alpha_image_info,
3809 *color_image_info;
3810
cristy4383ec82011-01-05 15:42:32 +00003811 MagickBooleanType
3812 logging;
3813
cristybb503372010-05-27 20:51:26 +00003814 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003815 y;
3816
3817 MagickBooleanType
3818 status;
3819
3820 png_uint_32
3821 jng_height,
3822 jng_width;
3823
3824 png_byte
3825 jng_color_type,
3826 jng_image_sample_depth,
3827 jng_image_compression_method,
3828 jng_image_interlace_method,
3829 jng_alpha_sample_depth,
3830 jng_alpha_compression_method,
3831 jng_alpha_filter_method,
3832 jng_alpha_interlace_method;
3833
cristy4c08aed2011-07-01 19:47:50 +00003834 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003835 *s;
3836
cristybb503372010-05-27 20:51:26 +00003837 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003838 i,
3839 x;
3840
cristy4c08aed2011-07-01 19:47:50 +00003841 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003842 *q;
3843
3844 register unsigned char
3845 *p;
3846
3847 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003848 read_JSEP,
3849 reading_idat,
3850 skip_to_iend;
3851
cristybb503372010-05-27 20:51:26 +00003852 size_t
cristy3ed852e2009-09-05 21:47:34 +00003853 length;
3854
3855 jng_alpha_compression_method=0;
3856 jng_alpha_sample_depth=8;
3857 jng_color_type=0;
3858 jng_height=0;
3859 jng_width=0;
3860 alpha_image=(Image *) NULL;
3861 color_image=(Image *) NULL;
3862 alpha_image_info=(ImageInfo *) NULL;
3863 color_image_info=(ImageInfo *) NULL;
3864
3865 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003866 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003867
3868 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003869
cristy4c08aed2011-07-01 19:47:50 +00003870 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003871 {
3872 /*
3873 Allocate next image structure.
3874 */
3875 if (logging != MagickFalse)
3876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3877 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003878
cristy9950d572011-10-01 18:22:35 +00003879 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003880
cristy3ed852e2009-09-05 21:47:34 +00003881 if (GetNextImageInList(image) == (Image *) NULL)
3882 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003883
cristy3ed852e2009-09-05 21:47:34 +00003884 image=SyncNextImageInList(image);
3885 }
3886 mng_info->image=image;
3887
3888 /*
3889 Signature bytes have already been read.
3890 */
3891
3892 read_JSEP=MagickFalse;
3893 reading_idat=MagickFalse;
3894 skip_to_iend=MagickFalse;
3895 for (;;)
3896 {
3897 char
3898 type[MaxTextExtent];
3899
3900 unsigned char
3901 *chunk;
3902
3903 unsigned int
3904 count;
3905
3906 /*
3907 Read a new JNG chunk.
3908 */
3909 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3910 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003911
cristy3ed852e2009-09-05 21:47:34 +00003912 if (status == MagickFalse)
3913 break;
glennrp0fe50b42010-11-16 03:52:51 +00003914
cristy3ed852e2009-09-05 21:47:34 +00003915 type[0]='\0';
3916 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3917 length=ReadBlobMSBLong(image);
3918 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3919
3920 if (logging != MagickFalse)
3921 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003922 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3923 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003924
3925 if (length > PNG_UINT_31_MAX || count == 0)
3926 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003927
cristy3ed852e2009-09-05 21:47:34 +00003928 p=NULL;
3929 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003930
cristy3ed852e2009-09-05 21:47:34 +00003931 if (length)
3932 {
3933 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003934
cristy3ed852e2009-09-05 21:47:34 +00003935 if (chunk == (unsigned char *) NULL)
3936 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003937
cristybb503372010-05-27 20:51:26 +00003938 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003939 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003940
cristy3ed852e2009-09-05 21:47:34 +00003941 p=chunk;
3942 }
glennrp47b9dd52010-11-24 18:12:06 +00003943
cristy3ed852e2009-09-05 21:47:34 +00003944 (void) ReadBlobMSBLong(image); /* read crc word */
3945
3946 if (skip_to_iend)
3947 {
3948 if (length)
3949 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003950
cristy3ed852e2009-09-05 21:47:34 +00003951 continue;
3952 }
3953
3954 if (memcmp(type,mng_JHDR,4) == 0)
3955 {
3956 if (length == 16)
3957 {
cristybb503372010-05-27 20:51:26 +00003958 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003959 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003960 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003961 (p[6] << 8) | p[7]);
3962 jng_color_type=p[8];
3963 jng_image_sample_depth=p[9];
3964 jng_image_compression_method=p[10];
3965 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003966
cristy3ed852e2009-09-05 21:47:34 +00003967 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3968 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003969
cristy3ed852e2009-09-05 21:47:34 +00003970 jng_alpha_sample_depth=p[12];
3971 jng_alpha_compression_method=p[13];
3972 jng_alpha_filter_method=p[14];
3973 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003974
cristy3ed852e2009-09-05 21:47:34 +00003975 if (logging != MagickFalse)
3976 {
3977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003978 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003979
cristy3ed852e2009-09-05 21:47:34 +00003980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003981 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003982
cristy3ed852e2009-09-05 21:47:34 +00003983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3984 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003985
cristy3ed852e2009-09-05 21:47:34 +00003986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3987 " jng_image_sample_depth: %3d",
3988 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003989
cristy3ed852e2009-09-05 21:47:34 +00003990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3991 " jng_image_compression_method:%3d",
3992 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003993
cristy3ed852e2009-09-05 21:47:34 +00003994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3995 " jng_image_interlace_method: %3d",
3996 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003997
cristy3ed852e2009-09-05 21:47:34 +00003998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3999 " jng_alpha_sample_depth: %3d",
4000 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00004001
cristy3ed852e2009-09-05 21:47:34 +00004002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4003 " jng_alpha_compression_method:%3d",
4004 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004005
cristy3ed852e2009-09-05 21:47:34 +00004006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4007 " jng_alpha_filter_method: %3d",
4008 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00004009
cristy3ed852e2009-09-05 21:47:34 +00004010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4011 " jng_alpha_interlace_method: %3d",
4012 jng_alpha_interlace_method);
4013 }
4014 }
glennrp47b9dd52010-11-24 18:12:06 +00004015
cristy3ed852e2009-09-05 21:47:34 +00004016 if (length)
4017 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004018
cristy3ed852e2009-09-05 21:47:34 +00004019 continue;
4020 }
4021
4022
4023 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4024 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4025 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4026 {
4027 /*
4028 o create color_image
4029 o open color_blob, attached to color_image
4030 o if (color type has alpha)
4031 open alpha_blob, attached to alpha_image
4032 */
4033
cristy73bd4a52010-10-05 11:24:23 +00004034 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004035
cristy3ed852e2009-09-05 21:47:34 +00004036 if (color_image_info == (ImageInfo *) NULL)
4037 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004038
cristy3ed852e2009-09-05 21:47:34 +00004039 GetImageInfo(color_image_info);
cristy9950d572011-10-01 18:22:35 +00004040 color_image=AcquireImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004041
cristy3ed852e2009-09-05 21:47:34 +00004042 if (color_image == (Image *) NULL)
4043 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4044
4045 if (logging != MagickFalse)
4046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4047 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004048
cristy3ed852e2009-09-05 21:47:34 +00004049 (void) AcquireUniqueFilename(color_image->filename);
4050 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4051 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004052
cristy3ed852e2009-09-05 21:47:34 +00004053 if (status == MagickFalse)
4054 return((Image *) NULL);
4055
4056 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4057 {
4058 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004059 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004060
cristy3ed852e2009-09-05 21:47:34 +00004061 if (alpha_image_info == (ImageInfo *) NULL)
4062 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004063
cristy3ed852e2009-09-05 21:47:34 +00004064 GetImageInfo(alpha_image_info);
cristy9950d572011-10-01 18:22:35 +00004065 alpha_image=AcquireImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004066
cristy3ed852e2009-09-05 21:47:34 +00004067 if (alpha_image == (Image *) NULL)
4068 {
4069 alpha_image=DestroyImage(alpha_image);
4070 ThrowReaderException(ResourceLimitError,
4071 "MemoryAllocationFailed");
4072 }
glennrp0fe50b42010-11-16 03:52:51 +00004073
cristy3ed852e2009-09-05 21:47:34 +00004074 if (logging != MagickFalse)
4075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4076 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004077
cristy3ed852e2009-09-05 21:47:34 +00004078 (void) AcquireUniqueFilename(alpha_image->filename);
4079 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4080 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004081
cristy3ed852e2009-09-05 21:47:34 +00004082 if (status == MagickFalse)
4083 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004084
cristy3ed852e2009-09-05 21:47:34 +00004085 if (jng_alpha_compression_method == 0)
4086 {
4087 unsigned char
4088 data[18];
4089
4090 if (logging != MagickFalse)
4091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4092 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004093
cristy3ed852e2009-09-05 21:47:34 +00004094 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4095 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004096
cristy3ed852e2009-09-05 21:47:34 +00004097 (void) WriteBlobMSBULong(alpha_image,13L);
4098 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004099 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004100 PNGLong(data+4,jng_width);
4101 PNGLong(data+8,jng_height);
4102 data[12]=jng_alpha_sample_depth;
4103 data[13]=0; /* color_type gray */
4104 data[14]=0; /* compression method 0 */
4105 data[15]=0; /* filter_method 0 */
4106 data[16]=0; /* interlace_method 0 */
4107 (void) WriteBlob(alpha_image,17,data);
4108 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4109 }
4110 }
4111 reading_idat=MagickTrue;
4112 }
4113
4114 if (memcmp(type,mng_JDAT,4) == 0)
4115 {
glennrp47b9dd52010-11-24 18:12:06 +00004116 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004117
4118 if (logging != MagickFalse)
4119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4120 " Copying JDAT chunk data to color_blob.");
4121
4122 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004123
cristy3ed852e2009-09-05 21:47:34 +00004124 if (length)
4125 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004126
cristy3ed852e2009-09-05 21:47:34 +00004127 continue;
4128 }
4129
4130 if (memcmp(type,mng_IDAT,4) == 0)
4131 {
4132 png_byte
4133 data[5];
4134
glennrp47b9dd52010-11-24 18:12:06 +00004135 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004136
4137 if (image_info->ping == MagickFalse)
4138 {
4139 if (logging != MagickFalse)
4140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4141 " Copying IDAT chunk data to alpha_blob.");
4142
cristybb503372010-05-27 20:51:26 +00004143 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004144 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004145 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004146 (void) WriteBlob(alpha_image,4,data);
4147 (void) WriteBlob(alpha_image,length,chunk);
4148 (void) WriteBlobMSBULong(alpha_image,
4149 crc32(crc32(0,data,4),chunk,(uInt) length));
4150 }
glennrp0fe50b42010-11-16 03:52:51 +00004151
cristy3ed852e2009-09-05 21:47:34 +00004152 if (length)
4153 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004154
cristy3ed852e2009-09-05 21:47:34 +00004155 continue;
4156 }
4157
4158 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4159 {
glennrp47b9dd52010-11-24 18:12:06 +00004160 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004161
4162 if (image_info->ping == MagickFalse)
4163 {
4164 if (logging != MagickFalse)
4165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4166 " Copying JDAA chunk data to alpha_blob.");
4167
4168 (void) WriteBlob(alpha_image,length,chunk);
4169 }
glennrp0fe50b42010-11-16 03:52:51 +00004170
cristy3ed852e2009-09-05 21:47:34 +00004171 if (length)
4172 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004173
cristy3ed852e2009-09-05 21:47:34 +00004174 continue;
4175 }
4176
4177 if (memcmp(type,mng_JSEP,4) == 0)
4178 {
4179 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004180
cristy3ed852e2009-09-05 21:47:34 +00004181 if (length)
4182 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004183
cristy3ed852e2009-09-05 21:47:34 +00004184 continue;
4185 }
4186
4187 if (memcmp(type,mng_bKGD,4) == 0)
4188 {
4189 if (length == 2)
4190 {
4191 image->background_color.red=ScaleCharToQuantum(p[1]);
4192 image->background_color.green=image->background_color.red;
4193 image->background_color.blue=image->background_color.red;
4194 }
glennrp0fe50b42010-11-16 03:52:51 +00004195
cristy3ed852e2009-09-05 21:47:34 +00004196 if (length == 6)
4197 {
4198 image->background_color.red=ScaleCharToQuantum(p[1]);
4199 image->background_color.green=ScaleCharToQuantum(p[3]);
4200 image->background_color.blue=ScaleCharToQuantum(p[5]);
4201 }
glennrp0fe50b42010-11-16 03:52:51 +00004202
cristy3ed852e2009-09-05 21:47:34 +00004203 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4204 continue;
4205 }
4206
4207 if (memcmp(type,mng_gAMA,4) == 0)
4208 {
4209 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004210 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004211
cristy3ed852e2009-09-05 21:47:34 +00004212 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4213 continue;
4214 }
4215
4216 if (memcmp(type,mng_cHRM,4) == 0)
4217 {
4218 if (length == 32)
4219 {
cristy8182b072010-05-30 20:10:53 +00004220 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4221 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4222 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4223 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4224 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4225 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4226 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4227 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004228 }
glennrp47b9dd52010-11-24 18:12:06 +00004229
cristy3ed852e2009-09-05 21:47:34 +00004230 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4231 continue;
4232 }
4233
4234 if (memcmp(type,mng_sRGB,4) == 0)
4235 {
4236 if (length == 1)
4237 {
glennrpe610a072010-08-05 17:08:46 +00004238 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004239 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004240 image->gamma=0.45455f;
4241 image->chromaticity.red_primary.x=0.6400f;
4242 image->chromaticity.red_primary.y=0.3300f;
4243 image->chromaticity.green_primary.x=0.3000f;
4244 image->chromaticity.green_primary.y=0.6000f;
4245 image->chromaticity.blue_primary.x=0.1500f;
4246 image->chromaticity.blue_primary.y=0.0600f;
4247 image->chromaticity.white_point.x=0.3127f;
4248 image->chromaticity.white_point.y=0.3290f;
4249 }
glennrp47b9dd52010-11-24 18:12:06 +00004250
cristy3ed852e2009-09-05 21:47:34 +00004251 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4252 continue;
4253 }
4254
4255 if (memcmp(type,mng_oFFs,4) == 0)
4256 {
4257 if (length > 8)
4258 {
glennrp5eae7602011-02-22 15:21:32 +00004259 image->page.x=(ssize_t) mng_get_long(p);
4260 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004261
cristy3ed852e2009-09-05 21:47:34 +00004262 if ((int) p[8] != 0)
4263 {
4264 image->page.x/=10000;
4265 image->page.y/=10000;
4266 }
4267 }
glennrp47b9dd52010-11-24 18:12:06 +00004268
cristy3ed852e2009-09-05 21:47:34 +00004269 if (length)
4270 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004271
cristy3ed852e2009-09-05 21:47:34 +00004272 continue;
4273 }
4274
4275 if (memcmp(type,mng_pHYs,4) == 0)
4276 {
4277 if (length > 8)
4278 {
cristy2a11bef2011-10-28 18:33:11 +00004279 image->resolution.x=(double) mng_get_long(p);
4280 image->resolution.y=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004281 if ((int) p[8] == PNG_RESOLUTION_METER)
4282 {
4283 image->units=PixelsPerCentimeterResolution;
cristy2a11bef2011-10-28 18:33:11 +00004284 image->resolution.x=image->resolution.x/100.0f;
4285 image->resolution.y=image->resolution.y/100.0f;
cristy3ed852e2009-09-05 21:47:34 +00004286 }
4287 }
glennrp0fe50b42010-11-16 03:52:51 +00004288
cristy3ed852e2009-09-05 21:47:34 +00004289 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4290 continue;
4291 }
4292
4293#if 0
4294 if (memcmp(type,mng_iCCP,4) == 0)
4295 {
glennrpfd05d622011-02-25 04:10:33 +00004296 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004297 if (length)
4298 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004299
cristy3ed852e2009-09-05 21:47:34 +00004300 continue;
4301 }
4302#endif
4303
4304 if (length)
4305 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4306
4307 if (memcmp(type,mng_IEND,4))
4308 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004309
cristy3ed852e2009-09-05 21:47:34 +00004310 break;
4311 }
4312
4313
4314 /* IEND found */
4315
4316 /*
4317 Finish up reading image data:
4318
4319 o read main image from color_blob.
4320
4321 o close color_blob.
4322
4323 o if (color_type has alpha)
4324 if alpha_encoding is PNG
4325 read secondary image from alpha_blob via ReadPNG
4326 if alpha_encoding is JPEG
4327 read secondary image from alpha_blob via ReadJPEG
4328
4329 o close alpha_blob.
4330
4331 o copy intensity of secondary image into
cristy4c08aed2011-07-01 19:47:50 +00004332 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004333
4334 o destroy the secondary image.
4335 */
4336
4337 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004338
cristy3ed852e2009-09-05 21:47:34 +00004339 if (logging != MagickFalse)
4340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4341 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004342
cristy3b6fd2e2011-05-20 12:53:50 +00004343 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004344 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004345
cristy3ed852e2009-09-05 21:47:34 +00004346 color_image_info->ping=MagickFalse; /* To do: avoid this */
4347 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004348
cristy3ed852e2009-09-05 21:47:34 +00004349 if (jng_image == (Image *) NULL)
4350 return((Image *) NULL);
4351
4352 (void) RelinquishUniqueFileResource(color_image->filename);
4353 color_image=DestroyImage(color_image);
4354 color_image_info=DestroyImageInfo(color_image_info);
4355
4356 if (jng_image == (Image *) NULL)
4357 return((Image *) NULL);
4358
4359 if (logging != MagickFalse)
4360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4361 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004362
cristy3ed852e2009-09-05 21:47:34 +00004363 image->rows=jng_height;
4364 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004365
cristybb503372010-05-27 20:51:26 +00004366 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004367 {
cristyc82a27b2011-10-21 01:07:16 +00004368 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00004369 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004370 for (x=(ssize_t) image->columns; x != 0; x--)
4371 {
4372 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4373 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4374 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004375 q+=GetPixelChannels(image);
4376 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004377 }
glennrp47b9dd52010-11-24 18:12:06 +00004378
cristy3ed852e2009-09-05 21:47:34 +00004379 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4380 break;
4381 }
glennrp0fe50b42010-11-16 03:52:51 +00004382
cristy3ed852e2009-09-05 21:47:34 +00004383 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004384
cristy3ed852e2009-09-05 21:47:34 +00004385 if (image_info->ping == MagickFalse)
4386 {
4387 if (jng_color_type >= 12)
4388 {
4389 if (jng_alpha_compression_method == 0)
4390 {
4391 png_byte
4392 data[5];
4393 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4394 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004395 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004396 (void) WriteBlob(alpha_image,4,data);
4397 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4398 }
glennrp0fe50b42010-11-16 03:52:51 +00004399
cristy3ed852e2009-09-05 21:47:34 +00004400 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004401
cristy3ed852e2009-09-05 21:47:34 +00004402 if (logging != MagickFalse)
4403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00004404 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004405
cristy3b6fd2e2011-05-20 12:53:50 +00004406 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004407 "%s",alpha_image->filename);
4408
4409 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004410
cristy3ed852e2009-09-05 21:47:34 +00004411 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004412 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004413 {
4414 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristyc82a27b2011-10-21 01:07:16 +00004415 exception);
cristy3ed852e2009-09-05 21:47:34 +00004416 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004417
cristy3ed852e2009-09-05 21:47:34 +00004418 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00004419 for (x=(ssize_t) image->columns; x != 0; x--)
4420 {
4421 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004422 q+=GetPixelChannels(image);
4423 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004424 }
glennrp0fe50b42010-11-16 03:52:51 +00004425
cristy3ed852e2009-09-05 21:47:34 +00004426 else
cristy4c08aed2011-07-01 19:47:50 +00004427 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004428 {
cristy4c08aed2011-07-01 19:47:50 +00004429 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4430 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004431 image->matte=MagickTrue;
cristyed231572011-07-14 02:18:59 +00004432 q+=GetPixelChannels(image);
4433 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004434 }
glennrp0fe50b42010-11-16 03:52:51 +00004435
cristy3ed852e2009-09-05 21:47:34 +00004436 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4437 break;
4438 }
4439 (void) RelinquishUniqueFileResource(alpha_image->filename);
4440 alpha_image=DestroyImage(alpha_image);
4441 alpha_image_info=DestroyImageInfo(alpha_image_info);
4442 if (jng_image != (Image *) NULL)
4443 jng_image=DestroyImage(jng_image);
4444 }
4445 }
4446
glennrp47b9dd52010-11-24 18:12:06 +00004447 /* Read the JNG image. */
4448
cristy3ed852e2009-09-05 21:47:34 +00004449 if (mng_info->mng_type == 0)
4450 {
4451 mng_info->mng_width=jng_width;
4452 mng_info->mng_height=jng_height;
4453 }
glennrp0fe50b42010-11-16 03:52:51 +00004454
cristy3ed852e2009-09-05 21:47:34 +00004455 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004456 {
4457 image->page.width=jng_width;
4458 image->page.height=jng_height;
4459 }
4460
cristy3ed852e2009-09-05 21:47:34 +00004461 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004462 {
4463 image->page.x=mng_info->x_off[mng_info->object_id];
4464 image->page.y=mng_info->y_off[mng_info->object_id];
4465 }
4466
cristy3ed852e2009-09-05 21:47:34 +00004467 else
glennrp0fe50b42010-11-16 03:52:51 +00004468 {
4469 image->page.y=mng_info->y_off[mng_info->object_id];
4470 }
4471
cristy3ed852e2009-09-05 21:47:34 +00004472 mng_info->image_found++;
4473 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4474 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004475
cristy3ed852e2009-09-05 21:47:34 +00004476 if (logging != MagickFalse)
4477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4478 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004479
cristy3ed852e2009-09-05 21:47:34 +00004480 return(image);
4481}
4482
4483/*
4484%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4485% %
4486% %
4487% %
4488% R e a d J N G I m a g e %
4489% %
4490% %
4491% %
4492%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4493%
4494% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4495% (including the 8-byte signature) and returns it. It allocates the memory
4496% necessary for the new Image structure and returns a pointer to the new
4497% image.
4498%
4499% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4500%
4501% The format of the ReadJNGImage method is:
4502%
4503% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4504% *exception)
4505%
4506% A description of each parameter follows:
4507%
4508% o image_info: the image info.
4509%
4510% o exception: return any errors or warnings in this structure.
4511%
4512*/
4513
4514static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4515{
4516 Image
4517 *image,
4518 *previous;
4519
4520 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004521 have_mng_structure,
4522 logging,
cristy3ed852e2009-09-05 21:47:34 +00004523 status;
4524
4525 MngInfo
4526 *mng_info;
4527
4528 char
4529 magic_number[MaxTextExtent];
4530
cristy3ed852e2009-09-05 21:47:34 +00004531 size_t
4532 count;
4533
4534 /*
4535 Open image file.
4536 */
4537 assert(image_info != (const ImageInfo *) NULL);
4538 assert(image_info->signature == MagickSignature);
4539 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4540 assert(exception != (ExceptionInfo *) NULL);
4541 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004542 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy9950d572011-10-01 18:22:35 +00004543 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004544 mng_info=(MngInfo *) NULL;
4545 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004546
cristy3ed852e2009-09-05 21:47:34 +00004547 if (status == MagickFalse)
4548 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004549
cristy3ed852e2009-09-05 21:47:34 +00004550 if (LocaleCompare(image_info->magick,"JNG") != 0)
4551 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004552
glennrp47b9dd52010-11-24 18:12:06 +00004553 /* Verify JNG signature. */
4554
cristy3ed852e2009-09-05 21:47:34 +00004555 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004556
glennrp3b8763e2011-02-21 12:08:18 +00004557 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004558 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004559
glennrp47b9dd52010-11-24 18:12:06 +00004560 /* Allocate a MngInfo structure. */
4561
cristy3ed852e2009-09-05 21:47:34 +00004562 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004563 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004564
cristy3ed852e2009-09-05 21:47:34 +00004565 if (mng_info == (MngInfo *) NULL)
4566 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004567
glennrp47b9dd52010-11-24 18:12:06 +00004568 /* Initialize members of the MngInfo structure. */
4569
cristy3ed852e2009-09-05 21:47:34 +00004570 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4571 have_mng_structure=MagickTrue;
4572
4573 mng_info->image=image;
4574 previous=image;
4575 image=ReadOneJNGImage(mng_info,image_info,exception);
4576 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004577
cristy3ed852e2009-09-05 21:47:34 +00004578 if (image == (Image *) NULL)
4579 {
4580 if (IsImageObject(previous) != MagickFalse)
4581 {
4582 (void) CloseBlob(previous);
4583 (void) DestroyImageList(previous);
4584 }
glennrp0fe50b42010-11-16 03:52:51 +00004585
cristy3ed852e2009-09-05 21:47:34 +00004586 if (logging != MagickFalse)
4587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4588 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004589
cristy3ed852e2009-09-05 21:47:34 +00004590 return((Image *) NULL);
4591 }
4592 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004593
cristy3ed852e2009-09-05 21:47:34 +00004594 if (image->columns == 0 || image->rows == 0)
4595 {
4596 if (logging != MagickFalse)
4597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4598 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004599
cristy3ed852e2009-09-05 21:47:34 +00004600 ThrowReaderException(CorruptImageError,"CorruptImage");
4601 }
glennrp0fe50b42010-11-16 03:52:51 +00004602
cristy3ed852e2009-09-05 21:47:34 +00004603 if (logging != MagickFalse)
4604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004605
cristy3ed852e2009-09-05 21:47:34 +00004606 return(image);
4607}
4608#endif
4609
4610static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4611{
4612 char
4613 page_geometry[MaxTextExtent];
4614
4615 Image
4616 *image,
4617 *previous;
4618
cristy4383ec82011-01-05 15:42:32 +00004619 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004620 logging,
4621 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004622
cristy3ed852e2009-09-05 21:47:34 +00004623 volatile int
4624 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004625 object_id,
4626 term_chunk_found,
4627 skip_to_iend;
4628
cristybb503372010-05-27 20:51:26 +00004629 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004630 image_count=0;
4631
4632 MagickBooleanType
4633 status;
4634
4635 MagickOffsetType
4636 offset;
4637
4638 MngInfo
4639 *mng_info;
4640
4641 MngBox
4642 default_fb,
4643 fb,
4644 previous_fb;
4645
4646#if defined(MNG_INSERT_LAYERS)
cristy101ab702011-10-13 13:06:32 +00004647 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004648 mng_background_color;
4649#endif
4650
4651 register unsigned char
4652 *p;
4653
cristybb503372010-05-27 20:51:26 +00004654 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004655 i;
4656
4657 size_t
4658 count;
4659
cristybb503372010-05-27 20:51:26 +00004660 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004661 loop_level;
4662
4663 volatile short
4664 skipping_loop;
4665
4666#if defined(MNG_INSERT_LAYERS)
4667 unsigned int
4668 mandatory_back=0;
4669#endif
4670
4671 volatile unsigned int
4672#ifdef MNG_OBJECT_BUFFERS
4673 mng_background_object=0,
4674#endif
4675 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4676
cristybb503372010-05-27 20:51:26 +00004677 size_t
cristy3ed852e2009-09-05 21:47:34 +00004678 default_frame_timeout,
4679 frame_timeout,
4680#if defined(MNG_INSERT_LAYERS)
4681 image_height,
4682 image_width,
4683#endif
4684 length;
4685
glennrp38ea0832010-06-02 18:50:28 +00004686 /* These delays are all measured in image ticks_per_second,
4687 * not in MNG ticks_per_second
4688 */
cristybb503372010-05-27 20:51:26 +00004689 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004690 default_frame_delay,
4691 final_delay,
4692 final_image_delay,
4693 frame_delay,
4694#if defined(MNG_INSERT_LAYERS)
4695 insert_layers,
4696#endif
4697 mng_iterations=1,
4698 simplicity=0,
4699 subframe_height=0,
4700 subframe_width=0;
4701
4702 previous_fb.top=0;
4703 previous_fb.bottom=0;
4704 previous_fb.left=0;
4705 previous_fb.right=0;
4706 default_fb.top=0;
4707 default_fb.bottom=0;
4708 default_fb.left=0;
4709 default_fb.right=0;
4710
glennrp47b9dd52010-11-24 18:12:06 +00004711 /* Open image file. */
4712
cristy3ed852e2009-09-05 21:47:34 +00004713 assert(image_info != (const ImageInfo *) NULL);
4714 assert(image_info->signature == MagickSignature);
4715 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4716 assert(exception != (ExceptionInfo *) NULL);
4717 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004718 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy9950d572011-10-01 18:22:35 +00004719 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004720 mng_info=(MngInfo *) NULL;
4721 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004722
cristy3ed852e2009-09-05 21:47:34 +00004723 if (status == MagickFalse)
4724 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004725
cristy3ed852e2009-09-05 21:47:34 +00004726 first_mng_object=MagickFalse;
4727 skipping_loop=(-1);
4728 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004729
4730 /* Allocate a MngInfo structure. */
4731
cristy73bd4a52010-10-05 11:24:23 +00004732 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004733
cristy3ed852e2009-09-05 21:47:34 +00004734 if (mng_info == (MngInfo *) NULL)
4735 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004736
glennrp47b9dd52010-11-24 18:12:06 +00004737 /* Initialize members of the MngInfo structure. */
4738
cristy3ed852e2009-09-05 21:47:34 +00004739 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4740 mng_info->image=image;
4741 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004742
4743 if (LocaleCompare(image_info->magick,"MNG") == 0)
4744 {
4745 char
4746 magic_number[MaxTextExtent];
4747
glennrp47b9dd52010-11-24 18:12:06 +00004748 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004749 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4750 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4751 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004752
4753 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004754 for (i=0; i < MNG_MAX_OBJECTS; i++)
4755 {
cristybb503372010-05-27 20:51:26 +00004756 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4757 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004758 }
4759 mng_info->exists[0]=MagickTrue;
4760 }
glennrp47b9dd52010-11-24 18:12:06 +00004761
cristy3ed852e2009-09-05 21:47:34 +00004762 first_mng_object=MagickTrue;
4763 mng_type=0;
4764#if defined(MNG_INSERT_LAYERS)
4765 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4766#endif
4767 default_frame_delay=0;
4768 default_frame_timeout=0;
4769 frame_delay=0;
4770 final_delay=1;
4771 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4772 object_id=0;
4773 skip_to_iend=MagickFalse;
4774 term_chunk_found=MagickFalse;
4775 mng_info->framing_mode=1;
4776#if defined(MNG_INSERT_LAYERS)
4777 mandatory_back=MagickFalse;
4778#endif
4779#if defined(MNG_INSERT_LAYERS)
4780 mng_background_color=image->background_color;
4781#endif
4782 default_fb=mng_info->frame;
4783 previous_fb=mng_info->frame;
4784 do
4785 {
4786 char
4787 type[MaxTextExtent];
4788
4789 if (LocaleCompare(image_info->magick,"MNG") == 0)
4790 {
4791 unsigned char
4792 *chunk;
4793
4794 /*
4795 Read a new chunk.
4796 */
4797 type[0]='\0';
4798 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4799 length=ReadBlobMSBLong(image);
4800 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4801
4802 if (logging != MagickFalse)
4803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004804 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4805 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004806
4807 if (length > PNG_UINT_31_MAX)
4808 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004809
cristy3ed852e2009-09-05 21:47:34 +00004810 if (count == 0)
4811 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004812
cristy3ed852e2009-09-05 21:47:34 +00004813 p=NULL;
4814 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004815
cristy3ed852e2009-09-05 21:47:34 +00004816 if (length)
4817 {
4818 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004819
cristy3ed852e2009-09-05 21:47:34 +00004820 if (chunk == (unsigned char *) NULL)
4821 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004822
cristybb503372010-05-27 20:51:26 +00004823 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004824 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004825
cristy3ed852e2009-09-05 21:47:34 +00004826 p=chunk;
4827 }
glennrp0fe50b42010-11-16 03:52:51 +00004828
cristy3ed852e2009-09-05 21:47:34 +00004829 (void) ReadBlobMSBLong(image); /* read crc word */
4830
4831#if !defined(JNG_SUPPORTED)
4832 if (memcmp(type,mng_JHDR,4) == 0)
4833 {
4834 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004835
cristy3ed852e2009-09-05 21:47:34 +00004836 if (mng_info->jhdr_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00004837 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004838 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004839
cristy3ed852e2009-09-05 21:47:34 +00004840 mng_info->jhdr_warning++;
4841 }
4842#endif
4843 if (memcmp(type,mng_DHDR,4) == 0)
4844 {
4845 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004846
cristy3ed852e2009-09-05 21:47:34 +00004847 if (mng_info->dhdr_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00004848 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004849 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004850
cristy3ed852e2009-09-05 21:47:34 +00004851 mng_info->dhdr_warning++;
4852 }
4853 if (memcmp(type,mng_MEND,4) == 0)
4854 break;
glennrp47b9dd52010-11-24 18:12:06 +00004855
cristy3ed852e2009-09-05 21:47:34 +00004856 if (skip_to_iend)
4857 {
4858 if (memcmp(type,mng_IEND,4) == 0)
4859 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004860
cristy3ed852e2009-09-05 21:47:34 +00004861 if (length)
4862 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004863
cristy3ed852e2009-09-05 21:47:34 +00004864 if (logging != MagickFalse)
4865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4866 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004867
cristy3ed852e2009-09-05 21:47:34 +00004868 continue;
4869 }
glennrp0fe50b42010-11-16 03:52:51 +00004870
cristy3ed852e2009-09-05 21:47:34 +00004871 if (memcmp(type,mng_MHDR,4) == 0)
4872 {
cristybb503372010-05-27 20:51:26 +00004873 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004874 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004875
cristybb503372010-05-27 20:51:26 +00004876 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004877 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004878
cristy3ed852e2009-09-05 21:47:34 +00004879 if (logging != MagickFalse)
4880 {
4881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004882 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004884 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004885 }
glennrp0fe50b42010-11-16 03:52:51 +00004886
cristy3ed852e2009-09-05 21:47:34 +00004887 p+=8;
cristy8182b072010-05-30 20:10:53 +00004888 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004889
cristy3ed852e2009-09-05 21:47:34 +00004890 if (mng_info->ticks_per_second == 0)
4891 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004892
cristy3ed852e2009-09-05 21:47:34 +00004893 else
4894 default_frame_delay=1UL*image->ticks_per_second/
4895 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004896
cristy3ed852e2009-09-05 21:47:34 +00004897 frame_delay=default_frame_delay;
4898 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004899
cristy3ed852e2009-09-05 21:47:34 +00004900 if (length > 16)
4901 {
4902 p+=16;
cristy8182b072010-05-30 20:10:53 +00004903 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004904 }
glennrp0fe50b42010-11-16 03:52:51 +00004905
cristy3ed852e2009-09-05 21:47:34 +00004906 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004907
cristy3ed852e2009-09-05 21:47:34 +00004908 if ((simplicity != 0) && ((simplicity | 11) == 11))
4909 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004910
cristy3ed852e2009-09-05 21:47:34 +00004911 if ((simplicity != 0) && ((simplicity | 9) == 9))
4912 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004913
cristy3ed852e2009-09-05 21:47:34 +00004914#if defined(MNG_INSERT_LAYERS)
4915 if (mng_type != 3)
4916 insert_layers=MagickTrue;
4917#endif
cristy4c08aed2011-07-01 19:47:50 +00004918 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004919 {
glennrp47b9dd52010-11-24 18:12:06 +00004920 /* Allocate next image structure. */
cristy9950d572011-10-01 18:22:35 +00004921 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004922
cristy3ed852e2009-09-05 21:47:34 +00004923 if (GetNextImageInList(image) == (Image *) NULL)
4924 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004925
cristy3ed852e2009-09-05 21:47:34 +00004926 image=SyncNextImageInList(image);
4927 mng_info->image=image;
4928 }
4929
4930 if ((mng_info->mng_width > 65535L) ||
4931 (mng_info->mng_height > 65535L))
4932 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004933
cristy3b6fd2e2011-05-20 12:53:50 +00004934 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004935 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004936 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004937
cristy3ed852e2009-09-05 21:47:34 +00004938 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004939 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004940 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004941 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004942 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004943
cristy3ed852e2009-09-05 21:47:34 +00004944 for (i=0; i < MNG_MAX_OBJECTS; i++)
4945 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004946
cristy3ed852e2009-09-05 21:47:34 +00004947 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4948 continue;
4949 }
4950
4951 if (memcmp(type,mng_TERM,4) == 0)
4952 {
4953 int
4954 repeat=0;
4955
4956
4957 if (length)
4958 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004959
cristy3ed852e2009-09-05 21:47:34 +00004960 if (repeat == 3)
4961 {
cristy8182b072010-05-30 20:10:53 +00004962 final_delay=(png_uint_32) mng_get_long(&p[2]);
4963 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004964
cristy3ed852e2009-09-05 21:47:34 +00004965 if (mng_iterations == PNG_UINT_31_MAX)
4966 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004967
cristy3ed852e2009-09-05 21:47:34 +00004968 image->iterations=mng_iterations;
4969 term_chunk_found=MagickTrue;
4970 }
glennrp0fe50b42010-11-16 03:52:51 +00004971
cristy3ed852e2009-09-05 21:47:34 +00004972 if (logging != MagickFalse)
4973 {
4974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4975 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004976
cristy3ed852e2009-09-05 21:47:34 +00004977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004978 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004979
cristy3ed852e2009-09-05 21:47:34 +00004980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004981 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004982 }
glennrp0fe50b42010-11-16 03:52:51 +00004983
cristy3ed852e2009-09-05 21:47:34 +00004984 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4985 continue;
4986 }
4987 if (memcmp(type,mng_DEFI,4) == 0)
4988 {
4989 if (mng_type == 3)
cristyc82a27b2011-10-21 01:07:16 +00004990 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004991 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4992 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004993
cristy3ed852e2009-09-05 21:47:34 +00004994 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004995
cristy3ed852e2009-09-05 21:47:34 +00004996 if (mng_type == 2 && object_id != 0)
cristyc82a27b2011-10-21 01:07:16 +00004997 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004998 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4999 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005000
cristy3ed852e2009-09-05 21:47:34 +00005001 if (object_id > MNG_MAX_OBJECTS)
5002 {
5003 /*
5004 Instead ofsuing a warning we should allocate a larger
5005 MngInfo structure and continue.
5006 */
cristyc82a27b2011-10-21 01:07:16 +00005007 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005008 CoderError,"object id too large","`%s'",image->filename);
5009 object_id=MNG_MAX_OBJECTS;
5010 }
glennrp0fe50b42010-11-16 03:52:51 +00005011
cristy3ed852e2009-09-05 21:47:34 +00005012 if (mng_info->exists[object_id])
5013 if (mng_info->frozen[object_id])
5014 {
5015 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
cristyc82a27b2011-10-21 01:07:16 +00005016 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005017 GetMagickModule(),CoderError,
5018 "DEFI cannot redefine a frozen MNG object","`%s'",
5019 image->filename);
5020 continue;
5021 }
glennrp0fe50b42010-11-16 03:52:51 +00005022
cristy3ed852e2009-09-05 21:47:34 +00005023 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005024
cristy3ed852e2009-09-05 21:47:34 +00005025 if (length > 2)
5026 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00005027
cristy3ed852e2009-09-05 21:47:34 +00005028 /*
5029 Extract object offset info.
5030 */
5031 if (length > 11)
5032 {
glennrp0fe50b42010-11-16 03:52:51 +00005033 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5034 (p[5] << 16) | (p[6] << 8) | p[7]);
5035
5036 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5037 (p[9] << 16) | (p[10] << 8) | p[11]);
5038
cristy3ed852e2009-09-05 21:47:34 +00005039 if (logging != MagickFalse)
5040 {
5041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005042 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005043 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005044
cristy3ed852e2009-09-05 21:47:34 +00005045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005046 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005047 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005048 }
5049 }
glennrp0fe50b42010-11-16 03:52:51 +00005050
cristy3ed852e2009-09-05 21:47:34 +00005051 /*
5052 Extract object clipping info.
5053 */
5054 if (length > 27)
5055 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5056 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005057
cristy3ed852e2009-09-05 21:47:34 +00005058 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5059 continue;
5060 }
5061 if (memcmp(type,mng_bKGD,4) == 0)
5062 {
5063 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005064
cristy3ed852e2009-09-05 21:47:34 +00005065 if (length > 5)
5066 {
5067 mng_info->mng_global_bkgd.red=
5068 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005069
cristy3ed852e2009-09-05 21:47:34 +00005070 mng_info->mng_global_bkgd.green=
5071 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005072
cristy3ed852e2009-09-05 21:47:34 +00005073 mng_info->mng_global_bkgd.blue=
5074 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005075
cristy3ed852e2009-09-05 21:47:34 +00005076 mng_info->have_global_bkgd=MagickTrue;
5077 }
glennrp0fe50b42010-11-16 03:52:51 +00005078
cristy3ed852e2009-09-05 21:47:34 +00005079 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5080 continue;
5081 }
5082 if (memcmp(type,mng_BACK,4) == 0)
5083 {
5084#if defined(MNG_INSERT_LAYERS)
5085 if (length > 6)
5086 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005087
cristy3ed852e2009-09-05 21:47:34 +00005088 else
5089 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005090
cristy3ed852e2009-09-05 21:47:34 +00005091 if (mandatory_back && length > 5)
5092 {
5093 mng_background_color.red=
5094 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005095
cristy3ed852e2009-09-05 21:47:34 +00005096 mng_background_color.green=
5097 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005098
cristy3ed852e2009-09-05 21:47:34 +00005099 mng_background_color.blue=
5100 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005101
cristy4c08aed2011-07-01 19:47:50 +00005102 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005103 }
glennrp0fe50b42010-11-16 03:52:51 +00005104
cristy3ed852e2009-09-05 21:47:34 +00005105#ifdef MNG_OBJECT_BUFFERS
5106 if (length > 8)
5107 mng_background_object=(p[7] << 8) | p[8];
5108#endif
5109#endif
5110 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5111 continue;
5112 }
glennrp47b9dd52010-11-24 18:12:06 +00005113
cristy3ed852e2009-09-05 21:47:34 +00005114 if (memcmp(type,mng_PLTE,4) == 0)
5115 {
glennrp47b9dd52010-11-24 18:12:06 +00005116 /* Read global PLTE. */
5117
cristy3ed852e2009-09-05 21:47:34 +00005118 if (length && (length < 769))
5119 {
5120 if (mng_info->global_plte == (png_colorp) NULL)
5121 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5122 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005123
cristybb503372010-05-27 20:51:26 +00005124 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005125 {
5126 mng_info->global_plte[i].red=p[3*i];
5127 mng_info->global_plte[i].green=p[3*i+1];
5128 mng_info->global_plte[i].blue=p[3*i+2];
5129 }
glennrp0fe50b42010-11-16 03:52:51 +00005130
cristy35ef8242010-06-03 16:24:13 +00005131 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005132 }
5133#ifdef MNG_LOOSE
5134 for ( ; i < 256; i++)
5135 {
5136 mng_info->global_plte[i].red=i;
5137 mng_info->global_plte[i].green=i;
5138 mng_info->global_plte[i].blue=i;
5139 }
glennrp0fe50b42010-11-16 03:52:51 +00005140
cristy3ed852e2009-09-05 21:47:34 +00005141 if (length)
5142 mng_info->global_plte_length=256;
5143#endif
5144 else
5145 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005146
cristy3ed852e2009-09-05 21:47:34 +00005147 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5148 continue;
5149 }
glennrp47b9dd52010-11-24 18:12:06 +00005150
cristy3ed852e2009-09-05 21:47:34 +00005151 if (memcmp(type,mng_tRNS,4) == 0)
5152 {
5153 /* read global tRNS */
5154
5155 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005156 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005157 mng_info->global_trns[i]=p[i];
5158
5159#ifdef MNG_LOOSE
5160 for ( ; i < 256; i++)
5161 mng_info->global_trns[i]=255;
5162#endif
cristy12560f32010-06-03 16:51:08 +00005163 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005164 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5165 continue;
5166 }
5167 if (memcmp(type,mng_gAMA,4) == 0)
5168 {
5169 if (length == 4)
5170 {
cristybb503372010-05-27 20:51:26 +00005171 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005172 igamma;
5173
cristy8182b072010-05-30 20:10:53 +00005174 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005175 mng_info->global_gamma=((float) igamma)*0.00001;
5176 mng_info->have_global_gama=MagickTrue;
5177 }
glennrp0fe50b42010-11-16 03:52:51 +00005178
cristy3ed852e2009-09-05 21:47:34 +00005179 else
5180 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005181
cristy3ed852e2009-09-05 21:47:34 +00005182 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5183 continue;
5184 }
5185
5186 if (memcmp(type,mng_cHRM,4) == 0)
5187 {
glennrp47b9dd52010-11-24 18:12:06 +00005188 /* Read global cHRM */
5189
cristy3ed852e2009-09-05 21:47:34 +00005190 if (length == 32)
5191 {
cristy8182b072010-05-30 20:10:53 +00005192 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5193 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5194 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005195 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005196 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005197 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005198 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005199 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005200 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005201 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005202 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005203 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005204 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005205 mng_info->have_global_chrm=MagickTrue;
5206 }
5207 else
5208 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005209
cristy3ed852e2009-09-05 21:47:34 +00005210 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5211 continue;
5212 }
glennrp47b9dd52010-11-24 18:12:06 +00005213
cristy3ed852e2009-09-05 21:47:34 +00005214 if (memcmp(type,mng_sRGB,4) == 0)
5215 {
5216 /*
5217 Read global sRGB.
5218 */
5219 if (length)
5220 {
glennrpe610a072010-08-05 17:08:46 +00005221 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005222 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005223 mng_info->have_global_srgb=MagickTrue;
5224 }
5225 else
5226 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005227
cristy3ed852e2009-09-05 21:47:34 +00005228 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5229 continue;
5230 }
glennrp47b9dd52010-11-24 18:12:06 +00005231
cristy3ed852e2009-09-05 21:47:34 +00005232 if (memcmp(type,mng_iCCP,4) == 0)
5233 {
glennrpfd05d622011-02-25 04:10:33 +00005234 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005235
5236 /*
5237 Read global iCCP.
5238 */
5239 if (length)
5240 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005241
cristy3ed852e2009-09-05 21:47:34 +00005242 continue;
5243 }
glennrp47b9dd52010-11-24 18:12:06 +00005244
cristy3ed852e2009-09-05 21:47:34 +00005245 if (memcmp(type,mng_FRAM,4) == 0)
5246 {
5247 if (mng_type == 3)
cristyc82a27b2011-10-21 01:07:16 +00005248 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005249 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5250 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005251
cristy3ed852e2009-09-05 21:47:34 +00005252 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5253 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005254
cristy3ed852e2009-09-05 21:47:34 +00005255 frame_delay=default_frame_delay;
5256 frame_timeout=default_frame_timeout;
5257 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005258
cristy3ed852e2009-09-05 21:47:34 +00005259 if (length)
5260 if (p[0])
5261 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005262
cristy3ed852e2009-09-05 21:47:34 +00005263 if (logging != MagickFalse)
5264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5265 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005266
cristy3ed852e2009-09-05 21:47:34 +00005267 if (length > 6)
5268 {
glennrp47b9dd52010-11-24 18:12:06 +00005269 /* Note the delay and frame clipping boundaries. */
5270
cristy3ed852e2009-09-05 21:47:34 +00005271 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005272
cristybb503372010-05-27 20:51:26 +00005273 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005274 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005275
cristy3ed852e2009-09-05 21:47:34 +00005276 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005277
cristybb503372010-05-27 20:51:26 +00005278 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005279 {
5280 int
5281 change_delay,
5282 change_timeout,
5283 change_clipping;
5284
5285 change_delay=(*p++);
5286 change_timeout=(*p++);
5287 change_clipping=(*p++);
5288 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005289
cristy3ed852e2009-09-05 21:47:34 +00005290 if (change_delay)
5291 {
cristy8182b072010-05-30 20:10:53 +00005292 frame_delay=1UL*image->ticks_per_second*
5293 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005294
cristy8182b072010-05-30 20:10:53 +00005295 if (mng_info->ticks_per_second != 0)
5296 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005297
glennrpbb010dd2010-06-01 13:07:15 +00005298 else
5299 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005300
cristy3ed852e2009-09-05 21:47:34 +00005301 if (change_delay == 2)
5302 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005303
cristy3ed852e2009-09-05 21:47:34 +00005304 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005305
cristy3ed852e2009-09-05 21:47:34 +00005306 if (logging != MagickFalse)
5307 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005308 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005309 }
glennrp47b9dd52010-11-24 18:12:06 +00005310
cristy3ed852e2009-09-05 21:47:34 +00005311 if (change_timeout)
5312 {
glennrpbb010dd2010-06-01 13:07:15 +00005313 frame_timeout=1UL*image->ticks_per_second*
5314 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005315
glennrpbb010dd2010-06-01 13:07:15 +00005316 if (mng_info->ticks_per_second != 0)
5317 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005318
glennrpbb010dd2010-06-01 13:07:15 +00005319 else
5320 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005321
cristy3ed852e2009-09-05 21:47:34 +00005322 if (change_delay == 2)
5323 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005324
cristy3ed852e2009-09-05 21:47:34 +00005325 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005326
cristy3ed852e2009-09-05 21:47:34 +00005327 if (logging != MagickFalse)
5328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005329 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005330 }
glennrp47b9dd52010-11-24 18:12:06 +00005331
cristy3ed852e2009-09-05 21:47:34 +00005332 if (change_clipping)
5333 {
5334 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5335 p+=17;
5336 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005337
cristy3ed852e2009-09-05 21:47:34 +00005338 if (logging != MagickFalse)
5339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005340 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005341 (double) fb.left,(double) fb.right,(double) fb.top,
5342 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005343
cristy3ed852e2009-09-05 21:47:34 +00005344 if (change_clipping == 2)
5345 default_fb=fb;
5346 }
5347 }
5348 }
5349 mng_info->clip=fb;
5350 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005351
cristybb503372010-05-27 20:51:26 +00005352 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005353 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005354
cristybb503372010-05-27 20:51:26 +00005355 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005356 -mng_info->clip.top);
5357 /*
5358 Insert a background layer behind the frame if framing_mode is 4.
5359 */
5360#if defined(MNG_INSERT_LAYERS)
5361 if (logging != MagickFalse)
5362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005363 " subframe_width=%.20g, subframe_height=%.20g",(double)
5364 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005365
cristy3ed852e2009-09-05 21:47:34 +00005366 if (insert_layers && (mng_info->framing_mode == 4) &&
5367 (subframe_width) && (subframe_height))
5368 {
glennrp47b9dd52010-11-24 18:12:06 +00005369 /* Allocate next image structure. */
cristy4c08aed2011-07-01 19:47:50 +00005370 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005371 {
cristy9950d572011-10-01 18:22:35 +00005372 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005373
cristy3ed852e2009-09-05 21:47:34 +00005374 if (GetNextImageInList(image) == (Image *) NULL)
5375 {
5376 image=DestroyImageList(image);
5377 MngInfoFreeStruct(mng_info,&have_mng_structure);
5378 return((Image *) NULL);
5379 }
glennrp47b9dd52010-11-24 18:12:06 +00005380
cristy3ed852e2009-09-05 21:47:34 +00005381 image=SyncNextImageInList(image);
5382 }
glennrp0fe50b42010-11-16 03:52:51 +00005383
cristy3ed852e2009-09-05 21:47:34 +00005384 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005385
cristy3ed852e2009-09-05 21:47:34 +00005386 if (term_chunk_found)
5387 {
5388 image->start_loop=MagickTrue;
5389 image->iterations=mng_iterations;
5390 term_chunk_found=MagickFalse;
5391 }
glennrp0fe50b42010-11-16 03:52:51 +00005392
cristy3ed852e2009-09-05 21:47:34 +00005393 else
5394 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005395
cristy3ed852e2009-09-05 21:47:34 +00005396 image->columns=subframe_width;
5397 image->rows=subframe_height;
5398 image->page.width=subframe_width;
5399 image->page.height=subframe_height;
5400 image->page.x=mng_info->clip.left;
5401 image->page.y=mng_info->clip.top;
5402 image->background_color=mng_background_color;
5403 image->matte=MagickFalse;
5404 image->delay=0;
cristyea1a8aa2011-10-20 13:24:06 +00005405 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005406
cristy3ed852e2009-09-05 21:47:34 +00005407 if (logging != MagickFalse)
5408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005409 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005410 (double) mng_info->clip.left,(double) mng_info->clip.right,
5411 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005412 }
5413#endif
5414 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5415 continue;
5416 }
5417 if (memcmp(type,mng_CLIP,4) == 0)
5418 {
5419 unsigned int
5420 first_object,
5421 last_object;
5422
5423 /*
5424 Read CLIP.
5425 */
5426 first_object=(p[0] << 8) | p[1];
5427 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005428
cristy3ed852e2009-09-05 21:47:34 +00005429 for (i=(int) first_object; i <= (int) last_object; i++)
5430 {
5431 if (mng_info->exists[i] && !mng_info->frozen[i])
5432 {
5433 MngBox
5434 box;
5435
5436 box=mng_info->object_clip[i];
5437 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5438 }
5439 }
glennrp47b9dd52010-11-24 18:12:06 +00005440
cristy3ed852e2009-09-05 21:47:34 +00005441 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5442 continue;
5443 }
5444 if (memcmp(type,mng_SAVE,4) == 0)
5445 {
5446 for (i=1; i < MNG_MAX_OBJECTS; i++)
5447 if (mng_info->exists[i])
5448 {
5449 mng_info->frozen[i]=MagickTrue;
5450#ifdef MNG_OBJECT_BUFFERS
5451 if (mng_info->ob[i] != (MngBuffer *) NULL)
5452 mng_info->ob[i]->frozen=MagickTrue;
5453#endif
5454 }
glennrp0fe50b42010-11-16 03:52:51 +00005455
cristy3ed852e2009-09-05 21:47:34 +00005456 if (length)
5457 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005458
cristy3ed852e2009-09-05 21:47:34 +00005459 continue;
5460 }
5461
5462 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5463 {
glennrp47b9dd52010-11-24 18:12:06 +00005464 /* Read DISC or SEEK. */
5465
cristy3ed852e2009-09-05 21:47:34 +00005466 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5467 {
5468 for (i=1; i < MNG_MAX_OBJECTS; i++)
5469 MngInfoDiscardObject(mng_info,i);
5470 }
glennrp0fe50b42010-11-16 03:52:51 +00005471
cristy3ed852e2009-09-05 21:47:34 +00005472 else
5473 {
cristybb503372010-05-27 20:51:26 +00005474 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005475 j;
5476
cristybb503372010-05-27 20:51:26 +00005477 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005478 {
5479 i=p[j] << 8 | p[j+1];
5480 MngInfoDiscardObject(mng_info,i);
5481 }
5482 }
glennrp0fe50b42010-11-16 03:52:51 +00005483
cristy3ed852e2009-09-05 21:47:34 +00005484 if (length)
5485 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005486
cristy3ed852e2009-09-05 21:47:34 +00005487 continue;
5488 }
glennrp47b9dd52010-11-24 18:12:06 +00005489
cristy3ed852e2009-09-05 21:47:34 +00005490 if (memcmp(type,mng_MOVE,4) == 0)
5491 {
cristybb503372010-05-27 20:51:26 +00005492 size_t
cristy3ed852e2009-09-05 21:47:34 +00005493 first_object,
5494 last_object;
5495
glennrp47b9dd52010-11-24 18:12:06 +00005496 /* read MOVE */
5497
cristy3ed852e2009-09-05 21:47:34 +00005498 first_object=(p[0] << 8) | p[1];
5499 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005500 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005501 {
5502 if (mng_info->exists[i] && !mng_info->frozen[i])
5503 {
5504 MngPair
5505 new_pair;
5506
5507 MngPair
5508 old_pair;
5509
5510 old_pair.a=mng_info->x_off[i];
5511 old_pair.b=mng_info->y_off[i];
5512 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5513 mng_info->x_off[i]=new_pair.a;
5514 mng_info->y_off[i]=new_pair.b;
5515 }
5516 }
glennrp47b9dd52010-11-24 18:12:06 +00005517
cristy3ed852e2009-09-05 21:47:34 +00005518 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5519 continue;
5520 }
5521
5522 if (memcmp(type,mng_LOOP,4) == 0)
5523 {
cristybb503372010-05-27 20:51:26 +00005524 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005525 loop_level=chunk[0];
5526 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005527
5528 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005529 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005530
cristy3ed852e2009-09-05 21:47:34 +00005531 if (logging != MagickFalse)
5532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005533 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5534 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005535
cristy3ed852e2009-09-05 21:47:34 +00005536 if (loop_iters == 0)
5537 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005538
cristy3ed852e2009-09-05 21:47:34 +00005539 else
5540 {
5541 mng_info->loop_jump[loop_level]=TellBlob(image);
5542 mng_info->loop_count[loop_level]=loop_iters;
5543 }
glennrp0fe50b42010-11-16 03:52:51 +00005544
cristy3ed852e2009-09-05 21:47:34 +00005545 mng_info->loop_iteration[loop_level]=0;
5546 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5547 continue;
5548 }
glennrp47b9dd52010-11-24 18:12:06 +00005549
cristy3ed852e2009-09-05 21:47:34 +00005550 if (memcmp(type,mng_ENDL,4) == 0)
5551 {
5552 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005553
cristy3ed852e2009-09-05 21:47:34 +00005554 if (skipping_loop > 0)
5555 {
5556 if (skipping_loop == loop_level)
5557 {
5558 /*
5559 Found end of zero-iteration loop.
5560 */
5561 skipping_loop=(-1);
5562 mng_info->loop_active[loop_level]=0;
5563 }
5564 }
glennrp47b9dd52010-11-24 18:12:06 +00005565
cristy3ed852e2009-09-05 21:47:34 +00005566 else
5567 {
5568 if (mng_info->loop_active[loop_level] == 1)
5569 {
5570 mng_info->loop_count[loop_level]--;
5571 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005572
cristy3ed852e2009-09-05 21:47:34 +00005573 if (logging != MagickFalse)
5574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005575 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005576 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005577 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005578
cristy3ed852e2009-09-05 21:47:34 +00005579 if (mng_info->loop_count[loop_level] != 0)
5580 {
5581 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5582 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005583
cristy3ed852e2009-09-05 21:47:34 +00005584 if (offset < 0)
5585 ThrowReaderException(CorruptImageError,
5586 "ImproperImageHeader");
5587 }
glennrp47b9dd52010-11-24 18:12:06 +00005588
cristy3ed852e2009-09-05 21:47:34 +00005589 else
5590 {
5591 short
5592 last_level;
5593
5594 /*
5595 Finished loop.
5596 */
5597 mng_info->loop_active[loop_level]=0;
5598 last_level=(-1);
5599 for (i=0; i < loop_level; i++)
5600 if (mng_info->loop_active[i] == 1)
5601 last_level=(short) i;
5602 loop_level=last_level;
5603 }
5604 }
5605 }
glennrp47b9dd52010-11-24 18:12:06 +00005606
cristy3ed852e2009-09-05 21:47:34 +00005607 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5608 continue;
5609 }
glennrp47b9dd52010-11-24 18:12:06 +00005610
cristy3ed852e2009-09-05 21:47:34 +00005611 if (memcmp(type,mng_CLON,4) == 0)
5612 {
5613 if (mng_info->clon_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005614 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005615 CoderError,"CLON is not implemented yet","`%s'",
5616 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005617
cristy3ed852e2009-09-05 21:47:34 +00005618 mng_info->clon_warning++;
5619 }
glennrp47b9dd52010-11-24 18:12:06 +00005620
cristy3ed852e2009-09-05 21:47:34 +00005621 if (memcmp(type,mng_MAGN,4) == 0)
5622 {
5623 png_uint_16
5624 magn_first,
5625 magn_last,
5626 magn_mb,
5627 magn_ml,
5628 magn_mr,
5629 magn_mt,
5630 magn_mx,
5631 magn_my,
5632 magn_methx,
5633 magn_methy;
5634
5635 if (length > 1)
5636 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005637
cristy3ed852e2009-09-05 21:47:34 +00005638 else
5639 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005640
cristy3ed852e2009-09-05 21:47:34 +00005641 if (length > 3)
5642 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005643
cristy3ed852e2009-09-05 21:47:34 +00005644 else
5645 magn_last=magn_first;
5646#ifndef MNG_OBJECT_BUFFERS
5647 if (magn_first || magn_last)
5648 if (mng_info->magn_warning == 0)
5649 {
cristyc82a27b2011-10-21 01:07:16 +00005650 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005651 GetMagickModule(),CoderError,
5652 "MAGN is not implemented yet for nonzero objects",
5653 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005654
cristy3ed852e2009-09-05 21:47:34 +00005655 mng_info->magn_warning++;
5656 }
5657#endif
5658 if (length > 4)
5659 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005660
cristy3ed852e2009-09-05 21:47:34 +00005661 else
5662 magn_methx=0;
5663
5664 if (length > 6)
5665 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005666
cristy3ed852e2009-09-05 21:47:34 +00005667 else
5668 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005669
cristy3ed852e2009-09-05 21:47:34 +00005670 if (magn_mx == 0)
5671 magn_mx=1;
5672
5673 if (length > 8)
5674 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005675
cristy3ed852e2009-09-05 21:47:34 +00005676 else
5677 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005678
cristy3ed852e2009-09-05 21:47:34 +00005679 if (magn_my == 0)
5680 magn_my=1;
5681
5682 if (length > 10)
5683 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005684
cristy3ed852e2009-09-05 21:47:34 +00005685 else
5686 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005687
cristy3ed852e2009-09-05 21:47:34 +00005688 if (magn_ml == 0)
5689 magn_ml=1;
5690
5691 if (length > 12)
5692 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005693
cristy3ed852e2009-09-05 21:47:34 +00005694 else
5695 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005696
cristy3ed852e2009-09-05 21:47:34 +00005697 if (magn_mr == 0)
5698 magn_mr=1;
5699
5700 if (length > 14)
5701 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005702
cristy3ed852e2009-09-05 21:47:34 +00005703 else
5704 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005705
cristy3ed852e2009-09-05 21:47:34 +00005706 if (magn_mt == 0)
5707 magn_mt=1;
5708
5709 if (length > 16)
5710 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005711
cristy3ed852e2009-09-05 21:47:34 +00005712 else
5713 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005714
cristy3ed852e2009-09-05 21:47:34 +00005715 if (magn_mb == 0)
5716 magn_mb=1;
5717
5718 if (length > 17)
5719 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005720
cristy3ed852e2009-09-05 21:47:34 +00005721 else
5722 magn_methy=magn_methx;
5723
glennrp47b9dd52010-11-24 18:12:06 +00005724
cristy3ed852e2009-09-05 21:47:34 +00005725 if (magn_methx > 5 || magn_methy > 5)
5726 if (mng_info->magn_warning == 0)
5727 {
cristyc82a27b2011-10-21 01:07:16 +00005728 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005729 GetMagickModule(),CoderError,
5730 "Unknown MAGN method in MNG datastream","`%s'",
5731 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005732
cristy3ed852e2009-09-05 21:47:34 +00005733 mng_info->magn_warning++;
5734 }
5735#ifdef MNG_OBJECT_BUFFERS
5736 /* Magnify existing objects in the range magn_first to magn_last */
5737#endif
5738 if (magn_first == 0 || magn_last == 0)
5739 {
5740 /* Save the magnification factors for object 0 */
5741 mng_info->magn_mb=magn_mb;
5742 mng_info->magn_ml=magn_ml;
5743 mng_info->magn_mr=magn_mr;
5744 mng_info->magn_mt=magn_mt;
5745 mng_info->magn_mx=magn_mx;
5746 mng_info->magn_my=magn_my;
5747 mng_info->magn_methx=magn_methx;
5748 mng_info->magn_methy=magn_methy;
5749 }
5750 }
glennrp47b9dd52010-11-24 18:12:06 +00005751
cristy3ed852e2009-09-05 21:47:34 +00005752 if (memcmp(type,mng_PAST,4) == 0)
5753 {
5754 if (mng_info->past_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005755 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005756 CoderError,"PAST is not implemented yet","`%s'",
5757 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005758
cristy3ed852e2009-09-05 21:47:34 +00005759 mng_info->past_warning++;
5760 }
glennrp47b9dd52010-11-24 18:12:06 +00005761
cristy3ed852e2009-09-05 21:47:34 +00005762 if (memcmp(type,mng_SHOW,4) == 0)
5763 {
5764 if (mng_info->show_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005765 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005766 CoderError,"SHOW is not implemented yet","`%s'",
5767 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005768
cristy3ed852e2009-09-05 21:47:34 +00005769 mng_info->show_warning++;
5770 }
glennrp47b9dd52010-11-24 18:12:06 +00005771
cristy3ed852e2009-09-05 21:47:34 +00005772 if (memcmp(type,mng_sBIT,4) == 0)
5773 {
5774 if (length < 4)
5775 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005776
cristy3ed852e2009-09-05 21:47:34 +00005777 else
5778 {
5779 mng_info->global_sbit.gray=p[0];
5780 mng_info->global_sbit.red=p[0];
5781 mng_info->global_sbit.green=p[1];
5782 mng_info->global_sbit.blue=p[2];
5783 mng_info->global_sbit.alpha=p[3];
5784 mng_info->have_global_sbit=MagickTrue;
5785 }
5786 }
5787 if (memcmp(type,mng_pHYs,4) == 0)
5788 {
5789 if (length > 8)
5790 {
5791 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005792 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005793 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005794 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005795 mng_info->global_phys_unit_type=p[8];
5796 mng_info->have_global_phys=MagickTrue;
5797 }
glennrp47b9dd52010-11-24 18:12:06 +00005798
cristy3ed852e2009-09-05 21:47:34 +00005799 else
5800 mng_info->have_global_phys=MagickFalse;
5801 }
5802 if (memcmp(type,mng_pHYg,4) == 0)
5803 {
5804 if (mng_info->phyg_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005805 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005806 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005807
cristy3ed852e2009-09-05 21:47:34 +00005808 mng_info->phyg_warning++;
5809 }
5810 if (memcmp(type,mng_BASI,4) == 0)
5811 {
5812 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005813
cristy3ed852e2009-09-05 21:47:34 +00005814 if (mng_info->basi_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005815 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005816 CoderError,"BASI is not implemented yet","`%s'",
5817 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005818
cristy3ed852e2009-09-05 21:47:34 +00005819 mng_info->basi_warning++;
5820#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005821 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005822 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005823 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005824 (p[6] << 8) | p[7]);
5825 basi_color_type=p[8];
5826 basi_compression_method=p[9];
5827 basi_filter_type=p[10];
5828 basi_interlace_method=p[11];
5829 if (length > 11)
5830 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005831
cristy3ed852e2009-09-05 21:47:34 +00005832 else
5833 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005834
cristy3ed852e2009-09-05 21:47:34 +00005835 if (length > 13)
5836 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005837
cristy3ed852e2009-09-05 21:47:34 +00005838 else
5839 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005840
cristy3ed852e2009-09-05 21:47:34 +00005841 if (length > 15)
5842 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005843
cristy3ed852e2009-09-05 21:47:34 +00005844 else
5845 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005846
cristy3ed852e2009-09-05 21:47:34 +00005847 if (length > 17)
5848 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005849
cristy3ed852e2009-09-05 21:47:34 +00005850 else
5851 {
5852 if (basi_sample_depth == 16)
5853 basi_alpha=65535L;
5854 else
5855 basi_alpha=255;
5856 }
glennrp47b9dd52010-11-24 18:12:06 +00005857
cristy3ed852e2009-09-05 21:47:34 +00005858 if (length > 19)
5859 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005860
cristy3ed852e2009-09-05 21:47:34 +00005861 else
5862 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005863
cristy3ed852e2009-09-05 21:47:34 +00005864#endif
5865 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5866 continue;
5867 }
glennrp47b9dd52010-11-24 18:12:06 +00005868
cristy3ed852e2009-09-05 21:47:34 +00005869 if (memcmp(type,mng_IHDR,4)
5870#if defined(JNG_SUPPORTED)
5871 && memcmp(type,mng_JHDR,4)
5872#endif
5873 )
5874 {
5875 /* Not an IHDR or JHDR chunk */
5876 if (length)
5877 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005878
cristy3ed852e2009-09-05 21:47:34 +00005879 continue;
5880 }
5881/* Process IHDR */
5882 if (logging != MagickFalse)
5883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5884 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005885
cristy3ed852e2009-09-05 21:47:34 +00005886 mng_info->exists[object_id]=MagickTrue;
5887 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005888
cristy3ed852e2009-09-05 21:47:34 +00005889 if (mng_info->invisible[object_id])
5890 {
5891 if (logging != MagickFalse)
5892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5893 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005894
cristy3ed852e2009-09-05 21:47:34 +00005895 skip_to_iend=MagickTrue;
5896 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5897 continue;
5898 }
5899#if defined(MNG_INSERT_LAYERS)
5900 if (length < 8)
5901 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005902
cristy8182b072010-05-30 20:10:53 +00005903 image_width=(size_t) mng_get_long(p);
5904 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005905#endif
5906 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5907
5908 /*
5909 Insert a transparent background layer behind the entire animation
5910 if it is not full screen.
5911 */
5912#if defined(MNG_INSERT_LAYERS)
5913 if (insert_layers && mng_type && first_mng_object)
5914 {
5915 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5916 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005917 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005918 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005919 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005920 {
cristy4c08aed2011-07-01 19:47:50 +00005921 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005922 {
5923 /*
5924 Allocate next image structure.
5925 */
cristy9950d572011-10-01 18:22:35 +00005926 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005927
cristy3ed852e2009-09-05 21:47:34 +00005928 if (GetNextImageInList(image) == (Image *) NULL)
5929 {
5930 image=DestroyImageList(image);
5931 MngInfoFreeStruct(mng_info,&have_mng_structure);
5932 return((Image *) NULL);
5933 }
glennrp47b9dd52010-11-24 18:12:06 +00005934
cristy3ed852e2009-09-05 21:47:34 +00005935 image=SyncNextImageInList(image);
5936 }
5937 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005938
cristy3ed852e2009-09-05 21:47:34 +00005939 if (term_chunk_found)
5940 {
5941 image->start_loop=MagickTrue;
5942 image->iterations=mng_iterations;
5943 term_chunk_found=MagickFalse;
5944 }
glennrp47b9dd52010-11-24 18:12:06 +00005945
cristy3ed852e2009-09-05 21:47:34 +00005946 else
5947 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005948
5949 /* Make a background rectangle. */
5950
cristy3ed852e2009-09-05 21:47:34 +00005951 image->delay=0;
5952 image->columns=mng_info->mng_width;
5953 image->rows=mng_info->mng_height;
5954 image->page.width=mng_info->mng_width;
5955 image->page.height=mng_info->mng_height;
5956 image->page.x=0;
5957 image->page.y=0;
5958 image->background_color=mng_background_color;
cristyea1a8aa2011-10-20 13:24:06 +00005959 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005960 if (logging != MagickFalse)
5961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005962 " Inserted transparent background layer, W=%.20g, H=%.20g",
5963 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005964 }
5965 }
5966 /*
5967 Insert a background layer behind the upcoming image if
5968 framing_mode is 3, and we haven't already inserted one.
5969 */
5970 if (insert_layers && (mng_info->framing_mode == 3) &&
5971 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5972 (simplicity & 0x08)))
5973 {
cristy4c08aed2011-07-01 19:47:50 +00005974 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005975 {
5976 /*
5977 Allocate next image structure.
5978 */
cristy9950d572011-10-01 18:22:35 +00005979 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005980
cristy3ed852e2009-09-05 21:47:34 +00005981 if (GetNextImageInList(image) == (Image *) NULL)
5982 {
5983 image=DestroyImageList(image);
5984 MngInfoFreeStruct(mng_info,&have_mng_structure);
5985 return((Image *) NULL);
5986 }
glennrp47b9dd52010-11-24 18:12:06 +00005987
cristy3ed852e2009-09-05 21:47:34 +00005988 image=SyncNextImageInList(image);
5989 }
glennrp0fe50b42010-11-16 03:52:51 +00005990
cristy3ed852e2009-09-05 21:47:34 +00005991 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005992
cristy3ed852e2009-09-05 21:47:34 +00005993 if (term_chunk_found)
5994 {
5995 image->start_loop=MagickTrue;
5996 image->iterations=mng_iterations;
5997 term_chunk_found=MagickFalse;
5998 }
glennrp0fe50b42010-11-16 03:52:51 +00005999
cristy3ed852e2009-09-05 21:47:34 +00006000 else
6001 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006002
cristy3ed852e2009-09-05 21:47:34 +00006003 image->delay=0;
6004 image->columns=subframe_width;
6005 image->rows=subframe_height;
6006 image->page.width=subframe_width;
6007 image->page.height=subframe_height;
6008 image->page.x=mng_info->clip.left;
6009 image->page.y=mng_info->clip.top;
6010 image->background_color=mng_background_color;
6011 image->matte=MagickFalse;
cristyea1a8aa2011-10-20 13:24:06 +00006012 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006013
cristy3ed852e2009-09-05 21:47:34 +00006014 if (logging != MagickFalse)
6015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00006016 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00006017 (double) mng_info->clip.left,(double) mng_info->clip.right,
6018 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00006019 }
6020#endif /* MNG_INSERT_LAYERS */
6021 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006022
cristy4c08aed2011-07-01 19:47:50 +00006023 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006024 {
6025 /*
6026 Allocate next image structure.
6027 */
cristy9950d572011-10-01 18:22:35 +00006028 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006029
cristy3ed852e2009-09-05 21:47:34 +00006030 if (GetNextImageInList(image) == (Image *) NULL)
6031 {
6032 image=DestroyImageList(image);
6033 MngInfoFreeStruct(mng_info,&have_mng_structure);
6034 return((Image *) NULL);
6035 }
glennrp47b9dd52010-11-24 18:12:06 +00006036
cristy3ed852e2009-09-05 21:47:34 +00006037 image=SyncNextImageInList(image);
6038 }
6039 mng_info->image=image;
6040 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6041 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006042
cristy3ed852e2009-09-05 21:47:34 +00006043 if (status == MagickFalse)
6044 break;
glennrp0fe50b42010-11-16 03:52:51 +00006045
cristy3ed852e2009-09-05 21:47:34 +00006046 if (term_chunk_found)
6047 {
6048 image->start_loop=MagickTrue;
6049 term_chunk_found=MagickFalse;
6050 }
glennrp0fe50b42010-11-16 03:52:51 +00006051
cristy3ed852e2009-09-05 21:47:34 +00006052 else
6053 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006054
cristy3ed852e2009-09-05 21:47:34 +00006055 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6056 {
6057 image->delay=frame_delay;
6058 frame_delay=default_frame_delay;
6059 }
glennrp0fe50b42010-11-16 03:52:51 +00006060
cristy3ed852e2009-09-05 21:47:34 +00006061 else
6062 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006063
cristy3ed852e2009-09-05 21:47:34 +00006064 image->page.width=mng_info->mng_width;
6065 image->page.height=mng_info->mng_height;
6066 image->page.x=mng_info->x_off[object_id];
6067 image->page.y=mng_info->y_off[object_id];
6068 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006069
cristy3ed852e2009-09-05 21:47:34 +00006070 /*
6071 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6072 */
glennrp47b9dd52010-11-24 18:12:06 +00006073
cristy3ed852e2009-09-05 21:47:34 +00006074 if (logging != MagickFalse)
6075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6076 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6077 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006078
cristybb503372010-05-27 20:51:26 +00006079 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006080
cristy3ed852e2009-09-05 21:47:34 +00006081 if (offset < 0)
6082 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6083 }
6084
6085 previous=image;
6086 mng_info->image=image;
6087 mng_info->mng_type=mng_type;
6088 mng_info->object_id=object_id;
6089
6090 if (memcmp(type,mng_IHDR,4) == 0)
6091 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006092
cristy3ed852e2009-09-05 21:47:34 +00006093#if defined(JNG_SUPPORTED)
6094 else
6095 image=ReadOneJNGImage(mng_info,image_info,exception);
6096#endif
6097
6098 if (image == (Image *) NULL)
6099 {
6100 if (IsImageObject(previous) != MagickFalse)
6101 {
6102 (void) DestroyImageList(previous);
6103 (void) CloseBlob(previous);
6104 }
glennrp47b9dd52010-11-24 18:12:06 +00006105
cristy3ed852e2009-09-05 21:47:34 +00006106 MngInfoFreeStruct(mng_info,&have_mng_structure);
6107 return((Image *) NULL);
6108 }
glennrp0fe50b42010-11-16 03:52:51 +00006109
cristy3ed852e2009-09-05 21:47:34 +00006110 if (image->columns == 0 || image->rows == 0)
6111 {
6112 (void) CloseBlob(image);
6113 image=DestroyImageList(image);
6114 MngInfoFreeStruct(mng_info,&have_mng_structure);
6115 return((Image *) NULL);
6116 }
glennrp0fe50b42010-11-16 03:52:51 +00006117
cristy3ed852e2009-09-05 21:47:34 +00006118 mng_info->image=image;
6119
6120 if (mng_type)
6121 {
6122 MngBox
6123 crop_box;
6124
6125 if (mng_info->magn_methx || mng_info->magn_methy)
6126 {
6127 png_uint_32
6128 magnified_height,
6129 magnified_width;
6130
6131 if (logging != MagickFalse)
6132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6133 " Processing MNG MAGN chunk");
6134
6135 if (mng_info->magn_methx == 1)
6136 {
6137 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006138
cristy3ed852e2009-09-05 21:47:34 +00006139 if (image->columns > 1)
6140 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006141
cristy3ed852e2009-09-05 21:47:34 +00006142 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006143 magnified_width += (png_uint_32)
6144 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006145 }
glennrp47b9dd52010-11-24 18:12:06 +00006146
cristy3ed852e2009-09-05 21:47:34 +00006147 else
6148 {
cristy4e5bc842010-06-09 13:56:01 +00006149 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006150
cristy3ed852e2009-09-05 21:47:34 +00006151 if (image->columns > 1)
6152 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006153
cristy3ed852e2009-09-05 21:47:34 +00006154 if (image->columns > 2)
6155 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006156
cristy3ed852e2009-09-05 21:47:34 +00006157 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006158 magnified_width += (png_uint_32)
6159 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006160 }
glennrp47b9dd52010-11-24 18:12:06 +00006161
cristy3ed852e2009-09-05 21:47:34 +00006162 if (mng_info->magn_methy == 1)
6163 {
6164 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006165
cristy3ed852e2009-09-05 21:47:34 +00006166 if (image->rows > 1)
6167 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006168
cristy3ed852e2009-09-05 21:47:34 +00006169 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006170 magnified_height += (png_uint_32)
6171 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006172 }
glennrp47b9dd52010-11-24 18:12:06 +00006173
cristy3ed852e2009-09-05 21:47:34 +00006174 else
6175 {
cristy4e5bc842010-06-09 13:56:01 +00006176 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006177
cristy3ed852e2009-09-05 21:47:34 +00006178 if (image->rows > 1)
6179 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006180
cristy3ed852e2009-09-05 21:47:34 +00006181 if (image->rows > 2)
6182 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006183
cristy3ed852e2009-09-05 21:47:34 +00006184 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006185 magnified_height += (png_uint_32)
6186 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006187 }
glennrp47b9dd52010-11-24 18:12:06 +00006188
cristy3ed852e2009-09-05 21:47:34 +00006189 if (magnified_height > image->rows ||
6190 magnified_width > image->columns)
6191 {
6192 Image
6193 *large_image;
6194
6195 int
6196 yy;
6197
cristy4c08aed2011-07-01 19:47:50 +00006198 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006199 *next,
6200 *prev;
6201
6202 png_uint_16
6203 magn_methx,
6204 magn_methy;
6205
cristy4c08aed2011-07-01 19:47:50 +00006206 ssize_t
6207 m,
6208 y;
6209
6210 register Quantum
6211 *n,
6212 *q;
6213
6214 register ssize_t
6215 x;
6216
glennrp47b9dd52010-11-24 18:12:06 +00006217 /* Allocate next image structure. */
6218
cristy3ed852e2009-09-05 21:47:34 +00006219 if (logging != MagickFalse)
6220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6221 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006222
cristy9950d572011-10-01 18:22:35 +00006223 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006224
cristy3ed852e2009-09-05 21:47:34 +00006225 if (GetNextImageInList(image) == (Image *) NULL)
6226 {
6227 image=DestroyImageList(image);
6228 MngInfoFreeStruct(mng_info,&have_mng_structure);
6229 return((Image *) NULL);
6230 }
6231
6232 large_image=SyncNextImageInList(image);
6233
6234 large_image->columns=magnified_width;
6235 large_image->rows=magnified_height;
6236
6237 magn_methx=mng_info->magn_methx;
6238 magn_methy=mng_info->magn_methy;
6239
glennrp3faa9a32011-04-23 14:00:25 +00006240#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006241#define QM unsigned short
6242 if (magn_methx != 1 || magn_methy != 1)
6243 {
6244 /*
6245 Scale pixels to unsigned shorts to prevent
6246 overflow of intermediate values of interpolations
6247 */
cristybb503372010-05-27 20:51:26 +00006248 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006249 {
6250 q=GetAuthenticPixels(image,0,y,image->columns,1,
6251 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006252
cristybb503372010-05-27 20:51:26 +00006253 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006254 {
cristy4c08aed2011-07-01 19:47:50 +00006255 SetPixelRed(image,ScaleQuantumToShort(
6256 GetPixelRed(image,q)),q);
6257 SetPixelGreen(image,ScaleQuantumToShort(
6258 GetPixelGreen(image,q)),q);
6259 SetPixelBlue(image,ScaleQuantumToShort(
6260 GetPixelBlue(image,q)),q);
6261 SetPixelAlpha(image,ScaleQuantumToShort(
6262 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006263 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006264 }
glennrp47b9dd52010-11-24 18:12:06 +00006265
cristy3ed852e2009-09-05 21:47:34 +00006266 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6267 break;
6268 }
6269 }
6270#else
6271#define QM Quantum
6272#endif
6273
6274 if (image->matte != MagickFalse)
cristyea1a8aa2011-10-20 13:24:06 +00006275 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006276
cristy3ed852e2009-09-05 21:47:34 +00006277 else
6278 {
cristy4c08aed2011-07-01 19:47:50 +00006279 large_image->background_color.alpha=OpaqueAlpha;
cristyea1a8aa2011-10-20 13:24:06 +00006280 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006281
cristy3ed852e2009-09-05 21:47:34 +00006282 if (magn_methx == 4)
6283 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006284
cristy3ed852e2009-09-05 21:47:34 +00006285 if (magn_methx == 5)
6286 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006287
cristy3ed852e2009-09-05 21:47:34 +00006288 if (magn_methy == 4)
6289 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006290
cristy3ed852e2009-09-05 21:47:34 +00006291 if (magn_methy == 5)
6292 magn_methy=3;
6293 }
6294
6295 /* magnify the rows into the right side of the large image */
6296
6297 if (logging != MagickFalse)
6298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006299 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006300 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006301 yy=0;
cristy8a20fa02011-12-27 15:54:31 +00006302 length=(size_t) image->columns*GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00006303 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6304 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006305
cristy4c08aed2011-07-01 19:47:50 +00006306 if ((prev == (Quantum *) NULL) ||
6307 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006308 {
6309 image=DestroyImageList(image);
6310 MngInfoFreeStruct(mng_info,&have_mng_structure);
6311 ThrowReaderException(ResourceLimitError,
6312 "MemoryAllocationFailed");
6313 }
glennrp47b9dd52010-11-24 18:12:06 +00006314
cristy3ed852e2009-09-05 21:47:34 +00006315 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6316 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006317
cristybb503372010-05-27 20:51:26 +00006318 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006319 {
6320 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006321 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006322
cristybb503372010-05-27 20:51:26 +00006323 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6324 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006325
cristybb503372010-05-27 20:51:26 +00006326 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6327 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006328
cristybb503372010-05-27 20:51:26 +00006329 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006330 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006331
cristy3ed852e2009-09-05 21:47:34 +00006332 else
cristybb503372010-05-27 20:51:26 +00006333 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006334
cristy3ed852e2009-09-05 21:47:34 +00006335 n=prev;
6336 prev=next;
6337 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006338
cristybb503372010-05-27 20:51:26 +00006339 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006340 {
6341 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6342 exception);
6343 (void) CopyMagickMemory(next,n,length);
6344 }
glennrp47b9dd52010-11-24 18:12:06 +00006345
cristy3ed852e2009-09-05 21:47:34 +00006346 for (i=0; i < m; i++, yy++)
6347 {
cristy4c08aed2011-07-01 19:47:50 +00006348 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006349 *pixels;
6350
cristybb503372010-05-27 20:51:26 +00006351 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006352 pixels=prev;
6353 n=next;
6354 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006355 1,exception);
cristy97707062011-12-27 18:25:00 +00006356 q+=(large_image->columns-image->columns)*
6357 GetPixelChannels(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006358
cristybb503372010-05-27 20:51:26 +00006359 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006360 {
glennrpfd05d622011-02-25 04:10:33 +00006361 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006362 /*
6363 if (image->storage_class == PseudoClass)
6364 {
6365 }
6366 */
6367
6368 if (magn_methy <= 1)
6369 {
glennrpbb4f99d2011-05-22 11:13:17 +00006370 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006371 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
glennrp847370c2011-07-05 17:37:15 +00006372 SetPixelGreen(large_image,GetPixelGreen(image,
6373 pixels),q);
6374 SetPixelBlue(large_image,GetPixelBlue(image,
6375 pixels),q);
6376 SetPixelAlpha(large_image,GetPixelAlpha(image,
6377 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006378 }
glennrp47b9dd52010-11-24 18:12:06 +00006379
cristy3ed852e2009-09-05 21:47:34 +00006380 else if (magn_methy == 2 || magn_methy == 4)
6381 {
6382 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006383 {
glennrp847370c2011-07-05 17:37:15 +00006384 SetPixelRed(large_image,GetPixelRed(image,
6385 pixels),q);
6386 SetPixelGreen(large_image,GetPixelGreen(image,
6387 pixels),q);
6388 SetPixelBlue(large_image,GetPixelBlue(image,
6389 pixels),q);
6390 SetPixelAlpha(large_image,GetPixelAlpha(image,
6391 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006392 }
glennrp47b9dd52010-11-24 18:12:06 +00006393
cristy3ed852e2009-09-05 21:47:34 +00006394 else
6395 {
6396 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006397 SetPixelRed(large_image,((QM) (((ssize_t)
6398 (2*i*(GetPixelRed(image,n)
6399 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006400 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006401 +GetPixelRed(image,pixels)))),q);
6402 SetPixelGreen(large_image,((QM) (((ssize_t)
6403 (2*i*(GetPixelGreen(image,n)
6404 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006405 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006406 +GetPixelGreen(image,pixels)))),q);
6407 SetPixelBlue(large_image,((QM) (((ssize_t)
6408 (2*i*(GetPixelBlue(image,n)
6409 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006410 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006411 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006412
cristy3ed852e2009-09-05 21:47:34 +00006413 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006414 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6415 (2*i*(GetPixelAlpha(image,n)
6416 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006417 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006418 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006419 }
glennrp47b9dd52010-11-24 18:12:06 +00006420
cristy3ed852e2009-09-05 21:47:34 +00006421 if (magn_methy == 4)
6422 {
6423 /* Replicate nearest */
6424 if (i <= ((m+1) << 1))
glennrp847370c2011-07-05 17:37:15 +00006425 SetPixelAlpha(large_image,GetPixelAlpha(image,
6426 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006427 else
glennrp847370c2011-07-05 17:37:15 +00006428 SetPixelAlpha(large_image,GetPixelAlpha(image,
6429 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006430 }
6431 }
glennrp47b9dd52010-11-24 18:12:06 +00006432
cristy3ed852e2009-09-05 21:47:34 +00006433 else /* if (magn_methy == 3 || magn_methy == 5) */
6434 {
6435 /* Replicate nearest */
6436 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006437 {
glennrp847370c2011-07-05 17:37:15 +00006438 SetPixelRed(large_image,GetPixelRed(image,
6439 pixels),q);
6440 SetPixelGreen(large_image,GetPixelGreen(image,
6441 pixels),q);
6442 SetPixelBlue(large_image,GetPixelBlue(image,
6443 pixels),q);
6444 SetPixelAlpha(large_image,GetPixelAlpha(image,
6445 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006446 }
glennrp47b9dd52010-11-24 18:12:06 +00006447
cristy3ed852e2009-09-05 21:47:34 +00006448 else
glennrpbb4f99d2011-05-22 11:13:17 +00006449 {
cristy4c08aed2011-07-01 19:47:50 +00006450 SetPixelRed(large_image,GetPixelRed(image,n),q);
glennrp847370c2011-07-05 17:37:15 +00006451 SetPixelGreen(large_image,GetPixelGreen(image,n),
6452 q);
6453 SetPixelBlue(large_image,GetPixelBlue(image,n),
6454 q);
6455 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6456 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006457 }
glennrp47b9dd52010-11-24 18:12:06 +00006458
cristy3ed852e2009-09-05 21:47:34 +00006459 if (magn_methy == 5)
6460 {
cristy4c08aed2011-07-01 19:47:50 +00006461 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6462 (GetPixelAlpha(image,n)
6463 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006464 +m))/((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006465 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006466 }
6467 }
cristyed231572011-07-14 02:18:59 +00006468 n+=GetPixelChannels(image);
6469 q+=GetPixelChannels(large_image);
6470 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006471 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006472
cristy3ed852e2009-09-05 21:47:34 +00006473 if (SyncAuthenticPixels(large_image,exception) == 0)
6474 break;
glennrp47b9dd52010-11-24 18:12:06 +00006475
cristy3ed852e2009-09-05 21:47:34 +00006476 } /* i */
6477 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006478
cristy4c08aed2011-07-01 19:47:50 +00006479 prev=(Quantum *) RelinquishMagickMemory(prev);
6480 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006481
6482 length=image->columns;
6483
6484 if (logging != MagickFalse)
6485 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6486 " Delete original image");
6487
6488 DeleteImageFromList(&image);
6489
6490 image=large_image;
6491
6492 mng_info->image=image;
6493
6494 /* magnify the columns */
6495 if (logging != MagickFalse)
6496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006497 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006498
cristybb503372010-05-27 20:51:26 +00006499 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006500 {
cristy4c08aed2011-07-01 19:47:50 +00006501 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006502 *pixels;
6503
6504 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristyed231572011-07-14 02:18:59 +00006505 pixels=q+(image->columns-length)*GetPixelChannels(image);
6506 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006507
cristybb503372010-05-27 20:51:26 +00006508 for (x=(ssize_t) (image->columns-length);
6509 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006510 {
cristyed231572011-07-14 02:18:59 +00006511 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006512
cristybb503372010-05-27 20:51:26 +00006513 if (x == (ssize_t) (image->columns-length))
6514 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006515
cristybb503372010-05-27 20:51:26 +00006516 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6517 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006518
cristybb503372010-05-27 20:51:26 +00006519 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6520 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006521
cristybb503372010-05-27 20:51:26 +00006522 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006523 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006524
cristy3ed852e2009-09-05 21:47:34 +00006525 else
cristybb503372010-05-27 20:51:26 +00006526 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006527
cristy3ed852e2009-09-05 21:47:34 +00006528 for (i=0; i < m; i++)
6529 {
6530 if (magn_methx <= 1)
6531 {
6532 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006533 SetPixelRed(image,GetPixelRed(image,pixels),q);
6534 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6535 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6536 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006537 }
glennrp47b9dd52010-11-24 18:12:06 +00006538
cristy3ed852e2009-09-05 21:47:34 +00006539 else if (magn_methx == 2 || magn_methx == 4)
6540 {
6541 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006542 {
cristy4c08aed2011-07-01 19:47:50 +00006543 SetPixelRed(image,GetPixelRed(image,pixels),q);
6544 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6545 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6546 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006547 }
glennrp47b9dd52010-11-24 18:12:06 +00006548
cristyed231572011-07-14 02:18:59 +00006549 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006550 else
6551 {
6552 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006553 SetPixelRed(image,(QM) ((2*i*(
6554 GetPixelRed(image,n)
6555 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006556 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006557 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006558
cristy4c08aed2011-07-01 19:47:50 +00006559 SetPixelGreen(image,(QM) ((2*i*(
6560 GetPixelGreen(image,n)
6561 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006562 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006563 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006564
cristy4c08aed2011-07-01 19:47:50 +00006565 SetPixelBlue(image,(QM) ((2*i*(
6566 GetPixelBlue(image,n)
6567 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006568 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006569 GetPixelBlue(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006570 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006571 SetPixelAlpha(image,(QM) ((2*i*(
6572 GetPixelAlpha(image,n)
6573 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006574 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006575 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006576 }
glennrp47b9dd52010-11-24 18:12:06 +00006577
cristy3ed852e2009-09-05 21:47:34 +00006578 if (magn_methx == 4)
6579 {
6580 /* Replicate nearest */
6581 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006582 {
cristy4c08aed2011-07-01 19:47:50 +00006583 SetPixelAlpha(image,
6584 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006585 }
cristy3ed852e2009-09-05 21:47:34 +00006586 else
glennrpbb4f99d2011-05-22 11:13:17 +00006587 {
cristy4c08aed2011-07-01 19:47:50 +00006588 SetPixelAlpha(image,
6589 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006590 }
cristy3ed852e2009-09-05 21:47:34 +00006591 }
6592 }
glennrp47b9dd52010-11-24 18:12:06 +00006593
cristy3ed852e2009-09-05 21:47:34 +00006594 else /* if (magn_methx == 3 || magn_methx == 5) */
6595 {
6596 /* Replicate nearest */
6597 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006598 {
cristy4c08aed2011-07-01 19:47:50 +00006599 SetPixelRed(image,GetPixelRed(image,pixels),q);
6600 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6601 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6602 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006603 }
glennrp47b9dd52010-11-24 18:12:06 +00006604
cristy3ed852e2009-09-05 21:47:34 +00006605 else
glennrpbb4f99d2011-05-22 11:13:17 +00006606 {
cristy4c08aed2011-07-01 19:47:50 +00006607 SetPixelRed(image,GetPixelRed(image,n),q);
6608 SetPixelGreen(image,GetPixelGreen(image,n),q);
6609 SetPixelBlue(image,GetPixelBlue(image,n),q);
6610 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006611 }
glennrp47b9dd52010-11-24 18:12:06 +00006612
cristy3ed852e2009-09-05 21:47:34 +00006613 if (magn_methx == 5)
6614 {
6615 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006616 SetPixelAlpha(image,
6617 (QM) ((2*i*( GetPixelAlpha(image,n)
6618 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006619 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006620 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006621 }
6622 }
cristyed231572011-07-14 02:18:59 +00006623 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006624 }
cristyed231572011-07-14 02:18:59 +00006625 n+=GetPixelChannels(image);
6626 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006627 }
glennrp47b9dd52010-11-24 18:12:06 +00006628
cristy3ed852e2009-09-05 21:47:34 +00006629 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6630 break;
6631 }
glennrp3faa9a32011-04-23 14:00:25 +00006632#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006633 if (magn_methx != 1 || magn_methy != 1)
6634 {
6635 /*
6636 Rescale pixels to Quantum
6637 */
cristybb503372010-05-27 20:51:26 +00006638 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006639 {
6640 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006641
cristybb503372010-05-27 20:51:26 +00006642 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006643 {
cristy4c08aed2011-07-01 19:47:50 +00006644 SetPixelRed(image,ScaleShortToQuantum(
6645 GetPixelRed(image,q)),q);
6646 SetPixelGreen(image,ScaleShortToQuantum(
6647 GetPixelGreen(image,q)),q);
6648 SetPixelBlue(image,ScaleShortToQuantum(
6649 GetPixelBlue(image,q)),q);
6650 SetPixelAlpha(image,ScaleShortToQuantum(
6651 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006652 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006653 }
glennrp47b9dd52010-11-24 18:12:06 +00006654
cristy3ed852e2009-09-05 21:47:34 +00006655 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6656 break;
6657 }
6658 }
6659#endif
6660 if (logging != MagickFalse)
6661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6662 " Finished MAGN processing");
6663 }
6664 }
6665
6666 /*
6667 Crop_box is with respect to the upper left corner of the MNG.
6668 */
6669 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6670 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6671 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6672 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6673 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6674 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6675 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6676 if ((crop_box.left != (mng_info->image_box.left
6677 +mng_info->x_off[object_id])) ||
6678 (crop_box.right != (mng_info->image_box.right
6679 +mng_info->x_off[object_id])) ||
6680 (crop_box.top != (mng_info->image_box.top
6681 +mng_info->y_off[object_id])) ||
6682 (crop_box.bottom != (mng_info->image_box.bottom
6683 +mng_info->y_off[object_id])))
6684 {
6685 if (logging != MagickFalse)
6686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6687 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006688
cristy3ed852e2009-09-05 21:47:34 +00006689 if ((crop_box.left < crop_box.right) &&
6690 (crop_box.top < crop_box.bottom))
6691 {
6692 Image
6693 *im;
6694
6695 RectangleInfo
6696 crop_info;
6697
6698 /*
6699 Crop_info is with respect to the upper left corner of
6700 the image.
6701 */
6702 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6703 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006704 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6705 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006706 image->page.width=image->columns;
6707 image->page.height=image->rows;
6708 image->page.x=0;
6709 image->page.y=0;
6710 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006711
cristy3ed852e2009-09-05 21:47:34 +00006712 if (im != (Image *) NULL)
6713 {
6714 image->columns=im->columns;
6715 image->rows=im->rows;
6716 im=DestroyImage(im);
6717 image->page.width=image->columns;
6718 image->page.height=image->rows;
6719 image->page.x=crop_box.left;
6720 image->page.y=crop_box.top;
6721 }
6722 }
glennrp47b9dd52010-11-24 18:12:06 +00006723
cristy3ed852e2009-09-05 21:47:34 +00006724 else
6725 {
6726 /*
6727 No pixels in crop area. The MNG spec still requires
6728 a layer, though, so make a single transparent pixel in
6729 the top left corner.
6730 */
6731 image->columns=1;
6732 image->rows=1;
6733 image->colors=2;
cristyea1a8aa2011-10-20 13:24:06 +00006734 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006735 image->page.width=1;
6736 image->page.height=1;
6737 image->page.x=0;
6738 image->page.y=0;
6739 }
6740 }
6741#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6742 image=mng_info->image;
6743#endif
6744 }
6745
glennrp2b013e42010-11-24 16:55:50 +00006746#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6747 /* PNG does not handle depths greater than 16 so reduce it even
glennrp2aec3322012-01-02 20:16:54 +00006748 * if lossy.
glennrp2b013e42010-11-24 16:55:50 +00006749 */
6750 if (image->depth > 16)
6751 image->depth=16;
6752#endif
6753
glennrp3faa9a32011-04-23 14:00:25 +00006754#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp2aec3322012-01-02 20:16:54 +00006755 if (image->depth > 8)
6756 {
6757 /* To do: fill low byte properly */
6758 image->depth=16;
6759 }
6760
cristyc82a27b2011-10-21 01:07:16 +00006761 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00006762 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006763#endif
glennrpd6afd542010-11-19 01:53:05 +00006764
cristy3ed852e2009-09-05 21:47:34 +00006765 if (image_info->number_scenes != 0)
6766 {
6767 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006768 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006769 break;
6770 }
glennrpd6afd542010-11-19 01:53:05 +00006771
cristy3ed852e2009-09-05 21:47:34 +00006772 if (logging != MagickFalse)
6773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6774 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006775
cristy3ed852e2009-09-05 21:47:34 +00006776 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006777
cristy3ed852e2009-09-05 21:47:34 +00006778 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006779
cristy3ed852e2009-09-05 21:47:34 +00006780 if (logging != MagickFalse)
6781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6782 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006783
cristy3ed852e2009-09-05 21:47:34 +00006784#if defined(MNG_INSERT_LAYERS)
6785 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6786 (mng_info->mng_height))
6787 {
6788 /*
6789 Insert a background layer if nothing else was found.
6790 */
6791 if (logging != MagickFalse)
6792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6793 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006794
cristy4c08aed2011-07-01 19:47:50 +00006795 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006796 {
6797 /*
6798 Allocate next image structure.
6799 */
cristy9950d572011-10-01 18:22:35 +00006800 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006801 if (GetNextImageInList(image) == (Image *) NULL)
6802 {
6803 image=DestroyImageList(image);
6804 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006805
cristy3ed852e2009-09-05 21:47:34 +00006806 if (logging != MagickFalse)
6807 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6808 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006809
cristy3ed852e2009-09-05 21:47:34 +00006810 return((Image *) NULL);
6811 }
6812 image=SyncNextImageInList(image);
6813 }
6814 image->columns=mng_info->mng_width;
6815 image->rows=mng_info->mng_height;
6816 image->page.width=mng_info->mng_width;
6817 image->page.height=mng_info->mng_height;
6818 image->page.x=0;
6819 image->page.y=0;
6820 image->background_color=mng_background_color;
6821 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006822
cristy3ed852e2009-09-05 21:47:34 +00006823 if (image_info->ping == MagickFalse)
cristyea1a8aa2011-10-20 13:24:06 +00006824 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006825
cristy3ed852e2009-09-05 21:47:34 +00006826 mng_info->image_found++;
6827 }
6828#endif
6829 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006830
cristy3ed852e2009-09-05 21:47:34 +00006831 if (mng_iterations == 1)
6832 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006833
cristy3ed852e2009-09-05 21:47:34 +00006834 while (GetPreviousImageInList(image) != (Image *) NULL)
6835 {
6836 image_count++;
6837 if (image_count > 10*mng_info->image_found)
6838 {
6839 if (logging != MagickFalse)
6840 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006841
cristyc82a27b2011-10-21 01:07:16 +00006842 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006843 CoderError,"Linked list is corrupted, beginning of list not found",
6844 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006845
cristy3ed852e2009-09-05 21:47:34 +00006846 return((Image *) NULL);
6847 }
glennrp0fe50b42010-11-16 03:52:51 +00006848
cristy3ed852e2009-09-05 21:47:34 +00006849 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006850
cristy3ed852e2009-09-05 21:47:34 +00006851 if (GetNextImageInList(image) == (Image *) NULL)
6852 {
6853 if (logging != MagickFalse)
6854 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006855
cristyc82a27b2011-10-21 01:07:16 +00006856 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006857 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6858 image_info->filename);
6859 }
6860 }
glennrp47b9dd52010-11-24 18:12:06 +00006861
cristy3ed852e2009-09-05 21:47:34 +00006862 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6863 GetNextImageInList(image) ==
6864 (Image *) NULL)
6865 {
6866 if (logging != MagickFalse)
6867 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6868 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006869
cristyc82a27b2011-10-21 01:07:16 +00006870 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006871 CoderError,"image->next for first image is NULL but shouldn't be.",
6872 "`%s'",image_info->filename);
6873 }
glennrp47b9dd52010-11-24 18:12:06 +00006874
cristy3ed852e2009-09-05 21:47:34 +00006875 if (mng_info->image_found == 0)
6876 {
6877 if (logging != MagickFalse)
6878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6879 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006880
cristyc82a27b2011-10-21 01:07:16 +00006881 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006882 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006883
cristy3ed852e2009-09-05 21:47:34 +00006884 if (image != (Image *) NULL)
6885 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006886
cristy3ed852e2009-09-05 21:47:34 +00006887 MngInfoFreeStruct(mng_info,&have_mng_structure);
6888 return((Image *) NULL);
6889 }
6890
6891 if (mng_info->ticks_per_second)
6892 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6893 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006894
cristy3ed852e2009-09-05 21:47:34 +00006895 else
6896 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006897
cristy3ed852e2009-09-05 21:47:34 +00006898 /* Find final nonzero image delay */
6899 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006900
cristy3ed852e2009-09-05 21:47:34 +00006901 while (GetNextImageInList(image) != (Image *) NULL)
6902 {
6903 if (image->delay)
6904 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006905
cristy3ed852e2009-09-05 21:47:34 +00006906 image=GetNextImageInList(image);
6907 }
glennrp0fe50b42010-11-16 03:52:51 +00006908
cristy3ed852e2009-09-05 21:47:34 +00006909 if (final_delay < final_image_delay)
6910 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006911
cristy3ed852e2009-09-05 21:47:34 +00006912 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006913
cristy3ed852e2009-09-05 21:47:34 +00006914 if (logging != MagickFalse)
6915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006916 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6917 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006918
cristy3ed852e2009-09-05 21:47:34 +00006919 if (logging != MagickFalse)
6920 {
6921 int
6922 scene;
6923
6924 scene=0;
6925 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006926
cristy3ed852e2009-09-05 21:47:34 +00006927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6928 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006929
cristy3ed852e2009-09-05 21:47:34 +00006930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006931 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006932
cristy3ed852e2009-09-05 21:47:34 +00006933 while (GetNextImageInList(image) != (Image *) NULL)
6934 {
6935 image=GetNextImageInList(image);
6936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006937 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006938 }
6939 }
6940
6941 image=GetFirstImageInList(image);
6942#ifdef MNG_COALESCE_LAYERS
6943 if (insert_layers)
6944 {
6945 Image
6946 *next_image,
6947 *next;
6948
cristybb503372010-05-27 20:51:26 +00006949 size_t
cristy3ed852e2009-09-05 21:47:34 +00006950 scene;
6951
6952 if (logging != MagickFalse)
6953 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006954
cristy3ed852e2009-09-05 21:47:34 +00006955 scene=image->scene;
cristyc82a27b2011-10-21 01:07:16 +00006956 next_image=CoalesceImages(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006957
cristy3ed852e2009-09-05 21:47:34 +00006958 if (next_image == (Image *) NULL)
6959 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006960
cristy3ed852e2009-09-05 21:47:34 +00006961 image=DestroyImageList(image);
6962 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006963
cristy3ed852e2009-09-05 21:47:34 +00006964 for (next=image; next != (Image *) NULL; next=next_image)
6965 {
6966 next->page.width=mng_info->mng_width;
6967 next->page.height=mng_info->mng_height;
6968 next->page.x=0;
6969 next->page.y=0;
6970 next->scene=scene++;
6971 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006972
cristy3ed852e2009-09-05 21:47:34 +00006973 if (next_image == (Image *) NULL)
6974 break;
glennrp47b9dd52010-11-24 18:12:06 +00006975
cristy3ed852e2009-09-05 21:47:34 +00006976 if (next->delay == 0)
6977 {
6978 scene--;
6979 next_image->previous=GetPreviousImageInList(next);
6980 if (GetPreviousImageInList(next) == (Image *) NULL)
6981 image=next_image;
6982 else
6983 next->previous->next=next_image;
6984 next=DestroyImage(next);
6985 }
6986 }
6987 }
6988#endif
6989
6990 while (GetNextImageInList(image) != (Image *) NULL)
6991 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006992
cristy3ed852e2009-09-05 21:47:34 +00006993 image->dispose=BackgroundDispose;
6994
6995 if (logging != MagickFalse)
6996 {
6997 int
6998 scene;
6999
7000 scene=0;
7001 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007002
cristy3ed852e2009-09-05 21:47:34 +00007003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7004 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00007005
cristy3ed852e2009-09-05 21:47:34 +00007006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007007 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7008 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00007009
cristy3ed852e2009-09-05 21:47:34 +00007010 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00007011 {
7012 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007013
cristyf2faecf2010-05-28 19:19:36 +00007014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007015 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7016 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00007017 }
7018 }
glennrp47b9dd52010-11-24 18:12:06 +00007019
cristy3ed852e2009-09-05 21:47:34 +00007020 image=GetFirstImageInList(image);
7021 MngInfoFreeStruct(mng_info,&have_mng_structure);
7022 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00007023
cristy3ed852e2009-09-05 21:47:34 +00007024 if (logging != MagickFalse)
7025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00007026
cristy3ed852e2009-09-05 21:47:34 +00007027 return(GetFirstImageInList(image));
7028}
glennrp25c1e2b2010-03-25 01:39:56 +00007029#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007030static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7031{
7032 printf("Your PNG library is too old: You have libpng-%s\n",
7033 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00007034
cristy3ed852e2009-09-05 21:47:34 +00007035 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7036 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007037
cristy3ed852e2009-09-05 21:47:34 +00007038 return(Image *) NULL;
7039}
glennrp47b9dd52010-11-24 18:12:06 +00007040
cristy3ed852e2009-09-05 21:47:34 +00007041static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7042{
7043 return(ReadPNGImage(image_info,exception));
7044}
glennrp25c1e2b2010-03-25 01:39:56 +00007045#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007046#endif
7047
7048/*
7049%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7050% %
7051% %
7052% %
7053% R e g i s t e r P N G I m a g e %
7054% %
7055% %
7056% %
7057%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7058%
7059% RegisterPNGImage() adds properties for the PNG image format to
7060% the list of supported formats. The properties include the image format
7061% tag, a method to read and/or write the format, whether the format
7062% supports the saving of more than one frame to the same file or blob,
7063% whether the format supports native in-memory I/O, and a brief
7064% description of the format.
7065%
7066% The format of the RegisterPNGImage method is:
7067%
cristybb503372010-05-27 20:51:26 +00007068% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007069%
7070*/
cristybb503372010-05-27 20:51:26 +00007071ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007072{
7073 char
7074 version[MaxTextExtent];
7075
7076 MagickInfo
7077 *entry;
7078
7079 static const char
7080 *PNGNote=
7081 {
7082 "See http://www.libpng.org/ for details about the PNG format."
7083 },
glennrp47b9dd52010-11-24 18:12:06 +00007084
cristy3ed852e2009-09-05 21:47:34 +00007085 *JNGNote=
7086 {
7087 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7088 "format."
7089 },
glennrp47b9dd52010-11-24 18:12:06 +00007090
cristy3ed852e2009-09-05 21:47:34 +00007091 *MNGNote=
7092 {
7093 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7094 "format."
7095 };
7096
7097 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007098
cristy3ed852e2009-09-05 21:47:34 +00007099#if defined(PNG_LIBPNG_VER_STRING)
7100 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7101 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007102
cristy3ed852e2009-09-05 21:47:34 +00007103 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7104 {
7105 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7106 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7107 MaxTextExtent);
7108 }
7109#endif
glennrp47b9dd52010-11-24 18:12:06 +00007110
cristy3ed852e2009-09-05 21:47:34 +00007111 entry=SetMagickInfo("MNG");
7112 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007113
cristy3ed852e2009-09-05 21:47:34 +00007114#if defined(MAGICKCORE_PNG_DELEGATE)
7115 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7116 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7117#endif
glennrp47b9dd52010-11-24 18:12:06 +00007118
cristy3ed852e2009-09-05 21:47:34 +00007119 entry->magick=(IsImageFormatHandler *) IsMNG;
7120 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007121
cristy3ed852e2009-09-05 21:47:34 +00007122 if (*version != '\0')
7123 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007124
cristy3ed852e2009-09-05 21:47:34 +00007125 entry->module=ConstantString("PNG");
7126 entry->note=ConstantString(MNGNote);
7127 (void) RegisterMagickInfo(entry);
7128
7129 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007130
cristy3ed852e2009-09-05 21:47:34 +00007131#if defined(MAGICKCORE_PNG_DELEGATE)
7132 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7133 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7134#endif
glennrp47b9dd52010-11-24 18:12:06 +00007135
cristy3ed852e2009-09-05 21:47:34 +00007136 entry->magick=(IsImageFormatHandler *) IsPNG;
7137 entry->adjoin=MagickFalse;
7138 entry->description=ConstantString("Portable Network Graphics");
7139 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007140
cristy3ed852e2009-09-05 21:47:34 +00007141 if (*version != '\0')
7142 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007143
cristy3ed852e2009-09-05 21:47:34 +00007144 entry->note=ConstantString(PNGNote);
7145 (void) RegisterMagickInfo(entry);
7146
7147 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007148
cristy3ed852e2009-09-05 21:47:34 +00007149#if defined(MAGICKCORE_PNG_DELEGATE)
7150 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7151 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7152#endif
glennrp47b9dd52010-11-24 18:12:06 +00007153
cristy3ed852e2009-09-05 21:47:34 +00007154 entry->magick=(IsImageFormatHandler *) IsPNG;
7155 entry->adjoin=MagickFalse;
7156 entry->description=ConstantString(
7157 "8-bit indexed with optional binary transparency");
7158 entry->module=ConstantString("PNG");
7159 (void) RegisterMagickInfo(entry);
7160
7161 entry=SetMagickInfo("PNG24");
7162 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007163
cristy3ed852e2009-09-05 21:47:34 +00007164#if defined(ZLIB_VERSION)
7165 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7166 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007167
cristy3ed852e2009-09-05 21:47:34 +00007168 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7169 {
7170 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7171 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7172 }
7173#endif
glennrp47b9dd52010-11-24 18:12:06 +00007174
cristy3ed852e2009-09-05 21:47:34 +00007175 if (*version != '\0')
7176 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007177
cristy3ed852e2009-09-05 21:47:34 +00007178#if defined(MAGICKCORE_PNG_DELEGATE)
7179 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7180 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7181#endif
glennrp47b9dd52010-11-24 18:12:06 +00007182
cristy3ed852e2009-09-05 21:47:34 +00007183 entry->magick=(IsImageFormatHandler *) IsPNG;
7184 entry->adjoin=MagickFalse;
7185 entry->description=ConstantString("opaque 24-bit RGB");
7186 entry->module=ConstantString("PNG");
7187 (void) RegisterMagickInfo(entry);
7188
7189 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007190
cristy3ed852e2009-09-05 21:47:34 +00007191#if defined(MAGICKCORE_PNG_DELEGATE)
7192 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7193 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7194#endif
glennrp47b9dd52010-11-24 18:12:06 +00007195
cristy3ed852e2009-09-05 21:47:34 +00007196 entry->magick=(IsImageFormatHandler *) IsPNG;
7197 entry->adjoin=MagickFalse;
7198 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7199 entry->module=ConstantString("PNG");
7200 (void) RegisterMagickInfo(entry);
7201
7202 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007203
cristy3ed852e2009-09-05 21:47:34 +00007204#if defined(JNG_SUPPORTED)
7205#if defined(MAGICKCORE_PNG_DELEGATE)
7206 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7207 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7208#endif
7209#endif
glennrp47b9dd52010-11-24 18:12:06 +00007210
cristy3ed852e2009-09-05 21:47:34 +00007211 entry->magick=(IsImageFormatHandler *) IsJNG;
7212 entry->adjoin=MagickFalse;
7213 entry->description=ConstantString("JPEG Network Graphics");
7214 entry->module=ConstantString("PNG");
7215 entry->note=ConstantString(JNGNote);
7216 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007217
cristy18b17442009-10-25 18:36:48 +00007218#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007219 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007220#endif
glennrp47b9dd52010-11-24 18:12:06 +00007221
cristy3ed852e2009-09-05 21:47:34 +00007222 return(MagickImageCoderSignature);
7223}
7224
7225/*
7226%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7227% %
7228% %
7229% %
7230% U n r e g i s t e r P N G I m a g e %
7231% %
7232% %
7233% %
7234%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7235%
7236% UnregisterPNGImage() removes format registrations made by the
7237% PNG module from the list of supported formats.
7238%
7239% The format of the UnregisterPNGImage method is:
7240%
7241% UnregisterPNGImage(void)
7242%
7243*/
7244ModuleExport void UnregisterPNGImage(void)
7245{
7246 (void) UnregisterMagickInfo("MNG");
7247 (void) UnregisterMagickInfo("PNG");
7248 (void) UnregisterMagickInfo("PNG8");
7249 (void) UnregisterMagickInfo("PNG24");
7250 (void) UnregisterMagickInfo("PNG32");
7251 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007252
cristy3ed852e2009-09-05 21:47:34 +00007253#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007254 if (ping_semaphore != (SemaphoreInfo *) NULL)
7255 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007256#endif
7257}
7258
7259#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007260#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007261/*
7262%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7263% %
7264% %
7265% %
7266% W r i t e M N G I m a g e %
7267% %
7268% %
7269% %
7270%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7271%
7272% WriteMNGImage() writes an image in the Portable Network Graphics
7273% Group's "Multiple-image Network Graphics" encoded image format.
7274%
7275% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7276%
7277% The format of the WriteMNGImage method is:
7278%
cristy1e178e72011-08-28 19:44:34 +00007279% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7280% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007281%
7282% A description of each parameter follows.
7283%
7284% o image_info: the image info.
7285%
7286% o image: The image.
7287%
cristy1e178e72011-08-28 19:44:34 +00007288% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007289%
7290% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7291% "To do" under ReadPNGImage):
7292%
cristy3ed852e2009-09-05 21:47:34 +00007293% Preserve all unknown and not-yet-handled known chunks found in input
7294% PNG file and copy them into output PNG files according to the PNG
7295% copying rules.
7296%
7297% Write the iCCP chunk at MNG level when (icc profile length > 0)
7298%
7299% Improve selection of color type (use indexed-colour or indexed-colour
7300% with tRNS when 256 or fewer unique RGBA values are present).
7301%
7302% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7303% This will be complicated if we limit ourselves to generating MNG-LC
7304% files. For now we ignore disposal method 3 and simply overlay the next
7305% image on it.
7306%
7307% Check for identical PLTE's or PLTE/tRNS combinations and use a
7308% global MNG PLTE or PLTE/tRNS combination when appropriate.
7309% [mostly done 15 June 1999 but still need to take care of tRNS]
7310%
7311% Check for identical sRGB and replace with a global sRGB (and remove
7312% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7313% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7314% local gAMA/cHRM with local sRGB if appropriate).
7315%
7316% Check for identical sBIT chunks and write global ones.
7317%
7318% Provide option to skip writing the signature tEXt chunks.
7319%
7320% Use signatures to detect identical objects and reuse the first
7321% instance of such objects instead of writing duplicate objects.
7322%
7323% Use a smaller-than-32k value of compression window size when
7324% appropriate.
7325%
7326% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7327% ancillary text chunks and save profiles.
7328%
7329% Provide an option to force LC files (to ensure exact framing rate)
7330% instead of VLC.
7331%
7332% Provide an option to force VLC files instead of LC, even when offsets
7333% are present. This will involve expanding the embedded images with a
7334% transparent region at the top and/or left.
7335*/
7336
cristy3ed852e2009-09-05 21:47:34 +00007337static void
glennrpcf002022011-01-30 02:38:15 +00007338Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007339 png_info *ping_info, unsigned char *profile_type, unsigned char
7340 *profile_description, unsigned char *profile_data, png_uint_32 length)
7341{
cristy3ed852e2009-09-05 21:47:34 +00007342 png_textp
7343 text;
7344
cristybb503372010-05-27 20:51:26 +00007345 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007346 i;
7347
7348 unsigned char
7349 *sp;
7350
7351 png_charp
7352 dp;
7353
7354 png_uint_32
7355 allocated_length,
7356 description_length;
7357
7358 unsigned char
7359 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007360
cristy3ed852e2009-09-05 21:47:34 +00007361 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7362 return;
7363
7364 if (image_info->verbose)
7365 {
glennrp0fe50b42010-11-16 03:52:51 +00007366 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7367 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007368 }
glennrp0fe50b42010-11-16 03:52:51 +00007369
cristy3ed852e2009-09-05 21:47:34 +00007370 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7371 description_length=(png_uint_32) strlen((const char *) profile_description);
7372 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7373 + description_length);
7374 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7375 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7376 text[0].key[0]='\0';
7377 (void) ConcatenateMagickString(text[0].key,
7378 "Raw profile type ",MaxTextExtent);
7379 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7380 sp=profile_data;
7381 dp=text[0].text;
7382 *dp++='\n';
7383 (void) CopyMagickString(dp,(const char *) profile_description,
7384 allocated_length);
7385 dp+=description_length;
7386 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007387 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007388 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007389 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007390
cristybb503372010-05-27 20:51:26 +00007391 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007392 {
7393 if (i%36 == 0)
7394 *dp++='\n';
7395 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7396 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7397 }
glennrp47b9dd52010-11-24 18:12:06 +00007398
cristy3ed852e2009-09-05 21:47:34 +00007399 *dp++='\n';
7400 *dp='\0';
7401 text[0].text_length=(png_size_t) (dp-text[0].text);
7402 text[0].compression=image_info->compression == NoCompression ||
7403 (image_info->compression == UndefinedCompression &&
7404 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007405
cristy3ed852e2009-09-05 21:47:34 +00007406 if (text[0].text_length <= allocated_length)
7407 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007408
cristy3ed852e2009-09-05 21:47:34 +00007409 png_free(ping,text[0].text);
7410 png_free(ping,text[0].key);
7411 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007412}
7413
glennrpcf002022011-01-30 02:38:15 +00007414static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007415 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007416{
7417 char
7418 *name;
7419
7420 const StringInfo
7421 *profile;
7422
7423 unsigned char
7424 *data;
7425
7426 png_uint_32 length;
7427
7428 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007429
7430 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7431 {
cristy3ed852e2009-09-05 21:47:34 +00007432 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007433
cristy3ed852e2009-09-05 21:47:34 +00007434 if (profile != (const StringInfo *) NULL)
7435 {
7436 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007437 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007438
glennrp47b9dd52010-11-24 18:12:06 +00007439 if (LocaleNCompare(name,string,11) == 0)
7440 {
7441 if (logging != MagickFalse)
7442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7443 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007444
glennrpcf002022011-01-30 02:38:15 +00007445 ping_profile=CloneStringInfo(profile);
7446 data=GetStringInfoDatum(ping_profile),
7447 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007448 data[4]=data[3];
7449 data[3]=data[2];
7450 data[2]=data[1];
7451 data[1]=data[0];
7452 (void) WriteBlobMSBULong(image,length-5); /* data length */
7453 (void) WriteBlob(image,length-1,data+1);
7454 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007455 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007456 }
cristy3ed852e2009-09-05 21:47:34 +00007457 }
glennrp47b9dd52010-11-24 18:12:06 +00007458
cristy3ed852e2009-09-05 21:47:34 +00007459 name=GetNextImageProfile(image);
7460 }
glennrp47b9dd52010-11-24 18:12:06 +00007461
cristy3ed852e2009-09-05 21:47:34 +00007462 return(MagickTrue);
7463}
7464
glennrpb9cfe272010-12-21 15:08:06 +00007465
cristy3ed852e2009-09-05 21:47:34 +00007466/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007467static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +00007468 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007469{
7470 Image
7471 *image;
7472
7473 ImageInfo
7474 *image_info;
7475
cristy3ed852e2009-09-05 21:47:34 +00007476 char
7477 s[2];
7478
7479 const char
7480 *name,
7481 *property,
7482 *value;
7483
7484 const StringInfo
7485 *profile;
7486
cristy3ed852e2009-09-05 21:47:34 +00007487 int
cristy3ed852e2009-09-05 21:47:34 +00007488 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007489 pass;
7490
glennrpe9c26dc2010-05-30 01:56:35 +00007491 png_byte
7492 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007493
glennrp39992b42010-11-14 00:03:43 +00007494 png_color
7495 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007496
glennrp5af765f2010-03-30 11:12:18 +00007497 png_color_16
7498 ping_background,
7499 ping_trans_color;
7500
cristy3ed852e2009-09-05 21:47:34 +00007501 png_info
7502 *ping_info;
7503
7504 png_struct
7505 *ping;
7506
glennrp5af765f2010-03-30 11:12:18 +00007507 png_uint_32
7508 ping_height,
7509 ping_width;
7510
cristybb503372010-05-27 20:51:26 +00007511 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007512 y;
7513
7514 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007515 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007516 logging,
glennrp58e01762011-01-07 15:28:54 +00007517 matte,
7518
glennrpda8f3a72011-02-27 23:54:12 +00007519 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007520 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007521 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007522 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007523 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007524 ping_have_bKGD,
7525 ping_have_pHYs,
7526 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007527
7528 ping_exclude_bKGD,
7529 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007530 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007531 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007532 ping_exclude_gAMA,
7533 ping_exclude_iCCP,
7534 /* ping_exclude_iTXt, */
7535 ping_exclude_oFFs,
7536 ping_exclude_pHYs,
7537 ping_exclude_sRGB,
7538 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007539 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007540 ping_exclude_vpAg,
7541 ping_exclude_zCCP, /* hex-encoded iCCP */
7542 ping_exclude_zTXt,
7543
glennrp8d3d6e52011-04-19 04:39:51 +00007544 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007545 ping_need_colortype_warning,
7546
glennrp82b3c532011-03-22 19:20:54 +00007547 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007548 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007549 tried_333,
7550 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007551
7552 QuantumInfo
7553 *quantum_info;
7554
cristyc82a27b2011-10-21 01:07:16 +00007555 PNGErrorInfo
7556 error_info;
7557
cristybb503372010-05-27 20:51:26 +00007558 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007559 i,
7560 x;
7561
7562 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007563 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007564
glennrp5af765f2010-03-30 11:12:18 +00007565 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007566 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007567 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007568 ping_color_type,
7569 ping_interlace_method,
7570 ping_compression_method,
7571 ping_filter_method,
7572 ping_num_trans;
7573
cristybb503372010-05-27 20:51:26 +00007574 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007575 image_depth,
7576 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007577
cristybb503372010-05-27 20:51:26 +00007578 size_t
cristy3ed852e2009-09-05 21:47:34 +00007579 quality,
7580 rowbytes,
7581 save_image_depth;
7582
glennrpdfd70802010-11-14 01:23:35 +00007583 int
glennrpfd05d622011-02-25 04:10:33 +00007584 j,
glennrpf09bded2011-01-08 01:15:59 +00007585 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007586 number_opaque,
7587 number_semitransparent,
7588 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007589 ping_pHYs_unit_type;
7590
7591 png_uint_32
7592 ping_pHYs_x_resolution,
7593 ping_pHYs_y_resolution;
7594
cristy3ed852e2009-09-05 21:47:34 +00007595 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007596 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007597
cristyc82a27b2011-10-21 01:07:16 +00007598 image = CloneImage(IMimage,0,0,MagickFalse,exception);
glennrpb9cfe272010-12-21 15:08:06 +00007599 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007600 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007601 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007602
cristy3ed852e2009-09-05 21:47:34 +00007603#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007604 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007605#endif
7606
glennrp5af765f2010-03-30 11:12:18 +00007607 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007608 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007609 ping_color_type=0,
7610 ping_interlace_method=0,
7611 ping_compression_method=0,
7612 ping_filter_method=0,
7613 ping_num_trans = 0;
7614
7615 ping_background.red = 0;
7616 ping_background.green = 0;
7617 ping_background.blue = 0;
7618 ping_background.gray = 0;
7619 ping_background.index = 0;
7620
7621 ping_trans_color.red=0;
7622 ping_trans_color.green=0;
7623 ping_trans_color.blue=0;
7624 ping_trans_color.gray=0;
7625
glennrpdfd70802010-11-14 01:23:35 +00007626 ping_pHYs_unit_type = 0;
7627 ping_pHYs_x_resolution = 0;
7628 ping_pHYs_y_resolution = 0;
7629
glennrpda8f3a72011-02-27 23:54:12 +00007630 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007631 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007632 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007633 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007634 ping_have_bKGD=MagickFalse;
7635 ping_have_pHYs=MagickFalse;
7636 ping_have_tRNS=MagickFalse;
7637
glennrp0e8ea192010-12-24 18:00:33 +00007638 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7639 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007640 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007641 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007642 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007643 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7644 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7645 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7646 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7647 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7648 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007649 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007650 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7651 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7652 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7653
glennrp8d3d6e52011-04-19 04:39:51 +00007654 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007655 ping_need_colortype_warning = MagickFalse;
7656
cristy0d57eec2011-09-04 22:13:56 +00007657 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7658 * i.e., eliminate the ICC profile and set image->rendering_intent.
7659 * Note that this will not involve any changes to the actual pixels
7660 * but merely passes information to applications that read the resulting
7661 * PNG image.
7662 */
7663 if (ping_exclude_sRGB == MagickFalse)
7664 {
7665 char
7666 *name;
7667
7668 const StringInfo
7669 *profile;
7670
7671 ResetImageProfileIterator(image);
7672 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7673 {
7674 profile=GetImageProfile(image,name);
7675
7676 if (profile != (StringInfo *) NULL)
7677 {
7678 if ((LocaleCompare(name,"ICC") == 0) ||
glennrp29a106e2011-09-06 17:11:42 +00007679 (LocaleCompare(name,"ICM") == 0))
7680 {
glennrpee7b4c02011-10-04 01:21:09 +00007681 int
7682 icheck;
7683
7684 /* 0: not a known sRGB profile
7685 * 1: HP-Microsoft sRGB v2
7686 * 2: ICC sRGB v4 perceptual
7687 * 3: ICC sRGB v2 perceptual no black-compensation
7688 */
7689 png_uint_32
7690 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7691 check_len[4] = {0, 3144, 60960, 3052};
7692
7693 png_uint_32
7694 length,
7695 profile_crc;
7696
cristy0d57eec2011-09-04 22:13:56 +00007697 unsigned char
7698 *data;
7699
glennrp29a106e2011-09-06 17:11:42 +00007700 length=(png_uint_32) GetStringInfoLength(profile);
7701
glennrpee7b4c02011-10-04 01:21:09 +00007702 for (icheck=3; icheck > 0; icheck--)
cristy0d57eec2011-09-04 22:13:56 +00007703 {
glennrpee7b4c02011-10-04 01:21:09 +00007704 if (length == check_len[icheck])
glennrp29a106e2011-09-06 17:11:42 +00007705 {
glennrpee7b4c02011-10-04 01:21:09 +00007706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7707 " Got a %lu-byte ICC profile (potentially sRGB)",
7708 (unsigned long) length);
glennrp29a106e2011-09-06 17:11:42 +00007709
glennrpee7b4c02011-10-04 01:21:09 +00007710 data=GetStringInfoDatum(profile);
7711 profile_crc=crc32(0,data,length);
glennrp29a106e2011-09-06 17:11:42 +00007712
glennrpee7b4c02011-10-04 01:21:09 +00007713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe54cd4b2011-10-04 01:31:35 +00007714 " with crc=%8x",(unsigned int) profile_crc);
glennrpee7b4c02011-10-04 01:21:09 +00007715
7716 if (profile_crc == check_crc[icheck])
7717 {
7718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7719 " It is sRGB.");
7720 if (image->rendering_intent==UndefinedIntent)
7721 image->rendering_intent=PerceptualIntent;
7722 break;
7723 }
glennrp29a106e2011-09-06 17:11:42 +00007724 }
glennrp29a106e2011-09-06 17:11:42 +00007725 }
glennrpee7b4c02011-10-04 01:21:09 +00007726 if (icheck == 0)
glennrp29a106e2011-09-06 17:11:42 +00007727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpee7b4c02011-10-04 01:21:09 +00007728 " Got a %lu-byte ICC profile",
glennrp29a106e2011-09-06 17:11:42 +00007729 (unsigned long) length);
7730 }
cristy0d57eec2011-09-04 22:13:56 +00007731 }
7732 name=GetNextImageProfile(image);
7733 }
7734 }
7735
glennrp8bb3a022010-12-13 20:40:04 +00007736 number_opaque = 0;
7737 number_semitransparent = 0;
7738 number_transparent = 0;
7739
glennrpfd05d622011-02-25 04:10:33 +00007740 if (logging != MagickFalse)
7741 {
7742 if (image->storage_class == UndefinedClass)
7743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7744 " storage_class=UndefinedClass");
7745 if (image->storage_class == DirectClass)
7746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7747 " storage_class=DirectClass");
7748 if (image->storage_class == PseudoClass)
7749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7750 " storage_class=PseudoClass");
7751 }
glennrp28af3712011-04-06 18:07:30 +00007752
glennrp7e65e932011-08-19 02:31:16 +00007753 if (image->storage_class == PseudoClass &&
7754 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7755 (mng_info->write_png_colortype != 0 &&
7756 mng_info->write_png_colortype != 4)))
7757 {
cristyea1a8aa2011-10-20 13:24:06 +00007758 (void) SyncImage(image,exception);
glennrp7e65e932011-08-19 02:31:16 +00007759 image->storage_class = DirectClass;
7760 }
7761
glennrpc6c391a2011-04-27 02:23:56 +00007762 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007763 {
glennrpc6c391a2011-04-27 02:23:56 +00007764 if (image->storage_class != PseudoClass && image->colormap != NULL)
7765 {
7766 /* Free the bogus colormap; it can cause trouble later */
7767 if (logging != MagickFalse)
7768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7769 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00007770 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00007771 image->colormap=NULL;
7772 }
glennrp28af3712011-04-06 18:07:30 +00007773 }
glennrpbb4f99d2011-05-22 11:13:17 +00007774
cristy510d06a2011-07-06 23:43:54 +00007775 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristye941a752011-10-15 01:52:48 +00007776 (void) TransformImageColorspace(image,RGBColorspace,exception);
glennrp0fe50b42010-11-16 03:52:51 +00007777
glennrp3241bd02010-12-12 04:36:28 +00007778 /*
7779 Sometimes we get PseudoClass images whose RGB values don't match
7780 the colors in the colormap. This code syncs the RGB values.
7781 */
7782 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
cristyea1a8aa2011-10-20 13:24:06 +00007783 (void) SyncImage(image,exception);
glennrp3241bd02010-12-12 04:36:28 +00007784
glennrpa6a06632011-01-19 15:15:34 +00007785#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7786 if (image->depth > 8)
7787 {
7788 if (logging != MagickFalse)
7789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7790 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7791
7792 image->depth=8;
7793 }
7794#endif
7795
glennrp8e58efd2011-05-20 12:16:29 +00007796 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007797 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7798 {
cristy4c08aed2011-07-01 19:47:50 +00007799 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007800 *r;
7801
glennrp8e58efd2011-05-20 12:16:29 +00007802 if (image->depth > 8)
7803 {
7804#if MAGICKCORE_QUANTUM_DEPTH > 16
7805 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007806 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007807
7808 for (y=0; y < (ssize_t) image->rows; y++)
7809 {
cristy8a20fa02011-12-27 15:54:31 +00007810 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007811
cristy4c08aed2011-07-01 19:47:50 +00007812 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007813 break;
7814
7815 for (x=0; x < (ssize_t) image->columns; x++)
7816 {
glennrp54cf7972011-08-06 14:28:09 +00007817 LBR16PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007818 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007819 }
glennrpbb4f99d2011-05-22 11:13:17 +00007820
glennrp8e58efd2011-05-20 12:16:29 +00007821 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7822 break;
7823 }
7824
7825 if (image->storage_class == PseudoClass && image->colormap != NULL)
7826 {
cristy3e08f112011-05-24 13:19:30 +00007827 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007828 {
glennrp91d99252011-06-25 14:30:13 +00007829 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007830 }
7831 }
7832#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7833 }
7834
7835 else if (image->depth > 4)
7836 {
7837#if MAGICKCORE_QUANTUM_DEPTH > 8
7838 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007839 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007840
7841 for (y=0; y < (ssize_t) image->rows; y++)
7842 {
cristyc82a27b2011-10-21 01:07:16 +00007843 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007844
cristy4c08aed2011-07-01 19:47:50 +00007845 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007846 break;
7847
7848 for (x=0; x < (ssize_t) image->columns; x++)
7849 {
glennrp54cf7972011-08-06 14:28:09 +00007850 LBR08PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007851 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007852 }
glennrpbb4f99d2011-05-22 11:13:17 +00007853
glennrp8e58efd2011-05-20 12:16:29 +00007854 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7855 break;
7856 }
7857
7858 if (image->storage_class == PseudoClass && image->colormap != NULL)
7859 {
cristy3e08f112011-05-24 13:19:30 +00007860 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007861 {
glennrp91d99252011-06-25 14:30:13 +00007862 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007863 }
7864 }
7865#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7866 }
7867 else
7868 if (image->depth > 2)
7869 {
7870 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007871 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007872
7873 for (y=0; y < (ssize_t) image->rows; y++)
7874 {
cristy8a20fa02011-12-27 15:54:31 +00007875 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007876
cristy4c08aed2011-07-01 19:47:50 +00007877 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007878 break;
7879
7880 for (x=0; x < (ssize_t) image->columns; x++)
7881 {
glennrp54cf7972011-08-06 14:28:09 +00007882 LBR04PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007883 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007884 }
glennrpbb4f99d2011-05-22 11:13:17 +00007885
glennrp8e58efd2011-05-20 12:16:29 +00007886 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7887 break;
7888 }
7889
7890 if (image->storage_class == PseudoClass && image->colormap != NULL)
7891 {
cristy3e08f112011-05-24 13:19:30 +00007892 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007893 {
glennrp91d99252011-06-25 14:30:13 +00007894 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007895 }
7896 }
7897 }
7898
7899 else if (image->depth > 1)
7900 {
7901 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007902 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007903
7904 for (y=0; y < (ssize_t) image->rows; y++)
7905 {
cristy8a20fa02011-12-27 15:54:31 +00007906 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007907
cristy4c08aed2011-07-01 19:47:50 +00007908 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007909 break;
7910
7911 for (x=0; x < (ssize_t) image->columns; x++)
7912 {
glennrp54cf7972011-08-06 14:28:09 +00007913 LBR02PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007914 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007915 }
glennrpbb4f99d2011-05-22 11:13:17 +00007916
glennrp8e58efd2011-05-20 12:16:29 +00007917 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7918 break;
7919 }
7920
7921 if (image->storage_class == PseudoClass && image->colormap != NULL)
7922 {
cristy3e08f112011-05-24 13:19:30 +00007923 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007924 {
glennrp91d99252011-06-25 14:30:13 +00007925 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007926 }
7927 }
7928 }
7929 else
7930 {
7931 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007932 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007933
7934 for (y=0; y < (ssize_t) image->rows; y++)
7935 {
cristy8a20fa02011-12-27 15:54:31 +00007936 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007937
cristy4c08aed2011-07-01 19:47:50 +00007938 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007939 break;
7940
7941 for (x=0; x < (ssize_t) image->columns; x++)
7942 {
glennrp54cf7972011-08-06 14:28:09 +00007943 LBR01PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007944 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007945 }
glennrpbb4f99d2011-05-22 11:13:17 +00007946
glennrp8e58efd2011-05-20 12:16:29 +00007947 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7948 break;
7949 }
7950
7951 if (image->storage_class == PseudoClass && image->colormap != NULL)
7952 {
cristy3e08f112011-05-24 13:19:30 +00007953 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007954 {
glennrp91d99252011-06-25 14:30:13 +00007955 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007956 }
7957 }
7958 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007959 }
7960
glennrp67b9c1a2011-04-22 18:47:36 +00007961 /* To do: set to next higher multiple of 8 */
7962 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007963 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007964
glennrp2b013e42010-11-24 16:55:50 +00007965#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7966 /* PNG does not handle depths greater than 16 so reduce it even
7967 * if lossy
7968 */
glennrp8e58efd2011-05-20 12:16:29 +00007969 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007970 image->depth=16;
7971#endif
7972
glennrp3faa9a32011-04-23 14:00:25 +00007973#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp2aec3322012-01-02 20:16:54 +00007974 if (image->depth > 8)
7975 {
7976 /* To do: fill low byte properly */
7977 image->depth=16;
7978 }
7979
glennrpc722dd82011-02-24 05:13:21 +00007980 if (image->depth == 16 && mng_info->write_png_depth != 16)
cristyc82a27b2011-10-21 01:07:16 +00007981 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007982 image->depth = 8;
7983#endif
7984
glennrpc8c2f062011-02-25 19:00:33 +00007985 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007986 * we reduce the transparency to binary and run again, then if there
7987 * 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 +00007988 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7989 * palette. Then (To do) we take care of a final reduction that is only
7990 * needed if there are still 256 colors present and one of them has both
7991 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007992 */
glennrp82b3c532011-03-22 19:20:54 +00007993
glennrp8ca51ad2011-05-12 21:22:32 +00007994 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007995 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007996 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007997
glennrp8ca51ad2011-05-12 21:22:32 +00007998 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007999 {
8000 /* BUILD_PALETTE
8001 *
8002 * Sometimes we get DirectClass images that have 256 colors or fewer.
8003 * This code will build a colormap.
8004 *
8005 * Also, sometimes we get PseudoClass images with an out-of-date
8006 * colormap. This code will replace the colormap with a new one.
8007 * Sometimes we get PseudoClass images that have more than 256 colors.
8008 * This code will delete the colormap and change the image to
8009 * DirectClass.
8010 *
cristy4c08aed2011-07-01 19:47:50 +00008011 * If image->matte is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00008012 * even though it sometimes contains left-over non-opaque values.
8013 *
8014 * Also we gather some information (number of opaque, transparent,
8015 * and semitransparent pixels, and whether the image has any non-gray
8016 * pixels or only black-and-white pixels) that we might need later.
8017 *
8018 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8019 * we need to check for bogus non-opaque values, at least.
8020 */
glennrp3c218112010-11-27 15:31:26 +00008021
glennrpd71e86a2011-02-24 01:28:37 +00008022 int
8023 n;
glennrp3c218112010-11-27 15:31:26 +00008024
cristy101ab702011-10-13 13:06:32 +00008025 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008026 opaque[260],
8027 semitransparent[260],
8028 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00008029
cristy4c08aed2011-07-01 19:47:50 +00008030 register const Quantum
8031 *s;
glennrp8bb3a022010-12-13 20:40:04 +00008032
cristy4c08aed2011-07-01 19:47:50 +00008033 register Quantum
8034 *q,
glennrpfd05d622011-02-25 04:10:33 +00008035 *r;
8036
glennrpd71e86a2011-02-24 01:28:37 +00008037 if (logging != MagickFalse)
8038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8039 " Enter BUILD_PALETTE:");
8040
8041 if (logging != MagickFalse)
8042 {
glennrp03812ae2010-12-24 01:31:34 +00008043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008044 " image->columns=%.20g",(double) image->columns);
8045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8046 " image->rows=%.20g",(double) image->rows);
8047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8048 " image->matte=%.20g",(double) image->matte);
8049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8050 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008051
glennrpfd05d622011-02-25 04:10:33 +00008052 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008053 {
8054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008055 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008057 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008058
glennrpd71e86a2011-02-24 01:28:37 +00008059 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008060 {
glennrpd71e86a2011-02-24 01:28:37 +00008061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8062 " %d (%d,%d,%d,%d)",
8063 (int) i,
8064 (int) image->colormap[i].red,
8065 (int) image->colormap[i].green,
8066 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008067 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008068 }
glennrp2cc891a2010-12-24 13:44:32 +00008069
glennrpd71e86a2011-02-24 01:28:37 +00008070 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8071 {
8072 if (i > 255)
8073 {
8074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8075 " %d (%d,%d,%d,%d)",
8076 (int) i,
8077 (int) image->colormap[i].red,
8078 (int) image->colormap[i].green,
8079 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008080 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008081 }
8082 }
glennrp03812ae2010-12-24 01:31:34 +00008083 }
glennrp7ddcc222010-12-11 05:01:05 +00008084
glennrpd71e86a2011-02-24 01:28:37 +00008085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8086 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008087
glennrpd71e86a2011-02-24 01:28:37 +00008088 if (image->colors == 0)
cristyc458f912011-12-27 20:26:40 +00008089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8090 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008091
glennrp8d3d6e52011-04-19 04:39:51 +00008092 if (ping_preserve_colormap == MagickFalse)
8093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8094 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008095 }
8096
glennrpd71e86a2011-02-24 01:28:37 +00008097 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008098 number_opaque = 0;
8099 number_semitransparent = 0;
8100 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008101
8102 for (y=0; y < (ssize_t) image->rows; y++)
8103 {
8104 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8105
cristyacd2ed22011-08-30 01:44:23 +00008106 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008107 break;
8108
8109 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008110 {
glennrp4737d522011-04-29 03:33:42 +00008111 if (image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008112 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008113 {
8114 if (number_opaque < 259)
8115 {
8116 if (number_opaque == 0)
8117 {
cristy101ab702011-10-13 13:06:32 +00008118 GetPixelInfoPixel(image, q, opaque);
cristy4c08aed2011-07-01 19:47:50 +00008119 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008120 number_opaque=1;
8121 }
glennrp2cc891a2010-12-24 13:44:32 +00008122
glennrpd71e86a2011-02-24 01:28:37 +00008123 for (i=0; i< (ssize_t) number_opaque; i++)
8124 {
cristy4c08aed2011-07-01 19:47:50 +00008125 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008126 break;
8127 }
glennrp7ddcc222010-12-11 05:01:05 +00008128
cristyc458f912011-12-27 20:26:40 +00008129 if (i == (ssize_t) number_opaque && number_opaque < 259)
glennrpd71e86a2011-02-24 01:28:37 +00008130 {
8131 number_opaque++;
cristy101ab702011-10-13 13:06:32 +00008132 GetPixelInfoPixel(image, q, opaque+i);
cristy4c08aed2011-07-01 19:47:50 +00008133 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008134 }
8135 }
8136 }
cristy4c08aed2011-07-01 19:47:50 +00008137 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008138 {
8139 if (number_transparent < 259)
8140 {
8141 if (number_transparent == 0)
8142 {
cristy101ab702011-10-13 13:06:32 +00008143 GetPixelInfoPixel(image, q, transparent);
cristy4c08aed2011-07-01 19:47:50 +00008144 ping_trans_color.red=(unsigned short)
8145 GetPixelRed(image,q);
8146 ping_trans_color.green=(unsigned short)
8147 GetPixelGreen(image,q);
8148 ping_trans_color.blue=(unsigned short)
8149 GetPixelBlue(image,q);
8150 ping_trans_color.gray=(unsigned short)
8151 GetPixelRed(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008152 number_transparent = 1;
8153 }
8154
8155 for (i=0; i< (ssize_t) number_transparent; i++)
8156 {
cristy4c08aed2011-07-01 19:47:50 +00008157 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008158 break;
8159 }
8160
8161 if (i == (ssize_t) number_transparent &&
8162 number_transparent < 259)
8163 {
8164 number_transparent++;
cristy101ab702011-10-13 13:06:32 +00008165 GetPixelInfoPixel(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008166 }
8167 }
8168 }
8169 else
8170 {
8171 if (number_semitransparent < 259)
8172 {
8173 if (number_semitransparent == 0)
8174 {
cristy101ab702011-10-13 13:06:32 +00008175 GetPixelInfoPixel(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008176 number_semitransparent = 1;
8177 }
8178
8179 for (i=0; i< (ssize_t) number_semitransparent; i++)
8180 {
cristy4c08aed2011-07-01 19:47:50 +00008181 if (IsPixelEquivalent(image,q, semitransparent+i)
8182 && GetPixelAlpha(image,q) ==
8183 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008184 break;
8185 }
8186
8187 if (i == (ssize_t) number_semitransparent &&
8188 number_semitransparent < 259)
8189 {
8190 number_semitransparent++;
cristy101ab702011-10-13 13:06:32 +00008191 GetPixelInfoPixel(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008192 }
8193 }
8194 }
cristyed231572011-07-14 02:18:59 +00008195 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008196 }
8197 }
8198
cristy4054bfb2011-08-29 23:41:39 +00008199 if (mng_info->write_png8 == MagickFalse &&
8200 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008201 {
8202 /* Add the background color to the palette, if it
8203 * isn't already there.
8204 */
glennrpc6c391a2011-04-27 02:23:56 +00008205 if (logging != MagickFalse)
8206 {
8207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8208 " Check colormap for background (%d,%d,%d)",
8209 (int) image->background_color.red,
8210 (int) image->background_color.green,
8211 (int) image->background_color.blue);
8212 }
glennrpd71e86a2011-02-24 01:28:37 +00008213 for (i=0; i<number_opaque; i++)
8214 {
glennrpca7ad3a2011-04-26 04:44:54 +00008215 if (opaque[i].red == image->background_color.red &&
8216 opaque[i].green == image->background_color.green &&
8217 opaque[i].blue == image->background_color.blue)
8218 break;
glennrpd71e86a2011-02-24 01:28:37 +00008219 }
glennrpd71e86a2011-02-24 01:28:37 +00008220 if (number_opaque < 259 && i == number_opaque)
8221 {
glennrp8e045c82011-04-27 16:40:27 +00008222 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008223 ping_background.index = i;
8224 if (logging != MagickFalse)
8225 {
8226 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8227 " background_color index is %d",(int) i);
8228 }
8229
glennrpd71e86a2011-02-24 01:28:37 +00008230 }
glennrpa080bc32011-03-11 18:03:44 +00008231 else if (logging != MagickFalse)
8232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8233 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008234 }
8235
8236 image_colors=number_opaque+number_transparent+number_semitransparent;
8237
glennrpa080bc32011-03-11 18:03:44 +00008238 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8239 {
8240 /* No room for the background color; remove it. */
8241 number_opaque--;
8242 image_colors--;
8243 }
8244
glennrpd71e86a2011-02-24 01:28:37 +00008245 if (logging != MagickFalse)
8246 {
8247 if (image_colors > 256)
8248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8249 " image has more than 256 colors");
8250
8251 else
8252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8253 " image has %d colors",image_colors);
8254 }
8255
glennrp8d3d6e52011-04-19 04:39:51 +00008256 if (ping_preserve_colormap != MagickFalse)
8257 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008258
glennrpfd05d622011-02-25 04:10:33 +00008259 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008260 {
8261 ping_have_color=MagickFalse;
8262 ping_have_non_bw=MagickFalse;
8263
8264 if(image_colors > 256)
8265 {
8266 for (y=0; y < (ssize_t) image->rows; y++)
8267 {
8268 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8269
cristyacd2ed22011-08-30 01:44:23 +00008270 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008271 break;
8272
glennrpe5e6b802011-07-20 14:44:40 +00008273 s=q;
8274 for (x=0; x < (ssize_t) image->columns; x++)
8275 {
8276 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8277 GetPixelRed(image,s) != GetPixelBlue(image,s))
8278 {
8279 ping_have_color=MagickTrue;
8280 ping_have_non_bw=MagickTrue;
8281 break;
8282 }
8283 s+=GetPixelChannels(image);
8284 }
8285
8286 if (ping_have_color != MagickFalse)
8287 break;
8288
glennrpd71e86a2011-02-24 01:28:37 +00008289 /* Worst case is black-and-white; we are looking at every
8290 * pixel twice.
8291 */
8292
glennrpd71e86a2011-02-24 01:28:37 +00008293 if (ping_have_non_bw == MagickFalse)
8294 {
8295 s=q;
8296 for (x=0; x < (ssize_t) image->columns; x++)
8297 {
cristy4c08aed2011-07-01 19:47:50 +00008298 if (GetPixelRed(image,s) != 0 &&
8299 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008300 {
8301 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008302 break;
glennrpd71e86a2011-02-24 01:28:37 +00008303 }
cristyed231572011-07-14 02:18:59 +00008304 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008305 }
glennrpe5e6b802011-07-20 14:44:40 +00008306 }
glennrpd71e86a2011-02-24 01:28:37 +00008307 }
glennrpbb4f99d2011-05-22 11:13:17 +00008308 }
8309 }
glennrpd71e86a2011-02-24 01:28:37 +00008310
8311 if (image_colors < 257)
8312 {
cristy101ab702011-10-13 13:06:32 +00008313 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008314 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008315
glennrpd71e86a2011-02-24 01:28:37 +00008316 /*
8317 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008318 */
8319
glennrpd71e86a2011-02-24 01:28:37 +00008320 if (logging != MagickFalse)
8321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8322 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008323
glennrpd71e86a2011-02-24 01:28:37 +00008324 /* Sort palette, transparent first */;
8325
8326 n = 0;
8327
8328 for (i=0; i<number_transparent; i++)
8329 colormap[n++] = transparent[i];
8330
8331 for (i=0; i<number_semitransparent; i++)
8332 colormap[n++] = semitransparent[i];
8333
8334 for (i=0; i<number_opaque; i++)
8335 colormap[n++] = opaque[i];
8336
glennrpc6c391a2011-04-27 02:23:56 +00008337 ping_background.index +=
8338 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008339
glennrpd71e86a2011-02-24 01:28:37 +00008340 /* image_colors < 257; search the colormap instead of the pixels
8341 * to get ping_have_color and ping_have_non_bw
8342 */
8343 for (i=0; i<n; i++)
8344 {
8345 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008346 {
glennrpd71e86a2011-02-24 01:28:37 +00008347 if (colormap[i].red != colormap[i].green ||
8348 colormap[i].red != colormap[i].blue)
8349 {
8350 ping_have_color=MagickTrue;
8351 ping_have_non_bw=MagickTrue;
8352 break;
8353 }
8354 }
8355
8356 if (ping_have_non_bw == MagickFalse)
8357 {
8358 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008359 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008360 }
glennrp8bb3a022010-12-13 20:40:04 +00008361 }
8362
glennrpd71e86a2011-02-24 01:28:37 +00008363 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8364 (number_transparent == 0 && number_semitransparent == 0)) &&
8365 (((mng_info->write_png_colortype-1) ==
8366 PNG_COLOR_TYPE_PALETTE) ||
8367 (mng_info->write_png_colortype == 0)))
8368 {
glennrp6185c532011-01-14 17:58:40 +00008369 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008370 {
glennrpd71e86a2011-02-24 01:28:37 +00008371 if (n != (ssize_t) image_colors)
8372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8373 " image_colors (%d) and n (%d) don't match",
8374 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008375
glennrpd71e86a2011-02-24 01:28:37 +00008376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8377 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008378 }
glennrp03812ae2010-12-24 01:31:34 +00008379
glennrpd71e86a2011-02-24 01:28:37 +00008380 image->colors = image_colors;
8381
cristy018f07f2011-09-04 21:15:19 +00008382 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008383 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008384 ThrowWriterException(ResourceLimitError,
8385 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008386
8387 for (i=0; i< (ssize_t) image_colors; i++)
8388 image->colormap[i] = colormap[i];
8389
8390 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008391 {
glennrpd71e86a2011-02-24 01:28:37 +00008392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8393 " image->colors=%d (%d)",
8394 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008395
glennrpd71e86a2011-02-24 01:28:37 +00008396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8397 " Update the pixel indexes");
8398 }
glennrp6185c532011-01-14 17:58:40 +00008399
glennrpfd05d622011-02-25 04:10:33 +00008400 /* Sync the pixel indices with the new colormap */
8401
glennrpd71e86a2011-02-24 01:28:37 +00008402 for (y=0; y < (ssize_t) image->rows; y++)
8403 {
cristy97707062011-12-27 18:25:00 +00008404 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp6185c532011-01-14 17:58:40 +00008405
cristyacd2ed22011-08-30 01:44:23 +00008406 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008407 break;
glennrp6185c532011-01-14 17:58:40 +00008408
glennrpd71e86a2011-02-24 01:28:37 +00008409 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008410 {
glennrpd71e86a2011-02-24 01:28:37 +00008411 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008412 {
glennrpd71e86a2011-02-24 01:28:37 +00008413 if ((image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008414 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8415 image->colormap[i].red == GetPixelRed(image,q) &&
8416 image->colormap[i].green == GetPixelGreen(image,q) &&
8417 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008418 {
cristy4c08aed2011-07-01 19:47:50 +00008419 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008420 break;
glennrp6185c532011-01-14 17:58:40 +00008421 }
glennrp6185c532011-01-14 17:58:40 +00008422 }
cristyed231572011-07-14 02:18:59 +00008423 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008424 }
glennrp6185c532011-01-14 17:58:40 +00008425
glennrpd71e86a2011-02-24 01:28:37 +00008426 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8427 break;
8428 }
8429 }
8430 }
8431
8432 if (logging != MagickFalse)
8433 {
8434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8435 " image->colors=%d", (int) image->colors);
8436
8437 if (image->colormap != NULL)
8438 {
8439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008440 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008441
8442 for (i=0; i < (ssize_t) image->colors; i++)
8443 {
cristy72988482011-03-29 16:34:38 +00008444 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008445 {
8446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8447 " %d (%d,%d,%d,%d)",
8448 (int) i,
8449 (int) image->colormap[i].red,
8450 (int) image->colormap[i].green,
8451 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008452 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008453 }
glennrp6185c532011-01-14 17:58:40 +00008454 }
8455 }
glennrp03812ae2010-12-24 01:31:34 +00008456
glennrpd71e86a2011-02-24 01:28:37 +00008457 if (number_transparent < 257)
8458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8459 " number_transparent = %d",
8460 number_transparent);
8461 else
glennrp03812ae2010-12-24 01:31:34 +00008462
glennrpd71e86a2011-02-24 01:28:37 +00008463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8464 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008465
glennrpd71e86a2011-02-24 01:28:37 +00008466 if (number_opaque < 257)
8467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8468 " number_opaque = %d",
8469 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008470
glennrpd71e86a2011-02-24 01:28:37 +00008471 else
8472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8473 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008474
glennrpd71e86a2011-02-24 01:28:37 +00008475 if (number_semitransparent < 257)
8476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8477 " number_semitransparent = %d",
8478 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008479
glennrpd71e86a2011-02-24 01:28:37 +00008480 else
8481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8482 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008483
glennrpd71e86a2011-02-24 01:28:37 +00008484 if (ping_have_non_bw == MagickFalse)
8485 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8486 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008487
glennrpd71e86a2011-02-24 01:28:37 +00008488 else if (ping_have_color == MagickFalse)
8489 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8490 " All pixels and the background are gray");
8491
8492 else
8493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8494 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008495
glennrp03812ae2010-12-24 01:31:34 +00008496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8497 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008498 }
glennrpfd05d622011-02-25 04:10:33 +00008499
glennrpc8c2f062011-02-25 19:00:33 +00008500 if (mng_info->write_png8 == MagickFalse)
8501 break;
glennrpfd05d622011-02-25 04:10:33 +00008502
glennrpc8c2f062011-02-25 19:00:33 +00008503 /* Make any reductions necessary for the PNG8 format */
8504 if (image_colors <= 256 &&
8505 image_colors != 0 && image->colormap != NULL &&
8506 number_semitransparent == 0 &&
8507 number_transparent <= 1)
8508 break;
8509
8510 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008511 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8512 * transparent color so if more than one is transparent we merge
8513 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008514 */
glennrp130fc452011-08-20 03:43:18 +00008515 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008516 {
8517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8518 " Thresholding the alpha channel to binary");
8519
8520 for (y=0; y < (ssize_t) image->rows; y++)
8521 {
cristy8a20fa02011-12-27 15:54:31 +00008522 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008523
cristy4c08aed2011-07-01 19:47:50 +00008524 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008525 break;
8526
8527 for (x=0; x < (ssize_t) image->columns; x++)
8528 {
glennrpf73547f2011-08-20 04:40:26 +00008529 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008530 {
cristy803640d2011-11-17 02:11:32 +00008531 SetPixelInfoPixel(image,&image->background_color,r);
cristy4c08aed2011-07-01 19:47:50 +00008532 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008533 }
8534 else
cristy4c08aed2011-07-01 19:47:50 +00008535 SetPixelAlpha(image,OpaqueAlpha,r);
cristyed231572011-07-14 02:18:59 +00008536 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008537 }
glennrpbb4f99d2011-05-22 11:13:17 +00008538
glennrpc8c2f062011-02-25 19:00:33 +00008539 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8540 break;
8541
8542 if (image_colors != 0 && image_colors <= 256 &&
8543 image->colormap != NULL)
8544 for (i=0; i<image_colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00008545 image->colormap[i].alpha =
8546 (image->colormap[i].alpha > TransparentAlpha/2 ?
8547 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008548 }
8549 continue;
8550 }
8551
8552 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008553 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8554 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8555 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008556 */
glennrpd3371642011-03-22 19:42:23 +00008557 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8558 {
8559 if (logging != MagickFalse)
8560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8561 " Quantizing the background color to 4-4-4");
8562
8563 tried_444 = MagickTrue;
8564
glennrp91d99252011-06-25 14:30:13 +00008565 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008566
8567 if (logging != MagickFalse)
8568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8569 " Quantizing the pixel colors to 4-4-4");
8570
8571 if (image->colormap == NULL)
8572 {
8573 for (y=0; y < (ssize_t) image->rows; y++)
8574 {
cristy8a20fa02011-12-27 15:54:31 +00008575 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpd3371642011-03-22 19:42:23 +00008576
cristy4c08aed2011-07-01 19:47:50 +00008577 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008578 break;
8579
8580 for (x=0; x < (ssize_t) image->columns; x++)
8581 {
cristy4c08aed2011-07-01 19:47:50 +00008582 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008583 LBR04PixelRGB(r);
cristy8a20fa02011-12-27 15:54:31 +00008584 r+=GetPixelChannels(image);
glennrpd3371642011-03-22 19:42:23 +00008585 }
glennrpbb4f99d2011-05-22 11:13:17 +00008586
glennrpd3371642011-03-22 19:42:23 +00008587 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8588 break;
8589 }
8590 }
8591
8592 else /* Should not reach this; colormap already exists and
8593 must be <= 256 */
8594 {
8595 if (logging != MagickFalse)
8596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8597 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008598
glennrpd3371642011-03-22 19:42:23 +00008599 for (i=0; i<image_colors; i++)
8600 {
glennrp91d99252011-06-25 14:30:13 +00008601 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008602 }
8603 }
8604 continue;
8605 }
8606
glennrp82b3c532011-03-22 19:20:54 +00008607 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8608 {
8609 if (logging != MagickFalse)
8610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8611 " Quantizing the background color to 3-3-3");
8612
8613 tried_333 = MagickTrue;
8614
glennrp91d99252011-06-25 14:30:13 +00008615 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008616
8617 if (logging != MagickFalse)
8618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008619 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008620
8621 if (image->colormap == NULL)
8622 {
8623 for (y=0; y < (ssize_t) image->rows; y++)
8624 {
cristy8a20fa02011-12-27 15:54:31 +00008625 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp82b3c532011-03-22 19:20:54 +00008626
cristy4c08aed2011-07-01 19:47:50 +00008627 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008628 break;
8629
8630 for (x=0; x < (ssize_t) image->columns; x++)
8631 {
cristy4c08aed2011-07-01 19:47:50 +00008632 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8633 LBR03RGB(r);
cristy8a20fa02011-12-27 15:54:31 +00008634 r+=GetPixelChannels(image);
glennrp82b3c532011-03-22 19:20:54 +00008635 }
glennrpbb4f99d2011-05-22 11:13:17 +00008636
glennrp82b3c532011-03-22 19:20:54 +00008637 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8638 break;
8639 }
8640 }
8641
8642 else /* Should not reach this; colormap already exists and
8643 must be <= 256 */
8644 {
8645 if (logging != MagickFalse)
8646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008647 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008648 for (i=0; i<image_colors; i++)
8649 {
glennrp91d99252011-06-25 14:30:13 +00008650 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008651 }
glennrpd3371642011-03-22 19:42:23 +00008652 }
8653 continue;
glennrp82b3c532011-03-22 19:20:54 +00008654 }
glennrpc8c2f062011-02-25 19:00:33 +00008655
glennrp8ca51ad2011-05-12 21:22:32 +00008656 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008657 {
8658 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008660 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008661
glennrp8ca51ad2011-05-12 21:22:32 +00008662 tried_332 = MagickTrue;
8663
glennrp3faa9a32011-04-23 14:00:25 +00008664 /* Red and green were already done so we only quantize the blue
8665 * channel
8666 */
8667
glennrp91d99252011-06-25 14:30:13 +00008668 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008669
glennrpc8c2f062011-02-25 19:00:33 +00008670 if (logging != MagickFalse)
8671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008672 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008673
glennrpc8c2f062011-02-25 19:00:33 +00008674 if (image->colormap == NULL)
8675 {
8676 for (y=0; y < (ssize_t) image->rows; y++)
8677 {
cristy8a20fa02011-12-27 15:54:31 +00008678 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008679
cristy4c08aed2011-07-01 19:47:50 +00008680 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008681 break;
8682
8683 for (x=0; x < (ssize_t) image->columns; x++)
8684 {
cristy4c08aed2011-07-01 19:47:50 +00008685 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008686 LBR02PixelBlue(r);
cristy8a20fa02011-12-27 15:54:31 +00008687 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008688 }
glennrpbb4f99d2011-05-22 11:13:17 +00008689
glennrpc8c2f062011-02-25 19:00:33 +00008690 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8691 break;
8692 }
8693 }
glennrpfd05d622011-02-25 04:10:33 +00008694
glennrpc8c2f062011-02-25 19:00:33 +00008695 else /* Should not reach this; colormap already exists and
8696 must be <= 256 */
8697 {
8698 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008700 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008701 for (i=0; i<image_colors; i++)
8702 {
glennrp91d99252011-06-25 14:30:13 +00008703 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008704 }
8705 }
8706 continue;
8707 }
8708 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008709
8710 if (image_colors == 0 || image_colors > 256)
8711 {
8712 /* Take care of special case with 256 colors + 1 transparent
8713 * color. We don't need to quantize to 2-3-2-1; we only need to
8714 * eliminate one color, so we'll merge the two darkest red
8715 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8716 */
8717 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8718 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8719 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8720 {
8721 image->background_color.red=ScaleCharToQuantum(0x24);
8722 }
glennrpbb4f99d2011-05-22 11:13:17 +00008723
glennrp8ca51ad2011-05-12 21:22:32 +00008724 if (image->colormap == NULL)
8725 {
8726 for (y=0; y < (ssize_t) image->rows; y++)
8727 {
cristy8a20fa02011-12-27 15:54:31 +00008728 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008729
cristy4c08aed2011-07-01 19:47:50 +00008730 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008731 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008732
glennrp8ca51ad2011-05-12 21:22:32 +00008733 for (x=0; x < (ssize_t) image->columns; x++)
8734 {
cristy4c08aed2011-07-01 19:47:50 +00008735 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8736 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8737 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8738 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008739 {
cristy4c08aed2011-07-01 19:47:50 +00008740 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008741 }
cristyed231572011-07-14 02:18:59 +00008742 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008743 }
glennrpbb4f99d2011-05-22 11:13:17 +00008744
glennrp8ca51ad2011-05-12 21:22:32 +00008745 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8746 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008747
glennrp8ca51ad2011-05-12 21:22:32 +00008748 }
8749 }
8750
8751 else
8752 {
8753 for (i=0; i<image_colors; i++)
8754 {
8755 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8756 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8757 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8758 {
8759 image->colormap[i].red=ScaleCharToQuantum(0x24);
8760 }
8761 }
8762 }
8763 }
glennrpd71e86a2011-02-24 01:28:37 +00008764 }
glennrpfd05d622011-02-25 04:10:33 +00008765 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008766
glennrpfd05d622011-02-25 04:10:33 +00008767 /* If we are excluding the tRNS chunk and there is transparency,
8768 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8769 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008770 */
glennrp0e8ea192010-12-24 18:00:33 +00008771 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8772 (number_transparent != 0 || number_semitransparent != 0))
8773 {
glennrpd17915c2011-04-29 14:24:22 +00008774 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008775
8776 if (ping_have_color == MagickFalse)
8777 mng_info->write_png_colortype = 5;
8778
8779 else
8780 mng_info->write_png_colortype = 7;
8781
glennrp8d579662011-02-23 02:05:02 +00008782 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008783 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008784 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008785
glennrp0e8ea192010-12-24 18:00:33 +00008786 }
8787
glennrpfd05d622011-02-25 04:10:33 +00008788 /* See if cheap transparency is possible. It is only possible
8789 * when there is a single transparent color, no semitransparent
8790 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008791 * as the transparent color. We only need this information if
8792 * we are writing a PNG with colortype 0 or 2, and we have not
8793 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008794 */
glennrp5a39f372011-02-25 04:52:16 +00008795 if (number_transparent == 1 &&
8796 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008797 {
8798 ping_have_cheap_transparency = MagickTrue;
8799
8800 if (number_semitransparent != 0)
8801 ping_have_cheap_transparency = MagickFalse;
8802
8803 else if (image_colors == 0 || image_colors > 256 ||
8804 image->colormap == NULL)
8805 {
cristy4c08aed2011-07-01 19:47:50 +00008806 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008807 *q;
8808
glennrpfd05d622011-02-25 04:10:33 +00008809 for (y=0; y < (ssize_t) image->rows; y++)
8810 {
8811 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8812
cristyacd2ed22011-08-30 01:44:23 +00008813 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008814 break;
8815
8816 for (x=0; x < (ssize_t) image->columns; x++)
8817 {
cristy4c08aed2011-07-01 19:47:50 +00008818 if (GetPixelAlpha(image,q) != TransparentAlpha &&
glennrp847370c2011-07-05 17:37:15 +00008819 (unsigned short) GetPixelRed(image,q) ==
8820 ping_trans_color.red &&
8821 (unsigned short) GetPixelGreen(image,q) ==
8822 ping_trans_color.green &&
8823 (unsigned short) GetPixelBlue(image,q) ==
8824 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008825 {
8826 ping_have_cheap_transparency = MagickFalse;
8827 break;
8828 }
8829
cristyed231572011-07-14 02:18:59 +00008830 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008831 }
glennrpbb4f99d2011-05-22 11:13:17 +00008832
glennrpfd05d622011-02-25 04:10:33 +00008833 if (ping_have_cheap_transparency == MagickFalse)
8834 break;
8835 }
8836 }
8837 else
8838 {
glennrp67b9c1a2011-04-22 18:47:36 +00008839 /* Assuming that image->colormap[0] is the one transparent color
8840 * and that all others are opaque.
8841 */
glennrpfd05d622011-02-25 04:10:33 +00008842 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008843 for (i=1; i<image_colors; i++)
8844 if (image->colormap[i].red == image->colormap[0].red &&
8845 image->colormap[i].green == image->colormap[0].green &&
8846 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008847 {
glennrp67b9c1a2011-04-22 18:47:36 +00008848 ping_have_cheap_transparency = MagickFalse;
8849 break;
glennrpfd05d622011-02-25 04:10:33 +00008850 }
8851 }
glennrpbb4f99d2011-05-22 11:13:17 +00008852
glennrpfd05d622011-02-25 04:10:33 +00008853 if (logging != MagickFalse)
8854 {
8855 if (ping_have_cheap_transparency == MagickFalse)
8856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8857 " Cheap transparency is not possible.");
8858
8859 else
8860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8861 " Cheap transparency is possible.");
8862 }
8863 }
8864 else
8865 ping_have_cheap_transparency = MagickFalse;
8866
glennrp8640fb52010-11-23 15:48:26 +00008867 image_depth=image->depth;
8868
glennrp26c990a2010-11-23 02:23:20 +00008869 quantum_info = (QuantumInfo *) NULL;
8870 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008871 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008872 image_matte=image->matte;
8873
glennrp0fe50b42010-11-16 03:52:51 +00008874 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008875 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008876
glennrp52a479c2011-02-26 21:14:38 +00008877 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8878 (image->colors == 0 || image->colormap == NULL))
8879 {
glennrp52a479c2011-02-26 21:14:38 +00008880 image_info=DestroyImageInfo(image_info);
8881 image=DestroyImage(image);
cristyc82a27b2011-10-21 01:07:16 +00008882 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
glennrp15e01552011-03-06 23:29:17 +00008883 "Cannot write PNG8 or color-type 3; colormap is NULL",
8884 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008885#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8886 UnlockSemaphoreInfo(ping_semaphore);
8887#endif
8888 return(MagickFalse);
8889 }
8890
cristy3ed852e2009-09-05 21:47:34 +00008891 /*
8892 Allocate the PNG structures
8893 */
8894#ifdef PNG_USER_MEM_SUPPORTED
cristyc82a27b2011-10-21 01:07:16 +00008895 error_info.image=image;
8896 error_info.exception=exception;
8897 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008898 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8899 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008900
cristy3ed852e2009-09-05 21:47:34 +00008901#else
cristyc82a27b2011-10-21 01:07:16 +00008902 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008903 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008904
cristy3ed852e2009-09-05 21:47:34 +00008905#endif
8906 if (ping == (png_struct *) NULL)
8907 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008908
cristy3ed852e2009-09-05 21:47:34 +00008909 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008910
cristy3ed852e2009-09-05 21:47:34 +00008911 if (ping_info == (png_info *) NULL)
8912 {
8913 png_destroy_write_struct(&ping,(png_info **) NULL);
8914 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8915 }
glennrp0fe50b42010-11-16 03:52:51 +00008916
cristy3ed852e2009-09-05 21:47:34 +00008917 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008918 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008919
glennrp5af765f2010-03-30 11:12:18 +00008920 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008921 {
8922 /*
8923 PNG write failed.
8924 */
8925#ifdef PNG_DEBUG
8926 if (image_info->verbose)
8927 (void) printf("PNG write has failed.\n");
8928#endif
8929 png_destroy_write_struct(&ping,&ping_info);
8930#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008931 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008932#endif
glennrpda8f3a72011-02-27 23:54:12 +00008933 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008934 (void) CloseBlob(image);
8935 image_info=DestroyImageInfo(image_info);
8936 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008937 return(MagickFalse);
8938 }
8939 /*
8940 Prepare PNG for writing.
8941 */
8942#if defined(PNG_MNG_FEATURES_SUPPORTED)
8943 if (mng_info->write_mng)
8944 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008945
cristy3ed852e2009-09-05 21:47:34 +00008946#else
8947# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8948 if (mng_info->write_mng)
8949 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008950
cristy3ed852e2009-09-05 21:47:34 +00008951# endif
8952#endif
glennrp2b013e42010-11-24 16:55:50 +00008953
cristy3ed852e2009-09-05 21:47:34 +00008954 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008955
cristy4e5bc842010-06-09 13:56:01 +00008956 ping_width=(png_uint_32) image->columns;
8957 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008958
cristy3ed852e2009-09-05 21:47:34 +00008959 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8960 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008961
cristy3ed852e2009-09-05 21:47:34 +00008962 if (mng_info->write_png_depth != 0)
8963 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008964
cristy3ed852e2009-09-05 21:47:34 +00008965 /* Adjust requested depth to next higher valid depth if necessary */
8966 if (image_depth > 8)
8967 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008968
cristy3ed852e2009-09-05 21:47:34 +00008969 if ((image_depth > 4) && (image_depth < 8))
8970 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008971
cristy3ed852e2009-09-05 21:47:34 +00008972 if (image_depth == 3)
8973 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008974
cristy3ed852e2009-09-05 21:47:34 +00008975 if (logging != MagickFalse)
8976 {
8977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008978 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008980 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008982 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008984 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008986 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008987 }
glennrp8640fb52010-11-23 15:48:26 +00008988
cristy3ed852e2009-09-05 21:47:34 +00008989 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008990 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008991
glennrp26f37912010-12-23 16:22:42 +00008992
cristy3ed852e2009-09-05 21:47:34 +00008993#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008994 if (ping_exclude_pHYs == MagickFalse)
8995 {
cristy2a11bef2011-10-28 18:33:11 +00008996 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00008997 (!mng_info->write_mng || !mng_info->equal_physs))
8998 {
glennrp0fe50b42010-11-16 03:52:51 +00008999 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9001 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00009002
9003 if (image->units == PixelsPerInchResolution)
9004 {
glennrpdfd70802010-11-14 01:23:35 +00009005 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00009006 ping_pHYs_x_resolution=
cristy2a11bef2011-10-28 18:33:11 +00009007 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
glennrp823b55c2011-03-14 18:46:46 +00009008 ping_pHYs_y_resolution=
cristy2a11bef2011-10-28 18:33:11 +00009009 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00009010 }
glennrpdfd70802010-11-14 01:23:35 +00009011
cristy3ed852e2009-09-05 21:47:34 +00009012 else if (image->units == PixelsPerCentimeterResolution)
9013 {
glennrpdfd70802010-11-14 01:23:35 +00009014 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
cristy2a11bef2011-10-28 18:33:11 +00009015 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9016 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00009017 }
glennrp991d11d2010-11-12 21:55:28 +00009018
cristy3ed852e2009-09-05 21:47:34 +00009019 else
9020 {
glennrpdfd70802010-11-14 01:23:35 +00009021 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
cristy2a11bef2011-10-28 18:33:11 +00009022 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9023 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00009024 }
glennrp991d11d2010-11-12 21:55:28 +00009025
glennrp823b55c2011-03-14 18:46:46 +00009026 if (logging != MagickFalse)
9027 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9028 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9029 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9030 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009031 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009032 }
glennrp26f37912010-12-23 16:22:42 +00009033 }
cristy3ed852e2009-09-05 21:47:34 +00009034#endif
glennrpa521b2f2010-10-29 04:11:03 +00009035
glennrp26f37912010-12-23 16:22:42 +00009036 if (ping_exclude_bKGD == MagickFalse)
9037 {
glennrpa521b2f2010-10-29 04:11:03 +00009038 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009039 {
glennrpa521b2f2010-10-29 04:11:03 +00009040 unsigned int
9041 mask;
cristy3ed852e2009-09-05 21:47:34 +00009042
glennrpa521b2f2010-10-29 04:11:03 +00009043 mask=0xffff;
9044 if (ping_bit_depth == 8)
9045 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009046
glennrpa521b2f2010-10-29 04:11:03 +00009047 if (ping_bit_depth == 4)
9048 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009049
glennrpa521b2f2010-10-29 04:11:03 +00009050 if (ping_bit_depth == 2)
9051 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009052
glennrpa521b2f2010-10-29 04:11:03 +00009053 if (ping_bit_depth == 1)
9054 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009055
glennrpa521b2f2010-10-29 04:11:03 +00009056 ping_background.red=(png_uint_16)
9057 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009058
glennrpa521b2f2010-10-29 04:11:03 +00009059 ping_background.green=(png_uint_16)
9060 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009061
glennrpa521b2f2010-10-29 04:11:03 +00009062 ping_background.blue=(png_uint_16)
9063 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009064
9065 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009066 }
cristy3ed852e2009-09-05 21:47:34 +00009067
glennrp0fe50b42010-11-16 03:52:51 +00009068 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009069 {
9070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9071 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9073 " background_color index is %d",
9074 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009075
9076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9077 " ping_bit_depth=%d",ping_bit_depth);
9078 }
glennrp0fe50b42010-11-16 03:52:51 +00009079
9080 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009081 }
glennrp0fe50b42010-11-16 03:52:51 +00009082
cristy3ed852e2009-09-05 21:47:34 +00009083 /*
9084 Select the color type.
9085 */
9086 matte=image_matte;
9087 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009088
glennrp1273f7b2011-02-24 03:20:30 +00009089 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009090 {
glennrp0fe50b42010-11-16 03:52:51 +00009091
glennrpfd05d622011-02-25 04:10:33 +00009092 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009093 for reducing the sample depth from 8. */
9094
glennrp0fe50b42010-11-16 03:52:51 +00009095 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009096
glennrp8bb3a022010-12-13 20:40:04 +00009097 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009098
9099 /*
9100 Set image palette.
9101 */
9102 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9103
glennrp0fe50b42010-11-16 03:52:51 +00009104 if (logging != MagickFalse)
9105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9106 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009107 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009108
9109 for (i=0; i < (ssize_t) number_colors; i++)
9110 {
9111 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9112 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9113 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9114 if (logging != MagickFalse)
9115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009116#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009117 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009118#else
9119 " %5ld (%5d,%5d,%5d)",
9120#endif
glennrp0fe50b42010-11-16 03:52:51 +00009121 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9122
9123 }
glennrp2b013e42010-11-24 16:55:50 +00009124
glennrp8bb3a022010-12-13 20:40:04 +00009125 ping_have_PLTE=MagickTrue;
9126 image_depth=ping_bit_depth;
9127 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009128
glennrp58e01762011-01-07 15:28:54 +00009129 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009130 {
glennrp0fe50b42010-11-16 03:52:51 +00009131 /*
9132 Identify which colormap entry is transparent.
9133 */
9134 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009135 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009136
glennrp8bb3a022010-12-13 20:40:04 +00009137 for (i=0; i < (ssize_t) number_transparent; i++)
9138 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009139
glennrp0fe50b42010-11-16 03:52:51 +00009140
glennrp2cc891a2010-12-24 13:44:32 +00009141 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009142 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009143
9144 if (ping_num_trans == 0)
9145 ping_have_tRNS=MagickFalse;
9146
glennrp8bb3a022010-12-13 20:40:04 +00009147 else
9148 ping_have_tRNS=MagickTrue;
9149 }
glennrp0fe50b42010-11-16 03:52:51 +00009150
glennrp1273f7b2011-02-24 03:20:30 +00009151 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009152 {
glennrp1273f7b2011-02-24 03:20:30 +00009153 /*
9154 * Identify which colormap entry is the background color.
9155 */
9156
glennrp4f25bd02011-01-01 18:51:28 +00009157 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9158 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9159 break;
glennrp0fe50b42010-11-16 03:52:51 +00009160
glennrp4f25bd02011-01-01 18:51:28 +00009161 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009162
9163 if (logging != MagickFalse)
9164 {
9165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9166 " background_color index is %d",
9167 (int) ping_background.index);
9168 }
glennrp4f25bd02011-01-01 18:51:28 +00009169 }
cristy3ed852e2009-09-05 21:47:34 +00009170 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009171
glennrp7e65e932011-08-19 02:31:16 +00009172 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009173 {
9174 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009175 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009176 }
glennrp0fe50b42010-11-16 03:52:51 +00009177
glennrp7e65e932011-08-19 02:31:16 +00009178 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009179 {
9180 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009181 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009182 }
glennrp0fe50b42010-11-16 03:52:51 +00009183
glennrp8bb3a022010-12-13 20:40:04 +00009184 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009185 {
glennrp5af765f2010-03-30 11:12:18 +00009186 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009187
glennrp8bb3a022010-12-13 20:40:04 +00009188 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009189 {
glennrp5af765f2010-03-30 11:12:18 +00009190 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009191
glennrp5af765f2010-03-30 11:12:18 +00009192 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9193 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009194 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009195
glennrp8bb3a022010-12-13 20:40:04 +00009196 else
9197 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009198
9199 if (logging != MagickFalse)
9200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9201 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009202 }
glennrp0fe50b42010-11-16 03:52:51 +00009203
glennrp7c4c9e62011-03-21 20:23:32 +00009204 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009205 {
9206 if (logging != MagickFalse)
9207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009208 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009209
glennrpd6bf1612010-12-17 17:28:54 +00009210 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009211 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009212
glennrpd6bf1612010-12-17 17:28:54 +00009213 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009214 {
glennrp5af765f2010-03-30 11:12:18 +00009215 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009216 image_matte=MagickFalse;
9217 }
glennrp0fe50b42010-11-16 03:52:51 +00009218
glennrpd6bf1612010-12-17 17:28:54 +00009219 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009220 {
glennrp5af765f2010-03-30 11:12:18 +00009221 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009222 image_matte=MagickTrue;
9223 }
glennrp0fe50b42010-11-16 03:52:51 +00009224
glennrp5aa37f62011-01-02 03:07:57 +00009225 if (image_info->type == PaletteType ||
9226 image_info->type == PaletteMatteType)
9227 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9228
glennrp7c4c9e62011-03-21 20:23:32 +00009229 if (mng_info->write_png_colortype == 0 &&
9230 (image_info->type == UndefinedType ||
9231 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009232 {
glennrp5aa37f62011-01-02 03:07:57 +00009233 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009234 {
glennrp5aa37f62011-01-02 03:07:57 +00009235 if (image_matte == MagickFalse)
9236 {
9237 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9238 image_matte=MagickFalse;
9239 }
glennrp0fe50b42010-11-16 03:52:51 +00009240
glennrp0b206f52011-01-07 04:55:32 +00009241 else
glennrp5aa37f62011-01-02 03:07:57 +00009242 {
9243 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9244 image_matte=MagickTrue;
9245 }
9246 }
9247 else
glennrp8bb3a022010-12-13 20:40:04 +00009248 {
glennrp5aa37f62011-01-02 03:07:57 +00009249 if (image_matte == MagickFalse)
9250 {
9251 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9252 image_matte=MagickFalse;
9253 }
glennrp8bb3a022010-12-13 20:40:04 +00009254
glennrp0b206f52011-01-07 04:55:32 +00009255 else
glennrp5aa37f62011-01-02 03:07:57 +00009256 {
9257 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9258 image_matte=MagickTrue;
9259 }
9260 }
glennrp0fe50b42010-11-16 03:52:51 +00009261 }
glennrp5aa37f62011-01-02 03:07:57 +00009262
cristy3ed852e2009-09-05 21:47:34 +00009263 }
glennrp0fe50b42010-11-16 03:52:51 +00009264
cristy3ed852e2009-09-05 21:47:34 +00009265 if (logging != MagickFalse)
9266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009267 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009268
glennrp5af765f2010-03-30 11:12:18 +00009269 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009270 {
9271 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9272 ping_color_type == PNG_COLOR_TYPE_RGB ||
9273 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9274 ping_bit_depth=8;
9275 }
cristy3ed852e2009-09-05 21:47:34 +00009276
glennrpd6bf1612010-12-17 17:28:54 +00009277 old_bit_depth=ping_bit_depth;
9278
glennrp5af765f2010-03-30 11:12:18 +00009279 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009280 {
glennrp8d579662011-02-23 02:05:02 +00009281 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9282 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009283 }
glennrp8640fb52010-11-23 15:48:26 +00009284
glennrp5af765f2010-03-30 11:12:18 +00009285 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009286 {
cristy35ef8242010-06-03 16:24:13 +00009287 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009288 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009289
9290 if (image->colors == 0)
9291 {
glennrp0fe50b42010-11-16 03:52:51 +00009292 /* DO SOMETHING */
cristyc82a27b2011-10-21 01:07:16 +00009293 (void) ThrowMagickException(exception,
glennrp0f111982010-07-07 20:18:33 +00009294 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009295 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009296 }
9297
cristy35ef8242010-06-03 16:24:13 +00009298 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009299 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009300 }
glennrp2b013e42010-11-24 16:55:50 +00009301
glennrpd6bf1612010-12-17 17:28:54 +00009302 if (logging != MagickFalse)
9303 {
9304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9305 " Number of colors: %.20g",(double) image_colors);
9306
9307 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9308 " Tentative PNG bit depth: %d",ping_bit_depth);
9309 }
9310
9311 if (ping_bit_depth < (int) mng_info->write_png_depth)
9312 ping_bit_depth = mng_info->write_png_depth;
9313 }
glennrp2cc891a2010-12-24 13:44:32 +00009314
glennrp5af765f2010-03-30 11:12:18 +00009315 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009316
cristy3ed852e2009-09-05 21:47:34 +00009317 if (logging != MagickFalse)
9318 {
9319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009320 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009321
cristy3ed852e2009-09-05 21:47:34 +00009322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009323 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009324
cristy3ed852e2009-09-05 21:47:34 +00009325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009326 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009327
cristy3ed852e2009-09-05 21:47:34 +00009328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009329
glennrp8640fb52010-11-23 15:48:26 +00009330 " image->depth: %.20g",(double) image->depth);
9331
9332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009333 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009334 }
9335
glennrp58e01762011-01-07 15:28:54 +00009336 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009337 {
glennrp4f25bd02011-01-01 18:51:28 +00009338 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009339 {
glennrp7c4c9e62011-03-21 20:23:32 +00009340 if (mng_info->write_png_colortype == 0)
9341 {
9342 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009343
glennrp7c4c9e62011-03-21 20:23:32 +00009344 if (ping_have_color != MagickFalse)
9345 ping_color_type=PNG_COLOR_TYPE_RGBA;
9346 }
glennrp4f25bd02011-01-01 18:51:28 +00009347
9348 /*
9349 * Determine if there is any transparent color.
9350 */
9351 if (number_transparent + number_semitransparent == 0)
9352 {
9353 /*
9354 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9355 */
glennrpa6a06632011-01-19 15:15:34 +00009356
glennrp4f25bd02011-01-01 18:51:28 +00009357 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009358
9359 if (mng_info->write_png_colortype == 0)
9360 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009361 }
9362
9363 else
9364 {
9365 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009366 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009367
9368 mask=0xffff;
9369
9370 if (ping_bit_depth == 8)
9371 mask=0x00ff;
9372
9373 if (ping_bit_depth == 4)
9374 mask=0x000f;
9375
9376 if (ping_bit_depth == 2)
9377 mask=0x0003;
9378
9379 if (ping_bit_depth == 1)
9380 mask=0x0001;
9381
9382 ping_trans_color.red=(png_uint_16)
9383 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9384
9385 ping_trans_color.green=(png_uint_16)
9386 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9387
9388 ping_trans_color.blue=(png_uint_16)
9389 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9390
9391 ping_trans_color.gray=(png_uint_16)
cristy101ab702011-10-13 13:06:32 +00009392 (ScaleQuantumToShort(GetPixelInfoIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009393 image->colormap)) & mask);
9394
9395 ping_trans_color.index=(png_byte) 0;
9396
9397 ping_have_tRNS=MagickTrue;
9398 }
9399
9400 if (ping_have_tRNS != MagickFalse)
9401 {
9402 /*
glennrpfd05d622011-02-25 04:10:33 +00009403 * Determine if there is one and only one transparent color
9404 * and if so if it is fully transparent.
9405 */
9406 if (ping_have_cheap_transparency == MagickFalse)
9407 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009408 }
9409
9410 if (ping_have_tRNS != MagickFalse)
9411 {
glennrp7c4c9e62011-03-21 20:23:32 +00009412 if (mng_info->write_png_colortype == 0)
9413 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009414
9415 if (image_depth == 8)
9416 {
9417 ping_trans_color.red&=0xff;
9418 ping_trans_color.green&=0xff;
9419 ping_trans_color.blue&=0xff;
9420 ping_trans_color.gray&=0xff;
9421 }
9422 }
9423 }
cristy3ed852e2009-09-05 21:47:34 +00009424 else
9425 {
cristy3ed852e2009-09-05 21:47:34 +00009426 if (image_depth == 8)
9427 {
glennrp5af765f2010-03-30 11:12:18 +00009428 ping_trans_color.red&=0xff;
9429 ping_trans_color.green&=0xff;
9430 ping_trans_color.blue&=0xff;
9431 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009432 }
9433 }
9434 }
glennrp8640fb52010-11-23 15:48:26 +00009435
cristy3ed852e2009-09-05 21:47:34 +00009436 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009437
glennrp2e09f552010-11-14 00:38:48 +00009438 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009439 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009440
glennrp39992b42010-11-14 00:03:43 +00009441 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009442 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009443 ping_have_color == MagickFalse &&
9444 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009445 {
cristy35ef8242010-06-03 16:24:13 +00009446 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009447
cristy3ed852e2009-09-05 21:47:34 +00009448 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009449 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009450
glennrp7c4c9e62011-03-21 20:23:32 +00009451 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009452 {
glennrp5af765f2010-03-30 11:12:18 +00009453 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009454
cristy3ed852e2009-09-05 21:47:34 +00009455 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009456 {
9457 if (logging != MagickFalse)
9458 {
9459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9460 " Scaling ping_trans_color (0)");
9461 }
9462 ping_trans_color.gray*=0x0101;
9463 }
cristy3ed852e2009-09-05 21:47:34 +00009464 }
glennrp0fe50b42010-11-16 03:52:51 +00009465
cristy3ed852e2009-09-05 21:47:34 +00009466 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9467 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009468
glennrp136ee3a2011-04-27 15:47:45 +00009469 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009470 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009471 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009472
cristy3ed852e2009-09-05 21:47:34 +00009473 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009474 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009475
cristy3ed852e2009-09-05 21:47:34 +00009476 else
9477 {
glennrp5af765f2010-03-30 11:12:18 +00009478 ping_bit_depth=8;
9479 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009480 {
9481 if(!mng_info->write_png_depth)
9482 {
glennrp5af765f2010-03-30 11:12:18 +00009483 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009484
cristy35ef8242010-06-03 16:24:13 +00009485 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009486 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009487 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009488 }
9489 }
glennrp2b013e42010-11-24 16:55:50 +00009490
glennrp0fe50b42010-11-16 03:52:51 +00009491 else if (ping_color_type ==
9492 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009493 mng_info->IsPalette)
9494 {
cristy3ed852e2009-09-05 21:47:34 +00009495 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009496
cristy3ed852e2009-09-05 21:47:34 +00009497 int
9498 depth_4_ok=MagickTrue,
9499 depth_2_ok=MagickTrue,
9500 depth_1_ok=MagickTrue;
9501
cristybb503372010-05-27 20:51:26 +00009502 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009503 {
9504 unsigned char
9505 intensity;
9506
9507 intensity=ScaleQuantumToChar(image->colormap[i].red);
9508
9509 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9510 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9511 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9512 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009513 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009514 depth_1_ok=MagickFalse;
9515 }
glennrp2b013e42010-11-24 16:55:50 +00009516
cristy3ed852e2009-09-05 21:47:34 +00009517 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009518 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009519
cristy3ed852e2009-09-05 21:47:34 +00009520 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009521 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009522
cristy3ed852e2009-09-05 21:47:34 +00009523 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009524 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009525 }
9526 }
glennrp2b013e42010-11-24 16:55:50 +00009527
glennrp5af765f2010-03-30 11:12:18 +00009528 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009529 }
glennrp0fe50b42010-11-16 03:52:51 +00009530
cristy3ed852e2009-09-05 21:47:34 +00009531 else
glennrp0fe50b42010-11-16 03:52:51 +00009532
cristy3ed852e2009-09-05 21:47:34 +00009533 if (mng_info->IsPalette)
9534 {
glennrp17a14852010-05-10 03:01:59 +00009535 number_colors=image_colors;
9536
cristy3ed852e2009-09-05 21:47:34 +00009537 if (image_depth <= 8)
9538 {
cristy3ed852e2009-09-05 21:47:34 +00009539 /*
9540 Set image palette.
9541 */
glennrp5af765f2010-03-30 11:12:18 +00009542 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009543
glennrp58e01762011-01-07 15:28:54 +00009544 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009545 {
glennrp9c1eb072010-06-06 22:19:15 +00009546 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009547
glennrp3b51f0e2010-11-27 18:14:08 +00009548 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9550 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009551 }
glennrp0fe50b42010-11-16 03:52:51 +00009552
cristy3ed852e2009-09-05 21:47:34 +00009553 else
9554 {
cristybb503372010-05-27 20:51:26 +00009555 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009556 {
9557 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9558 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9559 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9560 }
glennrp0fe50b42010-11-16 03:52:51 +00009561
glennrp3b51f0e2010-11-27 18:14:08 +00009562 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009564 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009565 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009566
glennrp39992b42010-11-14 00:03:43 +00009567 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009568 }
glennrp0fe50b42010-11-16 03:52:51 +00009569
cristy3ed852e2009-09-05 21:47:34 +00009570 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009571 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009572 {
cristybefe4d22010-06-07 01:18:58 +00009573 size_t
9574 one;
9575
glennrp5af765f2010-03-30 11:12:18 +00009576 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009577 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009578
cristy94b11832011-09-08 19:46:03 +00009579 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009580 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009581 }
glennrp0fe50b42010-11-16 03:52:51 +00009582
glennrp5af765f2010-03-30 11:12:18 +00009583 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009584
glennrp58e01762011-01-07 15:28:54 +00009585 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009586 {
glennrp0fe50b42010-11-16 03:52:51 +00009587 /*
glennrpd6bf1612010-12-17 17:28:54 +00009588 * Set up trans_colors array.
9589 */
glennrp0fe50b42010-11-16 03:52:51 +00009590 assert(number_colors <= 256);
9591
glennrpd6bf1612010-12-17 17:28:54 +00009592 ping_num_trans=(unsigned short) (number_transparent +
9593 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009594
9595 if (ping_num_trans == 0)
9596 ping_have_tRNS=MagickFalse;
9597
glennrpd6bf1612010-12-17 17:28:54 +00009598 else
glennrp0fe50b42010-11-16 03:52:51 +00009599 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009600 if (logging != MagickFalse)
9601 {
9602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9603 " Scaling ping_trans_color (1)");
9604 }
glennrpd6bf1612010-12-17 17:28:54 +00009605 ping_have_tRNS=MagickTrue;
9606
9607 for (i=0; i < ping_num_trans; i++)
9608 {
cristy4c08aed2011-07-01 19:47:50 +00009609 ping_trans_alpha[i]= (png_byte)
9610 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009611 }
glennrp0fe50b42010-11-16 03:52:51 +00009612 }
9613 }
cristy3ed852e2009-09-05 21:47:34 +00009614 }
9615 }
glennrp0fe50b42010-11-16 03:52:51 +00009616
cristy3ed852e2009-09-05 21:47:34 +00009617 else
9618 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009619
cristy3ed852e2009-09-05 21:47:34 +00009620 if (image_depth < 8)
9621 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009622
cristy3ed852e2009-09-05 21:47:34 +00009623 if ((save_image_depth == 16) && (image_depth == 8))
9624 {
glennrp4f25bd02011-01-01 18:51:28 +00009625 if (logging != MagickFalse)
9626 {
9627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9628 " Scaling ping_trans_color from (%d,%d,%d)",
9629 (int) ping_trans_color.red,
9630 (int) ping_trans_color.green,
9631 (int) ping_trans_color.blue);
9632 }
9633
glennrp5af765f2010-03-30 11:12:18 +00009634 ping_trans_color.red*=0x0101;
9635 ping_trans_color.green*=0x0101;
9636 ping_trans_color.blue*=0x0101;
9637 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009638
9639 if (logging != MagickFalse)
9640 {
9641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9642 " to (%d,%d,%d)",
9643 (int) ping_trans_color.red,
9644 (int) ping_trans_color.green,
9645 (int) ping_trans_color.blue);
9646 }
cristy3ed852e2009-09-05 21:47:34 +00009647 }
9648 }
9649
cristy4383ec82011-01-05 15:42:32 +00009650 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9651 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009652
cristy3ed852e2009-09-05 21:47:34 +00009653 /*
9654 Adjust background and transparency samples in sub-8-bit grayscale files.
9655 */
glennrp5af765f2010-03-30 11:12:18 +00009656 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009657 PNG_COLOR_TYPE_GRAY)
9658 {
9659 png_uint_16
9660 maxval;
9661
cristy35ef8242010-06-03 16:24:13 +00009662 size_t
9663 one=1;
9664
cristy22ffd972010-06-03 16:51:47 +00009665 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009666
glennrp4f25bd02011-01-01 18:51:28 +00009667 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009668 {
cristy3ed852e2009-09-05 21:47:34 +00009669
glennrp9f0fa852011-12-15 12:20:50 +00009670 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9671 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9672 &image->background_color))) +.5)));
cristy3ed852e2009-09-05 21:47:34 +00009673
9674 if (logging != MagickFalse)
9675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009676 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9678 " background_color index is %d",
9679 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009680
glennrp991d11d2010-11-12 21:55:28 +00009681 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009682 }
cristy3ed852e2009-09-05 21:47:34 +00009683
glennrp3e3e20f2011-06-09 04:21:43 +00009684 if (logging != MagickFalse)
9685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9686 " Scaling ping_trans_color.gray from %d",
9687 (int)ping_trans_color.gray);
9688
glennrp9be9b1c2011-06-09 12:21:45 +00009689 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009690 ping_trans_color.gray)+.5);
9691
9692 if (logging != MagickFalse)
9693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9694 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009695 }
glennrp17a14852010-05-10 03:01:59 +00009696
glennrp26f37912010-12-23 16:22:42 +00009697 if (ping_exclude_bKGD == MagickFalse)
9698 {
glennrp1273f7b2011-02-24 03:20:30 +00009699 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009700 {
9701 /*
9702 Identify which colormap entry is the background color.
9703 */
9704
glennrp17a14852010-05-10 03:01:59 +00009705 number_colors=image_colors;
9706
glennrpa521b2f2010-10-29 04:11:03 +00009707 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9708 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009709 break;
9710
9711 ping_background.index=(png_byte) i;
9712
glennrp3b51f0e2010-11-27 18:14:08 +00009713 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009714 {
9715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009716 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009717 }
glennrp0fe50b42010-11-16 03:52:51 +00009718
cristy13d07042010-11-21 20:56:18 +00009719 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009720 {
9721 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009722
9723 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009724 {
9725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9726 " background =(%d,%d,%d)",
9727 (int) ping_background.red,
9728 (int) ping_background.green,
9729 (int) ping_background.blue);
9730 }
9731 }
glennrpa521b2f2010-10-29 04:11:03 +00009732
glennrpd6bf1612010-12-17 17:28:54 +00009733 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009734 {
glennrp3b51f0e2010-11-27 18:14:08 +00009735 if (logging != MagickFalse)
9736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9737 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009738 ping_have_bKGD = MagickFalse;
9739 }
glennrp17a14852010-05-10 03:01:59 +00009740 }
glennrp26f37912010-12-23 16:22:42 +00009741 }
glennrp17a14852010-05-10 03:01:59 +00009742
cristy3ed852e2009-09-05 21:47:34 +00009743 if (logging != MagickFalse)
9744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009745 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009746 /*
9747 Initialize compression level and filtering.
9748 */
9749 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009750 {
9751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9752 " Setting up deflate compression");
9753
9754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9755 " Compression buffer size: 32768");
9756 }
9757
cristy3ed852e2009-09-05 21:47:34 +00009758 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009759
cristy3ed852e2009-09-05 21:47:34 +00009760 if (logging != MagickFalse)
9761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9762 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009763
cristy4054bfb2011-08-29 23:41:39 +00009764 png_set_compression_mem_level(ping, 9);
9765
glennrp10d739e2011-06-29 18:00:52 +00009766 /* Untangle the "-quality" setting:
9767
9768 Undefined is 0; the default is used.
9769 Default is 75
9770
9771 10's digit:
9772
9773 0: Use Z_HUFFMAN_ONLY strategy with the
9774 zlib default compression level
9775
9776 1-9: the zlib compression level
9777
9778 1's digit:
9779
9780 0-4: the PNG filter method
9781
9782 5: libpng adaptive filtering if compression level > 5
9783 libpng filter type "none" if compression level <= 5
9784 or if image is grayscale or palette
9785
9786 6: libpng adaptive filtering
9787
9788 7: "LOCO" filtering (intrapixel differing) if writing
9789 a MNG, othewise "none". Did not work in IM-6.7.0-9
9790 and earlier because of a missing "else".
9791
9792 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009793 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009794
9795 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009796 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009797
9798 Note that using the -quality option, not all combinations of
9799 PNG filter type, zlib compression level, and zlib compression
cristy5e6be1e2011-07-16 01:23:39 +00009800 strategy are possible. This will be addressed soon in a
cristy5d6fc9c2011-12-27 03:10:42 +00009801 release that accomodates "-define png:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +00009802
9803 */
9804
cristy3ed852e2009-09-05 21:47:34 +00009805 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9806 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009807
glennrp18682582011-06-30 18:11:47 +00009808 if (quality <= 9)
9809 {
9810 if (mng_info->write_png_compression_strategy == 0)
9811 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9812 }
9813
9814 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009815 {
9816 int
9817 level;
9818
cristybb503372010-05-27 20:51:26 +00009819 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009820
glennrp18682582011-06-30 18:11:47 +00009821 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009822 }
glennrp0fe50b42010-11-16 03:52:51 +00009823
glennrp18682582011-06-30 18:11:47 +00009824 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009825 {
glennrp18682582011-06-30 18:11:47 +00009826 if ((quality %10) == 8 || (quality %10) == 9)
9827 mng_info->write_png_compression_strategy=Z_RLE;
cristy3ed852e2009-09-05 21:47:34 +00009828 }
glennrp0fe50b42010-11-16 03:52:51 +00009829
glennrp18682582011-06-30 18:11:47 +00009830 if (mng_info->write_png_compression_filter == 0)
9831 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9832
cristy3ed852e2009-09-05 21:47:34 +00009833 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009834 {
glennrp18682582011-06-30 18:11:47 +00009835 if (mng_info->write_png_compression_level)
9836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9837 " Compression level: %d",
9838 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009839
glennrp18682582011-06-30 18:11:47 +00009840 if (mng_info->write_png_compression_strategy)
9841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9842 " Compression strategy: %d",
9843 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009844
glennrp18682582011-06-30 18:11:47 +00009845 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9846 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009847
cristy4054bfb2011-08-29 23:41:39 +00009848 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +00009849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9850 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +00009851 else if (mng_info->write_png_compression_filter == 0 ||
9852 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +00009853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9854 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009855 else
9856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9857 " Base filter method: %d",
9858 (int) mng_info->write_png_compression_filter-1);
9859 }
glennrp2b013e42010-11-24 16:55:50 +00009860
glennrp18682582011-06-30 18:11:47 +00009861 if (mng_info->write_png_compression_level != 0)
9862 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9863
9864 if (mng_info->write_png_compression_filter == 6)
9865 {
9866 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9867 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9868 (quality < 50))
9869 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9870 else
9871 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9872 }
cristy4054bfb2011-08-29 23:41:39 +00009873 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +00009874 mng_info->write_png_compression_filter == 10)
9875 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9876
9877 else if (mng_info->write_png_compression_filter == 8)
9878 {
9879#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9880 if (mng_info->write_mng)
9881 {
9882 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9883 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9884 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9885 }
9886#endif
cristy4054bfb2011-08-29 23:41:39 +00009887 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +00009888 }
9889
9890 else if (mng_info->write_png_compression_filter == 9)
9891 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9892
9893 else if (mng_info->write_png_compression_filter != 0)
9894 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9895 mng_info->write_png_compression_filter-1);
9896
9897 if (mng_info->write_png_compression_strategy != 0)
9898 png_set_compression_strategy(ping,
9899 mng_info->write_png_compression_strategy-1);
9900
cristy0d57eec2011-09-04 22:13:56 +00009901 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9902 if (ping_exclude_sRGB != MagickFalse ||
9903 (image->rendering_intent == UndefinedIntent))
9904 {
9905 if ((ping_exclude_tEXt == MagickFalse ||
9906 ping_exclude_zTXt == MagickFalse) &&
9907 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009908 {
9909 ResetImageProfileIterator(image);
9910 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009911 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009912 profile=GetImageProfile(image,name);
9913
9914 if (profile != (StringInfo *) NULL)
9915 {
glennrp5af765f2010-03-30 11:12:18 +00009916#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009917 if ((LocaleCompare(name,"ICC") == 0) ||
9918 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009919 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009920
9921 if (ping_exclude_iCCP == MagickFalse)
9922 {
cristy9f027d12011-09-21 01:17:17 +00009923 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009924#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009925 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009926#else
9927 (png_const_bytep) GetStringInfoDatum(profile),
9928#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009929 (png_uint_32) GetStringInfoLength(profile));
9930 }
glennrp26f37912010-12-23 16:22:42 +00009931 }
glennrp0fe50b42010-11-16 03:52:51 +00009932
glennrpc8cbc5d2011-01-01 00:12:34 +00009933 else
cristy3ed852e2009-09-05 21:47:34 +00009934#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009935 if (ping_exclude_zCCP == MagickFalse)
9936 {
glennrpcf002022011-01-30 02:38:15 +00009937 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009938 (unsigned char *) name,(unsigned char *) name,
9939 GetStringInfoDatum(profile),
9940 (png_uint_32) GetStringInfoLength(profile));
9941 }
9942 }
glennrp0b206f52011-01-07 04:55:32 +00009943
glennrpc8cbc5d2011-01-01 00:12:34 +00009944 if (logging != MagickFalse)
9945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9946 " Setting up text chunk with %s profile",name);
9947
9948 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009949 }
cristy0d57eec2011-09-04 22:13:56 +00009950 }
cristy3ed852e2009-09-05 21:47:34 +00009951 }
9952
9953#if defined(PNG_WRITE_sRGB_SUPPORTED)
9954 if ((mng_info->have_write_global_srgb == 0) &&
9955 ((image->rendering_intent != UndefinedIntent) ||
9956 (image->colorspace == sRGBColorspace)))
9957 {
glennrp26f37912010-12-23 16:22:42 +00009958 if (ping_exclude_sRGB == MagickFalse)
9959 {
9960 /*
9961 Note image rendering intent.
9962 */
9963 if (logging != MagickFalse)
9964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9965 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009966
glennrp26f37912010-12-23 16:22:42 +00009967 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009968 Magick_RenderingIntent_to_PNG_RenderingIntent(
9969 image->rendering_intent)));
glennrp26f37912010-12-23 16:22:42 +00009970 }
cristy3ed852e2009-09-05 21:47:34 +00009971 }
glennrp26f37912010-12-23 16:22:42 +00009972
glennrp5af765f2010-03-30 11:12:18 +00009973 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009974#endif
9975 {
glennrp2cc891a2010-12-24 13:44:32 +00009976 if (ping_exclude_gAMA == MagickFalse &&
9977 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009978 (image->gamma < .45 || image->gamma > .46)))
9979 {
cristy3ed852e2009-09-05 21:47:34 +00009980 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9981 {
9982 /*
9983 Note image gamma.
9984 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9985 */
9986 if (logging != MagickFalse)
9987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9988 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009989
cristy3ed852e2009-09-05 21:47:34 +00009990 png_set_gAMA(ping,ping_info,image->gamma);
9991 }
glennrp26f37912010-12-23 16:22:42 +00009992 }
glennrp2b013e42010-11-24 16:55:50 +00009993
glennrp26f37912010-12-23 16:22:42 +00009994 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009995 {
glennrp26f37912010-12-23 16:22:42 +00009996 if ((mng_info->have_write_global_chrm == 0) &&
9997 (image->chromaticity.red_primary.x != 0.0))
9998 {
9999 /*
10000 Note image chromaticity.
10001 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10002 */
10003 PrimaryInfo
10004 bp,
10005 gp,
10006 rp,
10007 wp;
cristy3ed852e2009-09-05 21:47:34 +000010008
glennrp26f37912010-12-23 16:22:42 +000010009 wp=image->chromaticity.white_point;
10010 rp=image->chromaticity.red_primary;
10011 gp=image->chromaticity.green_primary;
10012 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +000010013
glennrp26f37912010-12-23 16:22:42 +000010014 if (logging != MagickFalse)
10015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10016 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010017
glennrp26f37912010-12-23 16:22:42 +000010018 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10019 bp.x,bp.y);
10020 }
10021 }
cristy3ed852e2009-09-05 21:47:34 +000010022 }
glennrpdfd70802010-11-14 01:23:35 +000010023
glennrp5af765f2010-03-30 11:12:18 +000010024 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +000010025
10026 if (mng_info->write_mng)
10027 png_set_sig_bytes(ping,8);
10028
cristy5d6fc9c2011-12-27 03:10:42 +000010029 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
cristy3ed852e2009-09-05 21:47:34 +000010030
glennrpd6bf1612010-12-17 17:28:54 +000010031 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +000010032 {
10033 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +000010034 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010035 {
glennrp5af765f2010-03-30 11:12:18 +000010036 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +000010037
glennrp5af765f2010-03-30 11:12:18 +000010038 if (ping_bit_depth < 8)
10039 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +000010040 }
glennrp0fe50b42010-11-16 03:52:51 +000010041
cristy3ed852e2009-09-05 21:47:34 +000010042 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +000010043 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +000010044 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +000010045 }
10046
glennrp0e8ea192010-12-24 18:00:33 +000010047 if (ping_need_colortype_warning != MagickFalse ||
10048 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +000010049 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +000010050 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +000010051 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +000010052 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +000010053 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +000010054 {
10055 if (logging != MagickFalse)
10056 {
glennrp0e8ea192010-12-24 18:00:33 +000010057 if (ping_need_colortype_warning != MagickFalse)
10058 {
10059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10060 " Image has transparency but tRNS chunk was excluded");
10061 }
10062
cristy3ed852e2009-09-05 21:47:34 +000010063 if (mng_info->write_png_depth)
10064 {
10065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010066 " Defined png:bit-depth=%u, Computed depth=%u",
cristy3ed852e2009-09-05 21:47:34 +000010067 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +000010068 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +000010069 }
glennrp0e8ea192010-12-24 18:00:33 +000010070
cristy3ed852e2009-09-05 21:47:34 +000010071 if (mng_info->write_png_colortype)
10072 {
10073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010074 " Defined png:color-type=%u, Computed color type=%u",
cristy3ed852e2009-09-05 21:47:34 +000010075 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +000010076 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010077 }
10078 }
glennrp0e8ea192010-12-24 18:00:33 +000010079
glennrp3bd2e412010-08-10 13:34:52 +000010080 png_warning(ping,
cristy5d6fc9c2011-12-27 03:10:42 +000010081 "Cannot write image with defined png:bit-depth or png:color-type.");
cristy3ed852e2009-09-05 21:47:34 +000010082 }
10083
glennrp58e01762011-01-07 15:28:54 +000010084 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010085 {
10086 /* Add an opaque matte channel */
10087 image->matte = MagickTrue;
cristye941a752011-10-15 01:52:48 +000010088 (void) SetImageAlpha(image,OpaqueAlpha,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010089
glennrpb4a13412010-05-05 12:47:19 +000010090 if (logging != MagickFalse)
10091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10092 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +000010093 }
10094
glennrp0e319732011-01-25 21:53:13 +000010095 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +000010096 {
glennrp991d11d2010-11-12 21:55:28 +000010097 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +000010098 {
glennrp991d11d2010-11-12 21:55:28 +000010099 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010100 if (logging != MagickFalse)
10101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10102 " Setting ping_have_tRNS=MagickTrue.");
10103 }
glennrpe9c26dc2010-05-30 01:56:35 +000010104 }
10105
cristy3ed852e2009-09-05 21:47:34 +000010106 if (logging != MagickFalse)
10107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10108 " Writing PNG header chunks");
10109
glennrp5af765f2010-03-30 11:12:18 +000010110 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10111 ping_bit_depth,ping_color_type,
10112 ping_interlace_method,ping_compression_method,
10113 ping_filter_method);
10114
glennrp39992b42010-11-14 00:03:43 +000010115 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10116 {
glennrpf09bded2011-01-08 01:15:59 +000010117 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010118
glennrp3b51f0e2010-11-27 18:14:08 +000010119 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +000010120 {
glennrp8640fb52010-11-23 15:48:26 +000010121 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +000010122 {
glennrpd6bf1612010-12-17 17:28:54 +000010123 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +000010124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010125 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10126 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010127 (int) palette[i].red,
10128 (int) palette[i].green,
10129 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +000010130 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010131 (int) ping_trans_alpha[i]);
10132 else
10133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010134 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010135 (int) i,
10136 (int) palette[i].red,
10137 (int) palette[i].green,
10138 (int) palette[i].blue);
10139 }
glennrp39992b42010-11-14 00:03:43 +000010140 }
glennrp39992b42010-11-14 00:03:43 +000010141 }
10142
glennrp26f37912010-12-23 16:22:42 +000010143 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010144 {
glennrp26f37912010-12-23 16:22:42 +000010145 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010146 {
glennrp26f37912010-12-23 16:22:42 +000010147 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010148 if (logging)
10149 {
10150 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10151 " Setting up bKGD chunk");
10152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10153 " background color = (%d,%d,%d)",
10154 (int) ping_background.red,
10155 (int) ping_background.green,
10156 (int) ping_background.blue);
10157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10158 " index = %d, gray=%d",
10159 (int) ping_background.index,
10160 (int) ping_background.gray);
10161 }
10162 }
glennrp26f37912010-12-23 16:22:42 +000010163 }
10164
10165 if (ping_exclude_pHYs == MagickFalse)
10166 {
10167 if (ping_have_pHYs != MagickFalse)
10168 {
10169 png_set_pHYs(ping,ping_info,
10170 ping_pHYs_x_resolution,
10171 ping_pHYs_y_resolution,
10172 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010173
10174 if (logging)
10175 {
10176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10177 " Setting up pHYs chunk");
10178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10179 " x_resolution=%lu",
10180 (unsigned long) ping_pHYs_x_resolution);
10181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10182 " y_resolution=%lu",
10183 (unsigned long) ping_pHYs_y_resolution);
10184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10185 " unit_type=%lu",
10186 (unsigned long) ping_pHYs_unit_type);
10187 }
glennrp26f37912010-12-23 16:22:42 +000010188 }
glennrpdfd70802010-11-14 01:23:35 +000010189 }
10190
10191#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010192 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010193 {
glennrp26f37912010-12-23 16:22:42 +000010194 if (image->page.x || image->page.y)
10195 {
10196 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10197 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010198
glennrp26f37912010-12-23 16:22:42 +000010199 if (logging != MagickFalse)
10200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10201 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10202 (int) image->page.x, (int) image->page.y);
10203 }
glennrpdfd70802010-11-14 01:23:35 +000010204 }
10205#endif
10206
glennrpda8f3a72011-02-27 23:54:12 +000010207 if (mng_info->need_blob != MagickFalse)
10208 {
cristyc82a27b2011-10-21 01:07:16 +000010209 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
glennrpda8f3a72011-02-27 23:54:12 +000010210 MagickFalse)
10211 png_error(ping,"WriteBlob Failed");
10212
10213 ping_have_blob=MagickTrue;
10214 }
10215
cristy3ed852e2009-09-05 21:47:34 +000010216 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010217
glennrp39992b42010-11-14 00:03:43 +000010218 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010219 {
glennrp3b51f0e2010-11-27 18:14:08 +000010220 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010221 {
10222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10223 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10224 }
10225
10226 if (ping_color_type == 3)
10227 (void) png_set_tRNS(ping, ping_info,
10228 ping_trans_alpha,
10229 ping_num_trans,
10230 NULL);
10231
10232 else
10233 {
10234 (void) png_set_tRNS(ping, ping_info,
10235 NULL,
10236 0,
10237 &ping_trans_color);
10238
glennrp3b51f0e2010-11-27 18:14:08 +000010239 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010240 {
10241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010242 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010243 (int) ping_trans_color.red,
10244 (int) ping_trans_color.green,
10245 (int) ping_trans_color.blue);
10246 }
10247 }
glennrp991d11d2010-11-12 21:55:28 +000010248 }
10249
cristy3ed852e2009-09-05 21:47:34 +000010250 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010251 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010252
cristy3ed852e2009-09-05 21:47:34 +000010253 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010254
cristy3ed852e2009-09-05 21:47:34 +000010255 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010256 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010257
glennrp26f37912010-12-23 16:22:42 +000010258 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010259 {
glennrp4f25bd02011-01-01 18:51:28 +000010260 if ((image->page.width != 0 && image->page.width != image->columns) ||
10261 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010262 {
10263 unsigned char
10264 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010265
glennrp26f37912010-12-23 16:22:42 +000010266 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10267 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010268 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010269 PNGLong(chunk+4,(png_uint_32) image->page.width);
10270 PNGLong(chunk+8,(png_uint_32) image->page.height);
10271 chunk[12]=0; /* unit = pixels */
10272 (void) WriteBlob(image,13,chunk);
10273 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10274 }
cristy3ed852e2009-09-05 21:47:34 +000010275 }
10276
10277#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010278 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010279#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010280 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010281#undef PNG_HAVE_IDAT
10282#endif
10283
10284 png_set_packing(ping);
10285 /*
10286 Allocate memory.
10287 */
10288 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010289 if (image_depth > 8)
10290 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010291 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010292 {
glennrpb4a13412010-05-05 12:47:19 +000010293 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010294 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010295 break;
glennrp0fe50b42010-11-16 03:52:51 +000010296
glennrpb4a13412010-05-05 12:47:19 +000010297 case PNG_COLOR_TYPE_GRAY_ALPHA:
10298 rowbytes*=2;
10299 break;
glennrp0fe50b42010-11-16 03:52:51 +000010300
glennrpb4a13412010-05-05 12:47:19 +000010301 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010302 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010303 break;
glennrp0fe50b42010-11-16 03:52:51 +000010304
glennrpb4a13412010-05-05 12:47:19 +000010305 default:
10306 break;
cristy3ed852e2009-09-05 21:47:34 +000010307 }
glennrp3b51f0e2010-11-27 18:14:08 +000010308
10309 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010310 {
10311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10312 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010313
glennrpb4a13412010-05-05 12:47:19 +000010314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010315 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010316 }
glennrpcf002022011-01-30 02:38:15 +000010317 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10318 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010319
glennrpcf002022011-01-30 02:38:15 +000010320 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010321 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010322
cristy3ed852e2009-09-05 21:47:34 +000010323 /*
10324 Initialize image scanlines.
10325 */
glennrp5af765f2010-03-30 11:12:18 +000010326 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010327 {
10328 /*
10329 PNG write failed.
10330 */
10331#ifdef PNG_DEBUG
10332 if (image_info->verbose)
10333 (void) printf("PNG write has failed.\n");
10334#endif
10335 png_destroy_write_struct(&ping,&ping_info);
10336 if (quantum_info != (QuantumInfo *) NULL)
10337 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010338 if (ping_pixels != (unsigned char *) NULL)
10339 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010340#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010341 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010342#endif
glennrpda8f3a72011-02-27 23:54:12 +000010343 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010344 (void) CloseBlob(image);
10345 image_info=DestroyImageInfo(image_info);
10346 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010347 return(MagickFalse);
10348 }
cristyed552522009-10-16 14:04:35 +000010349 quantum_info=AcquireQuantumInfo(image_info,image);
10350 if (quantum_info == (QuantumInfo *) NULL)
10351 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010352 quantum_info->format=UndefinedQuantumFormat;
10353 quantum_info->depth=image_depth;
10354 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010355
cristy3ed852e2009-09-05 21:47:34 +000010356 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010357 !mng_info->write_png32) &&
10358 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010359 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010360 image_matte == MagickFalse &&
10361 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010362 {
glennrp8bb3a022010-12-13 20:40:04 +000010363 /* Palette, Bilevel, or Opaque Monochrome */
cristy4c08aed2011-07-01 19:47:50 +000010364 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010365 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010366
cristy3ed852e2009-09-05 21:47:34 +000010367 quantum_info->depth=8;
10368 for (pass=0; pass < num_passes; pass++)
10369 {
10370 /*
10371 Convert PseudoClass image to a PNG monochrome image.
10372 */
cristybb503372010-05-27 20:51:26 +000010373 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010374 {
glennrpd71e86a2011-02-24 01:28:37 +000010375 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10377 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010378
cristyc82a27b2011-10-21 01:07:16 +000010379 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010380
cristy4c08aed2011-07-01 19:47:50 +000010381 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010382 break;
glennrp0fe50b42010-11-16 03:52:51 +000010383
cristy3ed852e2009-09-05 21:47:34 +000010384 if (mng_info->IsPalette)
10385 {
cristy4c08aed2011-07-01 19:47:50 +000010386 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010387 quantum_info,GrayQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010388 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10389 mng_info->write_png_depth &&
10390 mng_info->write_png_depth != old_bit_depth)
10391 {
10392 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010393 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010394 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010395 >> (8-old_bit_depth));
10396 }
10397 }
glennrp0fe50b42010-11-16 03:52:51 +000010398
cristy3ed852e2009-09-05 21:47:34 +000010399 else
10400 {
cristy4c08aed2011-07-01 19:47:50 +000010401 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010402 quantum_info,RedQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010403 }
glennrp0fe50b42010-11-16 03:52:51 +000010404
cristy3ed852e2009-09-05 21:47:34 +000010405 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010406 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010407 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010408 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010409
glennrp3b51f0e2010-11-27 18:14:08 +000010410 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10412 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010413
glennrpcf002022011-01-30 02:38:15 +000010414 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010415 }
10416 if (image->previous == (Image *) NULL)
10417 {
10418 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10419 if (status == MagickFalse)
10420 break;
10421 }
10422 }
10423 }
glennrp0fe50b42010-11-16 03:52:51 +000010424
glennrp8bb3a022010-12-13 20:40:04 +000010425 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010426 {
glennrp0fe50b42010-11-16 03:52:51 +000010427 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010428 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010429 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010430 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010431 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010432 {
cristy4c08aed2011-07-01 19:47:50 +000010433 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010434 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010435
glennrp8bb3a022010-12-13 20:40:04 +000010436 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010437 {
glennrp8bb3a022010-12-13 20:40:04 +000010438
cristybb503372010-05-27 20:51:26 +000010439 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010440 {
cristyc82a27b2011-10-21 01:07:16 +000010441 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010442
cristy4c08aed2011-07-01 19:47:50 +000010443 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010444 break;
glennrp2cc891a2010-12-24 13:44:32 +000010445
glennrp5af765f2010-03-30 11:12:18 +000010446 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010447 {
glennrp8bb3a022010-12-13 20:40:04 +000010448 if (mng_info->IsPalette)
cristy4c08aed2011-07-01 19:47:50 +000010449 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010450 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010451
glennrp8bb3a022010-12-13 20:40:04 +000010452 else
cristy4c08aed2011-07-01 19:47:50 +000010453 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010454 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010455
glennrp3b51f0e2010-11-27 18:14:08 +000010456 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010458 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010459 }
glennrp2cc891a2010-12-24 13:44:32 +000010460
glennrp8bb3a022010-12-13 20:40:04 +000010461 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10462 {
10463 if (logging != MagickFalse && y == 0)
10464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10465 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010466
cristy4c08aed2011-07-01 19:47:50 +000010467 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010468 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010469 }
glennrp2cc891a2010-12-24 13:44:32 +000010470
glennrp3b51f0e2010-11-27 18:14:08 +000010471 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010473 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010474
glennrpcf002022011-01-30 02:38:15 +000010475 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010476 }
glennrp2cc891a2010-12-24 13:44:32 +000010477
glennrp8bb3a022010-12-13 20:40:04 +000010478 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010479 {
glennrp8bb3a022010-12-13 20:40:04 +000010480 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10481 if (status == MagickFalse)
10482 break;
cristy3ed852e2009-09-05 21:47:34 +000010483 }
cristy3ed852e2009-09-05 21:47:34 +000010484 }
10485 }
glennrp8bb3a022010-12-13 20:40:04 +000010486
10487 else
10488 {
cristy4c08aed2011-07-01 19:47:50 +000010489 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010490 *p;
10491
10492 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010493 {
glennrp8bb3a022010-12-13 20:40:04 +000010494 if ((image_depth > 8) || (mng_info->write_png24 ||
10495 mng_info->write_png32 ||
10496 (!mng_info->write_png8 && !mng_info->IsPalette)))
10497 {
10498 for (y=0; y < (ssize_t) image->rows; y++)
10499 {
10500 p=GetVirtualPixels(image,0,y,image->columns,1,
cristyc82a27b2011-10-21 01:07:16 +000010501 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010502
cristy4c08aed2011-07-01 19:47:50 +000010503 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010504 break;
glennrp2cc891a2010-12-24 13:44:32 +000010505
glennrp8bb3a022010-12-13 20:40:04 +000010506 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10507 {
10508 if (image->storage_class == DirectClass)
cristy4c08aed2011-07-01 19:47:50 +000010509 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010510 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010511
glennrp8bb3a022010-12-13 20:40:04 +000010512 else
cristy4c08aed2011-07-01 19:47:50 +000010513 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010514 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010515 }
glennrp2cc891a2010-12-24 13:44:32 +000010516
glennrp8bb3a022010-12-13 20:40:04 +000010517 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10518 {
cristy4c08aed2011-07-01 19:47:50 +000010519 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010520 quantum_info,GrayAlphaQuantum,ping_pixels,
cristyc82a27b2011-10-21 01:07:16 +000010521 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010522
glennrp8bb3a022010-12-13 20:40:04 +000010523 if (logging != MagickFalse && y == 0)
10524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10525 " Writing GRAY_ALPHA PNG pixels (3)");
10526 }
glennrp2cc891a2010-12-24 13:44:32 +000010527
glennrp8bb3a022010-12-13 20:40:04 +000010528 else if (image_matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +000010529 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010530 quantum_info,RGBAQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010531
glennrp8bb3a022010-12-13 20:40:04 +000010532 else
cristy4c08aed2011-07-01 19:47:50 +000010533 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010534 quantum_info,RGBQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010535
glennrp8bb3a022010-12-13 20:40:04 +000010536 if (logging != MagickFalse && y == 0)
10537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10538 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010539
glennrpcf002022011-01-30 02:38:15 +000010540 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010541 }
10542 }
glennrp2cc891a2010-12-24 13:44:32 +000010543
glennrp8bb3a022010-12-13 20:40:04 +000010544 else
10545 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10546 mng_info->write_png32 ||
10547 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10548 {
10549 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10550 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10551 {
10552 if (logging != MagickFalse)
10553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10554 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010555
glennrp8bb3a022010-12-13 20:40:04 +000010556 quantum_info->depth=8;
10557 image_depth=8;
10558 }
glennrp2cc891a2010-12-24 13:44:32 +000010559
glennrp8bb3a022010-12-13 20:40:04 +000010560 for (y=0; y < (ssize_t) image->rows; y++)
10561 {
10562 if (logging != MagickFalse && y == 0)
10563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10564 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010565
cristy97707062011-12-27 18:25:00 +000010566 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000010567
cristy4c08aed2011-07-01 19:47:50 +000010568 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010569 break;
glennrp2cc891a2010-12-24 13:44:32 +000010570
glennrp8bb3a022010-12-13 20:40:04 +000010571 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010572 {
glennrp4bf89732011-03-21 13:48:28 +000010573 quantum_info->depth=image->depth;
10574
cristy4c08aed2011-07-01 19:47:50 +000010575 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010576 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp44757ab2011-03-17 12:57:03 +000010577 }
glennrp2cc891a2010-12-24 13:44:32 +000010578
glennrp8bb3a022010-12-13 20:40:04 +000010579 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10580 {
10581 if (logging != MagickFalse && y == 0)
10582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10583 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010584
cristy4c08aed2011-07-01 19:47:50 +000010585 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010586 quantum_info,GrayAlphaQuantum,ping_pixels,
cristyc82a27b2011-10-21 01:07:16 +000010587 exception);
glennrp8bb3a022010-12-13 20:40:04 +000010588 }
glennrp2cc891a2010-12-24 13:44:32 +000010589
glennrp8bb3a022010-12-13 20:40:04 +000010590 else
glennrp8bb3a022010-12-13 20:40:04 +000010591 {
cristy4c08aed2011-07-01 19:47:50 +000010592 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010593 quantum_info,IndexQuantum,ping_pixels,exception);
glennrp5eae7602011-02-22 15:21:32 +000010594
10595 if (logging != MagickFalse && y <= 2)
10596 {
10597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010598 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010599
10600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10601 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10602 (int)ping_pixels[0],(int)ping_pixels[1]);
10603 }
glennrp8bb3a022010-12-13 20:40:04 +000010604 }
glennrpcf002022011-01-30 02:38:15 +000010605 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010606 }
10607 }
glennrp2cc891a2010-12-24 13:44:32 +000010608
glennrp8bb3a022010-12-13 20:40:04 +000010609 if (image->previous == (Image *) NULL)
10610 {
10611 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10612 if (status == MagickFalse)
10613 break;
10614 }
cristy3ed852e2009-09-05 21:47:34 +000010615 }
glennrp8bb3a022010-12-13 20:40:04 +000010616 }
10617 }
10618
cristyb32b90a2009-09-07 21:45:48 +000010619 if (quantum_info != (QuantumInfo *) NULL)
10620 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010621
10622 if (logging != MagickFalse)
10623 {
10624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010625 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010626
cristy3ed852e2009-09-05 21:47:34 +000010627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010628 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010629
cristy3ed852e2009-09-05 21:47:34 +000010630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010631 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010632
cristy3ed852e2009-09-05 21:47:34 +000010633 if (mng_info->write_png_depth)
10634 {
10635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010636 " Defined png:bit-depth: %d",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010637 }
glennrp0fe50b42010-11-16 03:52:51 +000010638
cristy3ed852e2009-09-05 21:47:34 +000010639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010640 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010641
cristy3ed852e2009-09-05 21:47:34 +000010642 if (mng_info->write_png_colortype)
10643 {
10644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010645 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010646 }
glennrp0fe50b42010-11-16 03:52:51 +000010647
cristy3ed852e2009-09-05 21:47:34 +000010648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010649 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010650
cristy3ed852e2009-09-05 21:47:34 +000010651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010652 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010653 }
10654 /*
glennrpa0ed0092011-04-18 16:36:29 +000010655 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010656 */
glennrp823b55c2011-03-14 18:46:46 +000010657 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010658 {
glennrp26f37912010-12-23 16:22:42 +000010659 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010660 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010661 while (property != (const char *) NULL)
10662 {
10663 png_textp
10664 text;
glennrp2cc891a2010-12-24 13:44:32 +000010665
cristyd15e6592011-10-15 00:13:06 +000010666 value=GetImageProperty(image,property,exception);
glennrpa0ed0092011-04-18 16:36:29 +000010667
10668 /* Don't write any "png:" properties; those are just for "identify" */
10669 if (LocaleNCompare(property,"png:",4) != 0 &&
10670
10671 /* Suppress density and units if we wrote a pHYs chunk */
10672 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010673 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010674 LocaleCompare(property,"units") != 0) &&
10675
10676 /* Suppress the IM-generated Date:create and Date:modify */
10677 (ping_exclude_date == MagickFalse ||
10678 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010679 {
glennrpc70af4a2011-03-07 00:08:23 +000010680 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010681 {
glennrpc70af4a2011-03-07 00:08:23 +000010682 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10683 text[0].key=(char *) property;
10684 text[0].text=(char *) value;
10685 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010686
glennrpc70af4a2011-03-07 00:08:23 +000010687 if (ping_exclude_tEXt != MagickFalse)
10688 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10689
10690 else if (ping_exclude_zTXt != MagickFalse)
10691 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10692
10693 else
glennrp26f37912010-12-23 16:22:42 +000010694 {
glennrpc70af4a2011-03-07 00:08:23 +000010695 text[0].compression=image_info->compression == NoCompression ||
10696 (image_info->compression == UndefinedCompression &&
10697 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10698 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010699 }
glennrp2cc891a2010-12-24 13:44:32 +000010700
glennrpc70af4a2011-03-07 00:08:23 +000010701 if (logging != MagickFalse)
10702 {
10703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10704 " Setting up text chunk");
10705
10706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10707 " keyword: %s",text[0].key);
10708 }
10709
10710 png_set_text(ping,ping_info,text,1);
10711 png_free(ping,text);
10712 }
glennrp26f37912010-12-23 16:22:42 +000010713 }
10714 property=GetNextImageProperty(image);
10715 }
cristy3ed852e2009-09-05 21:47:34 +000010716 }
10717
10718 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010719 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010720
10721 if (logging != MagickFalse)
10722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10723 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010724
cristy3ed852e2009-09-05 21:47:34 +000010725 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010726
cristy3ed852e2009-09-05 21:47:34 +000010727 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10728 {
10729 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010730 (ping_width != mng_info->page.width) ||
10731 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010732 {
10733 unsigned char
10734 chunk[32];
10735
10736 /*
10737 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10738 */
10739 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10740 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010741 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010742 chunk[4]=4;
10743 chunk[5]=0; /* frame name separator (no name) */
10744 chunk[6]=1; /* flag for changing delay, for next frame only */
10745 chunk[7]=0; /* flag for changing frame timeout */
10746 chunk[8]=1; /* flag for changing frame clipping for next frame */
10747 chunk[9]=0; /* flag for changing frame sync_id */
10748 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10749 chunk[14]=0; /* clipping boundaries delta type */
10750 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10751 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010752 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010753 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10754 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010755 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010756 (void) WriteBlob(image,31,chunk);
10757 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10758 mng_info->old_framing_mode=4;
10759 mng_info->framing_mode=1;
10760 }
glennrp0fe50b42010-11-16 03:52:51 +000010761
cristy3ed852e2009-09-05 21:47:34 +000010762 else
10763 mng_info->framing_mode=3;
10764 }
10765 if (mng_info->write_mng && !mng_info->need_fram &&
10766 ((int) image->dispose == 3))
cristyc82a27b2011-10-21 01:07:16 +000010767 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010768 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010769 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010770
cristy3ed852e2009-09-05 21:47:34 +000010771 /*
10772 Free PNG resources.
10773 */
glennrp5af765f2010-03-30 11:12:18 +000010774
cristy3ed852e2009-09-05 21:47:34 +000010775 png_destroy_write_struct(&ping,&ping_info);
10776
glennrpcf002022011-01-30 02:38:15 +000010777 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010778
10779#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010780 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010781#endif
10782
glennrpda8f3a72011-02-27 23:54:12 +000010783 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010784 (void) CloseBlob(image);
10785
10786 image_info=DestroyImageInfo(image_info);
10787 image=DestroyImage(image);
10788
10789 /* Store bit depth actually written */
10790 s[0]=(char) ping_bit_depth;
10791 s[1]='\0';
10792
cristyd15e6592011-10-15 00:13:06 +000010793 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
glennrpb9cfe272010-12-21 15:08:06 +000010794
cristy3ed852e2009-09-05 21:47:34 +000010795 if (logging != MagickFalse)
10796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10797 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010798
cristy3ed852e2009-09-05 21:47:34 +000010799 return(MagickTrue);
10800/* End write one PNG image */
10801}
10802
10803/*
10804%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10805% %
10806% %
10807% %
10808% W r i t e P N G I m a g e %
10809% %
10810% %
10811% %
10812%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10813%
10814% WritePNGImage() writes a Portable Network Graphics (PNG) or
10815% Multiple-image Network Graphics (MNG) image file.
10816%
10817% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10818%
10819% The format of the WritePNGImage method is:
10820%
cristy1e178e72011-08-28 19:44:34 +000010821% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10822% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010823%
10824% A description of each parameter follows:
10825%
10826% o image_info: the image info.
10827%
10828% o image: The image.
10829%
cristy1e178e72011-08-28 19:44:34 +000010830% o exception: return any errors or warnings in this structure.
10831%
cristy3ed852e2009-09-05 21:47:34 +000010832% Returns MagickTrue on success, MagickFalse on failure.
10833%
10834% Communicating with the PNG encoder:
10835%
10836% While the datastream written is always in PNG format and normally would
10837% be given the "png" file extension, this method also writes the following
cristy5d6fc9c2011-12-27 03:10:42 +000010838% pseudo-formats which are subsets of png:
cristy3ed852e2009-09-05 21:47:34 +000010839%
glennrp5a39f372011-02-25 04:52:16 +000010840% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10841% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010842% is present, the tRNS chunk must only have values 0 and 255
10843% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010844% transparent). If other values are present they will be
10845% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010846% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000010847% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10848% of any resulting fully-transparent pixels is changed to
10849% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000010850%
10851% If you want better quantization or dithering of the colors
10852% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010853% PNG encoder. The pixels contain 8-bit indices even if
10854% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010855% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010856% PNG grayscale type might be slightly more efficient. Please
10857% note that writing to the PNG8 format may result in loss
10858% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010859%
10860% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10861% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010862% one of the colors as transparent. The only loss incurred
10863% is reduction of sample depth to 8. If the image has more
10864% than one transparent color, has semitransparent pixels, or
10865% has an opaque pixel with the same RGB components as the
10866% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010867%
10868% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10869% transparency is permitted, i.e., the alpha sample for
10870% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010871% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010872% The only loss in data is the reduction of the sample depth
10873% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010874%
10875% o -define: For more precise control of the PNG output, you can use the
10876% Image options "png:bit-depth" and "png:color-type". These
10877% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010878% from the application programming interfaces. The options
10879% are case-independent and are converted to lowercase before
10880% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010881%
10882% png:color-type can be 0, 2, 3, 4, or 6.
10883%
10884% When png:color-type is 0 (Grayscale), png:bit-depth can
10885% be 1, 2, 4, 8, or 16.
10886%
10887% When png:color-type is 2 (RGB), png:bit-depth can
10888% be 8 or 16.
10889%
10890% When png:color-type is 3 (Indexed), png:bit-depth can
10891% be 1, 2, 4, or 8. This refers to the number of bits
10892% used to store the index. The color samples always have
10893% bit-depth 8 in indexed PNG files.
10894%
10895% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10896% png:bit-depth can be 8 or 16.
10897%
glennrp5a39f372011-02-25 04:52:16 +000010898% If the image cannot be written without loss with the requested bit-depth
10899% and color-type, a PNG file will not be written, and the encoder will
10900% return MagickFalse.
10901%
cristy3ed852e2009-09-05 21:47:34 +000010902% Since image encoders should not be responsible for the "heavy lifting",
10903% the user should make sure that ImageMagick has already reduced the
10904% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010905% transparency prior to attempting to write the image with depth, color,
glennrp54dc0692011-07-01 19:02:13 +000010906% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010907%
cristy3ed852e2009-09-05 21:47:34 +000010908% Note that another definition, "png:bit-depth-written" exists, but it
10909% is not intended for external use. It is only used internally by the
10910% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10911%
10912% It is possible to request that the PNG encoder write previously-formatted
10913% ancillary chunks in the output PNG file, using the "-profile" commandline
10914% option as shown below or by setting the profile via a programming
10915% interface:
10916%
10917% -profile PNG-chunk-x:<file>
10918%
10919% where x is a location flag and <file> is a file containing the chunk
10920% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010921% This encoder will compute the chunk length and CRC, so those must not
10922% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010923%
10924% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10925% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10926% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010927% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010928%
glennrpbb8a7332010-11-13 15:17:35 +000010929% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010930%
glennrp3241bd02010-12-12 04:36:28 +000010931% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010932%
glennrpd6afd542010-11-19 01:53:05 +000010933% o 32-bit depth is reduced to 16.
10934% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10935% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010936% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010937% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010938% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010939% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10940% this can be done without loss and a larger bit depth N was not
cristy5d6fc9c2011-12-27 03:10:42 +000010941% requested via the "-define png:bit-depth=N" option.
glennrpd6afd542010-11-19 01:53:05 +000010942% o If matte channel is present but only one transparent color is
10943% present, RGB+tRNS is written instead of RGBA
10944% o Opaque matte channel is removed (or added, if color-type 4 or 6
10945% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010946%
cristy3ed852e2009-09-05 21:47:34 +000010947%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10948*/
10949static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +000010950 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010951{
10952 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010953 excluding,
10954 logging,
10955 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010956 status;
10957
10958 MngInfo
10959 *mng_info;
10960
10961 const char
10962 *value;
10963
10964 int
glennrp21f0e622011-01-07 16:20:57 +000010965 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010966 source;
10967
cristy3ed852e2009-09-05 21:47:34 +000010968 /*
10969 Open image file.
10970 */
10971 assert(image_info != (const ImageInfo *) NULL);
10972 assert(image_info->signature == MagickSignature);
10973 assert(image != (Image *) NULL);
10974 assert(image->signature == MagickSignature);
10975 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010976 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010977 /*
10978 Allocate a MngInfo structure.
10979 */
10980 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010981 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010982
cristy3ed852e2009-09-05 21:47:34 +000010983 if (mng_info == (MngInfo *) NULL)
10984 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010985
cristy3ed852e2009-09-05 21:47:34 +000010986 /*
10987 Initialize members of the MngInfo structure.
10988 */
10989 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10990 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010991 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010992 have_mng_structure=MagickTrue;
10993
10994 /* See if user has requested a specific PNG subformat */
10995
10996 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10997 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10998 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10999
11000 if (mng_info->write_png8)
11001 {
glennrp9c1eb072010-06-06 22:19:15 +000011002 mng_info->write_png_colortype = /* 3 */ 4;
11003 mng_info->write_png_depth = 8;
11004 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000011005 }
11006
11007 if (mng_info->write_png24)
11008 {
glennrp9c1eb072010-06-06 22:19:15 +000011009 mng_info->write_png_colortype = /* 2 */ 3;
11010 mng_info->write_png_depth = 8;
11011 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011012
glennrp9c1eb072010-06-06 22:19:15 +000011013 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011014 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011015
glennrp9c1eb072010-06-06 22:19:15 +000011016 else
cristy018f07f2011-09-04 21:15:19 +000011017 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011018
cristyea1a8aa2011-10-20 13:24:06 +000011019 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011020 }
11021
11022 if (mng_info->write_png32)
11023 {
glennrp9c1eb072010-06-06 22:19:15 +000011024 mng_info->write_png_colortype = /* 6 */ 7;
11025 mng_info->write_png_depth = 8;
11026 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011027
glennrp9c1eb072010-06-06 22:19:15 +000011028 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011029 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011030
glennrp9c1eb072010-06-06 22:19:15 +000011031 else
cristy018f07f2011-09-04 21:15:19 +000011032 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011033
cristyea1a8aa2011-10-20 13:24:06 +000011034 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011035 }
11036
11037 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011038
cristy3ed852e2009-09-05 21:47:34 +000011039 if (value != (char *) NULL)
11040 {
11041 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011042 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011043
cristy3ed852e2009-09-05 21:47:34 +000011044 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011045 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011046
cristy3ed852e2009-09-05 21:47:34 +000011047 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011048 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011049
cristy3ed852e2009-09-05 21:47:34 +000011050 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011051 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011052
cristy3ed852e2009-09-05 21:47:34 +000011053 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011054 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011055
glennrpbb8a7332010-11-13 15:17:35 +000011056 else
cristyc82a27b2011-10-21 01:07:16 +000011057 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011058 GetMagickModule(),CoderWarning,
11059 "ignoring invalid defined png:bit-depth",
11060 "=%s",value);
11061
cristy3ed852e2009-09-05 21:47:34 +000011062 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011064 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011065 }
glennrp0fe50b42010-11-16 03:52:51 +000011066
cristy3ed852e2009-09-05 21:47:34 +000011067 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011068
cristy3ed852e2009-09-05 21:47:34 +000011069 if (value != (char *) NULL)
11070 {
11071 /* We must store colortype+1 because 0 is a valid colortype */
11072 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011073 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011074
cristy3ed852e2009-09-05 21:47:34 +000011075 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011076 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011077
cristy3ed852e2009-09-05 21:47:34 +000011078 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011079 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011080
cristy3ed852e2009-09-05 21:47:34 +000011081 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011082 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011083
cristy3ed852e2009-09-05 21:47:34 +000011084 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011085 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011086
glennrpbb8a7332010-11-13 15:17:35 +000011087 else
cristyc82a27b2011-10-21 01:07:16 +000011088 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011089 GetMagickModule(),CoderWarning,
11090 "ignoring invalid defined png:color-type",
11091 "=%s",value);
11092
cristy3ed852e2009-09-05 21:47:34 +000011093 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011095 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011096 }
11097
glennrp0e8ea192010-12-24 18:00:33 +000011098 /* Check for chunks to be excluded:
11099 *
glennrp0dff56c2011-01-29 19:10:02 +000011100 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011101 * listed in the "unused_chunks" array, above.
11102 *
cristy5d6fc9c2011-12-27 03:10:42 +000011103 * Chunks can be listed for exclusion via a "png:exclude-chunk"
glennrp0e8ea192010-12-24 18:00:33 +000011104 * define (in the image properties or in the image artifacts)
11105 * or via a mng_info member. For convenience, in addition
11106 * to or instead of a comma-separated list of chunks, the
11107 * "exclude-chunk" string can be simply "all" or "none".
11108 *
11109 * The exclude-chunk define takes priority over the mng_info.
11110 *
cristy5d6fc9c2011-12-27 03:10:42 +000011111 * A "png:include-chunk" define takes priority over both the
11112 * mng_info and the "png:exclude-chunk" define. Like the
glennrp0e8ea192010-12-24 18:00:33 +000011113 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011114 * well as a comma-separated list. Chunks that are unknown to
11115 * ImageMagick are always excluded, regardless of their "copy-safe"
11116 * status according to the PNG specification, and even if they
11117 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000011118 *
11119 * Finally, all chunks listed in the "unused_chunks" array are
11120 * automatically excluded, regardless of the other instructions
11121 * or lack thereof.
11122 *
11123 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11124 * will not be written and the gAMA chunk will only be written if it
11125 * is not between .45 and .46, or approximately (1.0/2.2).
11126 *
11127 * If you exclude tRNS and the image has transparency, the colortype
11128 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11129 *
11130 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011131 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011132 */
11133
glennrp26f37912010-12-23 16:22:42 +000011134 mng_info->ping_exclude_bKGD=MagickFalse;
11135 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011136 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011137 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11138 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011139 mng_info->ping_exclude_iCCP=MagickFalse;
11140 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11141 mng_info->ping_exclude_oFFs=MagickFalse;
11142 mng_info->ping_exclude_pHYs=MagickFalse;
11143 mng_info->ping_exclude_sRGB=MagickFalse;
11144 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011145 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011146 mng_info->ping_exclude_vpAg=MagickFalse;
11147 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11148 mng_info->ping_exclude_zTXt=MagickFalse;
11149
glennrp8d3d6e52011-04-19 04:39:51 +000011150 mng_info->ping_preserve_colormap=MagickFalse;
11151
11152 value=GetImageArtifact(image,"png:preserve-colormap");
11153 if (value == NULL)
11154 value=GetImageOption(image_info,"png:preserve-colormap");
11155 if (value != NULL)
11156 mng_info->ping_preserve_colormap=MagickTrue;
11157
glennrp18682582011-06-30 18:11:47 +000011158 /* Thes compression-level, compression-strategy, and compression-filter
11159 * defines take precedence over values from the -quality option.
11160 */
11161 value=GetImageArtifact(image,"png:compression-level");
11162 if (value == NULL)
11163 value=GetImageOption(image_info,"png:compression-level");
11164 if (value != NULL)
11165 {
glennrp18682582011-06-30 18:11:47 +000011166 /* We have to add 1 to everything because 0 is a valid input,
11167 * and we want to use 0 (the default) to mean undefined.
11168 */
11169 if (LocaleCompare(value,"0") == 0)
11170 mng_info->write_png_compression_level = 1;
11171
11172 if (LocaleCompare(value,"1") == 0)
11173 mng_info->write_png_compression_level = 2;
11174
11175 else if (LocaleCompare(value,"2") == 0)
11176 mng_info->write_png_compression_level = 3;
11177
11178 else if (LocaleCompare(value,"3") == 0)
11179 mng_info->write_png_compression_level = 4;
11180
11181 else if (LocaleCompare(value,"4") == 0)
11182 mng_info->write_png_compression_level = 5;
11183
11184 else if (LocaleCompare(value,"5") == 0)
11185 mng_info->write_png_compression_level = 6;
11186
11187 else if (LocaleCompare(value,"6") == 0)
11188 mng_info->write_png_compression_level = 7;
11189
11190 else if (LocaleCompare(value,"7") == 0)
11191 mng_info->write_png_compression_level = 8;
11192
11193 else if (LocaleCompare(value,"8") == 0)
11194 mng_info->write_png_compression_level = 9;
11195
11196 else if (LocaleCompare(value,"9") == 0)
11197 mng_info->write_png_compression_level = 10;
11198
11199 else
cristyc82a27b2011-10-21 01:07:16 +000011200 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011201 GetMagickModule(),CoderWarning,
11202 "ignoring invalid defined png:compression-level",
11203 "=%s",value);
11204 }
11205
11206 value=GetImageArtifact(image,"png:compression-strategy");
11207 if (value == NULL)
11208 value=GetImageOption(image_info,"png:compression-strategy");
11209 if (value != NULL)
11210 {
11211
11212 if (LocaleCompare(value,"0") == 0)
11213 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11214
11215 else if (LocaleCompare(value,"1") == 0)
11216 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11217
11218 else if (LocaleCompare(value,"2") == 0)
11219 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11220
11221 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011222#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011223 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011224#else
11225 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11226#endif
glennrp18682582011-06-30 18:11:47 +000011227
11228 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011229#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011230 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011231#else
11232 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11233#endif
glennrp18682582011-06-30 18:11:47 +000011234
11235 else
cristyc82a27b2011-10-21 01:07:16 +000011236 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011237 GetMagickModule(),CoderWarning,
11238 "ignoring invalid defined png:compression-strategy",
11239 "=%s",value);
11240 }
11241
11242 value=GetImageArtifact(image,"png:compression-filter");
11243 if (value == NULL)
11244 value=GetImageOption(image_info,"png:compression-filter");
11245 if (value != NULL)
11246 {
11247
11248 /* To do: combinations of filters allowed by libpng
11249 * masks 0x08 through 0xf8
11250 *
11251 * Implement this as a comma-separated list of 0,1,2,3,4,5
11252 * where 5 is a special case meaning PNG_ALL_FILTERS.
11253 */
11254
11255 if (LocaleCompare(value,"0") == 0)
11256 mng_info->write_png_compression_filter = 1;
11257
11258 if (LocaleCompare(value,"1") == 0)
11259 mng_info->write_png_compression_filter = 2;
11260
11261 else if (LocaleCompare(value,"2") == 0)
11262 mng_info->write_png_compression_filter = 3;
11263
11264 else if (LocaleCompare(value,"3") == 0)
11265 mng_info->write_png_compression_filter = 4;
11266
11267 else if (LocaleCompare(value,"4") == 0)
11268 mng_info->write_png_compression_filter = 5;
11269
11270 else if (LocaleCompare(value,"5") == 0)
11271 mng_info->write_png_compression_filter = 6;
11272
glennrp18682582011-06-30 18:11:47 +000011273 else
cristyc82a27b2011-10-21 01:07:16 +000011274 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011275 GetMagickModule(),CoderWarning,
11276 "ignoring invalid defined png:compression-filter",
11277 "=%s",value);
11278 }
11279
glennrp03812ae2010-12-24 01:31:34 +000011280 excluding=MagickFalse;
11281
glennrp5c7cf4e2010-12-24 00:30:00 +000011282 for (source=0; source<1; source++)
11283 {
11284 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011285 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011286 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011287
11288 if (value == NULL)
11289 value=GetImageArtifact(image,"png:exclude-chunks");
11290 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011291 else
glennrpacba0042010-12-24 14:27:26 +000011292 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011293 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011294
glennrpacba0042010-12-24 14:27:26 +000011295 if (value == NULL)
11296 value=GetImageOption(image_info,"png:exclude-chunks");
11297 }
11298
glennrp03812ae2010-12-24 01:31:34 +000011299 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011300 {
glennrp03812ae2010-12-24 01:31:34 +000011301
11302 size_t
11303 last;
11304
11305 excluding=MagickTrue;
11306
11307 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011308 {
11309 if (source == 0)
11310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11311 " png:exclude-chunk=%s found in image artifacts.\n", value);
11312 else
11313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11314 " png:exclude-chunk=%s found in image properties.\n", value);
11315 }
glennrp03812ae2010-12-24 01:31:34 +000011316
11317 last=strlen(value);
11318
11319 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011320 {
glennrp03812ae2010-12-24 01:31:34 +000011321
11322 if (LocaleNCompare(value+i,"all",3) == 0)
11323 {
11324 mng_info->ping_exclude_bKGD=MagickTrue;
11325 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011326 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011327 mng_info->ping_exclude_EXIF=MagickTrue;
11328 mng_info->ping_exclude_gAMA=MagickTrue;
11329 mng_info->ping_exclude_iCCP=MagickTrue;
11330 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11331 mng_info->ping_exclude_oFFs=MagickTrue;
11332 mng_info->ping_exclude_pHYs=MagickTrue;
11333 mng_info->ping_exclude_sRGB=MagickTrue;
11334 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011335 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011336 mng_info->ping_exclude_vpAg=MagickTrue;
11337 mng_info->ping_exclude_zCCP=MagickTrue;
11338 mng_info->ping_exclude_zTXt=MagickTrue;
11339 i--;
11340 }
glennrp2cc891a2010-12-24 13:44:32 +000011341
glennrp03812ae2010-12-24 01:31:34 +000011342 if (LocaleNCompare(value+i,"none",4) == 0)
11343 {
11344 mng_info->ping_exclude_bKGD=MagickFalse;
11345 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011346 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011347 mng_info->ping_exclude_EXIF=MagickFalse;
11348 mng_info->ping_exclude_gAMA=MagickFalse;
11349 mng_info->ping_exclude_iCCP=MagickFalse;
11350 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11351 mng_info->ping_exclude_oFFs=MagickFalse;
11352 mng_info->ping_exclude_pHYs=MagickFalse;
11353 mng_info->ping_exclude_sRGB=MagickFalse;
11354 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011355 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011356 mng_info->ping_exclude_vpAg=MagickFalse;
11357 mng_info->ping_exclude_zCCP=MagickFalse;
11358 mng_info->ping_exclude_zTXt=MagickFalse;
11359 }
glennrp2cc891a2010-12-24 13:44:32 +000011360
glennrp03812ae2010-12-24 01:31:34 +000011361 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11362 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011363
glennrp03812ae2010-12-24 01:31:34 +000011364 if (LocaleNCompare(value+i,"chrm",4) == 0)
11365 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011366
glennrpa0ed0092011-04-18 16:36:29 +000011367 if (LocaleNCompare(value+i,"date",4) == 0)
11368 mng_info->ping_exclude_date=MagickTrue;
11369
glennrp03812ae2010-12-24 01:31:34 +000011370 if (LocaleNCompare(value+i,"exif",4) == 0)
11371 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011372
glennrp03812ae2010-12-24 01:31:34 +000011373 if (LocaleNCompare(value+i,"gama",4) == 0)
11374 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011375
glennrp03812ae2010-12-24 01:31:34 +000011376 if (LocaleNCompare(value+i,"iccp",4) == 0)
11377 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011378
glennrp03812ae2010-12-24 01:31:34 +000011379 /*
11380 if (LocaleNCompare(value+i,"itxt",4) == 0)
11381 mng_info->ping_exclude_iTXt=MagickTrue;
11382 */
glennrp2cc891a2010-12-24 13:44:32 +000011383
glennrp03812ae2010-12-24 01:31:34 +000011384 if (LocaleNCompare(value+i,"gama",4) == 0)
11385 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011386
glennrp03812ae2010-12-24 01:31:34 +000011387 if (LocaleNCompare(value+i,"offs",4) == 0)
11388 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011389
glennrp03812ae2010-12-24 01:31:34 +000011390 if (LocaleNCompare(value+i,"phys",4) == 0)
11391 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011392
glennrpa1e3b7b2010-12-24 16:37:33 +000011393 if (LocaleNCompare(value+i,"srgb",4) == 0)
11394 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011395
glennrp03812ae2010-12-24 01:31:34 +000011396 if (LocaleNCompare(value+i,"text",4) == 0)
11397 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011398
glennrpa1e3b7b2010-12-24 16:37:33 +000011399 if (LocaleNCompare(value+i,"trns",4) == 0)
11400 mng_info->ping_exclude_tRNS=MagickTrue;
11401
glennrp03812ae2010-12-24 01:31:34 +000011402 if (LocaleNCompare(value+i,"vpag",4) == 0)
11403 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011404
glennrp03812ae2010-12-24 01:31:34 +000011405 if (LocaleNCompare(value+i,"zccp",4) == 0)
11406 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011407
glennrp03812ae2010-12-24 01:31:34 +000011408 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11409 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011410
glennrp03812ae2010-12-24 01:31:34 +000011411 }
glennrpce91ed52010-12-23 22:37:49 +000011412 }
glennrp26f37912010-12-23 16:22:42 +000011413 }
11414
glennrp5c7cf4e2010-12-24 00:30:00 +000011415 for (source=0; source<1; source++)
11416 {
11417 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011418 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011419 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011420
11421 if (value == NULL)
11422 value=GetImageArtifact(image,"png:include-chunks");
11423 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011424 else
glennrpacba0042010-12-24 14:27:26 +000011425 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011426 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011427
glennrpacba0042010-12-24 14:27:26 +000011428 if (value == NULL)
11429 value=GetImageOption(image_info,"png:include-chunks");
11430 }
11431
glennrp03812ae2010-12-24 01:31:34 +000011432 if (value != NULL)
11433 {
11434 size_t
11435 last;
glennrp26f37912010-12-23 16:22:42 +000011436
glennrp03812ae2010-12-24 01:31:34 +000011437 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011438
glennrp03812ae2010-12-24 01:31:34 +000011439 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011440 {
11441 if (source == 0)
11442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11443 " png:include-chunk=%s found in image artifacts.\n", value);
11444 else
11445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11446 " png:include-chunk=%s found in image properties.\n", value);
11447 }
glennrp03812ae2010-12-24 01:31:34 +000011448
11449 last=strlen(value);
11450
11451 for (i=0; i<(int) last; i+=5)
11452 {
11453 if (LocaleNCompare(value+i,"all",3) == 0)
11454 {
11455 mng_info->ping_exclude_bKGD=MagickFalse;
11456 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011457 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011458 mng_info->ping_exclude_EXIF=MagickFalse;
11459 mng_info->ping_exclude_gAMA=MagickFalse;
11460 mng_info->ping_exclude_iCCP=MagickFalse;
11461 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11462 mng_info->ping_exclude_oFFs=MagickFalse;
11463 mng_info->ping_exclude_pHYs=MagickFalse;
11464 mng_info->ping_exclude_sRGB=MagickFalse;
11465 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011466 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011467 mng_info->ping_exclude_vpAg=MagickFalse;
11468 mng_info->ping_exclude_zCCP=MagickFalse;
11469 mng_info->ping_exclude_zTXt=MagickFalse;
11470 i--;
11471 }
glennrp2cc891a2010-12-24 13:44:32 +000011472
glennrp03812ae2010-12-24 01:31:34 +000011473 if (LocaleNCompare(value+i,"none",4) == 0)
11474 {
11475 mng_info->ping_exclude_bKGD=MagickTrue;
11476 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011477 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011478 mng_info->ping_exclude_EXIF=MagickTrue;
11479 mng_info->ping_exclude_gAMA=MagickTrue;
11480 mng_info->ping_exclude_iCCP=MagickTrue;
11481 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11482 mng_info->ping_exclude_oFFs=MagickTrue;
11483 mng_info->ping_exclude_pHYs=MagickTrue;
11484 mng_info->ping_exclude_sRGB=MagickTrue;
11485 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011486 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011487 mng_info->ping_exclude_vpAg=MagickTrue;
11488 mng_info->ping_exclude_zCCP=MagickTrue;
11489 mng_info->ping_exclude_zTXt=MagickTrue;
11490 }
glennrp2cc891a2010-12-24 13:44:32 +000011491
glennrp03812ae2010-12-24 01:31:34 +000011492 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11493 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011494
glennrp03812ae2010-12-24 01:31:34 +000011495 if (LocaleNCompare(value+i,"chrm",4) == 0)
11496 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011497
glennrpa0ed0092011-04-18 16:36:29 +000011498 if (LocaleNCompare(value+i,"date",4) == 0)
11499 mng_info->ping_exclude_date=MagickFalse;
11500
glennrp03812ae2010-12-24 01:31:34 +000011501 if (LocaleNCompare(value+i,"exif",4) == 0)
11502 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011503
glennrp03812ae2010-12-24 01:31:34 +000011504 if (LocaleNCompare(value+i,"gama",4) == 0)
11505 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011506
glennrp03812ae2010-12-24 01:31:34 +000011507 if (LocaleNCompare(value+i,"iccp",4) == 0)
11508 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011509
glennrp03812ae2010-12-24 01:31:34 +000011510 /*
11511 if (LocaleNCompare(value+i,"itxt",4) == 0)
11512 mng_info->ping_exclude_iTXt=MagickFalse;
11513 */
glennrp2cc891a2010-12-24 13:44:32 +000011514
glennrp03812ae2010-12-24 01:31:34 +000011515 if (LocaleNCompare(value+i,"gama",4) == 0)
11516 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011517
glennrp03812ae2010-12-24 01:31:34 +000011518 if (LocaleNCompare(value+i,"offs",4) == 0)
11519 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011520
glennrp03812ae2010-12-24 01:31:34 +000011521 if (LocaleNCompare(value+i,"phys",4) == 0)
11522 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011523
glennrpa1e3b7b2010-12-24 16:37:33 +000011524 if (LocaleNCompare(value+i,"srgb",4) == 0)
11525 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011526
glennrp03812ae2010-12-24 01:31:34 +000011527 if (LocaleNCompare(value+i,"text",4) == 0)
11528 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011529
glennrpa1e3b7b2010-12-24 16:37:33 +000011530 if (LocaleNCompare(value+i,"trns",4) == 0)
11531 mng_info->ping_exclude_tRNS=MagickFalse;
11532
glennrp03812ae2010-12-24 01:31:34 +000011533 if (LocaleNCompare(value+i,"vpag",4) == 0)
11534 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011535
glennrp03812ae2010-12-24 01:31:34 +000011536 if (LocaleNCompare(value+i,"zccp",4) == 0)
11537 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011538
glennrp03812ae2010-12-24 01:31:34 +000011539 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11540 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011541
glennrp03812ae2010-12-24 01:31:34 +000011542 }
glennrpce91ed52010-12-23 22:37:49 +000011543 }
glennrp26f37912010-12-23 16:22:42 +000011544 }
11545
glennrp03812ae2010-12-24 01:31:34 +000011546 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011547 {
11548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011549 " Chunks to be excluded from the output png:");
glennrp26f37912010-12-23 16:22:42 +000011550 if (mng_info->ping_exclude_bKGD != MagickFalse)
11551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11552 " bKGD");
11553 if (mng_info->ping_exclude_cHRM != MagickFalse)
11554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11555 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011556 if (mng_info->ping_exclude_date != MagickFalse)
11557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11558 " date");
glennrp26f37912010-12-23 16:22:42 +000011559 if (mng_info->ping_exclude_EXIF != MagickFalse)
11560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11561 " EXIF");
11562 if (mng_info->ping_exclude_gAMA != MagickFalse)
11563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11564 " gAMA");
11565 if (mng_info->ping_exclude_iCCP != MagickFalse)
11566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11567 " iCCP");
11568/*
11569 if (mng_info->ping_exclude_iTXt != MagickFalse)
11570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11571 " iTXt");
11572*/
11573 if (mng_info->ping_exclude_oFFs != MagickFalse)
11574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11575 " oFFs");
11576 if (mng_info->ping_exclude_pHYs != MagickFalse)
11577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11578 " pHYs");
11579 if (mng_info->ping_exclude_sRGB != MagickFalse)
11580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11581 " sRGB");
11582 if (mng_info->ping_exclude_tEXt != MagickFalse)
11583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11584 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011585 if (mng_info->ping_exclude_tRNS != MagickFalse)
11586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11587 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011588 if (mng_info->ping_exclude_vpAg != MagickFalse)
11589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11590 " vpAg");
11591 if (mng_info->ping_exclude_zCCP != MagickFalse)
11592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11593 " zCCP");
11594 if (mng_info->ping_exclude_zTXt != MagickFalse)
11595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11596 " zTXt");
11597 }
11598
glennrpb9cfe272010-12-21 15:08:06 +000011599 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011600
cristy018f07f2011-09-04 21:15:19 +000011601 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011602
11603 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011604
cristy3ed852e2009-09-05 21:47:34 +000011605 if (logging != MagickFalse)
11606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011607
cristy3ed852e2009-09-05 21:47:34 +000011608 return(status);
11609}
11610
11611#if defined(JNG_SUPPORTED)
11612
11613/* Write one JNG image */
11614static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +000011615 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011616{
11617 Image
11618 *jpeg_image;
11619
11620 ImageInfo
11621 *jpeg_image_info;
11622
11623 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011624 logging,
cristy3ed852e2009-09-05 21:47:34 +000011625 status;
11626
11627 size_t
11628 length;
11629
11630 unsigned char
11631 *blob,
11632 chunk[80],
11633 *p;
11634
11635 unsigned int
11636 jng_alpha_compression_method,
11637 jng_alpha_sample_depth,
11638 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011639 transparent;
11640
cristybb503372010-05-27 20:51:26 +000011641 size_t
glennrp59575fa2011-12-31 21:31:39 +000011642 jng_alpha_quality,
cristy3ed852e2009-09-05 21:47:34 +000011643 jng_quality;
11644
11645 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011646 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011647
11648 blob=(unsigned char *) NULL;
11649 jpeg_image=(Image *) NULL;
11650 jpeg_image_info=(ImageInfo *) NULL;
11651
11652 status=MagickTrue;
11653 transparent=image_info->type==GrayscaleMatteType ||
glennrp59575fa2011-12-31 21:31:39 +000011654 image_info->type==TrueColorMatteType || image->matte != MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +000011655
glennrp59575fa2011-12-31 21:31:39 +000011656 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
11657
11658 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
11659
11660 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
11661 image_info->quality;
11662
11663 if (jng_alpha_quality >= 1000)
11664 jng_alpha_quality /= 1000;
cristy3ed852e2009-09-05 21:47:34 +000011665
11666 if (transparent)
11667 {
cristybd5a96c2011-08-21 00:04:26 +000011668 ChannelType
11669 channel_mask;
11670
cristy3ed852e2009-09-05 21:47:34 +000011671 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011672
cristy3ed852e2009-09-05 21:47:34 +000011673 /* Create JPEG blob, image, and image_info */
11674 if (logging != MagickFalse)
11675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +000011676 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011677
cristy3ed852e2009-09-05 21:47:34 +000011678 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011679
cristy3ed852e2009-09-05 21:47:34 +000011680 if (jpeg_image_info == (ImageInfo *) NULL)
11681 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011682
cristy3ed852e2009-09-05 21:47:34 +000011683 if (logging != MagickFalse)
11684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11685 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011686
cristyc82a27b2011-10-21 01:07:16 +000011687 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011688
cristy3ed852e2009-09-05 21:47:34 +000011689 if (jpeg_image == (Image *) NULL)
11690 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011691
cristy3ed852e2009-09-05 21:47:34 +000011692 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristybd5a96c2011-08-21 00:04:26 +000011693 channel_mask=SetPixelChannelMask(jpeg_image,AlphaChannel);
cristye941a752011-10-15 01:52:48 +000011694 status=SeparateImage(jpeg_image,exception);
cristye2a912b2011-12-05 20:02:07 +000011695 (void) SetPixelChannelMapMask(jpeg_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +000011696 jpeg_image->matte=MagickFalse;
glennrp59575fa2011-12-31 21:31:39 +000011697 jpeg_image->quality=jng_alpha_quality;
cristy3ed852e2009-09-05 21:47:34 +000011698 jpeg_image_info->type=GrayscaleType;
cristy018f07f2011-09-04 21:15:19 +000011699 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000011700 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011701 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011702 "%s",jpeg_image->filename);
11703 }
glennrp59575fa2011-12-31 21:31:39 +000011704 else
11705 {
11706 jng_alpha_compression_method=0;
11707 jng_color_type=10;
11708 jng_alpha_sample_depth=0;
11709 }
cristy3ed852e2009-09-05 21:47:34 +000011710
11711 /* To do: check bit depth of PNG alpha channel */
11712
11713 /* Check if image is grayscale. */
11714 if (image_info->type != TrueColorMatteType && image_info->type !=
cristyc82a27b2011-10-21 01:07:16 +000011715 TrueColorType && ImageIsGray(image,exception))
cristy3ed852e2009-09-05 21:47:34 +000011716 jng_color_type-=2;
11717
glennrp59575fa2011-12-31 21:31:39 +000011718 if (logging != MagickFalse)
11719 {
11720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11721 " JNG Quality = %d",(int) jng_quality);
11722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11723 " JNG Color Type = %d",jng_color_type);
11724 if (transparent)
11725 {
11726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11727 " JNG Alpha Compression = %d",jng_alpha_compression_method);
11728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11729 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
11730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11731 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
11732 }
11733 }
11734
cristy3ed852e2009-09-05 21:47:34 +000011735 if (transparent)
11736 {
11737 if (jng_alpha_compression_method==0)
11738 {
11739 const char
11740 *value;
11741
cristy4c08aed2011-07-01 19:47:50 +000011742 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011743 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000011744 exception);
cristy3ed852e2009-09-05 21:47:34 +000011745 if (logging != MagickFalse)
11746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11747 " Creating PNG blob.");
11748 length=0;
11749
11750 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11751 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11752 jpeg_image_info->interlace=NoInterlace;
11753
glennrp2aec3322012-01-02 20:16:54 +000011754 /* Exclude all ancillary chunks */
11755 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
11756
cristy3ed852e2009-09-05 21:47:34 +000011757 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyc82a27b2011-10-21 01:07:16 +000011758 exception);
cristy3ed852e2009-09-05 21:47:34 +000011759
11760 /* Retrieve sample depth used */
cristyd15e6592011-10-15 00:13:06 +000011761 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
cristy3ed852e2009-09-05 21:47:34 +000011762 if (value != (char *) NULL)
11763 jng_alpha_sample_depth= (unsigned int) value[0];
11764 }
11765 else
11766 {
cristy4c08aed2011-07-01 19:47:50 +000011767 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011768
11769 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000011770 exception);
cristy3ed852e2009-09-05 21:47:34 +000011771
11772 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11773 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11774 jpeg_image_info->interlace=NoInterlace;
11775 if (logging != MagickFalse)
11776 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11777 " Creating blob.");
11778 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyc82a27b2011-10-21 01:07:16 +000011779 exception);
cristy3ed852e2009-09-05 21:47:34 +000011780 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011781
cristy3ed852e2009-09-05 21:47:34 +000011782 if (logging != MagickFalse)
11783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011784 " Successfully read jpeg_image into a blob, length=%.20g.",
11785 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011786
11787 }
11788 /* Destroy JPEG image and image_info */
11789 jpeg_image=DestroyImage(jpeg_image);
11790 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11791 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11792 }
11793
11794 /* Write JHDR chunk */
11795 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11796 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011797 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011798 PNGLong(chunk+4,(png_uint_32) image->columns);
11799 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011800 chunk[12]=jng_color_type;
11801 chunk[13]=8; /* sample depth */
11802 chunk[14]=8; /*jng_image_compression_method */
11803 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11804 chunk[16]=jng_alpha_sample_depth;
11805 chunk[17]=jng_alpha_compression_method;
11806 chunk[18]=0; /*jng_alpha_filter_method */
11807 chunk[19]=0; /*jng_alpha_interlace_method */
11808 (void) WriteBlob(image,20,chunk);
11809 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11810 if (logging != MagickFalse)
11811 {
11812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011813 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011814
cristy3ed852e2009-09-05 21:47:34 +000011815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011816 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011817
cristy3ed852e2009-09-05 21:47:34 +000011818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11819 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011820
cristy3ed852e2009-09-05 21:47:34 +000011821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11822 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011823
cristy3ed852e2009-09-05 21:47:34 +000011824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11825 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011826
cristy3ed852e2009-09-05 21:47:34 +000011827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11828 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011829
cristy3ed852e2009-09-05 21:47:34 +000011830 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11831 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011832
cristy3ed852e2009-09-05 21:47:34 +000011833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11834 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011835
cristy3ed852e2009-09-05 21:47:34 +000011836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11837 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011838
cristy3ed852e2009-09-05 21:47:34 +000011839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11840 " JNG alpha interlace:%5d",0);
11841 }
11842
glennrp0fe50b42010-11-16 03:52:51 +000011843 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011844 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011845
11846 /*
11847 Write leading ancillary chunks
11848 */
11849
11850 if (transparent)
11851 {
11852 /*
11853 Write JNG bKGD chunk
11854 */
11855
11856 unsigned char
11857 blue,
11858 green,
11859 red;
11860
cristybb503372010-05-27 20:51:26 +000011861 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011862 num_bytes;
11863
11864 if (jng_color_type == 8 || jng_color_type == 12)
11865 num_bytes=6L;
11866 else
11867 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011868 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011869 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011870 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011871 red=ScaleQuantumToChar(image->background_color.red);
11872 green=ScaleQuantumToChar(image->background_color.green);
11873 blue=ScaleQuantumToChar(image->background_color.blue);
11874 *(chunk+4)=0;
11875 *(chunk+5)=red;
11876 *(chunk+6)=0;
11877 *(chunk+7)=green;
11878 *(chunk+8)=0;
11879 *(chunk+9)=blue;
11880 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11881 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11882 }
11883
11884 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11885 {
11886 /*
11887 Write JNG sRGB chunk
11888 */
11889 (void) WriteBlobMSBULong(image,1L);
11890 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011891 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011892
cristy3ed852e2009-09-05 21:47:34 +000011893 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011894 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011895 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011896 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011897
cristy3ed852e2009-09-05 21:47:34 +000011898 else
glennrpe610a072010-08-05 17:08:46 +000011899 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011900 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011901 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011902
cristy3ed852e2009-09-05 21:47:34 +000011903 (void) WriteBlob(image,5,chunk);
11904 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11905 }
11906 else
11907 {
11908 if (image->gamma != 0.0)
11909 {
11910 /*
11911 Write JNG gAMA chunk
11912 */
11913 (void) WriteBlobMSBULong(image,4L);
11914 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011915 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011916 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011917 (void) WriteBlob(image,8,chunk);
11918 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11919 }
glennrp0fe50b42010-11-16 03:52:51 +000011920
cristy3ed852e2009-09-05 21:47:34 +000011921 if ((mng_info->equal_chrms == MagickFalse) &&
11922 (image->chromaticity.red_primary.x != 0.0))
11923 {
11924 PrimaryInfo
11925 primary;
11926
11927 /*
11928 Write JNG cHRM chunk
11929 */
11930 (void) WriteBlobMSBULong(image,32L);
11931 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011932 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011933 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011934 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11935 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011936 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011937 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11938 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011939 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011940 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11941 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011942 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011943 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11944 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011945 (void) WriteBlob(image,36,chunk);
11946 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11947 }
11948 }
glennrp0fe50b42010-11-16 03:52:51 +000011949
cristy2a11bef2011-10-28 18:33:11 +000011950 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000011951 {
11952 /*
11953 Write JNG pHYs chunk
11954 */
11955 (void) WriteBlobMSBULong(image,9L);
11956 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011957 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011958 if (image->units == PixelsPerInchResolution)
11959 {
cristy35ef8242010-06-03 16:24:13 +000011960 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011961 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011962
cristy35ef8242010-06-03 16:24:13 +000011963 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011964 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011965
cristy3ed852e2009-09-05 21:47:34 +000011966 chunk[12]=1;
11967 }
glennrp0fe50b42010-11-16 03:52:51 +000011968
cristy3ed852e2009-09-05 21:47:34 +000011969 else
11970 {
11971 if (image->units == PixelsPerCentimeterResolution)
11972 {
cristy35ef8242010-06-03 16:24:13 +000011973 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011974 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011975
cristy35ef8242010-06-03 16:24:13 +000011976 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011977 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011978
cristy3ed852e2009-09-05 21:47:34 +000011979 chunk[12]=1;
11980 }
glennrp0fe50b42010-11-16 03:52:51 +000011981
cristy3ed852e2009-09-05 21:47:34 +000011982 else
11983 {
cristy2a11bef2011-10-28 18:33:11 +000011984 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
11985 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011986 chunk[12]=0;
11987 }
11988 }
11989 (void) WriteBlob(image,13,chunk);
11990 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11991 }
11992
11993 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11994 {
11995 /*
11996 Write JNG oFFs chunk
11997 */
11998 (void) WriteBlobMSBULong(image,9L);
11999 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000012000 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000012001 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12002 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000012003 chunk[12]=0;
12004 (void) WriteBlob(image,13,chunk);
12005 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12006 }
12007 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12008 {
12009 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12010 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000012011 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000012012 PNGLong(chunk+4,(png_uint_32) image->page.width);
12013 PNGLong(chunk+8,(png_uint_32) image->page.height);
12014 chunk[12]=0; /* unit = pixels */
12015 (void) WriteBlob(image,13,chunk);
12016 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12017 }
12018
12019
12020 if (transparent)
12021 {
12022 if (jng_alpha_compression_method==0)
12023 {
cristybb503372010-05-27 20:51:26 +000012024 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012025 i;
12026
cristybb503372010-05-27 20:51:26 +000012027 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012028 len;
12029
12030 /* Write IDAT chunk header */
12031 if (logging != MagickFalse)
12032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012033 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000012034 length);
cristy3ed852e2009-09-05 21:47:34 +000012035
12036 /* Copy IDAT chunks */
12037 len=0;
12038 p=blob+8;
cristybb503372010-05-27 20:51:26 +000012039 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000012040 {
12041 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12042 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012043
cristy3ed852e2009-09-05 21:47:34 +000012044 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12045 {
12046 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000012047 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000012048 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000012049 (void) WriteBlob(image,(size_t) len+4,p);
12050 (void) WriteBlobMSBULong(image,
12051 crc32(0,p,(uInt) len+4));
12052 }
glennrp0fe50b42010-11-16 03:52:51 +000012053
cristy3ed852e2009-09-05 21:47:34 +000012054 else
12055 {
12056 if (logging != MagickFalse)
12057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012058 " Skipping %c%c%c%c chunk, length=%.20g.",
12059 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012060 }
12061 p+=(8+len);
12062 }
12063 }
12064 else
12065 {
12066 /* Write JDAA chunk header */
12067 if (logging != MagickFalse)
12068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012069 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012070 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012071 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012072 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012073 /* Write JDAT chunk(s) data */
12074 (void) WriteBlob(image,4,chunk);
12075 (void) WriteBlob(image,length,blob);
12076 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12077 (uInt) length));
12078 }
12079 blob=(unsigned char *) RelinquishMagickMemory(blob);
12080 }
12081
12082 /* Encode image as a JPEG blob */
12083 if (logging != MagickFalse)
12084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12085 " Creating jpeg_image_info.");
12086 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12087 if (jpeg_image_info == (ImageInfo *) NULL)
12088 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12089
12090 if (logging != MagickFalse)
12091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12092 " Creating jpeg_image.");
12093
cristyc82a27b2011-10-21 01:07:16 +000012094 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012095 if (jpeg_image == (Image *) NULL)
12096 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12097 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12098
12099 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012100 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012101 jpeg_image->filename);
12102
12103 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000012104 exception);
cristy3ed852e2009-09-05 21:47:34 +000012105
12106 if (logging != MagickFalse)
12107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012108 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12109 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012110
12111 if (jng_color_type == 8 || jng_color_type == 12)
12112 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012113
glennrp59575fa2011-12-31 21:31:39 +000012114 jpeg_image_info->quality=jng_quality;
12115 jpeg_image->quality=jng_quality;
cristy3ed852e2009-09-05 21:47:34 +000012116 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12117 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012118
cristy3ed852e2009-09-05 21:47:34 +000012119 if (logging != MagickFalse)
12120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12121 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012122
cristyc82a27b2011-10-21 01:07:16 +000012123 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
glennrp0fe50b42010-11-16 03:52:51 +000012124
cristy3ed852e2009-09-05 21:47:34 +000012125 if (logging != MagickFalse)
12126 {
12127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012128 " Successfully read jpeg_image into a blob, length=%.20g.",
12129 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012130
12131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012132 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012133 }
glennrp0fe50b42010-11-16 03:52:51 +000012134
cristy3ed852e2009-09-05 21:47:34 +000012135 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012136 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012137 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012138 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012139 (void) WriteBlob(image,4,chunk);
12140 (void) WriteBlob(image,length,blob);
12141 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12142
12143 jpeg_image=DestroyImage(jpeg_image);
12144 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12145 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12146 blob=(unsigned char *) RelinquishMagickMemory(blob);
12147
12148 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012149 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012150
12151 /* Write IEND chunk */
12152 (void) WriteBlobMSBULong(image,0L);
12153 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012154 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012155 (void) WriteBlob(image,4,chunk);
12156 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12157
12158 if (logging != MagickFalse)
12159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12160 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012161
cristy3ed852e2009-09-05 21:47:34 +000012162 return(status);
12163}
12164
12165
12166/*
12167%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12168% %
12169% %
12170% %
12171% W r i t e J N G I m a g e %
12172% %
12173% %
12174% %
12175%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12176%
12177% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12178%
12179% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12180%
12181% The format of the WriteJNGImage method is:
12182%
cristy1e178e72011-08-28 19:44:34 +000012183% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12184% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012185%
12186% A description of each parameter follows:
12187%
12188% o image_info: the image info.
12189%
12190% o image: The image.
12191%
cristy1e178e72011-08-28 19:44:34 +000012192% o exception: return any errors or warnings in this structure.
12193%
cristy3ed852e2009-09-05 21:47:34 +000012194%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12195*/
cristy1e178e72011-08-28 19:44:34 +000012196static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12197 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012198{
12199 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012200 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012201 logging,
cristy3ed852e2009-09-05 21:47:34 +000012202 status;
12203
12204 MngInfo
12205 *mng_info;
12206
cristy3ed852e2009-09-05 21:47:34 +000012207 /*
12208 Open image file.
12209 */
12210 assert(image_info != (const ImageInfo *) NULL);
12211 assert(image_info->signature == MagickSignature);
12212 assert(image != (Image *) NULL);
12213 assert(image->signature == MagickSignature);
12214 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012215 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristyc82a27b2011-10-21 01:07:16 +000012216 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012217 if (status == MagickFalse)
12218 return(status);
12219
12220 /*
12221 Allocate a MngInfo structure.
12222 */
12223 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012224 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012225 if (mng_info == (MngInfo *) NULL)
12226 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12227 /*
12228 Initialize members of the MngInfo structure.
12229 */
12230 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12231 mng_info->image=image;
12232 have_mng_structure=MagickTrue;
12233
12234 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12235
cristy018f07f2011-09-04 21:15:19 +000012236 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012237 (void) CloseBlob(image);
12238
12239 (void) CatchImageException(image);
12240 MngInfoFreeStruct(mng_info,&have_mng_structure);
12241 if (logging != MagickFalse)
12242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12243 return(status);
12244}
12245#endif
12246
cristy1e178e72011-08-28 19:44:34 +000012247static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12248 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012249{
12250 const char
12251 *option;
12252
12253 Image
12254 *next_image;
12255
12256 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012257 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012258 status;
12259
glennrp03812ae2010-12-24 01:31:34 +000012260 volatile MagickBooleanType
12261 logging;
12262
cristy3ed852e2009-09-05 21:47:34 +000012263 MngInfo
12264 *mng_info;
12265
12266 int
cristy3ed852e2009-09-05 21:47:34 +000012267 image_count,
12268 need_iterations,
12269 need_matte;
12270
12271 volatile int
12272#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12273 defined(PNG_MNG_FEATURES_SUPPORTED)
12274 need_local_plte,
12275#endif
12276 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012277 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012278 use_global_plte;
12279
cristybb503372010-05-27 20:51:26 +000012280 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012281 i;
12282
12283 unsigned char
12284 chunk[800];
12285
12286 volatile unsigned int
12287 write_jng,
12288 write_mng;
12289
cristybb503372010-05-27 20:51:26 +000012290 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012291 scene;
12292
cristybb503372010-05-27 20:51:26 +000012293 size_t
cristy3ed852e2009-09-05 21:47:34 +000012294 final_delay=0,
12295 initial_delay;
12296
glennrpd5045b42010-03-24 12:40:35 +000012297#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012298 if (image_info->verbose)
12299 printf("Your PNG library (libpng-%s) is rather old.\n",
12300 PNG_LIBPNG_VER_STRING);
12301#endif
12302
12303 /*
12304 Open image file.
12305 */
12306 assert(image_info != (const ImageInfo *) NULL);
12307 assert(image_info->signature == MagickSignature);
12308 assert(image != (Image *) NULL);
12309 assert(image->signature == MagickSignature);
12310 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012311 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristyc82a27b2011-10-21 01:07:16 +000012312 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012313 if (status == MagickFalse)
12314 return(status);
12315
12316 /*
12317 Allocate a MngInfo structure.
12318 */
12319 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012320 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012321 if (mng_info == (MngInfo *) NULL)
12322 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12323 /*
12324 Initialize members of the MngInfo structure.
12325 */
12326 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12327 mng_info->image=image;
12328 have_mng_structure=MagickTrue;
12329 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12330
12331 /*
12332 * See if user has requested a specific PNG subformat to be used
12333 * for all of the PNGs in the MNG being written, e.g.,
12334 *
12335 * convert *.png png8:animation.mng
12336 *
12337 * To do: check -define png:bit_depth and png:color_type as well,
12338 * or perhaps use mng:bit_depth and mng:color_type instead for
12339 * global settings.
12340 */
12341
12342 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12343 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12344 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12345
12346 write_jng=MagickFalse;
12347 if (image_info->compression == JPEGCompression)
12348 write_jng=MagickTrue;
12349
12350 mng_info->adjoin=image_info->adjoin &&
12351 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12352
cristy3ed852e2009-09-05 21:47:34 +000012353 if (logging != MagickFalse)
12354 {
12355 /* Log some info about the input */
12356 Image
12357 *p;
12358
12359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12360 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012361
cristy3ed852e2009-09-05 21:47:34 +000012362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012363 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012364
cristy3ed852e2009-09-05 21:47:34 +000012365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12366 " Type: %d",image_info->type);
12367
12368 scene=0;
12369 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12370 {
12371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012372 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012373
cristy3ed852e2009-09-05 21:47:34 +000012374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012375 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012376
cristy3ed852e2009-09-05 21:47:34 +000012377 if (p->matte)
12378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12379 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012380
cristy3ed852e2009-09-05 21:47:34 +000012381 else
12382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12383 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012384
cristy3ed852e2009-09-05 21:47:34 +000012385 if (p->storage_class == PseudoClass)
12386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12387 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012388
cristy3ed852e2009-09-05 21:47:34 +000012389 else
12390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12391 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012392
cristy3ed852e2009-09-05 21:47:34 +000012393 if (p->colors)
12394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012395 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012396
cristy3ed852e2009-09-05 21:47:34 +000012397 else
12398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12399 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012400
cristy3ed852e2009-09-05 21:47:34 +000012401 if (mng_info->adjoin == MagickFalse)
12402 break;
12403 }
12404 }
12405
cristy3ed852e2009-09-05 21:47:34 +000012406 use_global_plte=MagickFalse;
12407 all_images_are_gray=MagickFalse;
12408#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12409 need_local_plte=MagickTrue;
12410#endif
12411 need_defi=MagickFalse;
12412 need_matte=MagickFalse;
12413 mng_info->framing_mode=1;
12414 mng_info->old_framing_mode=1;
12415
12416 if (write_mng)
12417 if (image_info->page != (char *) NULL)
12418 {
12419 /*
12420 Determine image bounding box.
12421 */
12422 SetGeometry(image,&mng_info->page);
12423 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12424 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12425 }
12426 if (write_mng)
12427 {
12428 unsigned int
12429 need_geom;
12430
12431 unsigned short
12432 red,
12433 green,
12434 blue;
12435
12436 mng_info->page=image->page;
12437 need_geom=MagickTrue;
12438 if (mng_info->page.width || mng_info->page.height)
12439 need_geom=MagickFalse;
12440 /*
12441 Check all the scenes.
12442 */
12443 initial_delay=image->delay;
12444 need_iterations=MagickFalse;
12445 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12446 mng_info->equal_physs=MagickTrue,
12447 mng_info->equal_gammas=MagickTrue;
12448 mng_info->equal_srgbs=MagickTrue;
12449 mng_info->equal_backgrounds=MagickTrue;
12450 image_count=0;
12451#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12452 defined(PNG_MNG_FEATURES_SUPPORTED)
12453 all_images_are_gray=MagickTrue;
12454 mng_info->equal_palettes=MagickFalse;
12455 need_local_plte=MagickFalse;
12456#endif
12457 for (next_image=image; next_image != (Image *) NULL; )
12458 {
12459 if (need_geom)
12460 {
12461 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12462 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012463
cristy3ed852e2009-09-05 21:47:34 +000012464 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12465 mng_info->page.height=next_image->rows+next_image->page.y;
12466 }
glennrp0fe50b42010-11-16 03:52:51 +000012467
cristy3ed852e2009-09-05 21:47:34 +000012468 if (next_image->page.x || next_image->page.y)
12469 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012470
cristy3ed852e2009-09-05 21:47:34 +000012471 if (next_image->matte)
12472 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012473
cristy3ed852e2009-09-05 21:47:34 +000012474 if ((int) next_image->dispose >= BackgroundDispose)
12475 if (next_image->matte || next_image->page.x || next_image->page.y ||
12476 ((next_image->columns < mng_info->page.width) &&
12477 (next_image->rows < mng_info->page.height)))
12478 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012479
cristy3ed852e2009-09-05 21:47:34 +000012480 if (next_image->iterations)
12481 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012482
cristy3ed852e2009-09-05 21:47:34 +000012483 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012484
cristy3ed852e2009-09-05 21:47:34 +000012485 if (final_delay != initial_delay || final_delay > 1UL*
12486 next_image->ticks_per_second)
12487 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012488
cristy3ed852e2009-09-05 21:47:34 +000012489#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12490 defined(PNG_MNG_FEATURES_SUPPORTED)
12491 /*
12492 check for global palette possibility.
12493 */
12494 if (image->matte != MagickFalse)
12495 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012496
cristy3ed852e2009-09-05 21:47:34 +000012497 if (need_local_plte == 0)
12498 {
cristyc82a27b2011-10-21 01:07:16 +000012499 if (ImageIsGray(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012500 all_images_are_gray=MagickFalse;
12501 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12502 if (use_global_plte == 0)
12503 use_global_plte=mng_info->equal_palettes;
12504 need_local_plte=!mng_info->equal_palettes;
12505 }
12506#endif
12507 if (GetNextImageInList(next_image) != (Image *) NULL)
12508 {
12509 if (next_image->background_color.red !=
12510 next_image->next->background_color.red ||
12511 next_image->background_color.green !=
12512 next_image->next->background_color.green ||
12513 next_image->background_color.blue !=
12514 next_image->next->background_color.blue)
12515 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012516
cristy3ed852e2009-09-05 21:47:34 +000012517 if (next_image->gamma != next_image->next->gamma)
12518 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012519
cristy3ed852e2009-09-05 21:47:34 +000012520 if (next_image->rendering_intent !=
12521 next_image->next->rendering_intent)
12522 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012523
cristy3ed852e2009-09-05 21:47:34 +000012524 if ((next_image->units != next_image->next->units) ||
cristy2a11bef2011-10-28 18:33:11 +000012525 (next_image->resolution.x != next_image->next->resolution.x) ||
12526 (next_image->resolution.y != next_image->next->resolution.y))
cristy3ed852e2009-09-05 21:47:34 +000012527 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012528
cristy3ed852e2009-09-05 21:47:34 +000012529 if (mng_info->equal_chrms)
12530 {
12531 if (next_image->chromaticity.red_primary.x !=
12532 next_image->next->chromaticity.red_primary.x ||
12533 next_image->chromaticity.red_primary.y !=
12534 next_image->next->chromaticity.red_primary.y ||
12535 next_image->chromaticity.green_primary.x !=
12536 next_image->next->chromaticity.green_primary.x ||
12537 next_image->chromaticity.green_primary.y !=
12538 next_image->next->chromaticity.green_primary.y ||
12539 next_image->chromaticity.blue_primary.x !=
12540 next_image->next->chromaticity.blue_primary.x ||
12541 next_image->chromaticity.blue_primary.y !=
12542 next_image->next->chromaticity.blue_primary.y ||
12543 next_image->chromaticity.white_point.x !=
12544 next_image->next->chromaticity.white_point.x ||
12545 next_image->chromaticity.white_point.y !=
12546 next_image->next->chromaticity.white_point.y)
12547 mng_info->equal_chrms=MagickFalse;
12548 }
12549 }
12550 image_count++;
12551 next_image=GetNextImageInList(next_image);
12552 }
12553 if (image_count < 2)
12554 {
12555 mng_info->equal_backgrounds=MagickFalse;
12556 mng_info->equal_chrms=MagickFalse;
12557 mng_info->equal_gammas=MagickFalse;
12558 mng_info->equal_srgbs=MagickFalse;
12559 mng_info->equal_physs=MagickFalse;
12560 use_global_plte=MagickFalse;
12561#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12562 need_local_plte=MagickTrue;
12563#endif
12564 need_iterations=MagickFalse;
12565 }
glennrp0fe50b42010-11-16 03:52:51 +000012566
cristy3ed852e2009-09-05 21:47:34 +000012567 if (mng_info->need_fram == MagickFalse)
12568 {
12569 /*
12570 Only certain framing rates 100/n are exactly representable without
12571 the FRAM chunk but we'll allow some slop in VLC files
12572 */
12573 if (final_delay == 0)
12574 {
12575 if (need_iterations != MagickFalse)
12576 {
12577 /*
12578 It's probably a GIF with loop; don't run it *too* fast.
12579 */
glennrp02617122010-07-28 13:07:35 +000012580 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012581 {
12582 final_delay=10;
cristyc82a27b2011-10-21 01:07:16 +000012583 (void) ThrowMagickException(exception,GetMagickModule(),
12584 CoderWarning,
glennrpd908de42010-07-28 13:28:27 +000012585 "input has zero delay between all frames; assuming",
12586 " 10 cs `%s'","");
12587 }
cristy3ed852e2009-09-05 21:47:34 +000012588 }
12589 else
12590 mng_info->ticks_per_second=0;
12591 }
12592 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012593 mng_info->ticks_per_second=(png_uint_32)
12594 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012595 if (final_delay > 50)
12596 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012597
cristy3ed852e2009-09-05 21:47:34 +000012598 if (final_delay > 75)
12599 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012600
cristy3ed852e2009-09-05 21:47:34 +000012601 if (final_delay > 125)
12602 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012603
cristy3ed852e2009-09-05 21:47:34 +000012604 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12605 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12606 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12607 1UL*image->ticks_per_second))
12608 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12609 }
glennrp0fe50b42010-11-16 03:52:51 +000012610
cristy3ed852e2009-09-05 21:47:34 +000012611 if (mng_info->need_fram != MagickFalse)
12612 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12613 /*
12614 If pseudocolor, we should also check to see if all the
12615 palettes are identical and write a global PLTE if they are.
12616 ../glennrp Feb 99.
12617 */
12618 /*
12619 Write the MNG version 1.0 signature and MHDR chunk.
12620 */
12621 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12622 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12623 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012624 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012625 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12626 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012627 PNGLong(chunk+12,mng_info->ticks_per_second);
12628 PNGLong(chunk+16,0L); /* layer count=unknown */
12629 PNGLong(chunk+20,0L); /* frame count=unknown */
12630 PNGLong(chunk+24,0L); /* play time=unknown */
12631 if (write_jng)
12632 {
12633 if (need_matte)
12634 {
12635 if (need_defi || mng_info->need_fram || use_global_plte)
12636 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012637
cristy3ed852e2009-09-05 21:47:34 +000012638 else
12639 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12640 }
glennrp0fe50b42010-11-16 03:52:51 +000012641
cristy3ed852e2009-09-05 21:47:34 +000012642 else
12643 {
12644 if (need_defi || mng_info->need_fram || use_global_plte)
12645 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012646
cristy3ed852e2009-09-05 21:47:34 +000012647 else
12648 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12649 }
12650 }
glennrp0fe50b42010-11-16 03:52:51 +000012651
cristy3ed852e2009-09-05 21:47:34 +000012652 else
12653 {
12654 if (need_matte)
12655 {
12656 if (need_defi || mng_info->need_fram || use_global_plte)
12657 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012658
cristy3ed852e2009-09-05 21:47:34 +000012659 else
12660 PNGLong(chunk+28,9L); /* simplicity=VLC */
12661 }
glennrp0fe50b42010-11-16 03:52:51 +000012662
cristy3ed852e2009-09-05 21:47:34 +000012663 else
12664 {
12665 if (need_defi || mng_info->need_fram || use_global_plte)
12666 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012667
cristy3ed852e2009-09-05 21:47:34 +000012668 else
12669 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12670 }
12671 }
12672 (void) WriteBlob(image,32,chunk);
12673 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12674 option=GetImageOption(image_info,"mng:need-cacheoff");
12675 if (option != (const char *) NULL)
12676 {
12677 size_t
12678 length;
12679
12680 /*
12681 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12682 */
12683 PNGType(chunk,mng_nEED);
12684 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012685 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012686 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012687 length+=4;
12688 (void) WriteBlob(image,length,chunk);
12689 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12690 }
12691 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12692 (GetNextImageInList(image) != (Image *) NULL) &&
12693 (image->iterations != 1))
12694 {
12695 /*
12696 Write MNG TERM chunk
12697 */
12698 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12699 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012700 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012701 chunk[4]=3; /* repeat animation */
12702 chunk[5]=0; /* show last frame when done */
12703 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12704 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012705
cristy3ed852e2009-09-05 21:47:34 +000012706 if (image->iterations == 0)
12707 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012708
cristy3ed852e2009-09-05 21:47:34 +000012709 else
12710 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012711
cristy3ed852e2009-09-05 21:47:34 +000012712 if (logging != MagickFalse)
12713 {
12714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012715 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12716 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012717
cristy3ed852e2009-09-05 21:47:34 +000012718 if (image->iterations == 0)
12719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012720 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012721
cristy3ed852e2009-09-05 21:47:34 +000012722 else
12723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012724 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012725 }
12726 (void) WriteBlob(image,14,chunk);
12727 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12728 }
12729 /*
12730 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12731 */
12732 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12733 mng_info->equal_srgbs)
12734 {
12735 /*
12736 Write MNG sRGB chunk
12737 */
12738 (void) WriteBlobMSBULong(image,1L);
12739 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012740 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012741
cristy3ed852e2009-09-05 21:47:34 +000012742 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012743 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012744 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012745 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012746
cristy3ed852e2009-09-05 21:47:34 +000012747 else
glennrpe610a072010-08-05 17:08:46 +000012748 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012749 Magick_RenderingIntent_to_PNG_RenderingIntent(
12750 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012751
cristy3ed852e2009-09-05 21:47:34 +000012752 (void) WriteBlob(image,5,chunk);
12753 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12754 mng_info->have_write_global_srgb=MagickTrue;
12755 }
glennrp0fe50b42010-11-16 03:52:51 +000012756
cristy3ed852e2009-09-05 21:47:34 +000012757 else
12758 {
12759 if (image->gamma && mng_info->equal_gammas)
12760 {
12761 /*
12762 Write MNG gAMA chunk
12763 */
12764 (void) WriteBlobMSBULong(image,4L);
12765 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012766 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012767 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012768 (void) WriteBlob(image,8,chunk);
12769 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12770 mng_info->have_write_global_gama=MagickTrue;
12771 }
12772 if (mng_info->equal_chrms)
12773 {
12774 PrimaryInfo
12775 primary;
12776
12777 /*
12778 Write MNG cHRM chunk
12779 */
12780 (void) WriteBlobMSBULong(image,32L);
12781 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012782 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012783 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012784 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12785 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012786 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012787 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12788 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012789 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012790 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12791 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012792 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012793 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12794 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012795 (void) WriteBlob(image,36,chunk);
12796 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12797 mng_info->have_write_global_chrm=MagickTrue;
12798 }
12799 }
cristy2a11bef2011-10-28 18:33:11 +000012800 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012801 {
12802 /*
12803 Write MNG pHYs chunk
12804 */
12805 (void) WriteBlobMSBULong(image,9L);
12806 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012807 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012808
cristy3ed852e2009-09-05 21:47:34 +000012809 if (image->units == PixelsPerInchResolution)
12810 {
cristy35ef8242010-06-03 16:24:13 +000012811 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012812 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012813
cristy35ef8242010-06-03 16:24:13 +000012814 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012815 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012816
cristy3ed852e2009-09-05 21:47:34 +000012817 chunk[12]=1;
12818 }
glennrp0fe50b42010-11-16 03:52:51 +000012819
cristy3ed852e2009-09-05 21:47:34 +000012820 else
12821 {
12822 if (image->units == PixelsPerCentimeterResolution)
12823 {
cristy35ef8242010-06-03 16:24:13 +000012824 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012825 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012826
cristy35ef8242010-06-03 16:24:13 +000012827 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012828 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012829
cristy3ed852e2009-09-05 21:47:34 +000012830 chunk[12]=1;
12831 }
glennrp0fe50b42010-11-16 03:52:51 +000012832
cristy3ed852e2009-09-05 21:47:34 +000012833 else
12834 {
cristy2a11bef2011-10-28 18:33:11 +000012835 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12836 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012837 chunk[12]=0;
12838 }
12839 }
12840 (void) WriteBlob(image,13,chunk);
12841 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12842 }
12843 /*
12844 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12845 or does not cover the entire frame.
12846 */
12847 if (write_mng && (image->matte || image->page.x > 0 ||
12848 image->page.y > 0 || (image->page.width &&
12849 (image->page.width+image->page.x < mng_info->page.width))
12850 || (image->page.height && (image->page.height+image->page.y
12851 < mng_info->page.height))))
12852 {
12853 (void) WriteBlobMSBULong(image,6L);
12854 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012855 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012856 red=ScaleQuantumToShort(image->background_color.red);
12857 green=ScaleQuantumToShort(image->background_color.green);
12858 blue=ScaleQuantumToShort(image->background_color.blue);
12859 PNGShort(chunk+4,red);
12860 PNGShort(chunk+6,green);
12861 PNGShort(chunk+8,blue);
12862 (void) WriteBlob(image,10,chunk);
12863 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12864 if (mng_info->equal_backgrounds)
12865 {
12866 (void) WriteBlobMSBULong(image,6L);
12867 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012868 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012869 (void) WriteBlob(image,10,chunk);
12870 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12871 }
12872 }
12873
12874#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12875 if ((need_local_plte == MagickFalse) &&
12876 (image->storage_class == PseudoClass) &&
12877 (all_images_are_gray == MagickFalse))
12878 {
cristybb503372010-05-27 20:51:26 +000012879 size_t
cristy3ed852e2009-09-05 21:47:34 +000012880 data_length;
12881
12882 /*
12883 Write MNG PLTE chunk
12884 */
12885 data_length=3*image->colors;
12886 (void) WriteBlobMSBULong(image,data_length);
12887 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012888 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012889
cristybb503372010-05-27 20:51:26 +000012890 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012891 {
cristy5f07f702011-09-26 17:29:10 +000012892 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12893 image->colormap[i].red) & 0xff);
12894 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12895 image->colormap[i].green) & 0xff);
12896 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12897 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000012898 }
glennrp0fe50b42010-11-16 03:52:51 +000012899
cristy3ed852e2009-09-05 21:47:34 +000012900 (void) WriteBlob(image,data_length+4,chunk);
12901 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12902 mng_info->have_write_global_plte=MagickTrue;
12903 }
12904#endif
12905 }
12906 scene=0;
12907 mng_info->delay=0;
12908#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12909 defined(PNG_MNG_FEATURES_SUPPORTED)
12910 mng_info->equal_palettes=MagickFalse;
12911#endif
12912 do
12913 {
12914 if (mng_info->adjoin)
12915 {
12916#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12917 defined(PNG_MNG_FEATURES_SUPPORTED)
12918 /*
12919 If we aren't using a global palette for the entire MNG, check to
12920 see if we can use one for two or more consecutive images.
12921 */
12922 if (need_local_plte && use_global_plte && !all_images_are_gray)
12923 {
12924 if (mng_info->IsPalette)
12925 {
12926 /*
12927 When equal_palettes is true, this image has the same palette
12928 as the previous PseudoClass image
12929 */
12930 mng_info->have_write_global_plte=mng_info->equal_palettes;
12931 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12932 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12933 {
12934 /*
12935 Write MNG PLTE chunk
12936 */
cristybb503372010-05-27 20:51:26 +000012937 size_t
cristy3ed852e2009-09-05 21:47:34 +000012938 data_length;
12939
12940 data_length=3*image->colors;
12941 (void) WriteBlobMSBULong(image,data_length);
12942 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012943 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012944
cristybb503372010-05-27 20:51:26 +000012945 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012946 {
12947 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12948 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12949 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12950 }
glennrp0fe50b42010-11-16 03:52:51 +000012951
cristy3ed852e2009-09-05 21:47:34 +000012952 (void) WriteBlob(image,data_length+4,chunk);
12953 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12954 (uInt) (data_length+4)));
12955 mng_info->have_write_global_plte=MagickTrue;
12956 }
12957 }
12958 else
12959 mng_info->have_write_global_plte=MagickFalse;
12960 }
12961#endif
12962 if (need_defi)
12963 {
cristybb503372010-05-27 20:51:26 +000012964 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012965 previous_x,
12966 previous_y;
12967
12968 if (scene)
12969 {
12970 previous_x=mng_info->page.x;
12971 previous_y=mng_info->page.y;
12972 }
12973 else
12974 {
12975 previous_x=0;
12976 previous_y=0;
12977 }
12978 mng_info->page=image->page;
12979 if ((mng_info->page.x != previous_x) ||
12980 (mng_info->page.y != previous_y))
12981 {
12982 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12983 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012984 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012985 chunk[4]=0; /* object 0 MSB */
12986 chunk[5]=0; /* object 0 LSB */
12987 chunk[6]=0; /* visible */
12988 chunk[7]=0; /* abstract */
12989 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12990 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12991 (void) WriteBlob(image,16,chunk);
12992 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12993 }
12994 }
12995 }
12996
12997 mng_info->write_mng=write_mng;
12998
12999 if ((int) image->dispose >= 3)
13000 mng_info->framing_mode=3;
13001
13002 if (mng_info->need_fram && mng_info->adjoin &&
13003 ((image->delay != mng_info->delay) ||
13004 (mng_info->framing_mode != mng_info->old_framing_mode)))
13005 {
13006 if (image->delay == mng_info->delay)
13007 {
13008 /*
13009 Write a MNG FRAM chunk with the new framing mode.
13010 */
13011 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13012 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013013 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000013014 chunk[4]=(unsigned char) mng_info->framing_mode;
13015 (void) WriteBlob(image,5,chunk);
13016 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13017 }
13018 else
13019 {
13020 /*
13021 Write a MNG FRAM chunk with the delay.
13022 */
13023 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13024 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013025 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013026 chunk[4]=(unsigned char) mng_info->framing_mode;
13027 chunk[5]=0; /* frame name separator (no name) */
13028 chunk[6]=2; /* flag for changing default delay */
13029 chunk[7]=0; /* flag for changing frame timeout */
13030 chunk[8]=0; /* flag for changing frame clipping */
13031 chunk[9]=0; /* flag for changing frame sync_id */
13032 PNGLong(chunk+10,(png_uint_32)
13033 ((mng_info->ticks_per_second*
13034 image->delay)/MagickMax(image->ticks_per_second,1)));
13035 (void) WriteBlob(image,14,chunk);
13036 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000013037 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000013038 }
13039 mng_info->old_framing_mode=mng_info->framing_mode;
13040 }
13041
13042#if defined(JNG_SUPPORTED)
13043 if (image_info->compression == JPEGCompression)
13044 {
13045 ImageInfo
13046 *write_info;
13047
13048 if (logging != MagickFalse)
13049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13050 " Writing JNG object.");
13051 /* To do: specify the desired alpha compression method. */
13052 write_info=CloneImageInfo(image_info);
13053 write_info->compression=UndefinedCompression;
cristy018f07f2011-09-04 21:15:19 +000013054 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013055 write_info=DestroyImageInfo(write_info);
13056 }
13057 else
13058#endif
13059 {
13060 if (logging != MagickFalse)
13061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13062 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013063
glennrpb9cfe272010-12-21 15:08:06 +000013064 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013065 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013066
13067 /* We don't want any ancillary chunks written */
13068 mng_info->ping_exclude_bKGD=MagickTrue;
13069 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013070 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013071 mng_info->ping_exclude_EXIF=MagickTrue;
13072 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013073 mng_info->ping_exclude_iCCP=MagickTrue;
13074 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13075 mng_info->ping_exclude_oFFs=MagickTrue;
13076 mng_info->ping_exclude_pHYs=MagickTrue;
13077 mng_info->ping_exclude_sRGB=MagickTrue;
13078 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013079 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013080 mng_info->ping_exclude_vpAg=MagickTrue;
13081 mng_info->ping_exclude_zCCP=MagickTrue;
13082 mng_info->ping_exclude_zTXt=MagickTrue;
13083
cristy018f07f2011-09-04 21:15:19 +000013084 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013085 }
13086
13087 if (status == MagickFalse)
13088 {
13089 MngInfoFreeStruct(mng_info,&have_mng_structure);
13090 (void) CloseBlob(image);
13091 return(MagickFalse);
13092 }
13093 (void) CatchImageException(image);
13094 if (GetNextImageInList(image) == (Image *) NULL)
13095 break;
13096 image=SyncNextImageInList(image);
13097 status=SetImageProgress(image,SaveImagesTag,scene++,
13098 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013099
cristy3ed852e2009-09-05 21:47:34 +000013100 if (status == MagickFalse)
13101 break;
glennrp0fe50b42010-11-16 03:52:51 +000013102
cristy3ed852e2009-09-05 21:47:34 +000013103 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013104
cristy3ed852e2009-09-05 21:47:34 +000013105 if (write_mng)
13106 {
13107 while (GetPreviousImageInList(image) != (Image *) NULL)
13108 image=GetPreviousImageInList(image);
13109 /*
13110 Write the MEND chunk.
13111 */
13112 (void) WriteBlobMSBULong(image,0x00000000L);
13113 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013114 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013115 (void) WriteBlob(image,4,chunk);
13116 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13117 }
13118 /*
13119 Relinquish resources.
13120 */
13121 (void) CloseBlob(image);
13122 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013123
cristy3ed852e2009-09-05 21:47:34 +000013124 if (logging != MagickFalse)
13125 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013126
cristy3ed852e2009-09-05 21:47:34 +000013127 return(MagickTrue);
13128}
glennrpd5045b42010-03-24 12:40:35 +000013129#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013130
cristy3ed852e2009-09-05 21:47:34 +000013131static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13132{
glennrp3bd393f2011-12-21 18:54:53 +000013133 (void) image;
cristy3ed852e2009-09-05 21:47:34 +000013134 printf("Your PNG library is too old: You have libpng-%s\n",
13135 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013136
cristy3ed852e2009-09-05 21:47:34 +000013137 ThrowBinaryException(CoderError,"PNG library is too old",
13138 image_info->filename);
13139}
glennrp39992b42010-11-14 00:03:43 +000013140
cristy3ed852e2009-09-05 21:47:34 +000013141static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13142{
13143 return(WritePNGImage(image_info,image));
13144}
glennrpd5045b42010-03-24 12:40:35 +000013145#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013146#endif