blob: 0d67cabb59d3cfb4ea1cbf59c926328e352fe1be [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP N N GGGG %
7% P P NN N G %
8% PPPP N N N G GG %
9% P N NN G G %
10% P N N GGG %
11% %
12% %
13% Read/Write Portable Network Graphics Image Format %
14% %
15% Software Design %
16% John Cristy %
17% Glenn Randers-Pehrson %
18% November 1997 %
19% %
20% %
cristy1454be72011-12-19 01:52:48 +000021% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000022% dedicated to making software imaging solutions freely available. %
23% %
24% You may not use this file except in compliance with the License. You may %
25% obtain a copy of the License at %
26% %
27% http://www.imagemagick.org/script/license.php %
28% %
29% Unless required by applicable law or agreed to in writing, software %
30% distributed under the License is distributed on an "AS IS" BASIS, %
31% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32% See the License for the specific language governing permissions and %
33% limitations under the License. %
34% %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38*/
39
40
41/*
42 Include declarations.
43*/
cristy4c08aed2011-07-01 19:47:50 +000044#include "MagickCore/studio.h"
45#include "MagickCore/artifact.h"
46#include "MagickCore/attribute.h"
47#include "MagickCore/blob.h"
48#include "MagickCore/blob-private.h"
49#include "MagickCore/cache.h"
50#include "MagickCore/color.h"
51#include "MagickCore/color-private.h"
52#include "MagickCore/colormap.h"
53#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000054#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000055#include "MagickCore/constitute.h"
56#include "MagickCore/enhance.h"
57#include "MagickCore/exception.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/geometry.h"
60#include "MagickCore/histogram.h"
61#include "MagickCore/image.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/layer.h"
64#include "MagickCore/list.h"
65#include "MagickCore/log.h"
66#include "MagickCore/MagickCore.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/module.h"
69#include "MagickCore/monitor.h"
70#include "MagickCore/monitor-private.h"
71#include "MagickCore/option.h"
72#include "MagickCore/pixel.h"
73#include "MagickCore/pixel-accessor.h"
74#include "MagickCore/profile.h"
75#include "MagickCore/property.h"
76#include "MagickCore/quantum-private.h"
77#include "MagickCore/resource_.h"
78#include "MagickCore/semaphore.h"
79#include "MagickCore/quantum-private.h"
80#include "MagickCore/static.h"
81#include "MagickCore/statistic.h"
82#include "MagickCore/string_.h"
83#include "MagickCore/string-private.h"
84#include "MagickCore/transform.h"
85#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000086#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp286a6352009-11-09 02:58:50 +000087
glennrp7ef138c2009-11-10 13:50:20 +000088/* Suppress libpng pedantic warnings that were added in
89 * libpng-1.2.41 and libpng-1.4.0. If you are working on
glennrpfaa852b2010-03-30 12:17:00 +000090 * migration to libpng-1.5, remove these defines and then
glennrp7ef138c2009-11-10 13:50:20 +000091 * fix any code that generates warnings.
92 */
glennrp991e92a2010-01-28 03:09:00 +000093/* #define PNG_DEPRECATED Use of this function is deprecated */
glennrpfaa852b2010-03-30 12:17:00 +000094/* #define PNG_USE_RESULT The result of this function must be checked */
95/* #define PNG_NORETURN This function does not return */
96/* #define PNG_ALLOCATED The result of the function is new memory */
glennrp8371ecc2011-02-19 19:15:38 +000097/* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
glennrp5b927342011-04-14 14:31:48 +000098
99/* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
cristy75cfe702011-04-14 14:27:17 +0000100#define PNG_PTR_NORETURN
glennrp286a6352009-11-09 02:58:50 +0000101
cristy3ed852e2009-09-05 21:47:34 +0000102#include "png.h"
103#include "zlib.h"
104
105/* ImageMagick differences */
106#define first_scene scene
107
glennrpd5045b42010-03-24 12:40:35 +0000108#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000109/*
110 Optional declarations. Define or undefine them as you like.
111*/
112/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
113
114/*
115 Features under construction. Define these to work on them.
116*/
117#undef MNG_OBJECT_BUFFERS
118#undef MNG_BASI_SUPPORTED
119#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
120#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
cristy3ed852e2009-09-05 21:47:34 +0000121#if defined(MAGICKCORE_JPEG_DELEGATE)
122# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
123#endif
124#if !defined(RGBColorMatchExact)
125#define IsPNGColorEqual(color,target) \
glennrp8e045c82011-04-27 16:40:27 +0000126 (((color).red == (target).red) && \
127 ((color).green == (target).green) && \
128 ((color).blue == (target).blue))
cristy3ed852e2009-09-05 21:47:34 +0000129#endif
130
glennrp8e58efd2011-05-20 12:16:29 +0000131/* Macros for left-bit-replication to ensure that pixels
glennrp1a2061f2011-10-18 12:30:45 +0000132 * and PixelInfos all have the same image->depth, and for use
glennrp8e58efd2011-05-20 12:16:29 +0000133 * in PNG8 quantization.
134 */
135
136
137/* LBR01: Replicate top bit */
138
glennrp05001c32011-08-06 13:04:16 +0000139#define LBR01PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000140 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
141 0 : QuantumRange);
142
glennrp91d99252011-06-25 14:30:13 +0000143#define LBR01PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000144 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
145 0 : QuantumRange);
146
glennrp91d99252011-06-25 14:30:13 +0000147#define LBR01PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000148 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
149 0 : QuantumRange);
150
cristy4c08aed2011-07-01 19:47:50 +0000151#define LBR01PacketAlpha(pixelpacket) \
152 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000153 0 : QuantumRange);
154
glennrp91d99252011-06-25 14:30:13 +0000155#define LBR01PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000156 { \
glennrp05001c32011-08-06 13:04:16 +0000157 LBR01PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000158 LBR01PacketGreen((pixelpacket)); \
159 LBR01PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000160 }
glennrp8e58efd2011-05-20 12:16:29 +0000161
glennrp91d99252011-06-25 14:30:13 +0000162#define LBR01PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000163 { \
glennrp91d99252011-06-25 14:30:13 +0000164 LBR01PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000165 LBR01PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000166 }
glennrp8e58efd2011-05-20 12:16:29 +0000167
cristyef618312011-06-25 12:26:44 +0000168#define LBR01PixelRed(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000169 (ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000170 0 : QuantumRange);
171
glennrp54cf7972011-08-06 14:28:09 +0000172#define LBR01PixelGreen(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000173 (ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000174 0 : QuantumRange);
175
glennrp54cf7972011-08-06 14:28:09 +0000176#define LBR01PixelBlue(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000177 (ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000178 0 : QuantumRange);
179
glennrp54cf7972011-08-06 14:28:09 +0000180#define LBR01PixelAlpha(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000181 (ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000182 0 : QuantumRange);
183
glennrp54cf7972011-08-06 14:28:09 +0000184#define LBR01PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000185 { \
cristyef618312011-06-25 12:26:44 +0000186 LBR01PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000187 LBR01PixelGreen((pixel)); \
188 LBR01PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000189 }
glennrp8e58efd2011-05-20 12:16:29 +0000190
glennrp54cf7972011-08-06 14:28:09 +0000191#define LBR01PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000192 { \
glennrp54cf7972011-08-06 14:28:09 +0000193 LBR01PixelRGB((pixel)); \
194 LBR01PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000195 }
glennrp8e58efd2011-05-20 12:16:29 +0000196
197/* LBR02: Replicate top 2 bits */
198
glennrp05001c32011-08-06 13:04:16 +0000199#define LBR02PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000200 { \
201 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
202 (pixelpacket).red=ScaleCharToQuantum( \
203 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
204 }
glennrp91d99252011-06-25 14:30:13 +0000205#define LBR02PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000206 { \
207 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
208 (pixelpacket).green=ScaleCharToQuantum( \
209 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
210 }
glennrp91d99252011-06-25 14:30:13 +0000211#define LBR02PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000212 { \
213 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
214 (pixelpacket).blue=ScaleCharToQuantum( \
215 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
216 }
cristy4c08aed2011-07-01 19:47:50 +0000217#define LBR02PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000218 { \
cristy4c08aed2011-07-01 19:47:50 +0000219 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
220 (pixelpacket).alpha=ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000221 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
222 }
223
glennrp91d99252011-06-25 14:30:13 +0000224#define LBR02PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000225 { \
glennrp05001c32011-08-06 13:04:16 +0000226 LBR02PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000227 LBR02PacketGreen((pixelpacket)); \
228 LBR02PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000229 }
glennrp8e58efd2011-05-20 12:16:29 +0000230
glennrp91d99252011-06-25 14:30:13 +0000231#define LBR02PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000232 { \
glennrp91d99252011-06-25 14:30:13 +0000233 LBR02PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000234 LBR02PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000235 }
glennrp8e58efd2011-05-20 12:16:29 +0000236
cristyef618312011-06-25 12:26:44 +0000237#define LBR02PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000238 { \
cristy4c08aed2011-07-01 19:47:50 +0000239 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000240 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000241 SetPixelRed(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000242 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
243 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000244 }
glennrp54cf7972011-08-06 14:28:09 +0000245#define LBR02PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000246 { \
cristy4c08aed2011-07-01 19:47:50 +0000247 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000248 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000249 SetPixelGreen(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000250 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
251 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000252 }
glennrp54cf7972011-08-06 14:28:09 +0000253#define LBR02PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000254 { \
255 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000256 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
257 SetPixelBlue(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000258 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
259 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000260 }
glennrp54cf7972011-08-06 14:28:09 +0000261#define LBR02PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000262 { \
263 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000264 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
265 SetPixelAlpha(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000266 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
267 (pixel) ); \
glennrp8e58efd2011-05-20 12:16:29 +0000268 }
269
glennrp54cf7972011-08-06 14:28:09 +0000270#define LBR02PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000271 { \
cristyef618312011-06-25 12:26:44 +0000272 LBR02PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000273 LBR02PixelGreen((pixel)); \
274 LBR02PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000275 }
glennrp8e58efd2011-05-20 12:16:29 +0000276
glennrp54cf7972011-08-06 14:28:09 +0000277#define LBR02PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000278 { \
glennrp54cf7972011-08-06 14:28:09 +0000279 LBR02PixelRGB((pixel)); \
280 LBR02PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000281 }
glennrp8e58efd2011-05-20 12:16:29 +0000282
283/* LBR03: Replicate top 3 bits (only used with opaque pixels during
284 PNG8 quantization) */
285
glennrp05001c32011-08-06 13:04:16 +0000286#define LBR03PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000287 { \
288 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
289 (pixelpacket).red=ScaleCharToQuantum( \
290 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
291 }
glennrp91d99252011-06-25 14:30:13 +0000292#define LBR03PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000293 { \
294 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
295 (pixelpacket).green=ScaleCharToQuantum( \
296 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
297 }
glennrp91d99252011-06-25 14:30:13 +0000298#define LBR03PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000299 { \
300 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
301 (pixelpacket).blue=ScaleCharToQuantum( \
302 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
303 }
304
glennrp91d99252011-06-25 14:30:13 +0000305#define LBR03PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000306 { \
glennrp05001c32011-08-06 13:04:16 +0000307 LBR03PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000308 LBR03PacketGreen((pixelpacket)); \
309 LBR03PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000310 }
glennrp8e58efd2011-05-20 12:16:29 +0000311
cristyef618312011-06-25 12:26:44 +0000312#define LBR03PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000313 { \
cristy4c08aed2011-07-01 19:47:50 +0000314 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000315 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000316 SetPixelRed(image, ScaleCharToQuantum( \
317 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000318 }
cristy4c08aed2011-07-01 19:47:50 +0000319#define LBR03Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000320 { \
cristy4c08aed2011-07-01 19:47:50 +0000321 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000322 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000323 SetPixelGreen(image, ScaleCharToQuantum( \
324 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000325 }
cristy4c08aed2011-07-01 19:47:50 +0000326#define LBR03Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000327 { \
cristy4c08aed2011-07-01 19:47:50 +0000328 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000329 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000330 SetPixelBlue(image, ScaleCharToQuantum( \
331 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000332 }
333
cristy4c08aed2011-07-01 19:47:50 +0000334#define LBR03RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000335 { \
cristyef618312011-06-25 12:26:44 +0000336 LBR03PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000337 LBR03Green((pixel)); \
338 LBR03Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000339 }
glennrp8e58efd2011-05-20 12:16:29 +0000340
341/* LBR04: Replicate top 4 bits */
342
glennrp05001c32011-08-06 13:04:16 +0000343#define LBR04PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000344 { \
345 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
346 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
347 }
glennrp91d99252011-06-25 14:30:13 +0000348#define LBR04PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000349 { \
350 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
351 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
352 }
glennrp91d99252011-06-25 14:30:13 +0000353#define LBR04PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000354 { \
355 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
356 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
357 }
cristy4c08aed2011-07-01 19:47:50 +0000358#define LBR04PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000359 { \
cristy4c08aed2011-07-01 19:47:50 +0000360 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
361 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
glennrp8e58efd2011-05-20 12:16:29 +0000362 }
363
glennrp91d99252011-06-25 14:30:13 +0000364#define LBR04PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000365 { \
glennrp05001c32011-08-06 13:04:16 +0000366 LBR04PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000367 LBR04PacketGreen((pixelpacket)); \
368 LBR04PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000369 }
glennrp8e58efd2011-05-20 12:16:29 +0000370
glennrp91d99252011-06-25 14:30:13 +0000371#define LBR04PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000372 { \
glennrp91d99252011-06-25 14:30:13 +0000373 LBR04PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000374 LBR04PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000375 }
glennrp8e58efd2011-05-20 12:16:29 +0000376
cristyef618312011-06-25 12:26:44 +0000377#define LBR04PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000378 { \
cristy4c08aed2011-07-01 19:47:50 +0000379 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000380 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000381 SetPixelRed(image,\
382 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000383 }
glennrp54cf7972011-08-06 14:28:09 +0000384#define LBR04PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000385 { \
cristy4c08aed2011-07-01 19:47:50 +0000386 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000387 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000388 SetPixelGreen(image,\
389 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000390 }
glennrp54cf7972011-08-06 14:28:09 +0000391#define LBR04PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000392 { \
393 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000394 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
395 SetPixelBlue(image,\
396 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000397 }
glennrp54cf7972011-08-06 14:28:09 +0000398#define LBR04PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000399 { \
400 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000401 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
402 SetPixelAlpha(image,\
403 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000404 }
405
glennrp54cf7972011-08-06 14:28:09 +0000406#define LBR04PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000407 { \
cristyef618312011-06-25 12:26:44 +0000408 LBR04PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000409 LBR04PixelGreen((pixel)); \
410 LBR04PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000411 }
glennrp8e58efd2011-05-20 12:16:29 +0000412
glennrp54cf7972011-08-06 14:28:09 +0000413#define LBR04PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000414 { \
glennrp54cf7972011-08-06 14:28:09 +0000415 LBR04PixelRGB((pixel)); \
416 LBR04PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000417 }
glennrp8e58efd2011-05-20 12:16:29 +0000418
419
420/* LBR08: Replicate top 8 bits */
421
glennrp05001c32011-08-06 13:04:16 +0000422#define LBR08PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000423 { \
424 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
425 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
426 }
glennrp91d99252011-06-25 14:30:13 +0000427#define LBR08PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000428 { \
429 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
430 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
431 }
glennrp91d99252011-06-25 14:30:13 +0000432#define LBR08PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000433 { \
434 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
435 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
436 }
cristy4c08aed2011-07-01 19:47:50 +0000437#define LBR08PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000438 { \
cristy4c08aed2011-07-01 19:47:50 +0000439 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
440 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000441 }
442
glennrp91d99252011-06-25 14:30:13 +0000443#define LBR08PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000444 { \
glennrp05001c32011-08-06 13:04:16 +0000445 LBR08PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000446 LBR08PacketGreen((pixelpacket)); \
447 LBR08PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000448 }
glennrp8e58efd2011-05-20 12:16:29 +0000449
glennrp91d99252011-06-25 14:30:13 +0000450#define LBR08PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000451 { \
glennrp91d99252011-06-25 14:30:13 +0000452 LBR08PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000453 LBR08PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000454 }
glennrp8e58efd2011-05-20 12:16:29 +0000455
cristyef618312011-06-25 12:26:44 +0000456#define LBR08PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000457 { \
458 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000459 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
460 SetPixelRed(image,\
461 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000462 }
glennrp54cf7972011-08-06 14:28:09 +0000463#define LBR08PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000464 { \
465 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000466 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
467 SetPixelGreen(image,\
468 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000469 }
glennrp54cf7972011-08-06 14:28:09 +0000470#define LBR08PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000471 { \
472 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000473 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
474 SetPixelBlue(image,\
475 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000476 }
glennrp54cf7972011-08-06 14:28:09 +0000477#define LBR08PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000478 { \
479 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000480 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
481 SetPixelAlpha(image,\
482 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000483 }
484
glennrp54cf7972011-08-06 14:28:09 +0000485#define LBR08PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000486 { \
cristyef618312011-06-25 12:26:44 +0000487 LBR08PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000488 LBR08PixelGreen((pixel)); \
489 LBR08PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000490 }
glennrp8e58efd2011-05-20 12:16:29 +0000491
glennrp54cf7972011-08-06 14:28:09 +0000492#define LBR08PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000493 { \
glennrp54cf7972011-08-06 14:28:09 +0000494 LBR08PixelRGB((pixel)); \
495 LBR08PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000496 }
glennrp8e58efd2011-05-20 12:16:29 +0000497
498
499/* LBR16: Replicate top 16 bits */
500
glennrp05001c32011-08-06 13:04:16 +0000501#define LBR16PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000502 { \
503 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
504 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
505 }
glennrp91d99252011-06-25 14:30:13 +0000506#define LBR16PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000507 { \
508 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
509 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
510 }
glennrp91d99252011-06-25 14:30:13 +0000511#define LBR16PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000512 { \
513 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
514 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
515 }
cristy4c08aed2011-07-01 19:47:50 +0000516#define LBR16PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000517 { \
cristy4c08aed2011-07-01 19:47:50 +0000518 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
519 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000520 }
521
glennrp91d99252011-06-25 14:30:13 +0000522#define LBR16PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000523 { \
glennrp05001c32011-08-06 13:04:16 +0000524 LBR16PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000525 LBR16PacketGreen((pixelpacket)); \
526 LBR16PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000527 }
glennrp8e58efd2011-05-20 12:16:29 +0000528
glennrp91d99252011-06-25 14:30:13 +0000529#define LBR16PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000530 { \
glennrp91d99252011-06-25 14:30:13 +0000531 LBR16PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000532 LBR16PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000533 }
glennrp8e58efd2011-05-20 12:16:29 +0000534
cristyef618312011-06-25 12:26:44 +0000535#define LBR16PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000536 { \
537 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000538 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
539 SetPixelRed(image,\
540 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000541 }
glennrp54cf7972011-08-06 14:28:09 +0000542#define LBR16PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000543 { \
544 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000545 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
546 SetPixelGreen(image,\
547 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000548 }
glennrp54cf7972011-08-06 14:28:09 +0000549#define LBR16PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000550 { \
551 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000552 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
553 SetPixelBlue(image,\
554 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000555 }
glennrp54cf7972011-08-06 14:28:09 +0000556#define LBR16PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000557 { \
558 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000559 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
560 SetPixelAlpha(image,\
561 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000562 }
563
glennrp54cf7972011-08-06 14:28:09 +0000564#define LBR16PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000565 { \
cristyef618312011-06-25 12:26:44 +0000566 LBR16PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000567 LBR16PixelGreen((pixel)); \
568 LBR16PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000569 }
glennrp8e58efd2011-05-20 12:16:29 +0000570
glennrp54cf7972011-08-06 14:28:09 +0000571#define LBR16PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000572 { \
glennrp54cf7972011-08-06 14:28:09 +0000573 LBR16PixelRGB((pixel)); \
574 LBR16PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000575 }
glennrp8e58efd2011-05-20 12:16:29 +0000576
cristy3ed852e2009-09-05 21:47:34 +0000577/*
578 Establish thread safety.
579 setjmp/longjmp is claimed to be safe on these platforms:
580 setjmp/longjmp is alleged to be unsafe on these platforms:
581*/
582#ifndef SETJMP_IS_THREAD_SAFE
583#define PNG_SETJMP_NOT_THREAD_SAFE
584#endif
585
586#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
587static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000588 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000589#endif
590
591/*
592 This temporary until I set up malloc'ed object attributes array.
593 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
594 waste more memory.
595*/
596#define MNG_MAX_OBJECTS 256
597
598/*
599 If this not defined, spec is interpreted strictly. If it is
600 defined, an attempt will be made to recover from some errors,
601 including
602 o global PLTE too short
603*/
604#undef MNG_LOOSE
605
606/*
607 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
608 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
609 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
610 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
611 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
612 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
613 will be enabled by default in libpng-1.2.0.
614*/
cristy3ed852e2009-09-05 21:47:34 +0000615#ifdef PNG_MNG_FEATURES_SUPPORTED
616# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
617# define PNG_READ_EMPTY_PLTE_SUPPORTED
618# endif
619# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
620# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
621# endif
622#endif
623
624/*
cristybb503372010-05-27 20:51:26 +0000625 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000626 This macro is only defined in libpng-1.0.3 and later.
627 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
628*/
629#ifndef PNG_UINT_31_MAX
630#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
631#endif
632
633/*
634 Constant strings for known chunk types. If you need to add a chunk,
635 add a string holding the name here. To make the code more
636 portable, we use ASCII numbers like this, not characters.
637*/
638
glennrp85dcf872011-12-07 02:51:47 +0000639static png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
640static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
641static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
642static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
643static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
644static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
645static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
646static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
647static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
648static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
649static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
650static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
651static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
652static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
653static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
654static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
655static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
656static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
657static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
658static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
659static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
660static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
661static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
662static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
663static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
664static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
665static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
666static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
667static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
668static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
669static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
670static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
671static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
672static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000673
674#if defined(JNG_SUPPORTED)
glennrp85dcf872011-12-07 02:51:47 +0000675static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
676static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
677static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
678static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
679static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
680static png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000681#endif
682
683/*
684Other known chunks that are not yet supported by ImageMagick:
glennrp85dcf872011-12-07 02:51:47 +0000685static png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
686static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
687static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
688static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
689static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
690static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
691static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
692static png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000693*/
694
695typedef struct _MngBox
696{
cristy8182b072010-05-30 20:10:53 +0000697 long
cristy3ed852e2009-09-05 21:47:34 +0000698 left,
699 right,
700 top,
701 bottom;
702} MngBox;
703
704typedef struct _MngPair
705{
cristy8182b072010-05-30 20:10:53 +0000706 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000707 a,
708 b;
709} MngPair;
710
711#ifdef MNG_OBJECT_BUFFERS
712typedef struct _MngBuffer
713{
714
cristybb503372010-05-27 20:51:26 +0000715 size_t
cristy3ed852e2009-09-05 21:47:34 +0000716 height,
717 width;
718
719 Image
720 *image;
721
722 png_color
723 plte[256];
724
725 int
726 reference_count;
727
728 unsigned char
729 alpha_sample_depth,
730 compression_method,
731 color_type,
732 concrete,
733 filter_method,
734 frozen,
735 image_type,
736 interlace_method,
737 pixel_sample_depth,
738 plte_length,
739 sample_depth,
740 viewable;
741} MngBuffer;
742#endif
743
744typedef struct _MngInfo
745{
746
747#ifdef MNG_OBJECT_BUFFERS
748 MngBuffer
749 *ob[MNG_MAX_OBJECTS];
750#endif
751
752 Image *
753 image;
754
755 RectangleInfo
756 page;
757
758 int
759 adjoin,
760#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
761 bytes_in_read_buffer,
762 found_empty_plte,
763#endif
764 equal_backgrounds,
765 equal_chrms,
766 equal_gammas,
767#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
768 defined(PNG_MNG_FEATURES_SUPPORTED)
769 equal_palettes,
770#endif
771 equal_physs,
772 equal_srgbs,
773 framing_mode,
774 have_global_bkgd,
775 have_global_chrm,
776 have_global_gama,
777 have_global_phys,
778 have_global_sbit,
779 have_global_srgb,
780 have_saved_bkgd_index,
781 have_write_global_chrm,
782 have_write_global_gama,
783 have_write_global_plte,
784 have_write_global_srgb,
785 need_fram,
786 object_id,
787 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000788 saved_bkgd_index;
789
790 int
791 new_number_colors;
792
cristybb503372010-05-27 20:51:26 +0000793 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000794 image_found,
795 loop_count[256],
796 loop_iteration[256],
797 scenes_found,
798 x_off[MNG_MAX_OBJECTS],
799 y_off[MNG_MAX_OBJECTS];
800
801 MngBox
802 clip,
803 frame,
804 image_box,
805 object_clip[MNG_MAX_OBJECTS];
806
807 unsigned char
808 /* These flags could be combined into one byte */
809 exists[MNG_MAX_OBJECTS],
810 frozen[MNG_MAX_OBJECTS],
811 loop_active[256],
812 invisible[MNG_MAX_OBJECTS],
813 viewable[MNG_MAX_OBJECTS];
814
815 MagickOffsetType
816 loop_jump[256];
817
818 png_colorp
819 global_plte;
820
821 png_color_8
822 global_sbit;
823
824 png_byte
825#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
826 read_buffer[8],
827#endif
828 global_trns[256];
829
830 float
831 global_gamma;
832
833 ChromaticityInfo
834 global_chrm;
835
836 RenderingIntent
837 global_srgb_intent;
838
cristy35ef8242010-06-03 16:24:13 +0000839 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000840 delay,
841 global_plte_length,
842 global_trns_length,
843 global_x_pixels_per_unit,
844 global_y_pixels_per_unit,
845 mng_width,
846 mng_height,
847 ticks_per_second;
848
glennrpb9cfe272010-12-21 15:08:06 +0000849 MagickBooleanType
850 need_blob;
851
cristy3ed852e2009-09-05 21:47:34 +0000852 unsigned int
853 IsPalette,
854 global_phys_unit_type,
855 basi_warning,
856 clon_warning,
857 dhdr_warning,
858 jhdr_warning,
859 magn_warning,
860 past_warning,
861 phyg_warning,
862 phys_warning,
863 sbit_warning,
864 show_warning,
865 mng_type,
866 write_mng,
867 write_png_colortype,
868 write_png_depth,
glennrp18682582011-06-30 18:11:47 +0000869 write_png_compression_level,
870 write_png_compression_strategy,
871 write_png_compression_filter,
cristy3ed852e2009-09-05 21:47:34 +0000872 write_png8,
873 write_png24,
874 write_png32;
875
876#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000877 size_t
cristy3ed852e2009-09-05 21:47:34 +0000878 basi_width,
879 basi_height;
880
881 unsigned int
882 basi_depth,
883 basi_color_type,
884 basi_compression_method,
885 basi_filter_type,
886 basi_interlace_method,
887 basi_red,
888 basi_green,
889 basi_blue,
890 basi_alpha,
891 basi_viewable;
892#endif
893
894 png_uint_16
895 magn_first,
896 magn_last,
897 magn_mb,
898 magn_ml,
899 magn_mr,
900 magn_mt,
901 magn_mx,
902 magn_my,
903 magn_methx,
904 magn_methy;
905
cristy101ab702011-10-13 13:06:32 +0000906 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000907 mng_global_bkgd;
908
glennrp26f37912010-12-23 16:22:42 +0000909 /* Added at version 6.6.6-7 */
910 MagickBooleanType
911 ping_exclude_bKGD,
912 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000913 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000914 ping_exclude_EXIF,
915 ping_exclude_gAMA,
916 ping_exclude_iCCP,
917 /* ping_exclude_iTXt, */
918 ping_exclude_oFFs,
919 ping_exclude_pHYs,
920 ping_exclude_sRGB,
921 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000922 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000923 ping_exclude_vpAg,
924 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000925 ping_exclude_zTXt,
926 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000927
cristy3ed852e2009-09-05 21:47:34 +0000928} MngInfo;
929#endif /* VER */
930
931/*
932 Forward declarations.
933*/
934static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000935 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000936
cristy3ed852e2009-09-05 21:47:34 +0000937static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000938 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000939
cristy3ed852e2009-09-05 21:47:34 +0000940#if defined(JNG_SUPPORTED)
941static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000942 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000943#endif
944
glennrp0c3e06b2010-11-19 13:45:02 +0000945#if PNG_LIBPNG_VER > 10011
946
glennrpfd05d622011-02-25 04:10:33 +0000947
glennrp0c3e06b2010-11-19 13:45:02 +0000948#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
949static MagickBooleanType
cristyc82a27b2011-10-21 01:07:16 +0000950LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
glennrp0c3e06b2010-11-19 13:45:02 +0000951{
glennrp67b9c1a2011-04-22 18:47:36 +0000952 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
953 *
954 * This is true if the high byte and the next highest byte of
955 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000956 * are equal to each other. We check this by seeing if the samples
957 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000958 *
959 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000960 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000961 */
962
glennrp3faa9a32011-04-23 14:00:25 +0000963#define QuantumToCharToQuantumEqQuantum(quantum) \
964 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
965
glennrp0c3e06b2010-11-19 13:45:02 +0000966 MagickBooleanType
967 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000968
glennrp03e11f62011-04-22 13:30:16 +0000969 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000970 {
971
cristy4c08aed2011-07-01 19:47:50 +0000972 const Quantum
glennrp0c3e06b2010-11-19 13:45:02 +0000973 *p;
974
975 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000976 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
977 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
978 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
979 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000980
981 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
982 {
983 int indx;
984
985 for (indx=0; indx < (ssize_t) image->colors; indx++)
986 {
glennrp3faa9a32011-04-23 14:00:25 +0000987 ok_to_reduce=(
988 QuantumToCharToQuantumEqQuantum(
989 image->colormap[indx].red) &&
990 QuantumToCharToQuantumEqQuantum(
991 image->colormap[indx].green) &&
992 QuantumToCharToQuantumEqQuantum(
993 image->colormap[indx].blue)) ?
994 MagickTrue : MagickFalse;
995
glennrp0c3e06b2010-11-19 13:45:02 +0000996 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000997 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000998 }
999 }
1000
1001 if ((ok_to_reduce != MagickFalse) &&
1002 (image->storage_class != PseudoClass))
1003 {
1004 ssize_t
1005 y;
1006
1007 register ssize_t
1008 x;
1009
1010 for (y=0; y < (ssize_t) image->rows; y++)
1011 {
cristyc82a27b2011-10-21 01:07:16 +00001012 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0c3e06b2010-11-19 13:45:02 +00001013
cristy4c08aed2011-07-01 19:47:50 +00001014 if (p == (const Quantum *) NULL)
glennrp0c3e06b2010-11-19 13:45:02 +00001015 {
1016 ok_to_reduce = MagickFalse;
1017 break;
1018 }
1019
1020 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1021 {
glennrp3faa9a32011-04-23 14:00:25 +00001022 ok_to_reduce=
cristy4c08aed2011-07-01 19:47:50 +00001023 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1024 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1025 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
glennrp3faa9a32011-04-23 14:00:25 +00001026 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001027
1028 if (ok_to_reduce == MagickFalse)
1029 break;
1030
cristyed231572011-07-14 02:18:59 +00001031 p+=GetPixelChannels(image);
glennrp0c3e06b2010-11-19 13:45:02 +00001032 }
glennrp8640fb52010-11-23 15:48:26 +00001033 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +00001034 break;
1035 }
1036 }
1037
1038 if (ok_to_reduce != MagickFalse)
1039 {
glennrp0c3e06b2010-11-19 13:45:02 +00001040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001041 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +00001042 }
glennrpa6a06632011-01-19 15:15:34 +00001043 else
1044 {
1045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001046 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +00001047 }
glennrp0c3e06b2010-11-19 13:45:02 +00001048 }
1049
1050 return ok_to_reduce;
1051}
1052#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1053
glennrpe610a072010-08-05 17:08:46 +00001054static int
glennrpcf002022011-01-30 02:38:15 +00001055Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +00001056{
glennrpe610a072010-08-05 17:08:46 +00001057 switch (intent)
1058 {
1059 case PerceptualIntent:
1060 return 0;
glennrp0fe50b42010-11-16 03:52:51 +00001061
glennrpe610a072010-08-05 17:08:46 +00001062 case RelativeIntent:
1063 return 1;
glennrp0fe50b42010-11-16 03:52:51 +00001064
glennrpe610a072010-08-05 17:08:46 +00001065 case SaturationIntent:
1066 return 2;
glennrp0fe50b42010-11-16 03:52:51 +00001067
glennrpe610a072010-08-05 17:08:46 +00001068 case AbsoluteIntent:
1069 return 3;
glennrp0fe50b42010-11-16 03:52:51 +00001070
glennrpe610a072010-08-05 17:08:46 +00001071 default:
1072 return -1;
1073 }
1074}
1075
1076static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +00001077Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +00001078{
glennrpcf002022011-01-30 02:38:15 +00001079 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001080 {
1081 case 0:
1082 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001083
glennrpe610a072010-08-05 17:08:46 +00001084 case 1:
1085 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001086
glennrpe610a072010-08-05 17:08:46 +00001087 case 2:
1088 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001089
glennrpe610a072010-08-05 17:08:46 +00001090 case 3:
1091 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001092
glennrpe610a072010-08-05 17:08:46 +00001093 default:
1094 return UndefinedIntent;
1095 }
1096}
1097
cristybb503372010-05-27 20:51:26 +00001098static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001099{
1100 if (x > y)
1101 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001102
cristy3ed852e2009-09-05 21:47:34 +00001103 return(y);
1104}
glennrp0c3e06b2010-11-19 13:45:02 +00001105
cristybb503372010-05-27 20:51:26 +00001106static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001107{
1108 if (x < y)
1109 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001110
cristy3ed852e2009-09-05 21:47:34 +00001111 return(y);
1112}
glennrp0c3e06b2010-11-19 13:45:02 +00001113
cristy3ed852e2009-09-05 21:47:34 +00001114
1115/*
1116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1117% %
1118% %
1119% %
1120% I m a g e I s G r a y %
1121% %
1122% %
1123% %
1124%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1125% %
cristy4c08aed2011-07-01 19:47:50 +00001126% Like IsImageGray except does not change DirectClass to PseudoClass %
cristy3ed852e2009-09-05 21:47:34 +00001127% %
1128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1129*/
cristyc82a27b2011-10-21 01:07:16 +00001130static MagickBooleanType ImageIsGray(Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001131{
cristy4c08aed2011-07-01 19:47:50 +00001132 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001133 *p;
1134
cristybb503372010-05-27 20:51:26 +00001135 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001136 i,
1137 x,
1138 y;
1139
1140 assert(image != (Image *) NULL);
1141 assert(image->signature == MagickSignature);
1142 if (image->debug != MagickFalse)
1143 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1144
1145 if (image->storage_class == PseudoClass)
1146 {
cristybb503372010-05-27 20:51:26 +00001147 for (i=0; i < (ssize_t) image->colors; i++)
cristy101ab702011-10-13 13:06:32 +00001148 if (IsPixelInfoGray(image->colormap+i) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001149 return(MagickFalse);
1150 return(MagickTrue);
1151 }
cristybb503372010-05-27 20:51:26 +00001152 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001153 {
cristyc82a27b2011-10-21 01:07:16 +00001154 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001155 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001156 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +00001157 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001158 {
cristy4c08aed2011-07-01 19:47:50 +00001159 if (IsPixelGray(image,p) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001160 return(MagickFalse);
cristyed231572011-07-14 02:18:59 +00001161 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001162 }
1163 }
1164 return(MagickTrue);
1165}
glennrpd5045b42010-03-24 12:40:35 +00001166#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001167#endif /* MAGICKCORE_PNG_DELEGATE */
1168
1169/*
1170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1171% %
1172% %
1173% %
1174% I s M N G %
1175% %
1176% %
1177% %
1178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1179%
1180% IsMNG() returns MagickTrue if the image format type, identified by the
1181% magick string, is MNG.
1182%
1183% The format of the IsMNG method is:
1184%
1185% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1186%
1187% A description of each parameter follows:
1188%
1189% o magick: compare image format pattern against these bytes.
1190%
1191% o length: Specifies the length of the magick string.
1192%
1193%
1194*/
1195static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1196{
1197 if (length < 8)
1198 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001199
cristy3ed852e2009-09-05 21:47:34 +00001200 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1201 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001202
cristy3ed852e2009-09-05 21:47:34 +00001203 return(MagickFalse);
1204}
1205
1206/*
1207%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1208% %
1209% %
1210% %
1211% I s J N G %
1212% %
1213% %
1214% %
1215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1216%
1217% IsJNG() returns MagickTrue if the image format type, identified by the
1218% magick string, is JNG.
1219%
1220% The format of the IsJNG method is:
1221%
1222% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1223%
1224% A description of each parameter follows:
1225%
1226% o magick: compare image format pattern against these bytes.
1227%
1228% o length: Specifies the length of the magick string.
1229%
1230%
1231*/
1232static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1233{
1234 if (length < 8)
1235 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001236
cristy3ed852e2009-09-05 21:47:34 +00001237 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1238 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001239
cristy3ed852e2009-09-05 21:47:34 +00001240 return(MagickFalse);
1241}
1242
1243/*
1244%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245% %
1246% %
1247% %
1248% I s P N G %
1249% %
1250% %
1251% %
1252%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1253%
1254% IsPNG() returns MagickTrue if the image format type, identified by the
1255% magick string, is PNG.
1256%
1257% The format of the IsPNG method is:
1258%
1259% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1260%
1261% A description of each parameter follows:
1262%
1263% o magick: compare image format pattern against these bytes.
1264%
1265% o length: Specifies the length of the magick string.
1266%
1267*/
1268static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1269{
1270 if (length < 8)
1271 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001272
cristy3ed852e2009-09-05 21:47:34 +00001273 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1274 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001275
cristy3ed852e2009-09-05 21:47:34 +00001276 return(MagickFalse);
1277}
1278
1279#if defined(MAGICKCORE_PNG_DELEGATE)
1280#if defined(__cplusplus) || defined(c_plusplus)
1281extern "C" {
1282#endif
1283
glennrpd5045b42010-03-24 12:40:35 +00001284#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001285static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001286{
1287 unsigned char
1288 buffer[4];
1289
1290 assert(image != (Image *) NULL);
1291 assert(image->signature == MagickSignature);
1292 buffer[0]=(unsigned char) (value >> 24);
1293 buffer[1]=(unsigned char) (value >> 16);
1294 buffer[2]=(unsigned char) (value >> 8);
1295 buffer[3]=(unsigned char) value;
1296 return((size_t) WriteBlob(image,4,buffer));
1297}
1298
1299static void PNGLong(png_bytep p,png_uint_32 value)
1300{
1301 *p++=(png_byte) ((value >> 24) & 0xff);
1302 *p++=(png_byte) ((value >> 16) & 0xff);
1303 *p++=(png_byte) ((value >> 8) & 0xff);
1304 *p++=(png_byte) (value & 0xff);
1305}
1306
glennrpa521b2f2010-10-29 04:11:03 +00001307#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001308static void PNGsLong(png_bytep p,png_int_32 value)
1309{
1310 *p++=(png_byte) ((value >> 24) & 0xff);
1311 *p++=(png_byte) ((value >> 16) & 0xff);
1312 *p++=(png_byte) ((value >> 8) & 0xff);
1313 *p++=(png_byte) (value & 0xff);
1314}
glennrpa521b2f2010-10-29 04:11:03 +00001315#endif
cristy3ed852e2009-09-05 21:47:34 +00001316
1317static void PNGShort(png_bytep p,png_uint_16 value)
1318{
1319 *p++=(png_byte) ((value >> 8) & 0xff);
1320 *p++=(png_byte) (value & 0xff);
1321}
1322
1323static void PNGType(png_bytep p,png_bytep type)
1324{
1325 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1326}
1327
glennrp03812ae2010-12-24 01:31:34 +00001328static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1329 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001330{
1331 if (logging != MagickFalse)
1332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001333 " Writing %c%c%c%c chunk, length: %.20g",
1334 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001335}
glennrpd5045b42010-03-24 12:40:35 +00001336#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001337
1338#if defined(__cplusplus) || defined(c_plusplus)
1339}
1340#endif
1341
glennrpd5045b42010-03-24 12:40:35 +00001342#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001343/*
1344%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1345% %
1346% %
1347% %
1348% R e a d P N G I m a g e %
1349% %
1350% %
1351% %
1352%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1353%
1354% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1355% Multiple-image Network Graphics (MNG) image file and returns it. It
1356% allocates the memory necessary for the new Image structure and returns a
1357% pointer to the new image or set of images.
1358%
1359% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1360%
1361% The format of the ReadPNGImage method is:
1362%
1363% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1364%
1365% A description of each parameter follows:
1366%
1367% o image_info: the image info.
1368%
1369% o exception: return any errors or warnings in this structure.
1370%
1371% To do, more or less in chronological order (as of version 5.5.2,
1372% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1373%
1374% Get 16-bit cheap transparency working.
1375%
1376% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1377%
1378% Preserve all unknown and not-yet-handled known chunks found in input
1379% PNG file and copy them into output PNG files according to the PNG
1380% copying rules.
1381%
1382% (At this point, PNG encoding should be in full MNG compliance)
1383%
1384% Provide options for choice of background to use when the MNG BACK
1385% chunk is not present or is not mandatory (i.e., leave transparent,
1386% user specified, MNG BACK, PNG bKGD)
1387%
1388% Implement LOOP/ENDL [done, but could do discretionary loops more
1389% efficiently by linking in the duplicate frames.].
1390%
1391% Decode and act on the MHDR simplicity profile (offer option to reject
1392% files or attempt to process them anyway when the profile isn't LC or VLC).
1393%
1394% Upgrade to full MNG without Delta-PNG.
1395%
1396% o BACK [done a while ago except for background image ID]
1397% o MOVE [done 15 May 1999]
1398% o CLIP [done 15 May 1999]
1399% o DISC [done 19 May 1999]
1400% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1401% o SEEK [partially done 19 May 1999 (discard function only)]
1402% o SHOW
1403% o PAST
1404% o BASI
1405% o MNG-level tEXt/iTXt/zTXt
1406% o pHYg
1407% o pHYs
1408% o sBIT
1409% o bKGD
1410% o iTXt (wait for libpng implementation).
1411%
1412% Use the scene signature to discover when an identical scene is
1413% being reused, and just point to the original image->exception instead
1414% of storing another set of pixels. This not specific to MNG
1415% but could be applied generally.
1416%
1417% Upgrade to full MNG with Delta-PNG.
1418%
1419% JNG tEXt/iTXt/zTXt
1420%
1421% We will not attempt to read files containing the CgBI chunk.
1422% They are really Xcode files meant for display on the iPhone.
1423% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001424% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001425% since irretrievable loss of color data has occurred due to the
1426% use of premultiplied alpha.
1427*/
1428
1429#if defined(__cplusplus) || defined(c_plusplus)
1430extern "C" {
1431#endif
1432
1433/*
1434 This the function that does the actual reading of data. It is
1435 the same as the one supplied in libpng, except that it receives the
1436 datastream from the ReadBlob() function instead of standard input.
1437*/
1438static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1439{
1440 Image
1441 *image;
1442
1443 image=(Image *) png_get_io_ptr(png_ptr);
1444 if (length)
1445 {
1446 png_size_t
1447 check;
1448
1449 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1450 if (check != length)
1451 {
1452 char
1453 msg[MaxTextExtent];
1454
cristy3b6fd2e2011-05-20 12:53:50 +00001455 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001456 "Expected %.20g bytes; found %.20g bytes",(double) length,
1457 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001458 png_warning(png_ptr,msg);
1459 png_error(png_ptr,"Read Exception");
1460 }
1461 }
1462}
1463
1464#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1465 !defined(PNG_MNG_FEATURES_SUPPORTED)
1466/* We use mng_get_data() instead of png_get_data() if we have a libpng
1467 * older than libpng-1.0.3a, which was the first to allow the empty
1468 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1469 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1470 * encountered after an empty PLTE, so we have to look ahead for bKGD
1471 * chunks and remove them from the datastream that is passed to libpng,
1472 * and store their contents for later use.
1473 */
1474static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1475{
1476 MngInfo
1477 *mng_info;
1478
1479 Image
1480 *image;
1481
1482 png_size_t
1483 check;
1484
cristybb503372010-05-27 20:51:26 +00001485 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001486 i;
1487
1488 i=0;
1489 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1490 image=(Image *) mng_info->image;
1491 while (mng_info->bytes_in_read_buffer && length)
1492 {
1493 data[i]=mng_info->read_buffer[i];
1494 mng_info->bytes_in_read_buffer--;
1495 length--;
1496 i++;
1497 }
1498 if (length)
1499 {
1500 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001501
cristy3ed852e2009-09-05 21:47:34 +00001502 if (check != length)
1503 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001504
cristy3ed852e2009-09-05 21:47:34 +00001505 if (length == 4)
1506 {
1507 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1508 (data[3] == 0))
1509 {
1510 check=(png_size_t) ReadBlob(image,(size_t) length,
1511 (char *) mng_info->read_buffer);
1512 mng_info->read_buffer[4]=0;
1513 mng_info->bytes_in_read_buffer=4;
1514 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1515 mng_info->found_empty_plte=MagickTrue;
1516 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1517 {
1518 mng_info->found_empty_plte=MagickFalse;
1519 mng_info->have_saved_bkgd_index=MagickFalse;
1520 }
1521 }
glennrp0fe50b42010-11-16 03:52:51 +00001522
cristy3ed852e2009-09-05 21:47:34 +00001523 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1524 (data[3] == 1))
1525 {
1526 check=(png_size_t) ReadBlob(image,(size_t) length,
1527 (char *) mng_info->read_buffer);
1528 mng_info->read_buffer[4]=0;
1529 mng_info->bytes_in_read_buffer=4;
1530 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1531 if (mng_info->found_empty_plte)
1532 {
1533 /*
1534 Skip the bKGD data byte and CRC.
1535 */
1536 check=(png_size_t)
1537 ReadBlob(image,5,(char *) mng_info->read_buffer);
1538 check=(png_size_t) ReadBlob(image,(size_t) length,
1539 (char *) mng_info->read_buffer);
1540 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1541 mng_info->have_saved_bkgd_index=MagickTrue;
1542 mng_info->bytes_in_read_buffer=0;
1543 }
1544 }
1545 }
1546 }
1547}
1548#endif
1549
1550static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1551{
1552 Image
1553 *image;
1554
1555 image=(Image *) png_get_io_ptr(png_ptr);
1556 if (length)
1557 {
1558 png_size_t
1559 check;
1560
cristybb503372010-05-27 20:51:26 +00001561 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001562
cristy3ed852e2009-09-05 21:47:34 +00001563 if (check != length)
1564 png_error(png_ptr,"WriteBlob Failed");
1565 }
1566}
1567
1568static void png_flush_data(png_structp png_ptr)
1569{
1570 (void) png_ptr;
1571}
1572
1573#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1574static int PalettesAreEqual(Image *a,Image *b)
1575{
cristybb503372010-05-27 20:51:26 +00001576 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001577 i;
1578
1579 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1580 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001581
cristy3ed852e2009-09-05 21:47:34 +00001582 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1583 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001584
cristy3ed852e2009-09-05 21:47:34 +00001585 if (a->colors != b->colors)
1586 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001587
cristybb503372010-05-27 20:51:26 +00001588 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001589 {
1590 if ((a->colormap[i].red != b->colormap[i].red) ||
1591 (a->colormap[i].green != b->colormap[i].green) ||
1592 (a->colormap[i].blue != b->colormap[i].blue))
1593 return((int) MagickFalse);
1594 }
glennrp0fe50b42010-11-16 03:52:51 +00001595
cristy3ed852e2009-09-05 21:47:34 +00001596 return((int) MagickTrue);
1597}
1598#endif
1599
1600static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1601{
1602 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1603 mng_info->exists[i] && !mng_info->frozen[i])
1604 {
1605#ifdef MNG_OBJECT_BUFFERS
1606 if (mng_info->ob[i] != (MngBuffer *) NULL)
1607 {
1608 if (mng_info->ob[i]->reference_count > 0)
1609 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001610
cristy3ed852e2009-09-05 21:47:34 +00001611 if (mng_info->ob[i]->reference_count == 0)
1612 {
1613 if (mng_info->ob[i]->image != (Image *) NULL)
1614 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001615
cristy3ed852e2009-09-05 21:47:34 +00001616 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1617 }
1618 }
1619 mng_info->ob[i]=(MngBuffer *) NULL;
1620#endif
1621 mng_info->exists[i]=MagickFalse;
1622 mng_info->invisible[i]=MagickFalse;
1623 mng_info->viewable[i]=MagickFalse;
1624 mng_info->frozen[i]=MagickFalse;
1625 mng_info->x_off[i]=0;
1626 mng_info->y_off[i]=0;
1627 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001628 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001629 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001630 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001631 }
1632}
1633
glennrp21f0e622011-01-07 16:20:57 +00001634static void MngInfoFreeStruct(MngInfo *mng_info,
1635 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001636{
glennrp21f0e622011-01-07 16:20:57 +00001637 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001638 {
cristybb503372010-05-27 20:51:26 +00001639 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001640 i;
1641
1642 for (i=1; i < MNG_MAX_OBJECTS; i++)
1643 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001644
cristy3ed852e2009-09-05 21:47:34 +00001645 if (mng_info->global_plte != (png_colorp) NULL)
1646 mng_info->global_plte=(png_colorp)
1647 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001648
cristy3ed852e2009-09-05 21:47:34 +00001649 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1650 *have_mng_structure=MagickFalse;
1651 }
1652}
1653
1654static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1655{
1656 MngBox
1657 box;
1658
1659 box=box1;
1660 if (box.left < box2.left)
1661 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001662
cristy3ed852e2009-09-05 21:47:34 +00001663 if (box.top < box2.top)
1664 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001665
cristy3ed852e2009-09-05 21:47:34 +00001666 if (box.right > box2.right)
1667 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001668
cristy3ed852e2009-09-05 21:47:34 +00001669 if (box.bottom > box2.bottom)
1670 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001671
cristy3ed852e2009-09-05 21:47:34 +00001672 return box;
1673}
1674
1675static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1676{
1677 MngBox
1678 box;
1679
1680 /*
1681 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1682 */
cristybb503372010-05-27 20:51:26 +00001683 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1684 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1685 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1686 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001687 if (delta_type != 0)
1688 {
1689 box.left+=previous_box.left;
1690 box.right+=previous_box.right;
1691 box.top+=previous_box.top;
1692 box.bottom+=previous_box.bottom;
1693 }
glennrp0fe50b42010-11-16 03:52:51 +00001694
cristy3ed852e2009-09-05 21:47:34 +00001695 return(box);
1696}
1697
1698static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1699 unsigned char *p)
1700{
1701 MngPair
1702 pair;
1703 /*
cristybb503372010-05-27 20:51:26 +00001704 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001705 */
cristy8182b072010-05-30 20:10:53 +00001706 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1707 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001708
cristy3ed852e2009-09-05 21:47:34 +00001709 if (delta_type != 0)
1710 {
1711 pair.a+=previous_pair.a;
1712 pair.b+=previous_pair.b;
1713 }
glennrp0fe50b42010-11-16 03:52:51 +00001714
cristy3ed852e2009-09-05 21:47:34 +00001715 return(pair);
1716}
1717
cristy8182b072010-05-30 20:10:53 +00001718static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001719{
cristy8182b072010-05-30 20:10:53 +00001720 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001721}
1722
cristyc82a27b2011-10-21 01:07:16 +00001723typedef struct _PNGErrorInfo
cristy3ed852e2009-09-05 21:47:34 +00001724{
1725 Image
1726 *image;
1727
cristyc82a27b2011-10-21 01:07:16 +00001728 ExceptionInfo
1729 *exception;
1730} PNGErrorInfo;
glennrp0fe50b42010-11-16 03:52:51 +00001731
cristyc82a27b2011-10-21 01:07:16 +00001732static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1733{
1734 ExceptionInfo
1735 *exception;
glennrp0fe50b42010-11-16 03:52:51 +00001736
cristyc82a27b2011-10-21 01:07:16 +00001737 Image
1738 *image;
1739
1740 PNGErrorInfo
1741 *error_info;
1742
1743 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1744 image=error_info->image;
1745 exception=error_info->exception;
1746
1747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1748 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1749
1750 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1751 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001752
glennrpe4017e32011-01-08 17:16:09 +00001753#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001754 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1755 * are building with libpng-1.4.x and can be ignored.
1756 */
cristy3ed852e2009-09-05 21:47:34 +00001757 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001758#else
1759 png_longjmp(ping,1);
1760#endif
cristy3ed852e2009-09-05 21:47:34 +00001761}
1762
glennrpcf002022011-01-30 02:38:15 +00001763static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001764{
cristyc82a27b2011-10-21 01:07:16 +00001765 ExceptionInfo
1766 *exception;
1767
cristy3ed852e2009-09-05 21:47:34 +00001768 Image
1769 *image;
1770
cristyc82a27b2011-10-21 01:07:16 +00001771 PNGErrorInfo
1772 *error_info;
1773
cristy3ed852e2009-09-05 21:47:34 +00001774 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1775 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001776
cristyc82a27b2011-10-21 01:07:16 +00001777 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1778 image=error_info->image;
1779 exception=error_info->exception;
1780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1781 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001782
cristyc82a27b2011-10-21 01:07:16 +00001783 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
cristy3ed852e2009-09-05 21:47:34 +00001784 message,"`%s'",image->filename);
1785}
1786
1787#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001788static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001789{
1790#if (PNG_LIBPNG_VER < 10011)
1791 png_voidp
1792 ret;
1793
cristydf0d90e2011-12-12 01:03:55 +00001794 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001795 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001796
cristy3ed852e2009-09-05 21:47:34 +00001797 if (ret == NULL)
1798 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001799
cristy3ed852e2009-09-05 21:47:34 +00001800 return(ret);
1801#else
cristydf0d90e2011-12-12 01:03:55 +00001802 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001803 return((png_voidp) AcquireMagickMemory((size_t) size));
1804#endif
1805}
1806
1807/*
1808 Free a pointer. It is removed from the list at the same time.
1809*/
glennrpcf002022011-01-30 02:38:15 +00001810static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001811{
glennrp3bd393f2011-12-21 18:54:53 +00001812 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001813 ptr=RelinquishMagickMemory(ptr);
1814 return((png_free_ptr) NULL);
1815}
1816#endif
1817
1818#if defined(__cplusplus) || defined(c_plusplus)
1819}
1820#endif
1821
1822static int
glennrpcf002022011-01-30 02:38:15 +00001823Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristyd15e6592011-10-15 00:13:06 +00001824 png_textp text,int ii,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001825{
cristybb503372010-05-27 20:51:26 +00001826 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001827 i;
1828
1829 register unsigned char
1830 *dp;
1831
1832 register png_charp
1833 sp;
1834
1835 png_uint_32
1836 length,
1837 nibbles;
1838
1839 StringInfo
1840 *profile;
1841
glennrp0c3e06b2010-11-19 13:45:02 +00001842 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001843 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1844 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1845 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1846 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1847 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1848 13,14,15};
1849
1850 sp=text[ii].text+1;
1851 /* look for newline */
1852 while (*sp != '\n')
1853 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001854
cristy3ed852e2009-09-05 21:47:34 +00001855 /* look for length */
1856 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1857 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001858
cristyf2f27272009-12-17 14:48:46 +00001859 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001860
glennrp97f90e22011-02-22 05:47:58 +00001861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1862 " length: %lu",(unsigned long) length);
1863
cristy3ed852e2009-09-05 21:47:34 +00001864 while (*sp != ' ' && *sp != '\n')
1865 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001866
cristy3ed852e2009-09-05 21:47:34 +00001867 /* allocate space */
1868 if (length == 0)
1869 {
cristyc82a27b2011-10-21 01:07:16 +00001870 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001871 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1872 return(MagickFalse);
1873 }
glennrp0fe50b42010-11-16 03:52:51 +00001874
cristy8723e4b2011-09-01 13:11:19 +00001875 profile=BlobToStringInfo((const void *) NULL,length);
glennrp0fe50b42010-11-16 03:52:51 +00001876
cristy3ed852e2009-09-05 21:47:34 +00001877 if (profile == (StringInfo *) NULL)
1878 {
cristyc82a27b2011-10-21 01:07:16 +00001879 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001880 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1881 "unable to copy profile");
1882 return(MagickFalse);
1883 }
glennrp0fe50b42010-11-16 03:52:51 +00001884
cristy3ed852e2009-09-05 21:47:34 +00001885 /* copy profile, skipping white space and column 1 "=" signs */
1886 dp=GetStringInfoDatum(profile);
1887 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001888
cristybb503372010-05-27 20:51:26 +00001889 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001890 {
1891 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1892 {
1893 if (*sp == '\0')
1894 {
cristyc82a27b2011-10-21 01:07:16 +00001895 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001896 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1897 profile=DestroyStringInfo(profile);
1898 return(MagickFalse);
1899 }
1900 sp++;
1901 }
glennrp0fe50b42010-11-16 03:52:51 +00001902
cristy3ed852e2009-09-05 21:47:34 +00001903 if (i%2 == 0)
1904 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001905
cristy3ed852e2009-09-05 21:47:34 +00001906 else
1907 (*dp++)+=unhex[(int) *sp++];
1908 }
1909 /*
1910 We have already read "Raw profile type.
1911 */
cristyd15e6592011-10-15 00:13:06 +00001912 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001913 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001914
cristy3ed852e2009-09-05 21:47:34 +00001915 if (image_info->verbose)
1916 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001917
cristy3ed852e2009-09-05 21:47:34 +00001918 return MagickTrue;
1919}
1920
1921#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1922static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1923{
1924 Image
1925 *image;
1926
1927
1928 /* The unknown chunk structure contains the chunk data:
1929 png_byte name[5];
1930 png_byte *data;
1931 png_size_t size;
1932
1933 Note that libpng has already taken care of the CRC handling.
1934 */
1935
1936
1937 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1938 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1939 return(0); /* Did not recognize */
1940
1941 /* recognized vpAg */
1942
1943 if (chunk->size != 9)
1944 return(-1); /* Error return */
1945
1946 if (chunk->data[8] != 0)
1947 return(0); /* ImageMagick requires pixel units */
1948
1949 image=(Image *) png_get_user_chunk_ptr(ping);
1950
cristybb503372010-05-27 20:51:26 +00001951 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001952 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001953
cristybb503372010-05-27 20:51:26 +00001954 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001955 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1956
1957 /* Return one of the following: */
1958 /* return(-n); chunk had an error */
1959 /* return(0); did not recognize */
1960 /* return(n); success */
1961
1962 return(1);
1963
1964}
1965#endif
1966
1967/*
1968%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1969% %
1970% %
1971% %
1972% R e a d O n e P N G I m a g e %
1973% %
1974% %
1975% %
1976%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1977%
1978% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1979% (minus the 8-byte signature) and returns it. It allocates the memory
1980% necessary for the new Image structure and returns a pointer to the new
1981% image.
1982%
1983% The format of the ReadOnePNGImage method is:
1984%
1985% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1986% ExceptionInfo *exception)
1987%
1988% A description of each parameter follows:
1989%
1990% o mng_info: Specifies a pointer to a MngInfo structure.
1991%
1992% o image_info: the image info.
1993%
1994% o exception: return any errors or warnings in this structure.
1995%
1996*/
1997static Image *ReadOnePNGImage(MngInfo *mng_info,
1998 const ImageInfo *image_info, ExceptionInfo *exception)
1999{
2000 /* Read one PNG image */
2001
glennrpcc95c3f2011-04-18 16:46:48 +00002002 /* To do: Read the tIME chunk into the date:modify property */
2003 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2004
cristy3ed852e2009-09-05 21:47:34 +00002005 Image
2006 *image;
2007
2008 int
glennrp4eb39312011-03-30 21:34:55 +00002009 intent,
glennrpcb395ac2011-03-30 19:50:23 +00002010 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00002011 num_text,
glennrp4eb39312011-03-30 21:34:55 +00002012 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00002013 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00002014 pass,
2015 ping_bit_depth,
2016 ping_color_type,
2017 ping_interlace_method,
2018 ping_compression_method,
2019 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00002020 ping_num_trans,
2021 unit_type;
2022
2023 double
2024 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002025
2026 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002027 logging,
cristy3ed852e2009-09-05 21:47:34 +00002028 status;
2029
cristyc82a27b2011-10-21 01:07:16 +00002030 PixelInfo
2031 transparent_color;
2032
2033 PNGErrorInfo
2034 error_info;
2035
glennrpfaa852b2010-03-30 12:17:00 +00002036 png_bytep
2037 ping_trans_alpha;
2038
2039 png_color_16p
2040 ping_background,
2041 ping_trans_color;
2042
cristy3ed852e2009-09-05 21:47:34 +00002043 png_info
2044 *end_info,
2045 *ping_info;
2046
2047 png_struct
2048 *ping;
2049
2050 png_textp
2051 text;
2052
glennrpfaa852b2010-03-30 12:17:00 +00002053 png_uint_32
2054 ping_height,
2055 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002056 x_resolution,
2057 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002058
cristy3ed852e2009-09-05 21:47:34 +00002059 QuantumInfo
2060 *quantum_info;
2061
2062 unsigned char
glennrpcf002022011-01-30 02:38:15 +00002063 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002064
cristybb503372010-05-27 20:51:26 +00002065 ssize_t
cristy756ae432011-11-19 02:18:25 +00002066 ping_rowbytes,
cristy3ed852e2009-09-05 21:47:34 +00002067 y;
2068
2069 register unsigned char
2070 *p;
2071
cristybb503372010-05-27 20:51:26 +00002072 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002073 i,
2074 x;
2075
cristy4c08aed2011-07-01 19:47:50 +00002076 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002077 *q;
2078
2079 size_t
glennrp39992b42010-11-14 00:03:43 +00002080 length,
cristy3ed852e2009-09-05 21:47:34 +00002081 row_offset;
2082
cristyeb3b22a2011-03-31 20:16:11 +00002083 ssize_t
2084 j;
2085
cristy3ed852e2009-09-05 21:47:34 +00002086#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2087 png_byte unused_chunks[]=
2088 {
2089 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2090 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2091 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2092 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2093 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2094 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2095 };
2096#endif
2097
2098 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002099 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002100
2101#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002102 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002103#endif
2104
glennrp25c1e2b2010-03-25 01:39:56 +00002105#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002106 if (image_info->verbose)
2107 printf("Your PNG library (libpng-%s) is rather old.\n",
2108 PNG_LIBPNG_VER_STRING);
2109#endif
2110
glennrp61b4c952009-11-10 20:40:41 +00002111#if (PNG_LIBPNG_VER >= 10400)
2112# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2113 if (image_info->verbose)
2114 {
2115 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2116 PNG_LIBPNG_VER_STRING);
2117 printf("Please update it.\n");
2118 }
2119# endif
2120#endif
2121
2122
cristyed552522009-10-16 14:04:35 +00002123 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002124 image=mng_info->image;
2125
glennrpa6a06632011-01-19 15:15:34 +00002126 if (logging != MagickFalse)
2127 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2128 " image->matte=%d",(int) image->matte);
2129
glennrp0e319732011-01-25 21:53:13 +00002130 /* Set to an out-of-range color unless tRNS chunk is present */
2131 transparent_color.red=65537;
2132 transparent_color.green=65537;
2133 transparent_color.blue=65537;
cristy4c08aed2011-07-01 19:47:50 +00002134 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002135
glennrpcb395ac2011-03-30 19:50:23 +00002136 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002137 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002138 num_raw_profiles = 0;
2139
cristy3ed852e2009-09-05 21:47:34 +00002140 /*
2141 Allocate the PNG structures
2142 */
2143#ifdef PNG_USER_MEM_SUPPORTED
cristyc82a27b2011-10-21 01:07:16 +00002144 error_info.image=image;
2145 error_info.exception=exception;
2146 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002147 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2148 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002149#else
cristyc82a27b2011-10-21 01:07:16 +00002150 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002151 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002152#endif
2153 if (ping == (png_struct *) NULL)
2154 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002155
cristy3ed852e2009-09-05 21:47:34 +00002156 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002157
cristy3ed852e2009-09-05 21:47:34 +00002158 if (ping_info == (png_info *) NULL)
2159 {
2160 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2161 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2162 }
glennrp0fe50b42010-11-16 03:52:51 +00002163
cristy3ed852e2009-09-05 21:47:34 +00002164 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002165
cristy3ed852e2009-09-05 21:47:34 +00002166 if (end_info == (png_info *) NULL)
2167 {
2168 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2169 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2170 }
glennrp0fe50b42010-11-16 03:52:51 +00002171
glennrpcf002022011-01-30 02:38:15 +00002172 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002173
glennrpfaa852b2010-03-30 12:17:00 +00002174 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002175 {
2176 /*
2177 PNG image is corrupt.
2178 */
2179 png_destroy_read_struct(&ping,&ping_info,&end_info);
2180#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002181 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002182#endif
2183 if (logging != MagickFalse)
2184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2185 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002186
cristy3ed852e2009-09-05 21:47:34 +00002187 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002188 {
cristyc82a27b2011-10-21 01:07:16 +00002189 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002190 image->columns=0;
2191 }
glennrp0fe50b42010-11-16 03:52:51 +00002192
cristy3ed852e2009-09-05 21:47:34 +00002193 return(GetFirstImageInList(image));
2194 }
2195 /*
2196 Prepare PNG for reading.
2197 */
glennrpfaa852b2010-03-30 12:17:00 +00002198
cristy3ed852e2009-09-05 21:47:34 +00002199 mng_info->image_found++;
2200 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002201
cristy3ed852e2009-09-05 21:47:34 +00002202 if (LocaleCompare(image_info->magick,"MNG") == 0)
2203 {
2204#if defined(PNG_MNG_FEATURES_SUPPORTED)
2205 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2206 png_set_read_fn(ping,image,png_get_data);
2207#else
2208#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2209 png_permit_empty_plte(ping,MagickTrue);
2210 png_set_read_fn(ping,image,png_get_data);
2211#else
2212 mng_info->image=image;
2213 mng_info->bytes_in_read_buffer=0;
2214 mng_info->found_empty_plte=MagickFalse;
2215 mng_info->have_saved_bkgd_index=MagickFalse;
2216 png_set_read_fn(ping,mng_info,mng_get_data);
2217#endif
2218#endif
2219 }
glennrp0fe50b42010-11-16 03:52:51 +00002220
cristy3ed852e2009-09-05 21:47:34 +00002221 else
2222 png_set_read_fn(ping,image,png_get_data);
2223
2224#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2225 /* Ignore unused chunks and all unknown chunks except for vpAg */
2226 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2227 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2228 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2229 (int)sizeof(unused_chunks)/5);
2230 /* Callback for other unknown chunks */
2231 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2232#endif
2233
glennrp991e92a2010-01-28 03:09:00 +00002234#if (PNG_LIBPNG_VER < 10400)
2235# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2236 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002237 /* Disable thread-unsafe features of pnggccrd */
2238 if (png_access_version_number() >= 10200)
2239 {
2240 png_uint_32 mmx_disable_mask=0;
2241 png_uint_32 asm_flags;
2242
2243 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2244 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2245 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2246 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2247 asm_flags=png_get_asm_flags(ping);
2248 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2249 }
glennrp991e92a2010-01-28 03:09:00 +00002250# endif
cristy3ed852e2009-09-05 21:47:34 +00002251#endif
2252
2253 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002254
2255 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2256 &ping_bit_depth,&ping_color_type,
2257 &ping_interlace_method,&ping_compression_method,
2258 &ping_filter_method);
2259
2260 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2261 &ping_trans_color);
2262
2263 (void) png_get_bKGD(ping, ping_info, &ping_background);
2264
2265 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002266 {
glennrpfaa852b2010-03-30 12:17:00 +00002267 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2268 {
2269 png_set_packing(ping);
2270 ping_bit_depth = 8;
2271 }
cristy3ed852e2009-09-05 21:47:34 +00002272 }
glennrpfaa852b2010-03-30 12:17:00 +00002273
2274 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002275 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002276 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00002277 if (logging != MagickFalse)
2278 {
2279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002280 " PNG width: %.20g, height: %.20g",
2281 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002282
cristy3ed852e2009-09-05 21:47:34 +00002283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2284 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002285 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002286
cristy3ed852e2009-09-05 21:47:34 +00002287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2288 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002289 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002290
cristy3ed852e2009-09-05 21:47:34 +00002291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2292 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002293 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002294 }
2295
glennrpfaa852b2010-03-30 12:17:00 +00002296#ifdef PNG_READ_iCCP_SUPPORTED
2297 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002298 {
2299 int
2300 compression;
2301
glennrpe4017e32011-01-08 17:16:09 +00002302#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002303 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002304 info;
2305#else
2306 png_bytep
2307 info;
2308#endif
2309
2310 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002311 name;
2312
2313 png_uint_32
2314 profile_length;
2315
2316 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2317 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002318
cristy3ed852e2009-09-05 21:47:34 +00002319 if (profile_length != 0)
2320 {
2321 StringInfo
2322 *profile;
2323
2324 if (logging != MagickFalse)
2325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2326 " Reading PNG iCCP chunk.");
cristye8f8f382011-09-01 13:32:37 +00002327 profile=BlobToStringInfo(info,profile_length);
2328 if (profile == (StringInfo *) NULL)
2329 {
cristyc82a27b2011-10-21 01:07:16 +00002330 (void) ThrowMagickException(exception,GetMagickModule(),
cristye8f8f382011-09-01 13:32:37 +00002331 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2332 "unable to copy profile");
cristyad5b0852011-09-08 02:01:21 +00002333 return((Image *) NULL);
cristye8f8f382011-09-01 13:32:37 +00002334 }
cristy3ed852e2009-09-05 21:47:34 +00002335 SetStringInfoDatum(profile,(const unsigned char *) info);
cristyd15e6592011-10-15 00:13:06 +00002336 (void) SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00002337 profile=DestroyStringInfo(profile);
2338 }
2339 }
2340#endif
2341#if defined(PNG_READ_sRGB_SUPPORTED)
2342 {
cristy3ed852e2009-09-05 21:47:34 +00002343 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00002344 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2345 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00002346
cristy3ed852e2009-09-05 21:47:34 +00002347 if (png_get_sRGB(ping,ping_info,&intent))
2348 {
glennrpcf002022011-01-30 02:38:15 +00002349 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2350 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002351
cristy3ed852e2009-09-05 21:47:34 +00002352 if (logging != MagickFalse)
2353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002354 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002355 }
2356 }
2357#endif
2358 {
glennrpfaa852b2010-03-30 12:17:00 +00002359 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2360 if (mng_info->have_global_gama)
2361 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002362
cristy3ed852e2009-09-05 21:47:34 +00002363 if (png_get_gAMA(ping,ping_info,&file_gamma))
2364 {
2365 image->gamma=(float) file_gamma;
2366 if (logging != MagickFalse)
2367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2368 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2369 }
2370 }
glennrpfaa852b2010-03-30 12:17:00 +00002371 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2372 {
2373 if (mng_info->have_global_chrm != MagickFalse)
2374 {
2375 (void) png_set_cHRM(ping,ping_info,
2376 mng_info->global_chrm.white_point.x,
2377 mng_info->global_chrm.white_point.y,
2378 mng_info->global_chrm.red_primary.x,
2379 mng_info->global_chrm.red_primary.y,
2380 mng_info->global_chrm.green_primary.x,
2381 mng_info->global_chrm.green_primary.y,
2382 mng_info->global_chrm.blue_primary.x,
2383 mng_info->global_chrm.blue_primary.y);
2384 }
2385 }
glennrp0fe50b42010-11-16 03:52:51 +00002386
glennrpfaa852b2010-03-30 12:17:00 +00002387 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002388 {
2389 (void) png_get_cHRM(ping,ping_info,
2390 &image->chromaticity.white_point.x,
2391 &image->chromaticity.white_point.y,
2392 &image->chromaticity.red_primary.x,
2393 &image->chromaticity.red_primary.y,
2394 &image->chromaticity.green_primary.x,
2395 &image->chromaticity.green_primary.y,
2396 &image->chromaticity.blue_primary.x,
2397 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002398
cristy3ed852e2009-09-05 21:47:34 +00002399 if (logging != MagickFalse)
2400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2401 " Reading PNG cHRM chunk.");
2402 }
glennrp0fe50b42010-11-16 03:52:51 +00002403
glennrpe610a072010-08-05 17:08:46 +00002404 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002405 {
glennrpe610a072010-08-05 17:08:46 +00002406 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002407 Magick_RenderingIntent_to_PNG_RenderingIntent
2408 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00002409 png_set_gAMA(ping,ping_info,0.45455f);
2410 png_set_cHRM(ping,ping_info,
2411 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2412 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002413 }
cristy3ed852e2009-09-05 21:47:34 +00002414#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002415 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002416 {
cristy905ef802011-02-23 00:29:18 +00002417 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2418 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002419
cristy3ed852e2009-09-05 21:47:34 +00002420 if (logging != MagickFalse)
2421 if (image->page.x || image->page.y)
2422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002423 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2424 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002425 }
2426#endif
2427#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002428 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2429 {
2430 if (mng_info->have_global_phys)
2431 {
2432 png_set_pHYs(ping,ping_info,
2433 mng_info->global_x_pixels_per_unit,
2434 mng_info->global_y_pixels_per_unit,
2435 mng_info->global_phys_unit_type);
2436 }
2437 }
2438
2439 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002440 {
cristy3ed852e2009-09-05 21:47:34 +00002441 /*
2442 Set image resolution.
2443 */
2444 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002445 &unit_type);
cristy2a11bef2011-10-28 18:33:11 +00002446 image->resolution.x=(double) x_resolution;
2447 image->resolution.y=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002448
cristy3ed852e2009-09-05 21:47:34 +00002449 if (unit_type == PNG_RESOLUTION_METER)
2450 {
2451 image->units=PixelsPerCentimeterResolution;
cristy2a11bef2011-10-28 18:33:11 +00002452 image->resolution.x=(double) x_resolution/100.0;
2453 image->resolution.y=(double) y_resolution/100.0;
cristy3ed852e2009-09-05 21:47:34 +00002454 }
glennrp0fe50b42010-11-16 03:52:51 +00002455
cristy3ed852e2009-09-05 21:47:34 +00002456 if (logging != MagickFalse)
2457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002458 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2459 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002460 }
cristy3ed852e2009-09-05 21:47:34 +00002461#endif
glennrp823b55c2011-03-14 18:46:46 +00002462
glennrpfaa852b2010-03-30 12:17:00 +00002463 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002464 {
2465 int
2466 number_colors;
2467
2468 png_colorp
2469 palette;
2470
2471 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002472
cristy3ed852e2009-09-05 21:47:34 +00002473 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002474 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002475 {
2476 if (mng_info->global_plte_length)
2477 {
2478 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2479 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002480
glennrpfaa852b2010-03-30 12:17:00 +00002481 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002482 if (mng_info->global_trns_length)
2483 {
2484 if (mng_info->global_trns_length >
2485 mng_info->global_plte_length)
cristyc82a27b2011-10-21 01:07:16 +00002486 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00002487 GetMagickModule(),CoderError,
2488 "global tRNS has more entries than global PLTE",
2489 "`%s'",image_info->filename);
2490 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2491 (int) mng_info->global_trns_length,NULL);
2492 }
glennrpbfd9e612011-04-22 14:02:20 +00002493#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002494 if (
2495#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2496 mng_info->have_saved_bkgd_index ||
2497#endif
glennrpfaa852b2010-03-30 12:17:00 +00002498 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002499 {
2500 png_color_16
2501 background;
2502
2503#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2504 if (mng_info->have_saved_bkgd_index)
2505 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002506#endif
glennrpfaa852b2010-03-30 12:17:00 +00002507 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2508 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002509
cristy3ed852e2009-09-05 21:47:34 +00002510 background.red=(png_uint_16)
2511 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002512
cristy3ed852e2009-09-05 21:47:34 +00002513 background.green=(png_uint_16)
2514 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002515
cristy3ed852e2009-09-05 21:47:34 +00002516 background.blue=(png_uint_16)
2517 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002518
glennrpc6c391a2011-04-27 02:23:56 +00002519 background.gray=(png_uint_16)
2520 mng_info->global_plte[background.index].green;
2521
cristy3ed852e2009-09-05 21:47:34 +00002522 png_set_bKGD(ping,ping_info,&background);
2523 }
2524#endif
2525 }
2526 else
cristyc82a27b2011-10-21 01:07:16 +00002527 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00002528 CoderError,"No global PLTE in file","`%s'",
2529 image_info->filename);
2530 }
2531 }
2532
glennrpbfd9e612011-04-22 14:02:20 +00002533#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002534 if (mng_info->have_global_bkgd &&
2535 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002536 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002537
glennrpfaa852b2010-03-30 12:17:00 +00002538 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002539 {
glennrpbfd9e612011-04-22 14:02:20 +00002540 unsigned int
2541 bkgd_scale;
2542
cristy3ed852e2009-09-05 21:47:34 +00002543 /*
2544 Set image background color.
2545 */
2546 if (logging != MagickFalse)
2547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2548 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002549
glennrpbfd9e612011-04-22 14:02:20 +00002550 /* Scale background components to 16-bit, then scale
2551 * to quantum depth
2552 */
2553 if (logging != MagickFalse)
2554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2555 " raw ping_background=(%d,%d,%d).",ping_background->red,
2556 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002557
glennrpbfd9e612011-04-22 14:02:20 +00002558 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002559
glennrpbfd9e612011-04-22 14:02:20 +00002560 if (ping_bit_depth == 1)
2561 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002562
glennrpbfd9e612011-04-22 14:02:20 +00002563 else if (ping_bit_depth == 2)
2564 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002565
glennrpbfd9e612011-04-22 14:02:20 +00002566 else if (ping_bit_depth == 4)
2567 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002568
glennrpbfd9e612011-04-22 14:02:20 +00002569 if (ping_bit_depth <= 8)
2570 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002571
glennrpbfd9e612011-04-22 14:02:20 +00002572 ping_background->red *= bkgd_scale;
2573 ping_background->green *= bkgd_scale;
2574 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002575
glennrpbfd9e612011-04-22 14:02:20 +00002576 if (logging != MagickFalse)
2577 {
glennrp2cbb4482010-06-02 04:37:24 +00002578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2579 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002580
glennrp2cbb4482010-06-02 04:37:24 +00002581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2582 " ping_background=(%d,%d,%d).",ping_background->red,
2583 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002584 }
glennrp2cbb4482010-06-02 04:37:24 +00002585
glennrpbfd9e612011-04-22 14:02:20 +00002586 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002587 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002588
glennrpbfd9e612011-04-22 14:02:20 +00002589 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002590 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002591
glennrpbfd9e612011-04-22 14:02:20 +00002592 image->background_color.blue=
2593 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002594
cristy4c08aed2011-07-01 19:47:50 +00002595 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002596
glennrpbfd9e612011-04-22 14:02:20 +00002597 if (logging != MagickFalse)
2598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2599 " image->background_color=(%.20g,%.20g,%.20g).",
2600 (double) image->background_color.red,
2601 (double) image->background_color.green,
2602 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002603 }
glennrpbfd9e612011-04-22 14:02:20 +00002604#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002605
glennrpfaa852b2010-03-30 12:17:00 +00002606 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002607 {
2608 /*
glennrpa6a06632011-01-19 15:15:34 +00002609 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002610 */
2611 int
2612 max_sample;
2613
cristy35ef8242010-06-03 16:24:13 +00002614 size_t
2615 one=1;
2616
cristy3ed852e2009-09-05 21:47:34 +00002617 if (logging != MagickFalse)
2618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2619 " Reading PNG tRNS chunk.");
2620
cristyf9cca6a2010-06-04 23:49:28 +00002621 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002622
glennrpfaa852b2010-03-30 12:17:00 +00002623 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2624 (int)ping_trans_color->gray > max_sample) ||
2625 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2626 ((int)ping_trans_color->red > max_sample ||
2627 (int)ping_trans_color->green > max_sample ||
2628 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002629 {
2630 if (logging != MagickFalse)
2631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2632 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002633 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002634 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002635 image->matte=MagickFalse;
2636 }
2637 else
2638 {
glennrpa6a06632011-01-19 15:15:34 +00002639 int
2640 scale_to_short;
2641
2642 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2643
2644 /* Scale transparent_color to short */
2645 transparent_color.red= scale_to_short*ping_trans_color->red;
2646 transparent_color.green= scale_to_short*ping_trans_color->green;
2647 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy4c08aed2011-07-01 19:47:50 +00002648 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002649
glennrpfaa852b2010-03-30 12:17:00 +00002650 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002651 {
glennrp0f111982010-07-07 20:18:33 +00002652 if (logging != MagickFalse)
2653 {
2654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2655 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002656
glennrp0f111982010-07-07 20:18:33 +00002657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy101ab702011-10-13 13:06:32 +00002658 " scaled graylevel is %.20g.",transparent_color.alpha);
glennrp0f111982010-07-07 20:18:33 +00002659 }
cristy4c08aed2011-07-01 19:47:50 +00002660 transparent_color.red=transparent_color.alpha;
2661 transparent_color.green=transparent_color.alpha;
2662 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002663 }
2664 }
2665 }
2666#if defined(PNG_READ_sBIT_SUPPORTED)
2667 if (mng_info->have_global_sbit)
2668 {
glennrpfaa852b2010-03-30 12:17:00 +00002669 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002670 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2671 }
2672#endif
2673 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002674
cristy3ed852e2009-09-05 21:47:34 +00002675 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002676
2677 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2678
cristy3ed852e2009-09-05 21:47:34 +00002679 /*
2680 Initialize image structure.
2681 */
2682 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002683 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002684 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002685 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002686 if (mng_info->mng_type == 0)
2687 {
glennrpfaa852b2010-03-30 12:17:00 +00002688 mng_info->mng_width=ping_width;
2689 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002690 mng_info->frame=mng_info->image_box;
2691 mng_info->clip=mng_info->image_box;
2692 }
glennrp0fe50b42010-11-16 03:52:51 +00002693
cristy3ed852e2009-09-05 21:47:34 +00002694 else
2695 {
2696 image->page.y=mng_info->y_off[mng_info->object_id];
2697 }
glennrp0fe50b42010-11-16 03:52:51 +00002698
cristy3ed852e2009-09-05 21:47:34 +00002699 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002700 image->columns=ping_width;
2701 image->rows=ping_height;
cristy897f2bf2012-01-05 02:03:44 +00002702 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2703 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2704 image->colorspace=GRAYColorspace;
glennrpfaa852b2010-03-30 12:17:00 +00002705 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002706 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002707 {
cristybefe4d22010-06-07 01:18:58 +00002708 size_t
2709 one;
2710
cristy3ed852e2009-09-05 21:47:34 +00002711 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002712 one=1;
2713 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002714#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2715 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002716 image->colors=256;
2717#else
2718 if (image->colors > 65536L)
2719 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002720#endif
glennrpfaa852b2010-03-30 12:17:00 +00002721 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002722 {
2723 int
2724 number_colors;
2725
2726 png_colorp
2727 palette;
2728
2729 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002730 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002731
cristy3ed852e2009-09-05 21:47:34 +00002732 if (logging != MagickFalse)
2733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2734 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2735 }
2736 }
2737
2738 if (image->storage_class == PseudoClass)
2739 {
2740 /*
2741 Initialize image colormap.
2742 */
cristy018f07f2011-09-04 21:15:19 +00002743 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002744 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002745
glennrpfaa852b2010-03-30 12:17:00 +00002746 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002747 {
2748 int
2749 number_colors;
2750
2751 png_colorp
2752 palette;
2753
2754 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002755
glennrp6af6cf12011-04-22 13:05:16 +00002756 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002757 {
2758 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2759 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2760 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2761 }
glennrp6af6cf12011-04-22 13:05:16 +00002762
glennrp67b9c1a2011-04-22 18:47:36 +00002763 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002764 {
2765 image->colormap[i].red=0;
2766 image->colormap[i].green=0;
2767 image->colormap[i].blue=0;
2768 }
cristy3ed852e2009-09-05 21:47:34 +00002769 }
glennrp0fe50b42010-11-16 03:52:51 +00002770
cristy3ed852e2009-09-05 21:47:34 +00002771 else
2772 {
cristybb503372010-05-27 20:51:26 +00002773 size_t
cristy3ed852e2009-09-05 21:47:34 +00002774 scale;
2775
glennrpfaa852b2010-03-30 12:17:00 +00002776 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002777
cristy3ed852e2009-09-05 21:47:34 +00002778 if (scale < 1)
2779 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002780
cristybb503372010-05-27 20:51:26 +00002781 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002782 {
2783 image->colormap[i].red=(Quantum) (i*scale);
2784 image->colormap[i].green=(Quantum) (i*scale);
2785 image->colormap[i].blue=(Quantum) (i*scale);
2786 }
2787 }
2788 }
glennrp147bc912011-03-30 18:47:21 +00002789
glennrpcb395ac2011-03-30 19:50:23 +00002790 /* Set some properties for reporting by "identify" */
2791 {
glennrp147bc912011-03-30 18:47:21 +00002792 char
2793 msg[MaxTextExtent];
2794
2795 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2796 ping_interlace_method in value */
2797
cristy3b6fd2e2011-05-20 12:53:50 +00002798 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002799 "%d, %d",(int) ping_width, (int) ping_height);
cristy5d6fc9c2011-12-27 03:10:42 +00002800 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002801
cristy3b6fd2e2011-05-20 12:53:50 +00002802 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
cristy5d6fc9c2011-12-27 03:10:42 +00002803 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002804
cristy3b6fd2e2011-05-20 12:53:50 +00002805 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
cristy5d6fc9c2011-12-27 03:10:42 +00002806 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002807
cristy3b6fd2e2011-05-20 12:53:50 +00002808 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002809 (int) ping_interlace_method);
cristy5d6fc9c2011-12-27 03:10:42 +00002810 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
glennrpcb395ac2011-03-30 19:50:23 +00002811 }
glennrp147bc912011-03-30 18:47:21 +00002812
cristy3ed852e2009-09-05 21:47:34 +00002813 /*
2814 Read image scanlines.
2815 */
2816 if (image->delay != 0)
2817 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002818
glennrp0ca69b12010-07-26 01:57:52 +00002819 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002820 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2821 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002822 {
2823 if (logging != MagickFalse)
2824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002825 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002826 mng_info->scenes_found-1);
2827 png_destroy_read_struct(&ping,&ping_info,&end_info);
2828#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002829 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002830#endif
2831 if (logging != MagickFalse)
2832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2833 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002834
cristy3ed852e2009-09-05 21:47:34 +00002835 return(image);
2836 }
glennrp0fe50b42010-11-16 03:52:51 +00002837
cristy3ed852e2009-09-05 21:47:34 +00002838 if (logging != MagickFalse)
2839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2840 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002841
cristy3ed852e2009-09-05 21:47:34 +00002842 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002843 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2844 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002845
cristy3ed852e2009-09-05 21:47:34 +00002846 else
glennrpcf002022011-01-30 02:38:15 +00002847 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2848 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002849
glennrpcf002022011-01-30 02:38:15 +00002850 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002851 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002852
cristy3ed852e2009-09-05 21:47:34 +00002853 if (logging != MagickFalse)
2854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2855 " Converting PNG pixels to pixel packets");
2856 /*
2857 Convert PNG pixels to pixel packets.
2858 */
glennrpfaa852b2010-03-30 12:17:00 +00002859 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002860 {
2861 /*
2862 PNG image is corrupt.
2863 */
2864 png_destroy_read_struct(&ping,&ping_info,&end_info);
2865#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002866 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002867#endif
2868 if (quantum_info != (QuantumInfo *) NULL)
2869 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002870
glennrpcf002022011-01-30 02:38:15 +00002871 if (ping_pixels != (unsigned char *) NULL)
2872 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002873
cristy3ed852e2009-09-05 21:47:34 +00002874 if (logging != MagickFalse)
2875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2876 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002877
cristy3ed852e2009-09-05 21:47:34 +00002878 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002879 {
cristyc82a27b2011-10-21 01:07:16 +00002880 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002881 image->columns=0;
2882 }
glennrp0fe50b42010-11-16 03:52:51 +00002883
cristy3ed852e2009-09-05 21:47:34 +00002884 return(GetFirstImageInList(image));
2885 }
glennrp0fe50b42010-11-16 03:52:51 +00002886
cristyed552522009-10-16 14:04:35 +00002887 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002888
cristyed552522009-10-16 14:04:35 +00002889 if (quantum_info == (QuantumInfo *) NULL)
2890 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002891
glennrpc8cbc5d2011-01-01 00:12:34 +00002892 {
2893
2894 MagickBooleanType
2895 found_transparent_pixel;
2896
2897 found_transparent_pixel=MagickFalse;
2898
cristy3ed852e2009-09-05 21:47:34 +00002899 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002900 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002901 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002902 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002903 /*
2904 Convert image to DirectClass pixel packets.
2905 */
glennrp67b9c1a2011-04-22 18:47:36 +00002906#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2907 int
2908 depth;
2909
2910 depth=(ssize_t) ping_bit_depth;
2911#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002912 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2913 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2914 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2915 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002916
glennrpc8cbc5d2011-01-01 00:12:34 +00002917 for (y=0; y < (ssize_t) image->rows; y++)
2918 {
2919 if (num_passes > 1)
2920 row_offset=ping_rowbytes*y;
2921
2922 else
2923 row_offset=0;
2924
glennrpcf002022011-01-30 02:38:15 +00002925 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002926 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2927
cristyacd2ed22011-08-30 01:44:23 +00002928 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00002929 break;
2930
glennrpc8cbc5d2011-01-01 00:12:34 +00002931 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2932 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002933 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002934
2935 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2936 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002937 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002938
2939 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2940 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002941 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002942
2943 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2944 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002945 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002946
2947 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2948 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002949 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002950
glennrpc8cbc5d2011-01-01 00:12:34 +00002951 if (found_transparent_pixel == MagickFalse)
2952 {
2953 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002954 if (y== 0 && logging != MagickFalse)
2955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2956 " Looking for cheap transparent pixel");
2957
glennrpc8cbc5d2011-01-01 00:12:34 +00002958 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2959 {
glennrp5aa37f62011-01-02 03:07:57 +00002960 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2961 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy4c08aed2011-07-01 19:47:50 +00002962 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00002963 {
glennrpa6a06632011-01-19 15:15:34 +00002964 if (logging != MagickFalse)
2965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2966 " ...got one.");
2967
glennrpc8cbc5d2011-01-01 00:12:34 +00002968 found_transparent_pixel = MagickTrue;
2969 break;
2970 }
glennrp4f25bd02011-01-01 18:51:28 +00002971 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2972 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp847370c2011-07-05 17:37:15 +00002973 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2974 transparent_color.red &&
2975 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2976 transparent_color.green &&
2977 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2978 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002979 {
glennrpa6a06632011-01-19 15:15:34 +00002980 if (logging != MagickFalse)
2981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2982 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002983 found_transparent_pixel = MagickTrue;
2984 break;
2985 }
cristyed231572011-07-14 02:18:59 +00002986 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00002987 }
2988 }
2989
2990 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2991 {
2992 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2993 image->rows);
2994
2995 if (status == MagickFalse)
2996 break;
2997 }
2998 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2999 break;
3000 }
3001
3002 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3003 {
3004 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00003005 if (status == MagickFalse)
3006 break;
3007 }
cristy3ed852e2009-09-05 21:47:34 +00003008 }
cristy3ed852e2009-09-05 21:47:34 +00003009 }
glennrp0fe50b42010-11-16 03:52:51 +00003010
cristy3ed852e2009-09-05 21:47:34 +00003011 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00003012
cristy3ed852e2009-09-05 21:47:34 +00003013 for (pass=0; pass < num_passes; pass++)
3014 {
3015 Quantum
3016 *quantum_scanline;
3017
3018 register Quantum
3019 *r;
3020
3021 /*
3022 Convert grayscale image to PseudoClass pixel packets.
3023 */
glennrpc17d96f2011-06-27 01:20:11 +00003024 if (logging != MagickFalse)
3025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3026 " Converting grayscale pixels to pixel packets");
cristy4c08aed2011-07-01 19:47:50 +00003027
glennrpfaa852b2010-03-30 12:17:00 +00003028 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00003029 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00003030
cristy3ed852e2009-09-05 21:47:34 +00003031 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3032 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003033
cristy3ed852e2009-09-05 21:47:34 +00003034 if (quantum_scanline == (Quantum *) NULL)
3035 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003036
cristybb503372010-05-27 20:51:26 +00003037 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003038 {
3039 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003040 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003041
cristy3ed852e2009-09-05 21:47:34 +00003042 else
3043 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003044
glennrpcf002022011-01-30 02:38:15 +00003045 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00003046 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003047
cristyacd2ed22011-08-30 01:44:23 +00003048 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003049 break;
glennrp0fe50b42010-11-16 03:52:51 +00003050
glennrpcf002022011-01-30 02:38:15 +00003051 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003052 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003053
glennrpfaa852b2010-03-30 12:17:00 +00003054 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003055 {
3056 case 1:
3057 {
cristybb503372010-05-27 20:51:26 +00003058 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003059 bit;
3060
cristybb503372010-05-27 20:51:26 +00003061 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003062 {
3063 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003064 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003065 p++;
3066 }
glennrp0fe50b42010-11-16 03:52:51 +00003067
cristy3ed852e2009-09-05 21:47:34 +00003068 if ((image->columns % 8) != 0)
3069 {
cristybb503372010-05-27 20:51:26 +00003070 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003071 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003072 }
glennrp0fe50b42010-11-16 03:52:51 +00003073
cristy3ed852e2009-09-05 21:47:34 +00003074 break;
3075 }
glennrp47b9dd52010-11-24 18:12:06 +00003076
cristy3ed852e2009-09-05 21:47:34 +00003077 case 2:
3078 {
cristybb503372010-05-27 20:51:26 +00003079 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003080 {
glennrpa18d5bc2011-04-23 14:51:34 +00003081 *r++=(*p >> 6) & 0x03;
3082 *r++=(*p >> 4) & 0x03;
3083 *r++=(*p >> 2) & 0x03;
3084 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003085 }
glennrp0fe50b42010-11-16 03:52:51 +00003086
cristy3ed852e2009-09-05 21:47:34 +00003087 if ((image->columns % 4) != 0)
3088 {
cristybb503372010-05-27 20:51:26 +00003089 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003090 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003091 }
glennrp0fe50b42010-11-16 03:52:51 +00003092
cristy3ed852e2009-09-05 21:47:34 +00003093 break;
3094 }
glennrp47b9dd52010-11-24 18:12:06 +00003095
cristy3ed852e2009-09-05 21:47:34 +00003096 case 4:
3097 {
cristybb503372010-05-27 20:51:26 +00003098 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003099 {
glennrpa18d5bc2011-04-23 14:51:34 +00003100 *r++=(*p >> 4) & 0x0f;
3101 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003102 }
glennrp0fe50b42010-11-16 03:52:51 +00003103
cristy3ed852e2009-09-05 21:47:34 +00003104 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003105 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003106
cristy3ed852e2009-09-05 21:47:34 +00003107 break;
3108 }
glennrp47b9dd52010-11-24 18:12:06 +00003109
cristy3ed852e2009-09-05 21:47:34 +00003110 case 8:
3111 {
glennrpfaa852b2010-03-30 12:17:00 +00003112 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003113 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003114 {
glennrpa18d5bc2011-04-23 14:51:34 +00003115 *r++=*p++;
cristy4c08aed2011-07-01 19:47:50 +00003116 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3117 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003118 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003119 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003120 }
glennrp0fe50b42010-11-16 03:52:51 +00003121
cristy3ed852e2009-09-05 21:47:34 +00003122 else
cristybb503372010-05-27 20:51:26 +00003123 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003124 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003125
cristy3ed852e2009-09-05 21:47:34 +00003126 break;
3127 }
glennrp47b9dd52010-11-24 18:12:06 +00003128
cristy3ed852e2009-09-05 21:47:34 +00003129 case 16:
3130 {
cristybb503372010-05-27 20:51:26 +00003131 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003132 {
glennrpc17d96f2011-06-27 01:20:11 +00003133#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003134 size_t
3135 quantum;
3136
3137 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003138 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003139
3140 else
glennrpc17d96f2011-06-27 01:20:11 +00003141 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003142
glennrp58f77c72011-04-23 14:09:09 +00003143 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003144 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003145 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003146
3147 if (ping_color_type == 4)
3148 {
glennrpc17d96f2011-06-27 01:20:11 +00003149 if (image->colors > 256)
3150 quantum=((*p++) << 8);
3151 else
3152 quantum=0;
3153
glennrp58f77c72011-04-23 14:09:09 +00003154 quantum|=(*p++);
cristy4c08aed2011-07-01 19:47:50 +00003155 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3156 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003157 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003158 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003159 }
glennrp58f77c72011-04-23 14:09:09 +00003160
3161#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3162 *r++=(*p++);
3163 p++; /* strip low byte */
3164
3165 if (ping_color_type == 4)
3166 {
cristy4c08aed2011-07-01 19:47:50 +00003167 SetPixelAlpha(image,*p++,q);
3168 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003169 found_transparent_pixel = MagickTrue;
3170 p++;
cristyed231572011-07-14 02:18:59 +00003171 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003172 }
cristy3ed852e2009-09-05 21:47:34 +00003173#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003174 }
glennrp47b9dd52010-11-24 18:12:06 +00003175
cristy3ed852e2009-09-05 21:47:34 +00003176 break;
3177 }
glennrp47b9dd52010-11-24 18:12:06 +00003178
cristy3ed852e2009-09-05 21:47:34 +00003179 default:
3180 break;
3181 }
glennrp3faa9a32011-04-23 14:00:25 +00003182
cristy3ed852e2009-09-05 21:47:34 +00003183 /*
3184 Transfer image scanline.
3185 */
3186 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003187
cristy4c08aed2011-07-01 19:47:50 +00003188 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3189
cristyacd2ed22011-08-30 01:44:23 +00003190 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00003191 break;
cristybb503372010-05-27 20:51:26 +00003192 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00003193 {
3194 SetPixelIndex(image,*r++,q);
cristyed231572011-07-14 02:18:59 +00003195 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00003196 }
glennrp0fe50b42010-11-16 03:52:51 +00003197
cristy3ed852e2009-09-05 21:47:34 +00003198 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3199 break;
glennrp0fe50b42010-11-16 03:52:51 +00003200
cristy7a287bf2010-02-14 02:18:09 +00003201 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3202 {
cristycee97112010-05-28 00:44:52 +00003203 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003204 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003205
cristy7a287bf2010-02-14 02:18:09 +00003206 if (status == MagickFalse)
3207 break;
3208 }
cristy3ed852e2009-09-05 21:47:34 +00003209 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003210
cristy7a287bf2010-02-14 02:18:09 +00003211 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003212 {
3213 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003214
cristy3ed852e2009-09-05 21:47:34 +00003215 if (status == MagickFalse)
3216 break;
3217 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003218
cristy3ed852e2009-09-05 21:47:34 +00003219 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3220 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003221
3222 image->matte=found_transparent_pixel;
3223
3224 if (logging != MagickFalse)
3225 {
3226 if (found_transparent_pixel != MagickFalse)
3227 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3228 " Found transparent pixel");
3229 else
glennrp5aa37f62011-01-02 03:07:57 +00003230 {
3231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3232 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003233
glennrp5aa37f62011-01-02 03:07:57 +00003234 ping_color_type&=0x03;
3235 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003236 }
3237 }
3238
cristyb32b90a2009-09-07 21:45:48 +00003239 if (quantum_info != (QuantumInfo *) NULL)
3240 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00003241
cristy5c6f7892010-05-05 22:53:29 +00003242 if (image->storage_class == PseudoClass)
3243 {
cristyaeb2cbc2010-05-07 13:28:58 +00003244 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003245 matte;
3246
3247 matte=image->matte;
3248 image->matte=MagickFalse;
cristyea1a8aa2011-10-20 13:24:06 +00003249 (void) SyncImage(image,exception);
cristyaeb2cbc2010-05-07 13:28:58 +00003250 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003251 }
glennrp47b9dd52010-11-24 18:12:06 +00003252
glennrp4eb39312011-03-30 21:34:55 +00003253 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003254
3255 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003256 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003257 {
3258 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003259 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003260 image->colors=2;
cristyea1a8aa2011-10-20 13:24:06 +00003261 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003262#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003263 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003264#endif
3265 if (logging != MagickFalse)
3266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3267 " exit ReadOnePNGImage() early.");
3268 return(image);
3269 }
glennrp47b9dd52010-11-24 18:12:06 +00003270
glennrpfaa852b2010-03-30 12:17:00 +00003271 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003272 {
3273 ClassType
3274 storage_class;
3275
3276 /*
3277 Image has a transparent background.
3278 */
3279 storage_class=image->storage_class;
3280 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003281
glennrp3c218112010-11-27 15:31:26 +00003282/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003283
glennrp0fe50b42010-11-16 03:52:51 +00003284 if (storage_class == PseudoClass)
3285 {
3286 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003287 {
glennrp0fe50b42010-11-16 03:52:51 +00003288 for (x=0; x < ping_num_trans; x++)
3289 {
cristy6b7677c2012-01-01 20:59:57 +00003290 image->colormap[x].matte=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00003291 image->colormap[x].alpha =
3292 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003293 }
glennrpc11cf6a2010-03-20 16:46:19 +00003294 }
glennrp47b9dd52010-11-24 18:12:06 +00003295
glennrp0fe50b42010-11-16 03:52:51 +00003296 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3297 {
3298 for (x=0; x < (int) image->colors; x++)
3299 {
3300 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy4c08aed2011-07-01 19:47:50 +00003301 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003302 {
cristy6b7677c2012-01-01 20:59:57 +00003303 image->colormap[x].matte=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00003304 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003305 }
3306 }
3307 }
cristyea1a8aa2011-10-20 13:24:06 +00003308 (void) SyncImage(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003309 }
glennrp47b9dd52010-11-24 18:12:06 +00003310
glennrpa6a06632011-01-19 15:15:34 +00003311#if 1 /* Should have already been done above, but glennrp problem P10
3312 * needs this.
3313 */
glennrp0fe50b42010-11-16 03:52:51 +00003314 else
3315 {
3316 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003317 {
glennrp0fe50b42010-11-16 03:52:51 +00003318 image->storage_class=storage_class;
3319 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3320
cristyacd2ed22011-08-30 01:44:23 +00003321 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003322 break;
3323
glennrp0fe50b42010-11-16 03:52:51 +00003324
glennrpa6a06632011-01-19 15:15:34 +00003325 /* Caution: on a Q8 build, this does not distinguish between
3326 * 16-bit colors that differ only in the low byte
3327 */
glennrp0fe50b42010-11-16 03:52:51 +00003328 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3329 {
glennrp847370c2011-07-05 17:37:15 +00003330 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3331 transparent_color.red &&
3332 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3333 transparent_color.green &&
3334 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3335 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003336 {
cristy4c08aed2011-07-01 19:47:50 +00003337 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003338 }
glennrp0fe50b42010-11-16 03:52:51 +00003339
glennrp67b9c1a2011-04-22 18:47:36 +00003340#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003341 else
glennrp4f25bd02011-01-01 18:51:28 +00003342 {
cristy4c08aed2011-07-01 19:47:50 +00003343 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003344 }
glennrpa6a06632011-01-19 15:15:34 +00003345#endif
glennrp0fe50b42010-11-16 03:52:51 +00003346
cristyed231572011-07-14 02:18:59 +00003347 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003348 }
3349
3350 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3351 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003352 }
glennrp0fe50b42010-11-16 03:52:51 +00003353 }
glennrpa6a06632011-01-19 15:15:34 +00003354#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003355
cristy3ed852e2009-09-05 21:47:34 +00003356 image->storage_class=DirectClass;
3357 }
glennrp3c218112010-11-27 15:31:26 +00003358
cristyeb3b22a2011-03-31 20:16:11 +00003359 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003360 {
3361 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003362 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3363 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003364 else
glennrpa0ed0092011-04-18 16:36:29 +00003365 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3366 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003367
glennrp4eb39312011-03-30 21:34:55 +00003368 if (status != MagickFalse)
3369 for (i=0; i < (ssize_t) num_text; i++)
3370 {
3371 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003372
glennrp4eb39312011-03-30 21:34:55 +00003373 if (logging != MagickFalse)
3374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3375 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003376
glennrp4eb39312011-03-30 21:34:55 +00003377 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003378 {
cristyd15e6592011-10-15 00:13:06 +00003379 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i,
3380 exception);
glennrp4eb39312011-03-30 21:34:55 +00003381 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003382 }
glennrp0fe50b42010-11-16 03:52:51 +00003383
glennrp4eb39312011-03-30 21:34:55 +00003384 else
3385 {
3386 char
3387 *value;
3388
3389 length=text[i].text_length;
3390 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3391 sizeof(*value));
3392 if (value == (char *) NULL)
3393 {
cristyc82a27b2011-10-21 01:07:16 +00003394 (void) ThrowMagickException(exception,GetMagickModule(),
glennrp4eb39312011-03-30 21:34:55 +00003395 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3396 image->filename);
3397 break;
3398 }
3399 *value='\0';
3400 (void) ConcatenateMagickString(value,text[i].text,length+2);
3401
3402 /* Don't save "density" or "units" property if we have a pHYs
3403 * chunk
3404 */
3405 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3406 (LocaleCompare(text[i].key,"density") != 0 &&
3407 LocaleCompare(text[i].key,"units") != 0))
cristyd15e6592011-10-15 00:13:06 +00003408 (void) SetImageProperty(image,text[i].key,value,exception);
glennrp4eb39312011-03-30 21:34:55 +00003409
3410 if (logging != MagickFalse)
3411 {
3412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3413 " length: %lu",(unsigned long) length);
3414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3415 " Keyword: %s",text[i].key);
3416 }
3417
3418 value=DestroyString(value);
3419 }
3420 }
3421 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003422 }
glennrp3c218112010-11-27 15:31:26 +00003423
cristy3ed852e2009-09-05 21:47:34 +00003424#ifdef MNG_OBJECT_BUFFERS
3425 /*
3426 Store the object if necessary.
3427 */
3428 if (object_id && !mng_info->frozen[object_id])
3429 {
3430 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3431 {
3432 /*
3433 create a new object buffer.
3434 */
3435 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003436 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003437
cristy3ed852e2009-09-05 21:47:34 +00003438 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3439 {
3440 mng_info->ob[object_id]->image=(Image *) NULL;
3441 mng_info->ob[object_id]->reference_count=1;
3442 }
3443 }
glennrp47b9dd52010-11-24 18:12:06 +00003444
cristy3ed852e2009-09-05 21:47:34 +00003445 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3446 mng_info->ob[object_id]->frozen)
3447 {
3448 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
cristyc82a27b2011-10-21 01:07:16 +00003449 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003450 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3451 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003452
cristy3ed852e2009-09-05 21:47:34 +00003453 if (mng_info->ob[object_id]->frozen)
cristyc82a27b2011-10-21 01:07:16 +00003454 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003455 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3456 "`%s'",image->filename);
3457 }
glennrp0fe50b42010-11-16 03:52:51 +00003458
cristy3ed852e2009-09-05 21:47:34 +00003459 else
3460 {
cristy3ed852e2009-09-05 21:47:34 +00003461
3462 if (mng_info->ob[object_id]->image != (Image *) NULL)
3463 mng_info->ob[object_id]->image=DestroyImage
3464 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003465
cristy3ed852e2009-09-05 21:47:34 +00003466 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
cristyc82a27b2011-10-21 01:07:16 +00003467 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003468
cristy3ed852e2009-09-05 21:47:34 +00003469 if (mng_info->ob[object_id]->image != (Image *) NULL)
3470 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003471
cristy3ed852e2009-09-05 21:47:34 +00003472 else
cristyc82a27b2011-10-21 01:07:16 +00003473 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003474 ResourceLimitError,"Cloning image for object buffer failed",
3475 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003476
glennrpfaa852b2010-03-30 12:17:00 +00003477 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003478 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003479
glennrpfaa852b2010-03-30 12:17:00 +00003480 mng_info->ob[object_id]->width=ping_width;
3481 mng_info->ob[object_id]->height=ping_height;
3482 mng_info->ob[object_id]->color_type=ping_color_type;
3483 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3484 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3485 mng_info->ob[object_id]->compression_method=
3486 ping_compression_method;
3487 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003488
glennrpfaa852b2010-03-30 12:17:00 +00003489 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003490 {
3491 int
3492 number_colors;
3493
3494 png_colorp
3495 plte;
3496
3497 /*
3498 Copy the PLTE to the object buffer.
3499 */
3500 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3501 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003502
cristy3ed852e2009-09-05 21:47:34 +00003503 for (i=0; i < number_colors; i++)
3504 {
3505 mng_info->ob[object_id]->plte[i]=plte[i];
3506 }
3507 }
glennrp47b9dd52010-11-24 18:12:06 +00003508
cristy3ed852e2009-09-05 21:47:34 +00003509 else
3510 mng_info->ob[object_id]->plte_length=0;
3511 }
3512 }
3513#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003514
3515 /* Set image->matte to MagickTrue if the input colortype supports
3516 * alpha or if a valid tRNS chunk is present, no matter whether there
3517 * is actual transparency present.
3518 */
3519 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3520 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3521 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3522 MagickTrue : MagickFalse;
3523
glennrpcb395ac2011-03-30 19:50:23 +00003524 /* Set more properties for identify to retrieve */
3525 {
3526 char
3527 msg[MaxTextExtent];
3528
glennrp4eb39312011-03-30 21:34:55 +00003529 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003530 {
3531 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003532 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003533 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
cristy5d6fc9c2011-12-27 03:10:42 +00003534 (void) SetImageProperty(image,"png:text ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003535 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003536 }
3537
3538 if (num_raw_profiles != 0)
3539 {
cristy3b6fd2e2011-05-20 12:53:50 +00003540 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003541 "%d were found", num_raw_profiles);
cristy5d6fc9c2011-12-27 03:10:42 +00003542 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003543 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003544 }
3545
glennrpcb395ac2011-03-30 19:50:23 +00003546 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003547 {
cristy3b6fd2e2011-05-20 12:53:50 +00003548 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003549 "chunk was found (see Chromaticity, above)");
cristy5d6fc9c2011-12-27 03:10:42 +00003550 (void) SetImageProperty(image,"png:cHRM ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003551 exception);
glennrp59612252011-03-30 21:45:21 +00003552 }
glennrpcb395ac2011-03-30 19:50:23 +00003553
3554 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003555 {
cristy3b6fd2e2011-05-20 12:53:50 +00003556 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003557 "chunk was found (see Background color, above)");
cristy5d6fc9c2011-12-27 03:10:42 +00003558 (void) SetImageProperty(image,"png:bKGD ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003559 exception);
glennrp59612252011-03-30 21:45:21 +00003560 }
3561
cristy3b6fd2e2011-05-20 12:53:50 +00003562 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003563 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003564
3565 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy5d6fc9c2011-12-27 03:10:42 +00003566 (void) SetImageProperty(image,"png:iCCP ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003567 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003568
glennrpcb395ac2011-03-30 19:50:23 +00003569 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy5d6fc9c2011-12-27 03:10:42 +00003570 (void) SetImageProperty(image,"png:tRNS ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003571 exception);
glennrp4eb39312011-03-30 21:34:55 +00003572
3573#if defined(PNG_sRGB_SUPPORTED)
3574 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3575 {
cristy3b6fd2e2011-05-20 12:53:50 +00003576 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003577 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003578 (int) intent);
cristy5d6fc9c2011-12-27 03:10:42 +00003579 (void) SetImageProperty(image,"png:sRGB ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003580 exception);
glennrp4eb39312011-03-30 21:34:55 +00003581 }
3582#endif
3583
3584 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3585 {
cristy3b6fd2e2011-05-20 12:53:50 +00003586 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003587 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003588 file_gamma);
cristy5d6fc9c2011-12-27 03:10:42 +00003589 (void) SetImageProperty(image,"png:gAMA ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003590 exception);
glennrp4eb39312011-03-30 21:34:55 +00003591 }
3592
3593#if defined(PNG_pHYs_SUPPORTED)
3594 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3595 {
cristy3b6fd2e2011-05-20 12:53:50 +00003596 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003597 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003598 (double) x_resolution,(double) y_resolution, unit_type);
cristy5d6fc9c2011-12-27 03:10:42 +00003599 (void) SetImageProperty(image,"png:pHYs ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003600 exception);
glennrp4eb39312011-03-30 21:34:55 +00003601 }
3602#endif
3603
3604#if defined(PNG_oFFs_SUPPORTED)
3605 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3606 {
cristy3b6fd2e2011-05-20 12:53:50 +00003607 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003608 (double) image->page.x,(double) image->page.y);
cristy5d6fc9c2011-12-27 03:10:42 +00003609 (void) SetImageProperty(image,"png:oFFs ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003610 exception);
glennrp4eb39312011-03-30 21:34:55 +00003611 }
3612#endif
3613
glennrp07523c72011-03-31 18:12:10 +00003614 if ((image->page.width != 0 && image->page.width != image->columns) ||
3615 (image->page.height != 0 && image->page.height != image->rows))
3616 {
cristy3b6fd2e2011-05-20 12:53:50 +00003617 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003618 "width=%.20g, height=%.20g",
3619 (double) image->page.width,(double) image->page.height);
cristy5d6fc9c2011-12-27 03:10:42 +00003620 (void) SetImageProperty(image,"png:vpAg ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003621 exception);
glennrp07523c72011-03-31 18:12:10 +00003622 }
glennrpcb395ac2011-03-30 19:50:23 +00003623 }
3624
cristy3ed852e2009-09-05 21:47:34 +00003625 /*
3626 Relinquish resources.
3627 */
3628 png_destroy_read_struct(&ping,&ping_info,&end_info);
3629
glennrpcf002022011-01-30 02:38:15 +00003630 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003631#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003632 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003633#endif
3634
3635 if (logging != MagickFalse)
3636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3637 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003638
cristy3ed852e2009-09-05 21:47:34 +00003639 return(image);
3640
3641/* end of reading one PNG image */
3642}
3643
3644static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3645{
3646 Image
3647 *image,
3648 *previous;
3649
3650 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003651 have_mng_structure,
3652 logging,
cristy3ed852e2009-09-05 21:47:34 +00003653 status;
3654
3655 MngInfo
3656 *mng_info;
3657
3658 char
3659 magic_number[MaxTextExtent];
3660
cristy3ed852e2009-09-05 21:47:34 +00003661 ssize_t
3662 count;
3663
3664 /*
3665 Open image file.
3666 */
3667 assert(image_info != (const ImageInfo *) NULL);
3668 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003669
cristy3ed852e2009-09-05 21:47:34 +00003670 if (image_info->debug != MagickFalse)
3671 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3672 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003673
cristy3ed852e2009-09-05 21:47:34 +00003674 assert(exception != (ExceptionInfo *) NULL);
3675 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003676 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy9950d572011-10-01 18:22:35 +00003677 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003678 mng_info=(MngInfo *) NULL;
3679 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003680
cristy3ed852e2009-09-05 21:47:34 +00003681 if (status == MagickFalse)
3682 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003683
cristy3ed852e2009-09-05 21:47:34 +00003684 /*
3685 Verify PNG signature.
3686 */
3687 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003688
glennrpdde35db2011-02-21 12:06:32 +00003689 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003690 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003691
cristy3ed852e2009-09-05 21:47:34 +00003692 /*
3693 Allocate a MngInfo structure.
3694 */
3695 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003696 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003697
cristy3ed852e2009-09-05 21:47:34 +00003698 if (mng_info == (MngInfo *) NULL)
3699 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003700
cristy3ed852e2009-09-05 21:47:34 +00003701 /*
3702 Initialize members of the MngInfo structure.
3703 */
3704 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3705 mng_info->image=image;
3706 have_mng_structure=MagickTrue;
3707
3708 previous=image;
3709 image=ReadOnePNGImage(mng_info,image_info,exception);
3710 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003711
cristy3ed852e2009-09-05 21:47:34 +00003712 if (image == (Image *) NULL)
3713 {
3714 if (previous != (Image *) NULL)
3715 {
3716 if (previous->signature != MagickSignature)
3717 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003718
cristy3ed852e2009-09-05 21:47:34 +00003719 (void) CloseBlob(previous);
3720 (void) DestroyImageList(previous);
3721 }
glennrp0fe50b42010-11-16 03:52:51 +00003722
cristy3ed852e2009-09-05 21:47:34 +00003723 if (logging != MagickFalse)
3724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3725 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003726
cristy3ed852e2009-09-05 21:47:34 +00003727 return((Image *) NULL);
3728 }
glennrp47b9dd52010-11-24 18:12:06 +00003729
cristy3ed852e2009-09-05 21:47:34 +00003730 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003731
cristy3ed852e2009-09-05 21:47:34 +00003732 if ((image->columns == 0) || (image->rows == 0))
3733 {
3734 if (logging != MagickFalse)
3735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3736 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003737
cristy3ed852e2009-09-05 21:47:34 +00003738 ThrowReaderException(CorruptImageError,"CorruptImage");
3739 }
glennrp47b9dd52010-11-24 18:12:06 +00003740
cristy3ed852e2009-09-05 21:47:34 +00003741 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3742 {
cristy018f07f2011-09-04 21:15:19 +00003743 (void) SetImageType(image,TrueColorType,exception);
cristy3ed852e2009-09-05 21:47:34 +00003744 image->matte=MagickFalse;
3745 }
glennrp0fe50b42010-11-16 03:52:51 +00003746
cristy3ed852e2009-09-05 21:47:34 +00003747 if (LocaleCompare(image_info->magick,"PNG32") == 0)
cristy018f07f2011-09-04 21:15:19 +00003748 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003749
cristy3ed852e2009-09-05 21:47:34 +00003750 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3752 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3753 (double) image->page.width,(double) image->page.height,
3754 (double) image->page.x,(double) image->page.y);
3755
3756 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003758
cristy3ed852e2009-09-05 21:47:34 +00003759 return(image);
3760}
3761
3762
3763
3764#if defined(JNG_SUPPORTED)
3765/*
3766%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3767% %
3768% %
3769% %
3770% R e a d O n e J N G I m a g e %
3771% %
3772% %
3773% %
3774%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3775%
3776% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3777% (minus the 8-byte signature) and returns it. It allocates the memory
3778% necessary for the new Image structure and returns a pointer to the new
3779% image.
3780%
3781% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3782%
3783% The format of the ReadOneJNGImage method is:
3784%
3785% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3786% ExceptionInfo *exception)
3787%
3788% A description of each parameter follows:
3789%
3790% o mng_info: Specifies a pointer to a MngInfo structure.
3791%
3792% o image_info: the image info.
3793%
3794% o exception: return any errors or warnings in this structure.
3795%
3796*/
3797static Image *ReadOneJNGImage(MngInfo *mng_info,
3798 const ImageInfo *image_info, ExceptionInfo *exception)
3799{
3800 Image
3801 *alpha_image,
3802 *color_image,
3803 *image,
3804 *jng_image;
3805
3806 ImageInfo
3807 *alpha_image_info,
3808 *color_image_info;
3809
cristy4383ec82011-01-05 15:42:32 +00003810 MagickBooleanType
3811 logging;
3812
cristybb503372010-05-27 20:51:26 +00003813 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003814 y;
3815
3816 MagickBooleanType
3817 status;
3818
3819 png_uint_32
3820 jng_height,
3821 jng_width;
3822
3823 png_byte
3824 jng_color_type,
3825 jng_image_sample_depth,
3826 jng_image_compression_method,
3827 jng_image_interlace_method,
3828 jng_alpha_sample_depth,
3829 jng_alpha_compression_method,
3830 jng_alpha_filter_method,
3831 jng_alpha_interlace_method;
3832
cristy4c08aed2011-07-01 19:47:50 +00003833 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003834 *s;
3835
cristybb503372010-05-27 20:51:26 +00003836 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003837 i,
3838 x;
3839
cristy4c08aed2011-07-01 19:47:50 +00003840 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003841 *q;
3842
3843 register unsigned char
3844 *p;
3845
3846 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003847 read_JSEP,
3848 reading_idat,
3849 skip_to_iend;
3850
cristybb503372010-05-27 20:51:26 +00003851 size_t
cristy3ed852e2009-09-05 21:47:34 +00003852 length;
3853
3854 jng_alpha_compression_method=0;
3855 jng_alpha_sample_depth=8;
3856 jng_color_type=0;
3857 jng_height=0;
3858 jng_width=0;
3859 alpha_image=(Image *) NULL;
3860 color_image=(Image *) NULL;
3861 alpha_image_info=(ImageInfo *) NULL;
3862 color_image_info=(ImageInfo *) NULL;
3863
3864 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003865 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003866
3867 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003868
cristy4c08aed2011-07-01 19:47:50 +00003869 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003870 {
3871 /*
3872 Allocate next image structure.
3873 */
3874 if (logging != MagickFalse)
3875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3876 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003877
cristy9950d572011-10-01 18:22:35 +00003878 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003879
cristy3ed852e2009-09-05 21:47:34 +00003880 if (GetNextImageInList(image) == (Image *) NULL)
3881 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003882
cristy3ed852e2009-09-05 21:47:34 +00003883 image=SyncNextImageInList(image);
3884 }
3885 mng_info->image=image;
3886
3887 /*
3888 Signature bytes have already been read.
3889 */
3890
3891 read_JSEP=MagickFalse;
3892 reading_idat=MagickFalse;
3893 skip_to_iend=MagickFalse;
3894 for (;;)
3895 {
3896 char
3897 type[MaxTextExtent];
3898
3899 unsigned char
3900 *chunk;
3901
3902 unsigned int
3903 count;
3904
3905 /*
3906 Read a new JNG chunk.
3907 */
3908 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3909 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003910
cristy3ed852e2009-09-05 21:47:34 +00003911 if (status == MagickFalse)
3912 break;
glennrp0fe50b42010-11-16 03:52:51 +00003913
cristy3ed852e2009-09-05 21:47:34 +00003914 type[0]='\0';
3915 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3916 length=ReadBlobMSBLong(image);
3917 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3918
3919 if (logging != MagickFalse)
3920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003921 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3922 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003923
3924 if (length > PNG_UINT_31_MAX || count == 0)
3925 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003926
cristy3ed852e2009-09-05 21:47:34 +00003927 p=NULL;
3928 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003929
cristy3ed852e2009-09-05 21:47:34 +00003930 if (length)
3931 {
3932 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003933
cristy3ed852e2009-09-05 21:47:34 +00003934 if (chunk == (unsigned char *) NULL)
3935 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003936
cristybb503372010-05-27 20:51:26 +00003937 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003938 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003939
cristy3ed852e2009-09-05 21:47:34 +00003940 p=chunk;
3941 }
glennrp47b9dd52010-11-24 18:12:06 +00003942
cristy3ed852e2009-09-05 21:47:34 +00003943 (void) ReadBlobMSBLong(image); /* read crc word */
3944
3945 if (skip_to_iend)
3946 {
3947 if (length)
3948 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003949
cristy3ed852e2009-09-05 21:47:34 +00003950 continue;
3951 }
3952
3953 if (memcmp(type,mng_JHDR,4) == 0)
3954 {
3955 if (length == 16)
3956 {
cristybb503372010-05-27 20:51:26 +00003957 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003958 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003959 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003960 (p[6] << 8) | p[7]);
3961 jng_color_type=p[8];
3962 jng_image_sample_depth=p[9];
3963 jng_image_compression_method=p[10];
3964 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003965
cristy3ed852e2009-09-05 21:47:34 +00003966 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3967 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003968
cristy3ed852e2009-09-05 21:47:34 +00003969 jng_alpha_sample_depth=p[12];
3970 jng_alpha_compression_method=p[13];
3971 jng_alpha_filter_method=p[14];
3972 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003973
cristy3ed852e2009-09-05 21:47:34 +00003974 if (logging != MagickFalse)
3975 {
3976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003977 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003978
cristy3ed852e2009-09-05 21:47:34 +00003979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003980 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003981
cristy3ed852e2009-09-05 21:47:34 +00003982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3983 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003984
cristy3ed852e2009-09-05 21:47:34 +00003985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3986 " jng_image_sample_depth: %3d",
3987 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003988
cristy3ed852e2009-09-05 21:47:34 +00003989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3990 " jng_image_compression_method:%3d",
3991 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003992
cristy3ed852e2009-09-05 21:47:34 +00003993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3994 " jng_image_interlace_method: %3d",
3995 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003996
cristy3ed852e2009-09-05 21:47:34 +00003997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3998 " jng_alpha_sample_depth: %3d",
3999 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00004000
cristy3ed852e2009-09-05 21:47:34 +00004001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4002 " jng_alpha_compression_method:%3d",
4003 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004004
cristy3ed852e2009-09-05 21:47:34 +00004005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4006 " jng_alpha_filter_method: %3d",
4007 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00004008
cristy3ed852e2009-09-05 21:47:34 +00004009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4010 " jng_alpha_interlace_method: %3d",
4011 jng_alpha_interlace_method);
4012 }
4013 }
glennrp47b9dd52010-11-24 18:12:06 +00004014
cristy3ed852e2009-09-05 21:47:34 +00004015 if (length)
4016 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004017
cristy3ed852e2009-09-05 21:47:34 +00004018 continue;
4019 }
4020
4021
4022 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4023 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4024 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4025 {
4026 /*
4027 o create color_image
4028 o open color_blob, attached to color_image
4029 o if (color type has alpha)
4030 open alpha_blob, attached to alpha_image
4031 */
4032
cristy73bd4a52010-10-05 11:24:23 +00004033 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004034
cristy3ed852e2009-09-05 21:47:34 +00004035 if (color_image_info == (ImageInfo *) NULL)
4036 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004037
cristy3ed852e2009-09-05 21:47:34 +00004038 GetImageInfo(color_image_info);
cristy9950d572011-10-01 18:22:35 +00004039 color_image=AcquireImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004040
cristy3ed852e2009-09-05 21:47:34 +00004041 if (color_image == (Image *) NULL)
4042 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4043
4044 if (logging != MagickFalse)
4045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4046 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004047
cristy3ed852e2009-09-05 21:47:34 +00004048 (void) AcquireUniqueFilename(color_image->filename);
4049 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4050 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004051
cristy3ed852e2009-09-05 21:47:34 +00004052 if (status == MagickFalse)
4053 return((Image *) NULL);
4054
4055 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4056 {
4057 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004058 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004059
cristy3ed852e2009-09-05 21:47:34 +00004060 if (alpha_image_info == (ImageInfo *) NULL)
4061 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004062
cristy3ed852e2009-09-05 21:47:34 +00004063 GetImageInfo(alpha_image_info);
cristy9950d572011-10-01 18:22:35 +00004064 alpha_image=AcquireImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004065
cristy3ed852e2009-09-05 21:47:34 +00004066 if (alpha_image == (Image *) NULL)
4067 {
4068 alpha_image=DestroyImage(alpha_image);
4069 ThrowReaderException(ResourceLimitError,
4070 "MemoryAllocationFailed");
4071 }
glennrp0fe50b42010-11-16 03:52:51 +00004072
cristy3ed852e2009-09-05 21:47:34 +00004073 if (logging != MagickFalse)
4074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4075 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004076
cristy3ed852e2009-09-05 21:47:34 +00004077 (void) AcquireUniqueFilename(alpha_image->filename);
4078 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4079 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004080
cristy3ed852e2009-09-05 21:47:34 +00004081 if (status == MagickFalse)
4082 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004083
cristy3ed852e2009-09-05 21:47:34 +00004084 if (jng_alpha_compression_method == 0)
4085 {
4086 unsigned char
4087 data[18];
4088
4089 if (logging != MagickFalse)
4090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4091 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004092
cristy3ed852e2009-09-05 21:47:34 +00004093 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4094 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004095
cristy3ed852e2009-09-05 21:47:34 +00004096 (void) WriteBlobMSBULong(alpha_image,13L);
4097 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004098 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004099 PNGLong(data+4,jng_width);
4100 PNGLong(data+8,jng_height);
4101 data[12]=jng_alpha_sample_depth;
4102 data[13]=0; /* color_type gray */
4103 data[14]=0; /* compression method 0 */
4104 data[15]=0; /* filter_method 0 */
4105 data[16]=0; /* interlace_method 0 */
4106 (void) WriteBlob(alpha_image,17,data);
4107 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4108 }
4109 }
4110 reading_idat=MagickTrue;
4111 }
4112
4113 if (memcmp(type,mng_JDAT,4) == 0)
4114 {
glennrp47b9dd52010-11-24 18:12:06 +00004115 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004116
4117 if (logging != MagickFalse)
4118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4119 " Copying JDAT chunk data to color_blob.");
4120
4121 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004122
cristy3ed852e2009-09-05 21:47:34 +00004123 if (length)
4124 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004125
cristy3ed852e2009-09-05 21:47:34 +00004126 continue;
4127 }
4128
4129 if (memcmp(type,mng_IDAT,4) == 0)
4130 {
4131 png_byte
4132 data[5];
4133
glennrp47b9dd52010-11-24 18:12:06 +00004134 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004135
4136 if (image_info->ping == MagickFalse)
4137 {
4138 if (logging != MagickFalse)
4139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4140 " Copying IDAT chunk data to alpha_blob.");
4141
cristybb503372010-05-27 20:51:26 +00004142 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004143 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004144 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004145 (void) WriteBlob(alpha_image,4,data);
4146 (void) WriteBlob(alpha_image,length,chunk);
4147 (void) WriteBlobMSBULong(alpha_image,
4148 crc32(crc32(0,data,4),chunk,(uInt) length));
4149 }
glennrp0fe50b42010-11-16 03:52:51 +00004150
cristy3ed852e2009-09-05 21:47:34 +00004151 if (length)
4152 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004153
cristy3ed852e2009-09-05 21:47:34 +00004154 continue;
4155 }
4156
4157 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4158 {
glennrp47b9dd52010-11-24 18:12:06 +00004159 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004160
4161 if (image_info->ping == MagickFalse)
4162 {
4163 if (logging != MagickFalse)
4164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4165 " Copying JDAA chunk data to alpha_blob.");
4166
4167 (void) WriteBlob(alpha_image,length,chunk);
4168 }
glennrp0fe50b42010-11-16 03:52:51 +00004169
cristy3ed852e2009-09-05 21:47:34 +00004170 if (length)
4171 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004172
cristy3ed852e2009-09-05 21:47:34 +00004173 continue;
4174 }
4175
4176 if (memcmp(type,mng_JSEP,4) == 0)
4177 {
4178 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004179
cristy3ed852e2009-09-05 21:47:34 +00004180 if (length)
4181 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004182
cristy3ed852e2009-09-05 21:47:34 +00004183 continue;
4184 }
4185
4186 if (memcmp(type,mng_bKGD,4) == 0)
4187 {
4188 if (length == 2)
4189 {
4190 image->background_color.red=ScaleCharToQuantum(p[1]);
4191 image->background_color.green=image->background_color.red;
4192 image->background_color.blue=image->background_color.red;
4193 }
glennrp0fe50b42010-11-16 03:52:51 +00004194
cristy3ed852e2009-09-05 21:47:34 +00004195 if (length == 6)
4196 {
4197 image->background_color.red=ScaleCharToQuantum(p[1]);
4198 image->background_color.green=ScaleCharToQuantum(p[3]);
4199 image->background_color.blue=ScaleCharToQuantum(p[5]);
4200 }
glennrp0fe50b42010-11-16 03:52:51 +00004201
cristy3ed852e2009-09-05 21:47:34 +00004202 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4203 continue;
4204 }
4205
4206 if (memcmp(type,mng_gAMA,4) == 0)
4207 {
4208 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004209 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004210
cristy3ed852e2009-09-05 21:47:34 +00004211 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4212 continue;
4213 }
4214
4215 if (memcmp(type,mng_cHRM,4) == 0)
4216 {
4217 if (length == 32)
4218 {
cristy8182b072010-05-30 20:10:53 +00004219 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4220 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4221 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4222 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4223 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4224 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4225 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4226 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004227 }
glennrp47b9dd52010-11-24 18:12:06 +00004228
cristy3ed852e2009-09-05 21:47:34 +00004229 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4230 continue;
4231 }
4232
4233 if (memcmp(type,mng_sRGB,4) == 0)
4234 {
4235 if (length == 1)
4236 {
glennrpe610a072010-08-05 17:08:46 +00004237 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004238 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004239 image->gamma=0.45455f;
4240 image->chromaticity.red_primary.x=0.6400f;
4241 image->chromaticity.red_primary.y=0.3300f;
4242 image->chromaticity.green_primary.x=0.3000f;
4243 image->chromaticity.green_primary.y=0.6000f;
4244 image->chromaticity.blue_primary.x=0.1500f;
4245 image->chromaticity.blue_primary.y=0.0600f;
4246 image->chromaticity.white_point.x=0.3127f;
4247 image->chromaticity.white_point.y=0.3290f;
4248 }
glennrp47b9dd52010-11-24 18:12:06 +00004249
cristy3ed852e2009-09-05 21:47:34 +00004250 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4251 continue;
4252 }
4253
4254 if (memcmp(type,mng_oFFs,4) == 0)
4255 {
4256 if (length > 8)
4257 {
glennrp5eae7602011-02-22 15:21:32 +00004258 image->page.x=(ssize_t) mng_get_long(p);
4259 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004260
cristy3ed852e2009-09-05 21:47:34 +00004261 if ((int) p[8] != 0)
4262 {
4263 image->page.x/=10000;
4264 image->page.y/=10000;
4265 }
4266 }
glennrp47b9dd52010-11-24 18:12:06 +00004267
cristy3ed852e2009-09-05 21:47:34 +00004268 if (length)
4269 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004270
cristy3ed852e2009-09-05 21:47:34 +00004271 continue;
4272 }
4273
4274 if (memcmp(type,mng_pHYs,4) == 0)
4275 {
4276 if (length > 8)
4277 {
cristy2a11bef2011-10-28 18:33:11 +00004278 image->resolution.x=(double) mng_get_long(p);
4279 image->resolution.y=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004280 if ((int) p[8] == PNG_RESOLUTION_METER)
4281 {
4282 image->units=PixelsPerCentimeterResolution;
cristy2a11bef2011-10-28 18:33:11 +00004283 image->resolution.x=image->resolution.x/100.0f;
4284 image->resolution.y=image->resolution.y/100.0f;
cristy3ed852e2009-09-05 21:47:34 +00004285 }
4286 }
glennrp0fe50b42010-11-16 03:52:51 +00004287
cristy3ed852e2009-09-05 21:47:34 +00004288 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4289 continue;
4290 }
4291
4292#if 0
4293 if (memcmp(type,mng_iCCP,4) == 0)
4294 {
glennrpfd05d622011-02-25 04:10:33 +00004295 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004296 if (length)
4297 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004298
cristy3ed852e2009-09-05 21:47:34 +00004299 continue;
4300 }
4301#endif
4302
4303 if (length)
4304 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4305
4306 if (memcmp(type,mng_IEND,4))
4307 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004308
cristy3ed852e2009-09-05 21:47:34 +00004309 break;
4310 }
4311
4312
4313 /* IEND found */
4314
4315 /*
4316 Finish up reading image data:
4317
4318 o read main image from color_blob.
4319
4320 o close color_blob.
4321
4322 o if (color_type has alpha)
4323 if alpha_encoding is PNG
4324 read secondary image from alpha_blob via ReadPNG
4325 if alpha_encoding is JPEG
4326 read secondary image from alpha_blob via ReadJPEG
4327
4328 o close alpha_blob.
4329
4330 o copy intensity of secondary image into
cristy4c08aed2011-07-01 19:47:50 +00004331 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004332
4333 o destroy the secondary image.
4334 */
4335
4336 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004337
cristy3ed852e2009-09-05 21:47:34 +00004338 if (logging != MagickFalse)
4339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4340 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004341
cristy3b6fd2e2011-05-20 12:53:50 +00004342 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004343 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004344
cristy3ed852e2009-09-05 21:47:34 +00004345 color_image_info->ping=MagickFalse; /* To do: avoid this */
4346 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004347
cristy3ed852e2009-09-05 21:47:34 +00004348 if (jng_image == (Image *) NULL)
4349 return((Image *) NULL);
4350
4351 (void) RelinquishUniqueFileResource(color_image->filename);
4352 color_image=DestroyImage(color_image);
4353 color_image_info=DestroyImageInfo(color_image_info);
4354
4355 if (jng_image == (Image *) NULL)
4356 return((Image *) NULL);
4357
4358 if (logging != MagickFalse)
4359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4360 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004361
cristy3ed852e2009-09-05 21:47:34 +00004362 image->rows=jng_height;
4363 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004364
cristybb503372010-05-27 20:51:26 +00004365 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004366 {
cristyc82a27b2011-10-21 01:07:16 +00004367 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00004368 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004369 for (x=(ssize_t) image->columns; x != 0; x--)
4370 {
4371 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4372 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4373 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004374 q+=GetPixelChannels(image);
4375 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004376 }
glennrp47b9dd52010-11-24 18:12:06 +00004377
cristy3ed852e2009-09-05 21:47:34 +00004378 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4379 break;
4380 }
glennrp0fe50b42010-11-16 03:52:51 +00004381
cristy3ed852e2009-09-05 21:47:34 +00004382 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004383
cristy3ed852e2009-09-05 21:47:34 +00004384 if (image_info->ping == MagickFalse)
4385 {
4386 if (jng_color_type >= 12)
4387 {
4388 if (jng_alpha_compression_method == 0)
4389 {
4390 png_byte
4391 data[5];
4392 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4393 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004394 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004395 (void) WriteBlob(alpha_image,4,data);
4396 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4397 }
glennrp0fe50b42010-11-16 03:52:51 +00004398
cristy3ed852e2009-09-05 21:47:34 +00004399 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004400
cristy3ed852e2009-09-05 21:47:34 +00004401 if (logging != MagickFalse)
4402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00004403 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004404
cristy3b6fd2e2011-05-20 12:53:50 +00004405 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004406 "%s",alpha_image->filename);
4407
4408 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004409
cristy3ed852e2009-09-05 21:47:34 +00004410 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004411 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004412 {
4413 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristyc82a27b2011-10-21 01:07:16 +00004414 exception);
cristy3ed852e2009-09-05 21:47:34 +00004415 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004416
cristy3ed852e2009-09-05 21:47:34 +00004417 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00004418 for (x=(ssize_t) image->columns; x != 0; x--)
4419 {
4420 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004421 q+=GetPixelChannels(image);
4422 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004423 }
glennrp0fe50b42010-11-16 03:52:51 +00004424
cristy3ed852e2009-09-05 21:47:34 +00004425 else
cristy4c08aed2011-07-01 19:47:50 +00004426 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004427 {
cristy4c08aed2011-07-01 19:47:50 +00004428 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4429 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004430 image->matte=MagickTrue;
cristyed231572011-07-14 02:18:59 +00004431 q+=GetPixelChannels(image);
4432 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004433 }
glennrp0fe50b42010-11-16 03:52:51 +00004434
cristy3ed852e2009-09-05 21:47:34 +00004435 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4436 break;
4437 }
4438 (void) RelinquishUniqueFileResource(alpha_image->filename);
4439 alpha_image=DestroyImage(alpha_image);
4440 alpha_image_info=DestroyImageInfo(alpha_image_info);
4441 if (jng_image != (Image *) NULL)
4442 jng_image=DestroyImage(jng_image);
4443 }
4444 }
4445
glennrp47b9dd52010-11-24 18:12:06 +00004446 /* Read the JNG image. */
4447
cristy3ed852e2009-09-05 21:47:34 +00004448 if (mng_info->mng_type == 0)
4449 {
4450 mng_info->mng_width=jng_width;
4451 mng_info->mng_height=jng_height;
4452 }
glennrp0fe50b42010-11-16 03:52:51 +00004453
cristy3ed852e2009-09-05 21:47:34 +00004454 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004455 {
4456 image->page.width=jng_width;
4457 image->page.height=jng_height;
4458 }
4459
cristy3ed852e2009-09-05 21:47:34 +00004460 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004461 {
4462 image->page.x=mng_info->x_off[mng_info->object_id];
4463 image->page.y=mng_info->y_off[mng_info->object_id];
4464 }
4465
cristy3ed852e2009-09-05 21:47:34 +00004466 else
glennrp0fe50b42010-11-16 03:52:51 +00004467 {
4468 image->page.y=mng_info->y_off[mng_info->object_id];
4469 }
4470
cristy3ed852e2009-09-05 21:47:34 +00004471 mng_info->image_found++;
4472 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4473 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004474
cristy3ed852e2009-09-05 21:47:34 +00004475 if (logging != MagickFalse)
4476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4477 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004478
cristy3ed852e2009-09-05 21:47:34 +00004479 return(image);
4480}
4481
4482/*
4483%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4484% %
4485% %
4486% %
4487% R e a d J N G I m a g e %
4488% %
4489% %
4490% %
4491%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4492%
4493% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4494% (including the 8-byte signature) and returns it. It allocates the memory
4495% necessary for the new Image structure and returns a pointer to the new
4496% image.
4497%
4498% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4499%
4500% The format of the ReadJNGImage method is:
4501%
4502% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4503% *exception)
4504%
4505% A description of each parameter follows:
4506%
4507% o image_info: the image info.
4508%
4509% o exception: return any errors or warnings in this structure.
4510%
4511*/
4512
4513static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4514{
4515 Image
4516 *image,
4517 *previous;
4518
4519 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004520 have_mng_structure,
4521 logging,
cristy3ed852e2009-09-05 21:47:34 +00004522 status;
4523
4524 MngInfo
4525 *mng_info;
4526
4527 char
4528 magic_number[MaxTextExtent];
4529
cristy3ed852e2009-09-05 21:47:34 +00004530 size_t
4531 count;
4532
4533 /*
4534 Open image file.
4535 */
4536 assert(image_info != (const ImageInfo *) NULL);
4537 assert(image_info->signature == MagickSignature);
4538 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4539 assert(exception != (ExceptionInfo *) NULL);
4540 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004541 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy9950d572011-10-01 18:22:35 +00004542 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004543 mng_info=(MngInfo *) NULL;
4544 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004545
cristy3ed852e2009-09-05 21:47:34 +00004546 if (status == MagickFalse)
4547 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004548
cristy3ed852e2009-09-05 21:47:34 +00004549 if (LocaleCompare(image_info->magick,"JNG") != 0)
4550 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004551
glennrp47b9dd52010-11-24 18:12:06 +00004552 /* Verify JNG signature. */
4553
cristy3ed852e2009-09-05 21:47:34 +00004554 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004555
glennrp3b8763e2011-02-21 12:08:18 +00004556 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004557 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004558
glennrp47b9dd52010-11-24 18:12:06 +00004559 /* Allocate a MngInfo structure. */
4560
cristy3ed852e2009-09-05 21:47:34 +00004561 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004562 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004563
cristy3ed852e2009-09-05 21:47:34 +00004564 if (mng_info == (MngInfo *) NULL)
4565 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004566
glennrp47b9dd52010-11-24 18:12:06 +00004567 /* Initialize members of the MngInfo structure. */
4568
cristy3ed852e2009-09-05 21:47:34 +00004569 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4570 have_mng_structure=MagickTrue;
4571
4572 mng_info->image=image;
4573 previous=image;
4574 image=ReadOneJNGImage(mng_info,image_info,exception);
4575 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004576
cristy3ed852e2009-09-05 21:47:34 +00004577 if (image == (Image *) NULL)
4578 {
4579 if (IsImageObject(previous) != MagickFalse)
4580 {
4581 (void) CloseBlob(previous);
4582 (void) DestroyImageList(previous);
4583 }
glennrp0fe50b42010-11-16 03:52:51 +00004584
cristy3ed852e2009-09-05 21:47:34 +00004585 if (logging != MagickFalse)
4586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4587 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004588
cristy3ed852e2009-09-05 21:47:34 +00004589 return((Image *) NULL);
4590 }
4591 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004592
cristy3ed852e2009-09-05 21:47:34 +00004593 if (image->columns == 0 || image->rows == 0)
4594 {
4595 if (logging != MagickFalse)
4596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4597 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004598
cristy3ed852e2009-09-05 21:47:34 +00004599 ThrowReaderException(CorruptImageError,"CorruptImage");
4600 }
glennrp0fe50b42010-11-16 03:52:51 +00004601
cristy3ed852e2009-09-05 21:47:34 +00004602 if (logging != MagickFalse)
4603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004604
cristy3ed852e2009-09-05 21:47:34 +00004605 return(image);
4606}
4607#endif
4608
4609static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4610{
4611 char
4612 page_geometry[MaxTextExtent];
4613
4614 Image
4615 *image,
4616 *previous;
4617
cristy4383ec82011-01-05 15:42:32 +00004618 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004619 logging,
4620 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004621
cristy3ed852e2009-09-05 21:47:34 +00004622 volatile int
4623 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004624 object_id,
4625 term_chunk_found,
4626 skip_to_iend;
4627
cristybb503372010-05-27 20:51:26 +00004628 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004629 image_count=0;
4630
4631 MagickBooleanType
4632 status;
4633
4634 MagickOffsetType
4635 offset;
4636
4637 MngInfo
4638 *mng_info;
4639
4640 MngBox
4641 default_fb,
4642 fb,
4643 previous_fb;
4644
4645#if defined(MNG_INSERT_LAYERS)
cristy101ab702011-10-13 13:06:32 +00004646 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004647 mng_background_color;
4648#endif
4649
4650 register unsigned char
4651 *p;
4652
cristybb503372010-05-27 20:51:26 +00004653 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004654 i;
4655
4656 size_t
4657 count;
4658
cristybb503372010-05-27 20:51:26 +00004659 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004660 loop_level;
4661
4662 volatile short
4663 skipping_loop;
4664
4665#if defined(MNG_INSERT_LAYERS)
4666 unsigned int
4667 mandatory_back=0;
4668#endif
4669
4670 volatile unsigned int
4671#ifdef MNG_OBJECT_BUFFERS
4672 mng_background_object=0,
4673#endif
4674 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4675
cristybb503372010-05-27 20:51:26 +00004676 size_t
cristy3ed852e2009-09-05 21:47:34 +00004677 default_frame_timeout,
4678 frame_timeout,
4679#if defined(MNG_INSERT_LAYERS)
4680 image_height,
4681 image_width,
4682#endif
4683 length;
4684
glennrp38ea0832010-06-02 18:50:28 +00004685 /* These delays are all measured in image ticks_per_second,
4686 * not in MNG ticks_per_second
4687 */
cristybb503372010-05-27 20:51:26 +00004688 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004689 default_frame_delay,
4690 final_delay,
4691 final_image_delay,
4692 frame_delay,
4693#if defined(MNG_INSERT_LAYERS)
4694 insert_layers,
4695#endif
4696 mng_iterations=1,
4697 simplicity=0,
4698 subframe_height=0,
4699 subframe_width=0;
4700
4701 previous_fb.top=0;
4702 previous_fb.bottom=0;
4703 previous_fb.left=0;
4704 previous_fb.right=0;
4705 default_fb.top=0;
4706 default_fb.bottom=0;
4707 default_fb.left=0;
4708 default_fb.right=0;
4709
glennrp47b9dd52010-11-24 18:12:06 +00004710 /* Open image file. */
4711
cristy3ed852e2009-09-05 21:47:34 +00004712 assert(image_info != (const ImageInfo *) NULL);
4713 assert(image_info->signature == MagickSignature);
4714 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4715 assert(exception != (ExceptionInfo *) NULL);
4716 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004717 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy9950d572011-10-01 18:22:35 +00004718 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004719 mng_info=(MngInfo *) NULL;
4720 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004721
cristy3ed852e2009-09-05 21:47:34 +00004722 if (status == MagickFalse)
4723 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004724
cristy3ed852e2009-09-05 21:47:34 +00004725 first_mng_object=MagickFalse;
4726 skipping_loop=(-1);
4727 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004728
4729 /* Allocate a MngInfo structure. */
4730
cristy73bd4a52010-10-05 11:24:23 +00004731 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004732
cristy3ed852e2009-09-05 21:47:34 +00004733 if (mng_info == (MngInfo *) NULL)
4734 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004735
glennrp47b9dd52010-11-24 18:12:06 +00004736 /* Initialize members of the MngInfo structure. */
4737
cristy3ed852e2009-09-05 21:47:34 +00004738 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4739 mng_info->image=image;
4740 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004741
4742 if (LocaleCompare(image_info->magick,"MNG") == 0)
4743 {
4744 char
4745 magic_number[MaxTextExtent];
4746
glennrp47b9dd52010-11-24 18:12:06 +00004747 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004748 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4749 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4750 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004751
4752 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004753 for (i=0; i < MNG_MAX_OBJECTS; i++)
4754 {
cristybb503372010-05-27 20:51:26 +00004755 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4756 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004757 }
4758 mng_info->exists[0]=MagickTrue;
4759 }
glennrp47b9dd52010-11-24 18:12:06 +00004760
cristy3ed852e2009-09-05 21:47:34 +00004761 first_mng_object=MagickTrue;
4762 mng_type=0;
4763#if defined(MNG_INSERT_LAYERS)
4764 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4765#endif
4766 default_frame_delay=0;
4767 default_frame_timeout=0;
4768 frame_delay=0;
4769 final_delay=1;
4770 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4771 object_id=0;
4772 skip_to_iend=MagickFalse;
4773 term_chunk_found=MagickFalse;
4774 mng_info->framing_mode=1;
4775#if defined(MNG_INSERT_LAYERS)
4776 mandatory_back=MagickFalse;
4777#endif
4778#if defined(MNG_INSERT_LAYERS)
4779 mng_background_color=image->background_color;
4780#endif
4781 default_fb=mng_info->frame;
4782 previous_fb=mng_info->frame;
4783 do
4784 {
4785 char
4786 type[MaxTextExtent];
4787
4788 if (LocaleCompare(image_info->magick,"MNG") == 0)
4789 {
4790 unsigned char
4791 *chunk;
4792
4793 /*
4794 Read a new chunk.
4795 */
4796 type[0]='\0';
4797 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4798 length=ReadBlobMSBLong(image);
4799 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4800
4801 if (logging != MagickFalse)
4802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004803 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4804 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004805
4806 if (length > PNG_UINT_31_MAX)
4807 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004808
cristy3ed852e2009-09-05 21:47:34 +00004809 if (count == 0)
4810 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004811
cristy3ed852e2009-09-05 21:47:34 +00004812 p=NULL;
4813 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004814
cristy3ed852e2009-09-05 21:47:34 +00004815 if (length)
4816 {
4817 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004818
cristy3ed852e2009-09-05 21:47:34 +00004819 if (chunk == (unsigned char *) NULL)
4820 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004821
cristybb503372010-05-27 20:51:26 +00004822 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004823 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004824
cristy3ed852e2009-09-05 21:47:34 +00004825 p=chunk;
4826 }
glennrp0fe50b42010-11-16 03:52:51 +00004827
cristy3ed852e2009-09-05 21:47:34 +00004828 (void) ReadBlobMSBLong(image); /* read crc word */
4829
4830#if !defined(JNG_SUPPORTED)
4831 if (memcmp(type,mng_JHDR,4) == 0)
4832 {
4833 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004834
cristy3ed852e2009-09-05 21:47:34 +00004835 if (mng_info->jhdr_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00004836 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004837 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004838
cristy3ed852e2009-09-05 21:47:34 +00004839 mng_info->jhdr_warning++;
4840 }
4841#endif
4842 if (memcmp(type,mng_DHDR,4) == 0)
4843 {
4844 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004845
cristy3ed852e2009-09-05 21:47:34 +00004846 if (mng_info->dhdr_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00004847 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004848 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004849
cristy3ed852e2009-09-05 21:47:34 +00004850 mng_info->dhdr_warning++;
4851 }
4852 if (memcmp(type,mng_MEND,4) == 0)
4853 break;
glennrp47b9dd52010-11-24 18:12:06 +00004854
cristy3ed852e2009-09-05 21:47:34 +00004855 if (skip_to_iend)
4856 {
4857 if (memcmp(type,mng_IEND,4) == 0)
4858 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004859
cristy3ed852e2009-09-05 21:47:34 +00004860 if (length)
4861 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004862
cristy3ed852e2009-09-05 21:47:34 +00004863 if (logging != MagickFalse)
4864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4865 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004866
cristy3ed852e2009-09-05 21:47:34 +00004867 continue;
4868 }
glennrp0fe50b42010-11-16 03:52:51 +00004869
cristy3ed852e2009-09-05 21:47:34 +00004870 if (memcmp(type,mng_MHDR,4) == 0)
4871 {
cristybb503372010-05-27 20:51:26 +00004872 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004873 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004874
cristybb503372010-05-27 20:51:26 +00004875 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004876 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004877
cristy3ed852e2009-09-05 21:47:34 +00004878 if (logging != MagickFalse)
4879 {
4880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004881 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004883 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004884 }
glennrp0fe50b42010-11-16 03:52:51 +00004885
cristy3ed852e2009-09-05 21:47:34 +00004886 p+=8;
cristy8182b072010-05-30 20:10:53 +00004887 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004888
cristy3ed852e2009-09-05 21:47:34 +00004889 if (mng_info->ticks_per_second == 0)
4890 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004891
cristy3ed852e2009-09-05 21:47:34 +00004892 else
4893 default_frame_delay=1UL*image->ticks_per_second/
4894 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004895
cristy3ed852e2009-09-05 21:47:34 +00004896 frame_delay=default_frame_delay;
4897 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004898
cristy3ed852e2009-09-05 21:47:34 +00004899 if (length > 16)
4900 {
4901 p+=16;
cristy8182b072010-05-30 20:10:53 +00004902 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004903 }
glennrp0fe50b42010-11-16 03:52:51 +00004904
cristy3ed852e2009-09-05 21:47:34 +00004905 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004906
cristy3ed852e2009-09-05 21:47:34 +00004907 if ((simplicity != 0) && ((simplicity | 11) == 11))
4908 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004909
cristy3ed852e2009-09-05 21:47:34 +00004910 if ((simplicity != 0) && ((simplicity | 9) == 9))
4911 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004912
cristy3ed852e2009-09-05 21:47:34 +00004913#if defined(MNG_INSERT_LAYERS)
4914 if (mng_type != 3)
4915 insert_layers=MagickTrue;
4916#endif
cristy4c08aed2011-07-01 19:47:50 +00004917 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004918 {
glennrp47b9dd52010-11-24 18:12:06 +00004919 /* Allocate next image structure. */
cristy9950d572011-10-01 18:22:35 +00004920 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004921
cristy3ed852e2009-09-05 21:47:34 +00004922 if (GetNextImageInList(image) == (Image *) NULL)
4923 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004924
cristy3ed852e2009-09-05 21:47:34 +00004925 image=SyncNextImageInList(image);
4926 mng_info->image=image;
4927 }
4928
4929 if ((mng_info->mng_width > 65535L) ||
4930 (mng_info->mng_height > 65535L))
4931 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004932
cristy3b6fd2e2011-05-20 12:53:50 +00004933 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004934 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004935 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004936
cristy3ed852e2009-09-05 21:47:34 +00004937 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004938 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004939 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004940 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004941 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004942
cristy3ed852e2009-09-05 21:47:34 +00004943 for (i=0; i < MNG_MAX_OBJECTS; i++)
4944 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004945
cristy3ed852e2009-09-05 21:47:34 +00004946 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4947 continue;
4948 }
4949
4950 if (memcmp(type,mng_TERM,4) == 0)
4951 {
4952 int
4953 repeat=0;
4954
4955
4956 if (length)
4957 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004958
cristy3ed852e2009-09-05 21:47:34 +00004959 if (repeat == 3)
4960 {
cristy8182b072010-05-30 20:10:53 +00004961 final_delay=(png_uint_32) mng_get_long(&p[2]);
4962 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004963
cristy3ed852e2009-09-05 21:47:34 +00004964 if (mng_iterations == PNG_UINT_31_MAX)
4965 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004966
cristy3ed852e2009-09-05 21:47:34 +00004967 image->iterations=mng_iterations;
4968 term_chunk_found=MagickTrue;
4969 }
glennrp0fe50b42010-11-16 03:52:51 +00004970
cristy3ed852e2009-09-05 21:47:34 +00004971 if (logging != MagickFalse)
4972 {
4973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4974 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004975
cristy3ed852e2009-09-05 21:47:34 +00004976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004977 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004978
cristy3ed852e2009-09-05 21:47:34 +00004979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004980 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004981 }
glennrp0fe50b42010-11-16 03:52:51 +00004982
cristy3ed852e2009-09-05 21:47:34 +00004983 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4984 continue;
4985 }
4986 if (memcmp(type,mng_DEFI,4) == 0)
4987 {
4988 if (mng_type == 3)
cristyc82a27b2011-10-21 01:07:16 +00004989 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004990 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4991 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004992
cristy3ed852e2009-09-05 21:47:34 +00004993 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004994
cristy3ed852e2009-09-05 21:47:34 +00004995 if (mng_type == 2 && object_id != 0)
cristyc82a27b2011-10-21 01:07:16 +00004996 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004997 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4998 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004999
cristy3ed852e2009-09-05 21:47:34 +00005000 if (object_id > MNG_MAX_OBJECTS)
5001 {
5002 /*
5003 Instead ofsuing a warning we should allocate a larger
5004 MngInfo structure and continue.
5005 */
cristyc82a27b2011-10-21 01:07:16 +00005006 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005007 CoderError,"object id too large","`%s'",image->filename);
5008 object_id=MNG_MAX_OBJECTS;
5009 }
glennrp0fe50b42010-11-16 03:52:51 +00005010
cristy3ed852e2009-09-05 21:47:34 +00005011 if (mng_info->exists[object_id])
5012 if (mng_info->frozen[object_id])
5013 {
5014 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
cristyc82a27b2011-10-21 01:07:16 +00005015 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005016 GetMagickModule(),CoderError,
5017 "DEFI cannot redefine a frozen MNG object","`%s'",
5018 image->filename);
5019 continue;
5020 }
glennrp0fe50b42010-11-16 03:52:51 +00005021
cristy3ed852e2009-09-05 21:47:34 +00005022 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005023
cristy3ed852e2009-09-05 21:47:34 +00005024 if (length > 2)
5025 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00005026
cristy3ed852e2009-09-05 21:47:34 +00005027 /*
5028 Extract object offset info.
5029 */
5030 if (length > 11)
5031 {
glennrp0fe50b42010-11-16 03:52:51 +00005032 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5033 (p[5] << 16) | (p[6] << 8) | p[7]);
5034
5035 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5036 (p[9] << 16) | (p[10] << 8) | p[11]);
5037
cristy3ed852e2009-09-05 21:47:34 +00005038 if (logging != MagickFalse)
5039 {
5040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005041 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005042 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005043
cristy3ed852e2009-09-05 21:47:34 +00005044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005045 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005046 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005047 }
5048 }
glennrp0fe50b42010-11-16 03:52:51 +00005049
cristy3ed852e2009-09-05 21:47:34 +00005050 /*
5051 Extract object clipping info.
5052 */
5053 if (length > 27)
5054 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5055 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005056
cristy3ed852e2009-09-05 21:47:34 +00005057 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5058 continue;
5059 }
5060 if (memcmp(type,mng_bKGD,4) == 0)
5061 {
5062 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005063
cristy3ed852e2009-09-05 21:47:34 +00005064 if (length > 5)
5065 {
5066 mng_info->mng_global_bkgd.red=
5067 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005068
cristy3ed852e2009-09-05 21:47:34 +00005069 mng_info->mng_global_bkgd.green=
5070 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005071
cristy3ed852e2009-09-05 21:47:34 +00005072 mng_info->mng_global_bkgd.blue=
5073 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005074
cristy3ed852e2009-09-05 21:47:34 +00005075 mng_info->have_global_bkgd=MagickTrue;
5076 }
glennrp0fe50b42010-11-16 03:52:51 +00005077
cristy3ed852e2009-09-05 21:47:34 +00005078 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5079 continue;
5080 }
5081 if (memcmp(type,mng_BACK,4) == 0)
5082 {
5083#if defined(MNG_INSERT_LAYERS)
5084 if (length > 6)
5085 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005086
cristy3ed852e2009-09-05 21:47:34 +00005087 else
5088 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005089
cristy3ed852e2009-09-05 21:47:34 +00005090 if (mandatory_back && length > 5)
5091 {
5092 mng_background_color.red=
5093 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005094
cristy3ed852e2009-09-05 21:47:34 +00005095 mng_background_color.green=
5096 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005097
cristy3ed852e2009-09-05 21:47:34 +00005098 mng_background_color.blue=
5099 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005100
cristy4c08aed2011-07-01 19:47:50 +00005101 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005102 }
glennrp0fe50b42010-11-16 03:52:51 +00005103
cristy3ed852e2009-09-05 21:47:34 +00005104#ifdef MNG_OBJECT_BUFFERS
5105 if (length > 8)
5106 mng_background_object=(p[7] << 8) | p[8];
5107#endif
5108#endif
5109 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5110 continue;
5111 }
glennrp47b9dd52010-11-24 18:12:06 +00005112
cristy3ed852e2009-09-05 21:47:34 +00005113 if (memcmp(type,mng_PLTE,4) == 0)
5114 {
glennrp47b9dd52010-11-24 18:12:06 +00005115 /* Read global PLTE. */
5116
cristy3ed852e2009-09-05 21:47:34 +00005117 if (length && (length < 769))
5118 {
5119 if (mng_info->global_plte == (png_colorp) NULL)
5120 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5121 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005122
cristybb503372010-05-27 20:51:26 +00005123 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005124 {
5125 mng_info->global_plte[i].red=p[3*i];
5126 mng_info->global_plte[i].green=p[3*i+1];
5127 mng_info->global_plte[i].blue=p[3*i+2];
5128 }
glennrp0fe50b42010-11-16 03:52:51 +00005129
cristy35ef8242010-06-03 16:24:13 +00005130 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005131 }
5132#ifdef MNG_LOOSE
5133 for ( ; i < 256; i++)
5134 {
5135 mng_info->global_plte[i].red=i;
5136 mng_info->global_plte[i].green=i;
5137 mng_info->global_plte[i].blue=i;
5138 }
glennrp0fe50b42010-11-16 03:52:51 +00005139
cristy3ed852e2009-09-05 21:47:34 +00005140 if (length)
5141 mng_info->global_plte_length=256;
5142#endif
5143 else
5144 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005145
cristy3ed852e2009-09-05 21:47:34 +00005146 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5147 continue;
5148 }
glennrp47b9dd52010-11-24 18:12:06 +00005149
cristy3ed852e2009-09-05 21:47:34 +00005150 if (memcmp(type,mng_tRNS,4) == 0)
5151 {
5152 /* read global tRNS */
5153
5154 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005155 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005156 mng_info->global_trns[i]=p[i];
5157
5158#ifdef MNG_LOOSE
5159 for ( ; i < 256; i++)
5160 mng_info->global_trns[i]=255;
5161#endif
cristy12560f32010-06-03 16:51:08 +00005162 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005163 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5164 continue;
5165 }
5166 if (memcmp(type,mng_gAMA,4) == 0)
5167 {
5168 if (length == 4)
5169 {
cristybb503372010-05-27 20:51:26 +00005170 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005171 igamma;
5172
cristy8182b072010-05-30 20:10:53 +00005173 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005174 mng_info->global_gamma=((float) igamma)*0.00001;
5175 mng_info->have_global_gama=MagickTrue;
5176 }
glennrp0fe50b42010-11-16 03:52:51 +00005177
cristy3ed852e2009-09-05 21:47:34 +00005178 else
5179 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005180
cristy3ed852e2009-09-05 21:47:34 +00005181 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5182 continue;
5183 }
5184
5185 if (memcmp(type,mng_cHRM,4) == 0)
5186 {
glennrp47b9dd52010-11-24 18:12:06 +00005187 /* Read global cHRM */
5188
cristy3ed852e2009-09-05 21:47:34 +00005189 if (length == 32)
5190 {
cristy8182b072010-05-30 20:10:53 +00005191 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5192 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5193 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005194 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005195 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005196 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005197 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005198 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005199 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005200 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005201 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005202 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005203 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005204 mng_info->have_global_chrm=MagickTrue;
5205 }
5206 else
5207 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005208
cristy3ed852e2009-09-05 21:47:34 +00005209 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5210 continue;
5211 }
glennrp47b9dd52010-11-24 18:12:06 +00005212
cristy3ed852e2009-09-05 21:47:34 +00005213 if (memcmp(type,mng_sRGB,4) == 0)
5214 {
5215 /*
5216 Read global sRGB.
5217 */
5218 if (length)
5219 {
glennrpe610a072010-08-05 17:08:46 +00005220 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005221 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005222 mng_info->have_global_srgb=MagickTrue;
5223 }
5224 else
5225 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005226
cristy3ed852e2009-09-05 21:47:34 +00005227 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5228 continue;
5229 }
glennrp47b9dd52010-11-24 18:12:06 +00005230
cristy3ed852e2009-09-05 21:47:34 +00005231 if (memcmp(type,mng_iCCP,4) == 0)
5232 {
glennrpfd05d622011-02-25 04:10:33 +00005233 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005234
5235 /*
5236 Read global iCCP.
5237 */
5238 if (length)
5239 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005240
cristy3ed852e2009-09-05 21:47:34 +00005241 continue;
5242 }
glennrp47b9dd52010-11-24 18:12:06 +00005243
cristy3ed852e2009-09-05 21:47:34 +00005244 if (memcmp(type,mng_FRAM,4) == 0)
5245 {
5246 if (mng_type == 3)
cristyc82a27b2011-10-21 01:07:16 +00005247 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005248 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5249 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005250
cristy3ed852e2009-09-05 21:47:34 +00005251 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5252 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005253
cristy3ed852e2009-09-05 21:47:34 +00005254 frame_delay=default_frame_delay;
5255 frame_timeout=default_frame_timeout;
5256 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005257
cristy3ed852e2009-09-05 21:47:34 +00005258 if (length)
5259 if (p[0])
5260 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005261
cristy3ed852e2009-09-05 21:47:34 +00005262 if (logging != MagickFalse)
5263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5264 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005265
cristy3ed852e2009-09-05 21:47:34 +00005266 if (length > 6)
5267 {
glennrp47b9dd52010-11-24 18:12:06 +00005268 /* Note the delay and frame clipping boundaries. */
5269
cristy3ed852e2009-09-05 21:47:34 +00005270 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005271
cristybb503372010-05-27 20:51:26 +00005272 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005273 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005274
cristy3ed852e2009-09-05 21:47:34 +00005275 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005276
cristybb503372010-05-27 20:51:26 +00005277 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005278 {
5279 int
5280 change_delay,
5281 change_timeout,
5282 change_clipping;
5283
5284 change_delay=(*p++);
5285 change_timeout=(*p++);
5286 change_clipping=(*p++);
5287 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005288
cristy3ed852e2009-09-05 21:47:34 +00005289 if (change_delay)
5290 {
cristy8182b072010-05-30 20:10:53 +00005291 frame_delay=1UL*image->ticks_per_second*
5292 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005293
cristy8182b072010-05-30 20:10:53 +00005294 if (mng_info->ticks_per_second != 0)
5295 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005296
glennrpbb010dd2010-06-01 13:07:15 +00005297 else
5298 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005299
cristy3ed852e2009-09-05 21:47:34 +00005300 if (change_delay == 2)
5301 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005302
cristy3ed852e2009-09-05 21:47:34 +00005303 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005304
cristy3ed852e2009-09-05 21:47:34 +00005305 if (logging != MagickFalse)
5306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005307 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005308 }
glennrp47b9dd52010-11-24 18:12:06 +00005309
cristy3ed852e2009-09-05 21:47:34 +00005310 if (change_timeout)
5311 {
glennrpbb010dd2010-06-01 13:07:15 +00005312 frame_timeout=1UL*image->ticks_per_second*
5313 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005314
glennrpbb010dd2010-06-01 13:07:15 +00005315 if (mng_info->ticks_per_second != 0)
5316 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005317
glennrpbb010dd2010-06-01 13:07:15 +00005318 else
5319 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005320
cristy3ed852e2009-09-05 21:47:34 +00005321 if (change_delay == 2)
5322 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005323
cristy3ed852e2009-09-05 21:47:34 +00005324 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005325
cristy3ed852e2009-09-05 21:47:34 +00005326 if (logging != MagickFalse)
5327 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005328 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005329 }
glennrp47b9dd52010-11-24 18:12:06 +00005330
cristy3ed852e2009-09-05 21:47:34 +00005331 if (change_clipping)
5332 {
5333 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5334 p+=17;
5335 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005336
cristy3ed852e2009-09-05 21:47:34 +00005337 if (logging != MagickFalse)
5338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005339 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005340 (double) fb.left,(double) fb.right,(double) fb.top,
5341 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005342
cristy3ed852e2009-09-05 21:47:34 +00005343 if (change_clipping == 2)
5344 default_fb=fb;
5345 }
5346 }
5347 }
5348 mng_info->clip=fb;
5349 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005350
cristybb503372010-05-27 20:51:26 +00005351 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005352 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005353
cristybb503372010-05-27 20:51:26 +00005354 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005355 -mng_info->clip.top);
5356 /*
5357 Insert a background layer behind the frame if framing_mode is 4.
5358 */
5359#if defined(MNG_INSERT_LAYERS)
5360 if (logging != MagickFalse)
5361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005362 " subframe_width=%.20g, subframe_height=%.20g",(double)
5363 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005364
cristy3ed852e2009-09-05 21:47:34 +00005365 if (insert_layers && (mng_info->framing_mode == 4) &&
5366 (subframe_width) && (subframe_height))
5367 {
glennrp47b9dd52010-11-24 18:12:06 +00005368 /* Allocate next image structure. */
cristy4c08aed2011-07-01 19:47:50 +00005369 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005370 {
cristy9950d572011-10-01 18:22:35 +00005371 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005372
cristy3ed852e2009-09-05 21:47:34 +00005373 if (GetNextImageInList(image) == (Image *) NULL)
5374 {
5375 image=DestroyImageList(image);
5376 MngInfoFreeStruct(mng_info,&have_mng_structure);
5377 return((Image *) NULL);
5378 }
glennrp47b9dd52010-11-24 18:12:06 +00005379
cristy3ed852e2009-09-05 21:47:34 +00005380 image=SyncNextImageInList(image);
5381 }
glennrp0fe50b42010-11-16 03:52:51 +00005382
cristy3ed852e2009-09-05 21:47:34 +00005383 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005384
cristy3ed852e2009-09-05 21:47:34 +00005385 if (term_chunk_found)
5386 {
5387 image->start_loop=MagickTrue;
5388 image->iterations=mng_iterations;
5389 term_chunk_found=MagickFalse;
5390 }
glennrp0fe50b42010-11-16 03:52:51 +00005391
cristy3ed852e2009-09-05 21:47:34 +00005392 else
5393 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005394
cristy3ed852e2009-09-05 21:47:34 +00005395 image->columns=subframe_width;
5396 image->rows=subframe_height;
5397 image->page.width=subframe_width;
5398 image->page.height=subframe_height;
5399 image->page.x=mng_info->clip.left;
5400 image->page.y=mng_info->clip.top;
5401 image->background_color=mng_background_color;
5402 image->matte=MagickFalse;
5403 image->delay=0;
cristyea1a8aa2011-10-20 13:24:06 +00005404 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005405
cristy3ed852e2009-09-05 21:47:34 +00005406 if (logging != MagickFalse)
5407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005408 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005409 (double) mng_info->clip.left,(double) mng_info->clip.right,
5410 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005411 }
5412#endif
5413 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5414 continue;
5415 }
5416 if (memcmp(type,mng_CLIP,4) == 0)
5417 {
5418 unsigned int
5419 first_object,
5420 last_object;
5421
5422 /*
5423 Read CLIP.
5424 */
5425 first_object=(p[0] << 8) | p[1];
5426 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005427
cristy3ed852e2009-09-05 21:47:34 +00005428 for (i=(int) first_object; i <= (int) last_object; i++)
5429 {
5430 if (mng_info->exists[i] && !mng_info->frozen[i])
5431 {
5432 MngBox
5433 box;
5434
5435 box=mng_info->object_clip[i];
5436 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5437 }
5438 }
glennrp47b9dd52010-11-24 18:12:06 +00005439
cristy3ed852e2009-09-05 21:47:34 +00005440 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5441 continue;
5442 }
5443 if (memcmp(type,mng_SAVE,4) == 0)
5444 {
5445 for (i=1; i < MNG_MAX_OBJECTS; i++)
5446 if (mng_info->exists[i])
5447 {
5448 mng_info->frozen[i]=MagickTrue;
5449#ifdef MNG_OBJECT_BUFFERS
5450 if (mng_info->ob[i] != (MngBuffer *) NULL)
5451 mng_info->ob[i]->frozen=MagickTrue;
5452#endif
5453 }
glennrp0fe50b42010-11-16 03:52:51 +00005454
cristy3ed852e2009-09-05 21:47:34 +00005455 if (length)
5456 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005457
cristy3ed852e2009-09-05 21:47:34 +00005458 continue;
5459 }
5460
5461 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5462 {
glennrp47b9dd52010-11-24 18:12:06 +00005463 /* Read DISC or SEEK. */
5464
cristy3ed852e2009-09-05 21:47:34 +00005465 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5466 {
5467 for (i=1; i < MNG_MAX_OBJECTS; i++)
5468 MngInfoDiscardObject(mng_info,i);
5469 }
glennrp0fe50b42010-11-16 03:52:51 +00005470
cristy3ed852e2009-09-05 21:47:34 +00005471 else
5472 {
cristybb503372010-05-27 20:51:26 +00005473 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005474 j;
5475
cristybb503372010-05-27 20:51:26 +00005476 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005477 {
5478 i=p[j] << 8 | p[j+1];
5479 MngInfoDiscardObject(mng_info,i);
5480 }
5481 }
glennrp0fe50b42010-11-16 03:52:51 +00005482
cristy3ed852e2009-09-05 21:47:34 +00005483 if (length)
5484 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005485
cristy3ed852e2009-09-05 21:47:34 +00005486 continue;
5487 }
glennrp47b9dd52010-11-24 18:12:06 +00005488
cristy3ed852e2009-09-05 21:47:34 +00005489 if (memcmp(type,mng_MOVE,4) == 0)
5490 {
cristybb503372010-05-27 20:51:26 +00005491 size_t
cristy3ed852e2009-09-05 21:47:34 +00005492 first_object,
5493 last_object;
5494
glennrp47b9dd52010-11-24 18:12:06 +00005495 /* read MOVE */
5496
cristy3ed852e2009-09-05 21:47:34 +00005497 first_object=(p[0] << 8) | p[1];
5498 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005499 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005500 {
5501 if (mng_info->exists[i] && !mng_info->frozen[i])
5502 {
5503 MngPair
5504 new_pair;
5505
5506 MngPair
5507 old_pair;
5508
5509 old_pair.a=mng_info->x_off[i];
5510 old_pair.b=mng_info->y_off[i];
5511 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5512 mng_info->x_off[i]=new_pair.a;
5513 mng_info->y_off[i]=new_pair.b;
5514 }
5515 }
glennrp47b9dd52010-11-24 18:12:06 +00005516
cristy3ed852e2009-09-05 21:47:34 +00005517 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5518 continue;
5519 }
5520
5521 if (memcmp(type,mng_LOOP,4) == 0)
5522 {
cristybb503372010-05-27 20:51:26 +00005523 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005524 loop_level=chunk[0];
5525 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005526
5527 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005528 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005529
cristy3ed852e2009-09-05 21:47:34 +00005530 if (logging != MagickFalse)
5531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005532 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5533 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005534
cristy3ed852e2009-09-05 21:47:34 +00005535 if (loop_iters == 0)
5536 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005537
cristy3ed852e2009-09-05 21:47:34 +00005538 else
5539 {
5540 mng_info->loop_jump[loop_level]=TellBlob(image);
5541 mng_info->loop_count[loop_level]=loop_iters;
5542 }
glennrp0fe50b42010-11-16 03:52:51 +00005543
cristy3ed852e2009-09-05 21:47:34 +00005544 mng_info->loop_iteration[loop_level]=0;
5545 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5546 continue;
5547 }
glennrp47b9dd52010-11-24 18:12:06 +00005548
cristy3ed852e2009-09-05 21:47:34 +00005549 if (memcmp(type,mng_ENDL,4) == 0)
5550 {
5551 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005552
cristy3ed852e2009-09-05 21:47:34 +00005553 if (skipping_loop > 0)
5554 {
5555 if (skipping_loop == loop_level)
5556 {
5557 /*
5558 Found end of zero-iteration loop.
5559 */
5560 skipping_loop=(-1);
5561 mng_info->loop_active[loop_level]=0;
5562 }
5563 }
glennrp47b9dd52010-11-24 18:12:06 +00005564
cristy3ed852e2009-09-05 21:47:34 +00005565 else
5566 {
5567 if (mng_info->loop_active[loop_level] == 1)
5568 {
5569 mng_info->loop_count[loop_level]--;
5570 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005571
cristy3ed852e2009-09-05 21:47:34 +00005572 if (logging != MagickFalse)
5573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005574 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005575 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005576 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005577
cristy3ed852e2009-09-05 21:47:34 +00005578 if (mng_info->loop_count[loop_level] != 0)
5579 {
5580 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5581 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005582
cristy3ed852e2009-09-05 21:47:34 +00005583 if (offset < 0)
5584 ThrowReaderException(CorruptImageError,
5585 "ImproperImageHeader");
5586 }
glennrp47b9dd52010-11-24 18:12:06 +00005587
cristy3ed852e2009-09-05 21:47:34 +00005588 else
5589 {
5590 short
5591 last_level;
5592
5593 /*
5594 Finished loop.
5595 */
5596 mng_info->loop_active[loop_level]=0;
5597 last_level=(-1);
5598 for (i=0; i < loop_level; i++)
5599 if (mng_info->loop_active[i] == 1)
5600 last_level=(short) i;
5601 loop_level=last_level;
5602 }
5603 }
5604 }
glennrp47b9dd52010-11-24 18:12:06 +00005605
cristy3ed852e2009-09-05 21:47:34 +00005606 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5607 continue;
5608 }
glennrp47b9dd52010-11-24 18:12:06 +00005609
cristy3ed852e2009-09-05 21:47:34 +00005610 if (memcmp(type,mng_CLON,4) == 0)
5611 {
5612 if (mng_info->clon_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005613 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005614 CoderError,"CLON is not implemented yet","`%s'",
5615 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005616
cristy3ed852e2009-09-05 21:47:34 +00005617 mng_info->clon_warning++;
5618 }
glennrp47b9dd52010-11-24 18:12:06 +00005619
cristy3ed852e2009-09-05 21:47:34 +00005620 if (memcmp(type,mng_MAGN,4) == 0)
5621 {
5622 png_uint_16
5623 magn_first,
5624 magn_last,
5625 magn_mb,
5626 magn_ml,
5627 magn_mr,
5628 magn_mt,
5629 magn_mx,
5630 magn_my,
5631 magn_methx,
5632 magn_methy;
5633
5634 if (length > 1)
5635 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005636
cristy3ed852e2009-09-05 21:47:34 +00005637 else
5638 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005639
cristy3ed852e2009-09-05 21:47:34 +00005640 if (length > 3)
5641 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005642
cristy3ed852e2009-09-05 21:47:34 +00005643 else
5644 magn_last=magn_first;
5645#ifndef MNG_OBJECT_BUFFERS
5646 if (magn_first || magn_last)
5647 if (mng_info->magn_warning == 0)
5648 {
cristyc82a27b2011-10-21 01:07:16 +00005649 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005650 GetMagickModule(),CoderError,
5651 "MAGN is not implemented yet for nonzero objects",
5652 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005653
cristy3ed852e2009-09-05 21:47:34 +00005654 mng_info->magn_warning++;
5655 }
5656#endif
5657 if (length > 4)
5658 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005659
cristy3ed852e2009-09-05 21:47:34 +00005660 else
5661 magn_methx=0;
5662
5663 if (length > 6)
5664 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005665
cristy3ed852e2009-09-05 21:47:34 +00005666 else
5667 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005668
cristy3ed852e2009-09-05 21:47:34 +00005669 if (magn_mx == 0)
5670 magn_mx=1;
5671
5672 if (length > 8)
5673 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005674
cristy3ed852e2009-09-05 21:47:34 +00005675 else
5676 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005677
cristy3ed852e2009-09-05 21:47:34 +00005678 if (magn_my == 0)
5679 magn_my=1;
5680
5681 if (length > 10)
5682 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005683
cristy3ed852e2009-09-05 21:47:34 +00005684 else
5685 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005686
cristy3ed852e2009-09-05 21:47:34 +00005687 if (magn_ml == 0)
5688 magn_ml=1;
5689
5690 if (length > 12)
5691 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005692
cristy3ed852e2009-09-05 21:47:34 +00005693 else
5694 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005695
cristy3ed852e2009-09-05 21:47:34 +00005696 if (magn_mr == 0)
5697 magn_mr=1;
5698
5699 if (length > 14)
5700 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005701
cristy3ed852e2009-09-05 21:47:34 +00005702 else
5703 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005704
cristy3ed852e2009-09-05 21:47:34 +00005705 if (magn_mt == 0)
5706 magn_mt=1;
5707
5708 if (length > 16)
5709 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005710
cristy3ed852e2009-09-05 21:47:34 +00005711 else
5712 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005713
cristy3ed852e2009-09-05 21:47:34 +00005714 if (magn_mb == 0)
5715 magn_mb=1;
5716
5717 if (length > 17)
5718 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005719
cristy3ed852e2009-09-05 21:47:34 +00005720 else
5721 magn_methy=magn_methx;
5722
glennrp47b9dd52010-11-24 18:12:06 +00005723
cristy3ed852e2009-09-05 21:47:34 +00005724 if (magn_methx > 5 || magn_methy > 5)
5725 if (mng_info->magn_warning == 0)
5726 {
cristyc82a27b2011-10-21 01:07:16 +00005727 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005728 GetMagickModule(),CoderError,
5729 "Unknown MAGN method in MNG datastream","`%s'",
5730 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005731
cristy3ed852e2009-09-05 21:47:34 +00005732 mng_info->magn_warning++;
5733 }
5734#ifdef MNG_OBJECT_BUFFERS
5735 /* Magnify existing objects in the range magn_first to magn_last */
5736#endif
5737 if (magn_first == 0 || magn_last == 0)
5738 {
5739 /* Save the magnification factors for object 0 */
5740 mng_info->magn_mb=magn_mb;
5741 mng_info->magn_ml=magn_ml;
5742 mng_info->magn_mr=magn_mr;
5743 mng_info->magn_mt=magn_mt;
5744 mng_info->magn_mx=magn_mx;
5745 mng_info->magn_my=magn_my;
5746 mng_info->magn_methx=magn_methx;
5747 mng_info->magn_methy=magn_methy;
5748 }
5749 }
glennrp47b9dd52010-11-24 18:12:06 +00005750
cristy3ed852e2009-09-05 21:47:34 +00005751 if (memcmp(type,mng_PAST,4) == 0)
5752 {
5753 if (mng_info->past_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005754 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005755 CoderError,"PAST is not implemented yet","`%s'",
5756 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005757
cristy3ed852e2009-09-05 21:47:34 +00005758 mng_info->past_warning++;
5759 }
glennrp47b9dd52010-11-24 18:12:06 +00005760
cristy3ed852e2009-09-05 21:47:34 +00005761 if (memcmp(type,mng_SHOW,4) == 0)
5762 {
5763 if (mng_info->show_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005764 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005765 CoderError,"SHOW is not implemented yet","`%s'",
5766 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005767
cristy3ed852e2009-09-05 21:47:34 +00005768 mng_info->show_warning++;
5769 }
glennrp47b9dd52010-11-24 18:12:06 +00005770
cristy3ed852e2009-09-05 21:47:34 +00005771 if (memcmp(type,mng_sBIT,4) == 0)
5772 {
5773 if (length < 4)
5774 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005775
cristy3ed852e2009-09-05 21:47:34 +00005776 else
5777 {
5778 mng_info->global_sbit.gray=p[0];
5779 mng_info->global_sbit.red=p[0];
5780 mng_info->global_sbit.green=p[1];
5781 mng_info->global_sbit.blue=p[2];
5782 mng_info->global_sbit.alpha=p[3];
5783 mng_info->have_global_sbit=MagickTrue;
5784 }
5785 }
5786 if (memcmp(type,mng_pHYs,4) == 0)
5787 {
5788 if (length > 8)
5789 {
5790 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005791 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005792 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005793 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005794 mng_info->global_phys_unit_type=p[8];
5795 mng_info->have_global_phys=MagickTrue;
5796 }
glennrp47b9dd52010-11-24 18:12:06 +00005797
cristy3ed852e2009-09-05 21:47:34 +00005798 else
5799 mng_info->have_global_phys=MagickFalse;
5800 }
5801 if (memcmp(type,mng_pHYg,4) == 0)
5802 {
5803 if (mng_info->phyg_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005804 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005805 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005806
cristy3ed852e2009-09-05 21:47:34 +00005807 mng_info->phyg_warning++;
5808 }
5809 if (memcmp(type,mng_BASI,4) == 0)
5810 {
5811 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005812
cristy3ed852e2009-09-05 21:47:34 +00005813 if (mng_info->basi_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005814 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005815 CoderError,"BASI is not implemented yet","`%s'",
5816 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005817
cristy3ed852e2009-09-05 21:47:34 +00005818 mng_info->basi_warning++;
5819#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005820 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005821 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005822 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005823 (p[6] << 8) | p[7]);
5824 basi_color_type=p[8];
5825 basi_compression_method=p[9];
5826 basi_filter_type=p[10];
5827 basi_interlace_method=p[11];
5828 if (length > 11)
5829 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005830
cristy3ed852e2009-09-05 21:47:34 +00005831 else
5832 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005833
cristy3ed852e2009-09-05 21:47:34 +00005834 if (length > 13)
5835 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005836
cristy3ed852e2009-09-05 21:47:34 +00005837 else
5838 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005839
cristy3ed852e2009-09-05 21:47:34 +00005840 if (length > 15)
5841 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005842
cristy3ed852e2009-09-05 21:47:34 +00005843 else
5844 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005845
cristy3ed852e2009-09-05 21:47:34 +00005846 if (length > 17)
5847 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005848
cristy3ed852e2009-09-05 21:47:34 +00005849 else
5850 {
5851 if (basi_sample_depth == 16)
5852 basi_alpha=65535L;
5853 else
5854 basi_alpha=255;
5855 }
glennrp47b9dd52010-11-24 18:12:06 +00005856
cristy3ed852e2009-09-05 21:47:34 +00005857 if (length > 19)
5858 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005859
cristy3ed852e2009-09-05 21:47:34 +00005860 else
5861 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005862
cristy3ed852e2009-09-05 21:47:34 +00005863#endif
5864 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5865 continue;
5866 }
glennrp47b9dd52010-11-24 18:12:06 +00005867
cristy3ed852e2009-09-05 21:47:34 +00005868 if (memcmp(type,mng_IHDR,4)
5869#if defined(JNG_SUPPORTED)
5870 && memcmp(type,mng_JHDR,4)
5871#endif
5872 )
5873 {
5874 /* Not an IHDR or JHDR chunk */
5875 if (length)
5876 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005877
cristy3ed852e2009-09-05 21:47:34 +00005878 continue;
5879 }
5880/* Process IHDR */
5881 if (logging != MagickFalse)
5882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5883 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005884
cristy3ed852e2009-09-05 21:47:34 +00005885 mng_info->exists[object_id]=MagickTrue;
5886 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005887
cristy3ed852e2009-09-05 21:47:34 +00005888 if (mng_info->invisible[object_id])
5889 {
5890 if (logging != MagickFalse)
5891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5892 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005893
cristy3ed852e2009-09-05 21:47:34 +00005894 skip_to_iend=MagickTrue;
5895 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5896 continue;
5897 }
5898#if defined(MNG_INSERT_LAYERS)
5899 if (length < 8)
5900 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005901
cristy8182b072010-05-30 20:10:53 +00005902 image_width=(size_t) mng_get_long(p);
5903 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005904#endif
5905 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5906
5907 /*
5908 Insert a transparent background layer behind the entire animation
5909 if it is not full screen.
5910 */
5911#if defined(MNG_INSERT_LAYERS)
5912 if (insert_layers && mng_type && first_mng_object)
5913 {
5914 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5915 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005916 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005917 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005918 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005919 {
cristy4c08aed2011-07-01 19:47:50 +00005920 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005921 {
5922 /*
5923 Allocate next image structure.
5924 */
cristy9950d572011-10-01 18:22:35 +00005925 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005926
cristy3ed852e2009-09-05 21:47:34 +00005927 if (GetNextImageInList(image) == (Image *) NULL)
5928 {
5929 image=DestroyImageList(image);
5930 MngInfoFreeStruct(mng_info,&have_mng_structure);
5931 return((Image *) NULL);
5932 }
glennrp47b9dd52010-11-24 18:12:06 +00005933
cristy3ed852e2009-09-05 21:47:34 +00005934 image=SyncNextImageInList(image);
5935 }
5936 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005937
cristy3ed852e2009-09-05 21:47:34 +00005938 if (term_chunk_found)
5939 {
5940 image->start_loop=MagickTrue;
5941 image->iterations=mng_iterations;
5942 term_chunk_found=MagickFalse;
5943 }
glennrp47b9dd52010-11-24 18:12:06 +00005944
cristy3ed852e2009-09-05 21:47:34 +00005945 else
5946 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005947
5948 /* Make a background rectangle. */
5949
cristy3ed852e2009-09-05 21:47:34 +00005950 image->delay=0;
5951 image->columns=mng_info->mng_width;
5952 image->rows=mng_info->mng_height;
5953 image->page.width=mng_info->mng_width;
5954 image->page.height=mng_info->mng_height;
5955 image->page.x=0;
5956 image->page.y=0;
5957 image->background_color=mng_background_color;
cristyea1a8aa2011-10-20 13:24:06 +00005958 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005959 if (logging != MagickFalse)
5960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005961 " Inserted transparent background layer, W=%.20g, H=%.20g",
5962 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005963 }
5964 }
5965 /*
5966 Insert a background layer behind the upcoming image if
5967 framing_mode is 3, and we haven't already inserted one.
5968 */
5969 if (insert_layers && (mng_info->framing_mode == 3) &&
5970 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5971 (simplicity & 0x08)))
5972 {
cristy4c08aed2011-07-01 19:47:50 +00005973 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005974 {
5975 /*
5976 Allocate next image structure.
5977 */
cristy9950d572011-10-01 18:22:35 +00005978 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005979
cristy3ed852e2009-09-05 21:47:34 +00005980 if (GetNextImageInList(image) == (Image *) NULL)
5981 {
5982 image=DestroyImageList(image);
5983 MngInfoFreeStruct(mng_info,&have_mng_structure);
5984 return((Image *) NULL);
5985 }
glennrp47b9dd52010-11-24 18:12:06 +00005986
cristy3ed852e2009-09-05 21:47:34 +00005987 image=SyncNextImageInList(image);
5988 }
glennrp0fe50b42010-11-16 03:52:51 +00005989
cristy3ed852e2009-09-05 21:47:34 +00005990 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005991
cristy3ed852e2009-09-05 21:47:34 +00005992 if (term_chunk_found)
5993 {
5994 image->start_loop=MagickTrue;
5995 image->iterations=mng_iterations;
5996 term_chunk_found=MagickFalse;
5997 }
glennrp0fe50b42010-11-16 03:52:51 +00005998
cristy3ed852e2009-09-05 21:47:34 +00005999 else
6000 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006001
cristy3ed852e2009-09-05 21:47:34 +00006002 image->delay=0;
6003 image->columns=subframe_width;
6004 image->rows=subframe_height;
6005 image->page.width=subframe_width;
6006 image->page.height=subframe_height;
6007 image->page.x=mng_info->clip.left;
6008 image->page.y=mng_info->clip.top;
6009 image->background_color=mng_background_color;
6010 image->matte=MagickFalse;
cristyea1a8aa2011-10-20 13:24:06 +00006011 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006012
cristy3ed852e2009-09-05 21:47:34 +00006013 if (logging != MagickFalse)
6014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00006015 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00006016 (double) mng_info->clip.left,(double) mng_info->clip.right,
6017 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00006018 }
6019#endif /* MNG_INSERT_LAYERS */
6020 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006021
cristy4c08aed2011-07-01 19:47:50 +00006022 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006023 {
6024 /*
6025 Allocate next image structure.
6026 */
cristy9950d572011-10-01 18:22:35 +00006027 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006028
cristy3ed852e2009-09-05 21:47:34 +00006029 if (GetNextImageInList(image) == (Image *) NULL)
6030 {
6031 image=DestroyImageList(image);
6032 MngInfoFreeStruct(mng_info,&have_mng_structure);
6033 return((Image *) NULL);
6034 }
glennrp47b9dd52010-11-24 18:12:06 +00006035
cristy3ed852e2009-09-05 21:47:34 +00006036 image=SyncNextImageInList(image);
6037 }
6038 mng_info->image=image;
6039 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6040 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006041
cristy3ed852e2009-09-05 21:47:34 +00006042 if (status == MagickFalse)
6043 break;
glennrp0fe50b42010-11-16 03:52:51 +00006044
cristy3ed852e2009-09-05 21:47:34 +00006045 if (term_chunk_found)
6046 {
6047 image->start_loop=MagickTrue;
6048 term_chunk_found=MagickFalse;
6049 }
glennrp0fe50b42010-11-16 03:52:51 +00006050
cristy3ed852e2009-09-05 21:47:34 +00006051 else
6052 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006053
cristy3ed852e2009-09-05 21:47:34 +00006054 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6055 {
6056 image->delay=frame_delay;
6057 frame_delay=default_frame_delay;
6058 }
glennrp0fe50b42010-11-16 03:52:51 +00006059
cristy3ed852e2009-09-05 21:47:34 +00006060 else
6061 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006062
cristy3ed852e2009-09-05 21:47:34 +00006063 image->page.width=mng_info->mng_width;
6064 image->page.height=mng_info->mng_height;
6065 image->page.x=mng_info->x_off[object_id];
6066 image->page.y=mng_info->y_off[object_id];
6067 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006068
cristy3ed852e2009-09-05 21:47:34 +00006069 /*
6070 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6071 */
glennrp47b9dd52010-11-24 18:12:06 +00006072
cristy3ed852e2009-09-05 21:47:34 +00006073 if (logging != MagickFalse)
6074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6075 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6076 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006077
cristybb503372010-05-27 20:51:26 +00006078 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006079
cristy3ed852e2009-09-05 21:47:34 +00006080 if (offset < 0)
6081 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6082 }
6083
6084 previous=image;
6085 mng_info->image=image;
6086 mng_info->mng_type=mng_type;
6087 mng_info->object_id=object_id;
6088
6089 if (memcmp(type,mng_IHDR,4) == 0)
6090 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006091
cristy3ed852e2009-09-05 21:47:34 +00006092#if defined(JNG_SUPPORTED)
6093 else
6094 image=ReadOneJNGImage(mng_info,image_info,exception);
6095#endif
6096
6097 if (image == (Image *) NULL)
6098 {
6099 if (IsImageObject(previous) != MagickFalse)
6100 {
6101 (void) DestroyImageList(previous);
6102 (void) CloseBlob(previous);
6103 }
glennrp47b9dd52010-11-24 18:12:06 +00006104
cristy3ed852e2009-09-05 21:47:34 +00006105 MngInfoFreeStruct(mng_info,&have_mng_structure);
6106 return((Image *) NULL);
6107 }
glennrp0fe50b42010-11-16 03:52:51 +00006108
cristy3ed852e2009-09-05 21:47:34 +00006109 if (image->columns == 0 || image->rows == 0)
6110 {
6111 (void) CloseBlob(image);
6112 image=DestroyImageList(image);
6113 MngInfoFreeStruct(mng_info,&have_mng_structure);
6114 return((Image *) NULL);
6115 }
glennrp0fe50b42010-11-16 03:52:51 +00006116
cristy3ed852e2009-09-05 21:47:34 +00006117 mng_info->image=image;
6118
6119 if (mng_type)
6120 {
6121 MngBox
6122 crop_box;
6123
6124 if (mng_info->magn_methx || mng_info->magn_methy)
6125 {
6126 png_uint_32
6127 magnified_height,
6128 magnified_width;
6129
6130 if (logging != MagickFalse)
6131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6132 " Processing MNG MAGN chunk");
6133
6134 if (mng_info->magn_methx == 1)
6135 {
6136 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006137
cristy3ed852e2009-09-05 21:47:34 +00006138 if (image->columns > 1)
6139 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006140
cristy3ed852e2009-09-05 21:47:34 +00006141 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006142 magnified_width += (png_uint_32)
6143 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006144 }
glennrp47b9dd52010-11-24 18:12:06 +00006145
cristy3ed852e2009-09-05 21:47:34 +00006146 else
6147 {
cristy4e5bc842010-06-09 13:56:01 +00006148 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006149
cristy3ed852e2009-09-05 21:47:34 +00006150 if (image->columns > 1)
6151 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006152
cristy3ed852e2009-09-05 21:47:34 +00006153 if (image->columns > 2)
6154 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006155
cristy3ed852e2009-09-05 21:47:34 +00006156 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006157 magnified_width += (png_uint_32)
6158 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006159 }
glennrp47b9dd52010-11-24 18:12:06 +00006160
cristy3ed852e2009-09-05 21:47:34 +00006161 if (mng_info->magn_methy == 1)
6162 {
6163 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006164
cristy3ed852e2009-09-05 21:47:34 +00006165 if (image->rows > 1)
6166 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006167
cristy3ed852e2009-09-05 21:47:34 +00006168 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006169 magnified_height += (png_uint_32)
6170 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006171 }
glennrp47b9dd52010-11-24 18:12:06 +00006172
cristy3ed852e2009-09-05 21:47:34 +00006173 else
6174 {
cristy4e5bc842010-06-09 13:56:01 +00006175 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006176
cristy3ed852e2009-09-05 21:47:34 +00006177 if (image->rows > 1)
6178 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006179
cristy3ed852e2009-09-05 21:47:34 +00006180 if (image->rows > 2)
6181 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006182
cristy3ed852e2009-09-05 21:47:34 +00006183 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006184 magnified_height += (png_uint_32)
6185 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006186 }
glennrp47b9dd52010-11-24 18:12:06 +00006187
cristy3ed852e2009-09-05 21:47:34 +00006188 if (magnified_height > image->rows ||
6189 magnified_width > image->columns)
6190 {
6191 Image
6192 *large_image;
6193
6194 int
6195 yy;
6196
cristy4c08aed2011-07-01 19:47:50 +00006197 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006198 *next,
6199 *prev;
6200
6201 png_uint_16
6202 magn_methx,
6203 magn_methy;
6204
cristy4c08aed2011-07-01 19:47:50 +00006205 ssize_t
6206 m,
6207 y;
6208
6209 register Quantum
6210 *n,
6211 *q;
6212
6213 register ssize_t
6214 x;
6215
glennrp47b9dd52010-11-24 18:12:06 +00006216 /* Allocate next image structure. */
6217
cristy3ed852e2009-09-05 21:47:34 +00006218 if (logging != MagickFalse)
6219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6220 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006221
cristy9950d572011-10-01 18:22:35 +00006222 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006223
cristy3ed852e2009-09-05 21:47:34 +00006224 if (GetNextImageInList(image) == (Image *) NULL)
6225 {
6226 image=DestroyImageList(image);
6227 MngInfoFreeStruct(mng_info,&have_mng_structure);
6228 return((Image *) NULL);
6229 }
6230
6231 large_image=SyncNextImageInList(image);
6232
6233 large_image->columns=magnified_width;
6234 large_image->rows=magnified_height;
6235
6236 magn_methx=mng_info->magn_methx;
6237 magn_methy=mng_info->magn_methy;
6238
glennrp3faa9a32011-04-23 14:00:25 +00006239#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006240#define QM unsigned short
6241 if (magn_methx != 1 || magn_methy != 1)
6242 {
6243 /*
6244 Scale pixels to unsigned shorts to prevent
6245 overflow of intermediate values of interpolations
6246 */
cristybb503372010-05-27 20:51:26 +00006247 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006248 {
6249 q=GetAuthenticPixels(image,0,y,image->columns,1,
6250 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006251
cristybb503372010-05-27 20:51:26 +00006252 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006253 {
cristy4c08aed2011-07-01 19:47:50 +00006254 SetPixelRed(image,ScaleQuantumToShort(
6255 GetPixelRed(image,q)),q);
6256 SetPixelGreen(image,ScaleQuantumToShort(
6257 GetPixelGreen(image,q)),q);
6258 SetPixelBlue(image,ScaleQuantumToShort(
6259 GetPixelBlue(image,q)),q);
6260 SetPixelAlpha(image,ScaleQuantumToShort(
6261 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006262 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006263 }
glennrp47b9dd52010-11-24 18:12:06 +00006264
cristy3ed852e2009-09-05 21:47:34 +00006265 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6266 break;
6267 }
6268 }
6269#else
6270#define QM Quantum
6271#endif
6272
6273 if (image->matte != MagickFalse)
cristyea1a8aa2011-10-20 13:24:06 +00006274 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006275
cristy3ed852e2009-09-05 21:47:34 +00006276 else
6277 {
cristy4c08aed2011-07-01 19:47:50 +00006278 large_image->background_color.alpha=OpaqueAlpha;
cristyea1a8aa2011-10-20 13:24:06 +00006279 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006280
cristy3ed852e2009-09-05 21:47:34 +00006281 if (magn_methx == 4)
6282 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006283
cristy3ed852e2009-09-05 21:47:34 +00006284 if (magn_methx == 5)
6285 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006286
cristy3ed852e2009-09-05 21:47:34 +00006287 if (magn_methy == 4)
6288 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006289
cristy3ed852e2009-09-05 21:47:34 +00006290 if (magn_methy == 5)
6291 magn_methy=3;
6292 }
6293
6294 /* magnify the rows into the right side of the large image */
6295
6296 if (logging != MagickFalse)
6297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006298 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006299 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006300 yy=0;
cristy8a20fa02011-12-27 15:54:31 +00006301 length=(size_t) image->columns*GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00006302 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6303 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006304
cristy4c08aed2011-07-01 19:47:50 +00006305 if ((prev == (Quantum *) NULL) ||
6306 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006307 {
6308 image=DestroyImageList(image);
6309 MngInfoFreeStruct(mng_info,&have_mng_structure);
6310 ThrowReaderException(ResourceLimitError,
6311 "MemoryAllocationFailed");
6312 }
glennrp47b9dd52010-11-24 18:12:06 +00006313
cristy3ed852e2009-09-05 21:47:34 +00006314 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6315 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006316
cristybb503372010-05-27 20:51:26 +00006317 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006318 {
6319 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006320 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006321
cristybb503372010-05-27 20:51:26 +00006322 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6323 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006324
cristybb503372010-05-27 20:51:26 +00006325 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6326 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006327
cristybb503372010-05-27 20:51:26 +00006328 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006329 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006330
cristy3ed852e2009-09-05 21:47:34 +00006331 else
cristybb503372010-05-27 20:51:26 +00006332 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006333
cristy3ed852e2009-09-05 21:47:34 +00006334 n=prev;
6335 prev=next;
6336 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006337
cristybb503372010-05-27 20:51:26 +00006338 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006339 {
6340 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6341 exception);
6342 (void) CopyMagickMemory(next,n,length);
6343 }
glennrp47b9dd52010-11-24 18:12:06 +00006344
cristy3ed852e2009-09-05 21:47:34 +00006345 for (i=0; i < m; i++, yy++)
6346 {
cristy4c08aed2011-07-01 19:47:50 +00006347 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006348 *pixels;
6349
cristybb503372010-05-27 20:51:26 +00006350 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006351 pixels=prev;
6352 n=next;
6353 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006354 1,exception);
cristy97707062011-12-27 18:25:00 +00006355 q+=(large_image->columns-image->columns)*
6356 GetPixelChannels(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006357
cristybb503372010-05-27 20:51:26 +00006358 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006359 {
glennrpfd05d622011-02-25 04:10:33 +00006360 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006361 /*
6362 if (image->storage_class == PseudoClass)
6363 {
6364 }
6365 */
6366
6367 if (magn_methy <= 1)
6368 {
glennrpbb4f99d2011-05-22 11:13:17 +00006369 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006370 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
glennrp847370c2011-07-05 17:37:15 +00006371 SetPixelGreen(large_image,GetPixelGreen(image,
6372 pixels),q);
6373 SetPixelBlue(large_image,GetPixelBlue(image,
6374 pixels),q);
6375 SetPixelAlpha(large_image,GetPixelAlpha(image,
6376 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006377 }
glennrp47b9dd52010-11-24 18:12:06 +00006378
cristy3ed852e2009-09-05 21:47:34 +00006379 else if (magn_methy == 2 || magn_methy == 4)
6380 {
6381 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006382 {
glennrp847370c2011-07-05 17:37:15 +00006383 SetPixelRed(large_image,GetPixelRed(image,
6384 pixels),q);
6385 SetPixelGreen(large_image,GetPixelGreen(image,
6386 pixels),q);
6387 SetPixelBlue(large_image,GetPixelBlue(image,
6388 pixels),q);
6389 SetPixelAlpha(large_image,GetPixelAlpha(image,
6390 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006391 }
glennrp47b9dd52010-11-24 18:12:06 +00006392
cristy3ed852e2009-09-05 21:47:34 +00006393 else
6394 {
6395 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006396 SetPixelRed(large_image,((QM) (((ssize_t)
6397 (2*i*(GetPixelRed(image,n)
6398 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006399 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006400 +GetPixelRed(image,pixels)))),q);
6401 SetPixelGreen(large_image,((QM) (((ssize_t)
6402 (2*i*(GetPixelGreen(image,n)
6403 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006404 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006405 +GetPixelGreen(image,pixels)))),q);
6406 SetPixelBlue(large_image,((QM) (((ssize_t)
6407 (2*i*(GetPixelBlue(image,n)
6408 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006409 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006410 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006411
cristy3ed852e2009-09-05 21:47:34 +00006412 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006413 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6414 (2*i*(GetPixelAlpha(image,n)
6415 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006416 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006417 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006418 }
glennrp47b9dd52010-11-24 18:12:06 +00006419
cristy3ed852e2009-09-05 21:47:34 +00006420 if (magn_methy == 4)
6421 {
6422 /* Replicate nearest */
6423 if (i <= ((m+1) << 1))
glennrp847370c2011-07-05 17:37:15 +00006424 SetPixelAlpha(large_image,GetPixelAlpha(image,
6425 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006426 else
glennrp847370c2011-07-05 17:37:15 +00006427 SetPixelAlpha(large_image,GetPixelAlpha(image,
6428 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006429 }
6430 }
glennrp47b9dd52010-11-24 18:12:06 +00006431
cristy3ed852e2009-09-05 21:47:34 +00006432 else /* if (magn_methy == 3 || magn_methy == 5) */
6433 {
6434 /* Replicate nearest */
6435 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006436 {
glennrp847370c2011-07-05 17:37:15 +00006437 SetPixelRed(large_image,GetPixelRed(image,
6438 pixels),q);
6439 SetPixelGreen(large_image,GetPixelGreen(image,
6440 pixels),q);
6441 SetPixelBlue(large_image,GetPixelBlue(image,
6442 pixels),q);
6443 SetPixelAlpha(large_image,GetPixelAlpha(image,
6444 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006445 }
glennrp47b9dd52010-11-24 18:12:06 +00006446
cristy3ed852e2009-09-05 21:47:34 +00006447 else
glennrpbb4f99d2011-05-22 11:13:17 +00006448 {
cristy4c08aed2011-07-01 19:47:50 +00006449 SetPixelRed(large_image,GetPixelRed(image,n),q);
glennrp847370c2011-07-05 17:37:15 +00006450 SetPixelGreen(large_image,GetPixelGreen(image,n),
6451 q);
6452 SetPixelBlue(large_image,GetPixelBlue(image,n),
6453 q);
6454 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6455 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006456 }
glennrp47b9dd52010-11-24 18:12:06 +00006457
cristy3ed852e2009-09-05 21:47:34 +00006458 if (magn_methy == 5)
6459 {
cristy4c08aed2011-07-01 19:47:50 +00006460 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6461 (GetPixelAlpha(image,n)
6462 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006463 +m))/((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006464 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006465 }
6466 }
cristyed231572011-07-14 02:18:59 +00006467 n+=GetPixelChannels(image);
6468 q+=GetPixelChannels(large_image);
6469 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006470 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006471
cristy3ed852e2009-09-05 21:47:34 +00006472 if (SyncAuthenticPixels(large_image,exception) == 0)
6473 break;
glennrp47b9dd52010-11-24 18:12:06 +00006474
cristy3ed852e2009-09-05 21:47:34 +00006475 } /* i */
6476 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006477
cristy4c08aed2011-07-01 19:47:50 +00006478 prev=(Quantum *) RelinquishMagickMemory(prev);
6479 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006480
6481 length=image->columns;
6482
6483 if (logging != MagickFalse)
6484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6485 " Delete original image");
6486
6487 DeleteImageFromList(&image);
6488
6489 image=large_image;
6490
6491 mng_info->image=image;
6492
6493 /* magnify the columns */
6494 if (logging != MagickFalse)
6495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006496 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006497
cristybb503372010-05-27 20:51:26 +00006498 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006499 {
cristy4c08aed2011-07-01 19:47:50 +00006500 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006501 *pixels;
6502
6503 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristyed231572011-07-14 02:18:59 +00006504 pixels=q+(image->columns-length)*GetPixelChannels(image);
6505 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006506
cristybb503372010-05-27 20:51:26 +00006507 for (x=(ssize_t) (image->columns-length);
6508 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006509 {
cristyed231572011-07-14 02:18:59 +00006510 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006511
cristybb503372010-05-27 20:51:26 +00006512 if (x == (ssize_t) (image->columns-length))
6513 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006514
cristybb503372010-05-27 20:51:26 +00006515 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6516 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006517
cristybb503372010-05-27 20:51:26 +00006518 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6519 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006520
cristybb503372010-05-27 20:51:26 +00006521 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006522 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006523
cristy3ed852e2009-09-05 21:47:34 +00006524 else
cristybb503372010-05-27 20:51:26 +00006525 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006526
cristy3ed852e2009-09-05 21:47:34 +00006527 for (i=0; i < m; i++)
6528 {
6529 if (magn_methx <= 1)
6530 {
6531 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006532 SetPixelRed(image,GetPixelRed(image,pixels),q);
6533 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6534 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6535 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006536 }
glennrp47b9dd52010-11-24 18:12:06 +00006537
cristy3ed852e2009-09-05 21:47:34 +00006538 else if (magn_methx == 2 || magn_methx == 4)
6539 {
6540 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006541 {
cristy4c08aed2011-07-01 19:47:50 +00006542 SetPixelRed(image,GetPixelRed(image,pixels),q);
6543 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6544 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6545 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006546 }
glennrp47b9dd52010-11-24 18:12:06 +00006547
cristyed231572011-07-14 02:18:59 +00006548 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006549 else
6550 {
6551 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006552 SetPixelRed(image,(QM) ((2*i*(
6553 GetPixelRed(image,n)
6554 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006555 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006556 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006557
cristy4c08aed2011-07-01 19:47:50 +00006558 SetPixelGreen(image,(QM) ((2*i*(
6559 GetPixelGreen(image,n)
6560 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006561 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006562 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006563
cristy4c08aed2011-07-01 19:47:50 +00006564 SetPixelBlue(image,(QM) ((2*i*(
6565 GetPixelBlue(image,n)
6566 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006567 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006568 GetPixelBlue(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006569 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006570 SetPixelAlpha(image,(QM) ((2*i*(
6571 GetPixelAlpha(image,n)
6572 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006573 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006574 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006575 }
glennrp47b9dd52010-11-24 18:12:06 +00006576
cristy3ed852e2009-09-05 21:47:34 +00006577 if (magn_methx == 4)
6578 {
6579 /* Replicate nearest */
6580 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006581 {
cristy4c08aed2011-07-01 19:47:50 +00006582 SetPixelAlpha(image,
6583 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006584 }
cristy3ed852e2009-09-05 21:47:34 +00006585 else
glennrpbb4f99d2011-05-22 11:13:17 +00006586 {
cristy4c08aed2011-07-01 19:47:50 +00006587 SetPixelAlpha(image,
6588 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006589 }
cristy3ed852e2009-09-05 21:47:34 +00006590 }
6591 }
glennrp47b9dd52010-11-24 18:12:06 +00006592
cristy3ed852e2009-09-05 21:47:34 +00006593 else /* if (magn_methx == 3 || magn_methx == 5) */
6594 {
6595 /* Replicate nearest */
6596 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006597 {
cristy4c08aed2011-07-01 19:47:50 +00006598 SetPixelRed(image,GetPixelRed(image,pixels),q);
6599 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6600 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6601 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006602 }
glennrp47b9dd52010-11-24 18:12:06 +00006603
cristy3ed852e2009-09-05 21:47:34 +00006604 else
glennrpbb4f99d2011-05-22 11:13:17 +00006605 {
cristy4c08aed2011-07-01 19:47:50 +00006606 SetPixelRed(image,GetPixelRed(image,n),q);
6607 SetPixelGreen(image,GetPixelGreen(image,n),q);
6608 SetPixelBlue(image,GetPixelBlue(image,n),q);
6609 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006610 }
glennrp47b9dd52010-11-24 18:12:06 +00006611
cristy3ed852e2009-09-05 21:47:34 +00006612 if (magn_methx == 5)
6613 {
6614 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006615 SetPixelAlpha(image,
6616 (QM) ((2*i*( GetPixelAlpha(image,n)
6617 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006618 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006619 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006620 }
6621 }
cristyed231572011-07-14 02:18:59 +00006622 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006623 }
cristyed231572011-07-14 02:18:59 +00006624 n+=GetPixelChannels(image);
6625 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006626 }
glennrp47b9dd52010-11-24 18:12:06 +00006627
cristy3ed852e2009-09-05 21:47:34 +00006628 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6629 break;
6630 }
glennrp3faa9a32011-04-23 14:00:25 +00006631#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006632 if (magn_methx != 1 || magn_methy != 1)
6633 {
6634 /*
6635 Rescale pixels to Quantum
6636 */
cristybb503372010-05-27 20:51:26 +00006637 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006638 {
6639 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006640
cristybb503372010-05-27 20:51:26 +00006641 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006642 {
cristy4c08aed2011-07-01 19:47:50 +00006643 SetPixelRed(image,ScaleShortToQuantum(
6644 GetPixelRed(image,q)),q);
6645 SetPixelGreen(image,ScaleShortToQuantum(
6646 GetPixelGreen(image,q)),q);
6647 SetPixelBlue(image,ScaleShortToQuantum(
6648 GetPixelBlue(image,q)),q);
6649 SetPixelAlpha(image,ScaleShortToQuantum(
6650 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006651 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006652 }
glennrp47b9dd52010-11-24 18:12:06 +00006653
cristy3ed852e2009-09-05 21:47:34 +00006654 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6655 break;
6656 }
6657 }
6658#endif
6659 if (logging != MagickFalse)
6660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6661 " Finished MAGN processing");
6662 }
6663 }
6664
6665 /*
6666 Crop_box is with respect to the upper left corner of the MNG.
6667 */
6668 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6669 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6670 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6671 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6672 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6673 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6674 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6675 if ((crop_box.left != (mng_info->image_box.left
6676 +mng_info->x_off[object_id])) ||
6677 (crop_box.right != (mng_info->image_box.right
6678 +mng_info->x_off[object_id])) ||
6679 (crop_box.top != (mng_info->image_box.top
6680 +mng_info->y_off[object_id])) ||
6681 (crop_box.bottom != (mng_info->image_box.bottom
6682 +mng_info->y_off[object_id])))
6683 {
6684 if (logging != MagickFalse)
6685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6686 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006687
cristy3ed852e2009-09-05 21:47:34 +00006688 if ((crop_box.left < crop_box.right) &&
6689 (crop_box.top < crop_box.bottom))
6690 {
6691 Image
6692 *im;
6693
6694 RectangleInfo
6695 crop_info;
6696
6697 /*
6698 Crop_info is with respect to the upper left corner of
6699 the image.
6700 */
6701 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6702 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006703 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6704 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006705 image->page.width=image->columns;
6706 image->page.height=image->rows;
6707 image->page.x=0;
6708 image->page.y=0;
6709 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006710
cristy3ed852e2009-09-05 21:47:34 +00006711 if (im != (Image *) NULL)
6712 {
6713 image->columns=im->columns;
6714 image->rows=im->rows;
6715 im=DestroyImage(im);
6716 image->page.width=image->columns;
6717 image->page.height=image->rows;
6718 image->page.x=crop_box.left;
6719 image->page.y=crop_box.top;
6720 }
6721 }
glennrp47b9dd52010-11-24 18:12:06 +00006722
cristy3ed852e2009-09-05 21:47:34 +00006723 else
6724 {
6725 /*
6726 No pixels in crop area. The MNG spec still requires
6727 a layer, though, so make a single transparent pixel in
6728 the top left corner.
6729 */
6730 image->columns=1;
6731 image->rows=1;
6732 image->colors=2;
cristyea1a8aa2011-10-20 13:24:06 +00006733 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006734 image->page.width=1;
6735 image->page.height=1;
6736 image->page.x=0;
6737 image->page.y=0;
6738 }
6739 }
6740#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6741 image=mng_info->image;
6742#endif
6743 }
6744
glennrp2b013e42010-11-24 16:55:50 +00006745#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6746 /* PNG does not handle depths greater than 16 so reduce it even
glennrpcc5d45b2012-01-06 04:06:10 +00006747 * if lossy.
glennrp2b013e42010-11-24 16:55:50 +00006748 */
6749 if (image->depth > 16)
6750 image->depth=16;
6751#endif
6752
glennrp3faa9a32011-04-23 14:00:25 +00006753#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00006754 if (image->depth > 8)
6755 {
6756 /* To do: fill low byte properly */
6757 image->depth=16;
6758 }
6759
cristyc82a27b2011-10-21 01:07:16 +00006760 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00006761 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006762#endif
glennrpd6afd542010-11-19 01:53:05 +00006763
cristy3ed852e2009-09-05 21:47:34 +00006764 if (image_info->number_scenes != 0)
6765 {
6766 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006767 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006768 break;
6769 }
glennrpd6afd542010-11-19 01:53:05 +00006770
cristy3ed852e2009-09-05 21:47:34 +00006771 if (logging != MagickFalse)
6772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6773 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006774
cristy3ed852e2009-09-05 21:47:34 +00006775 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006776
cristy3ed852e2009-09-05 21:47:34 +00006777 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006778
cristy3ed852e2009-09-05 21:47:34 +00006779 if (logging != MagickFalse)
6780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6781 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006782
cristy3ed852e2009-09-05 21:47:34 +00006783#if defined(MNG_INSERT_LAYERS)
6784 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6785 (mng_info->mng_height))
6786 {
6787 /*
6788 Insert a background layer if nothing else was found.
6789 */
6790 if (logging != MagickFalse)
6791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6792 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006793
cristy4c08aed2011-07-01 19:47:50 +00006794 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006795 {
6796 /*
6797 Allocate next image structure.
6798 */
cristy9950d572011-10-01 18:22:35 +00006799 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006800 if (GetNextImageInList(image) == (Image *) NULL)
6801 {
6802 image=DestroyImageList(image);
6803 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006804
cristy3ed852e2009-09-05 21:47:34 +00006805 if (logging != MagickFalse)
6806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6807 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006808
cristy3ed852e2009-09-05 21:47:34 +00006809 return((Image *) NULL);
6810 }
6811 image=SyncNextImageInList(image);
6812 }
6813 image->columns=mng_info->mng_width;
6814 image->rows=mng_info->mng_height;
6815 image->page.width=mng_info->mng_width;
6816 image->page.height=mng_info->mng_height;
6817 image->page.x=0;
6818 image->page.y=0;
6819 image->background_color=mng_background_color;
6820 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006821
cristy3ed852e2009-09-05 21:47:34 +00006822 if (image_info->ping == MagickFalse)
cristyea1a8aa2011-10-20 13:24:06 +00006823 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006824
cristy3ed852e2009-09-05 21:47:34 +00006825 mng_info->image_found++;
6826 }
6827#endif
6828 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006829
cristy3ed852e2009-09-05 21:47:34 +00006830 if (mng_iterations == 1)
6831 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006832
cristy3ed852e2009-09-05 21:47:34 +00006833 while (GetPreviousImageInList(image) != (Image *) NULL)
6834 {
6835 image_count++;
6836 if (image_count > 10*mng_info->image_found)
6837 {
6838 if (logging != MagickFalse)
6839 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006840
cristyc82a27b2011-10-21 01:07:16 +00006841 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006842 CoderError,"Linked list is corrupted, beginning of list not found",
6843 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006844
cristy3ed852e2009-09-05 21:47:34 +00006845 return((Image *) NULL);
6846 }
glennrp0fe50b42010-11-16 03:52:51 +00006847
cristy3ed852e2009-09-05 21:47:34 +00006848 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006849
cristy3ed852e2009-09-05 21:47:34 +00006850 if (GetNextImageInList(image) == (Image *) NULL)
6851 {
6852 if (logging != MagickFalse)
6853 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006854
cristyc82a27b2011-10-21 01:07:16 +00006855 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006856 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6857 image_info->filename);
6858 }
6859 }
glennrp47b9dd52010-11-24 18:12:06 +00006860
cristy3ed852e2009-09-05 21:47:34 +00006861 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6862 GetNextImageInList(image) ==
6863 (Image *) NULL)
6864 {
6865 if (logging != MagickFalse)
6866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6867 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006868
cristyc82a27b2011-10-21 01:07:16 +00006869 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006870 CoderError,"image->next for first image is NULL but shouldn't be.",
6871 "`%s'",image_info->filename);
6872 }
glennrp47b9dd52010-11-24 18:12:06 +00006873
cristy3ed852e2009-09-05 21:47:34 +00006874 if (mng_info->image_found == 0)
6875 {
6876 if (logging != MagickFalse)
6877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6878 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006879
cristyc82a27b2011-10-21 01:07:16 +00006880 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006881 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006882
cristy3ed852e2009-09-05 21:47:34 +00006883 if (image != (Image *) NULL)
6884 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006885
cristy3ed852e2009-09-05 21:47:34 +00006886 MngInfoFreeStruct(mng_info,&have_mng_structure);
6887 return((Image *) NULL);
6888 }
6889
6890 if (mng_info->ticks_per_second)
6891 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6892 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006893
cristy3ed852e2009-09-05 21:47:34 +00006894 else
6895 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006896
cristy3ed852e2009-09-05 21:47:34 +00006897 /* Find final nonzero image delay */
6898 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006899
cristy3ed852e2009-09-05 21:47:34 +00006900 while (GetNextImageInList(image) != (Image *) NULL)
6901 {
6902 if (image->delay)
6903 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006904
cristy3ed852e2009-09-05 21:47:34 +00006905 image=GetNextImageInList(image);
6906 }
glennrp0fe50b42010-11-16 03:52:51 +00006907
cristy3ed852e2009-09-05 21:47:34 +00006908 if (final_delay < final_image_delay)
6909 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006910
cristy3ed852e2009-09-05 21:47:34 +00006911 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006912
cristy3ed852e2009-09-05 21:47:34 +00006913 if (logging != MagickFalse)
6914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006915 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6916 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006917
cristy3ed852e2009-09-05 21:47:34 +00006918 if (logging != MagickFalse)
6919 {
6920 int
6921 scene;
6922
6923 scene=0;
6924 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006925
cristy3ed852e2009-09-05 21:47:34 +00006926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6927 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006928
cristy3ed852e2009-09-05 21:47:34 +00006929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006930 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006931
cristy3ed852e2009-09-05 21:47:34 +00006932 while (GetNextImageInList(image) != (Image *) NULL)
6933 {
6934 image=GetNextImageInList(image);
6935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006936 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006937 }
6938 }
6939
6940 image=GetFirstImageInList(image);
6941#ifdef MNG_COALESCE_LAYERS
6942 if (insert_layers)
6943 {
6944 Image
6945 *next_image,
6946 *next;
6947
cristybb503372010-05-27 20:51:26 +00006948 size_t
cristy3ed852e2009-09-05 21:47:34 +00006949 scene;
6950
6951 if (logging != MagickFalse)
6952 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006953
cristy3ed852e2009-09-05 21:47:34 +00006954 scene=image->scene;
cristyc82a27b2011-10-21 01:07:16 +00006955 next_image=CoalesceImages(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006956
cristy3ed852e2009-09-05 21:47:34 +00006957 if (next_image == (Image *) NULL)
6958 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006959
cristy3ed852e2009-09-05 21:47:34 +00006960 image=DestroyImageList(image);
6961 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006962
cristy3ed852e2009-09-05 21:47:34 +00006963 for (next=image; next != (Image *) NULL; next=next_image)
6964 {
6965 next->page.width=mng_info->mng_width;
6966 next->page.height=mng_info->mng_height;
6967 next->page.x=0;
6968 next->page.y=0;
6969 next->scene=scene++;
6970 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006971
cristy3ed852e2009-09-05 21:47:34 +00006972 if (next_image == (Image *) NULL)
6973 break;
glennrp47b9dd52010-11-24 18:12:06 +00006974
cristy3ed852e2009-09-05 21:47:34 +00006975 if (next->delay == 0)
6976 {
6977 scene--;
6978 next_image->previous=GetPreviousImageInList(next);
6979 if (GetPreviousImageInList(next) == (Image *) NULL)
6980 image=next_image;
6981 else
6982 next->previous->next=next_image;
6983 next=DestroyImage(next);
6984 }
6985 }
6986 }
6987#endif
6988
6989 while (GetNextImageInList(image) != (Image *) NULL)
6990 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006991
cristy3ed852e2009-09-05 21:47:34 +00006992 image->dispose=BackgroundDispose;
6993
6994 if (logging != MagickFalse)
6995 {
6996 int
6997 scene;
6998
6999 scene=0;
7000 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007001
cristy3ed852e2009-09-05 21:47:34 +00007002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7003 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00007004
cristy3ed852e2009-09-05 21:47:34 +00007005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007006 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7007 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00007008
cristy3ed852e2009-09-05 21:47:34 +00007009 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00007010 {
7011 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007012
cristyf2faecf2010-05-28 19:19:36 +00007013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007014 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7015 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00007016 }
7017 }
glennrp47b9dd52010-11-24 18:12:06 +00007018
cristy3ed852e2009-09-05 21:47:34 +00007019 image=GetFirstImageInList(image);
7020 MngInfoFreeStruct(mng_info,&have_mng_structure);
7021 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00007022
cristy3ed852e2009-09-05 21:47:34 +00007023 if (logging != MagickFalse)
7024 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00007025
cristy3ed852e2009-09-05 21:47:34 +00007026 return(GetFirstImageInList(image));
7027}
glennrp25c1e2b2010-03-25 01:39:56 +00007028#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007029static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7030{
7031 printf("Your PNG library is too old: You have libpng-%s\n",
7032 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00007033
cristy3ed852e2009-09-05 21:47:34 +00007034 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7035 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007036
cristy3ed852e2009-09-05 21:47:34 +00007037 return(Image *) NULL;
7038}
glennrp47b9dd52010-11-24 18:12:06 +00007039
cristy3ed852e2009-09-05 21:47:34 +00007040static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7041{
7042 return(ReadPNGImage(image_info,exception));
7043}
glennrp25c1e2b2010-03-25 01:39:56 +00007044#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007045#endif
7046
7047/*
7048%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7049% %
7050% %
7051% %
7052% R e g i s t e r P N G I m a g e %
7053% %
7054% %
7055% %
7056%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7057%
7058% RegisterPNGImage() adds properties for the PNG image format to
7059% the list of supported formats. The properties include the image format
7060% tag, a method to read and/or write the format, whether the format
7061% supports the saving of more than one frame to the same file or blob,
7062% whether the format supports native in-memory I/O, and a brief
7063% description of the format.
7064%
7065% The format of the RegisterPNGImage method is:
7066%
cristybb503372010-05-27 20:51:26 +00007067% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007068%
7069*/
cristybb503372010-05-27 20:51:26 +00007070ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007071{
7072 char
7073 version[MaxTextExtent];
7074
7075 MagickInfo
7076 *entry;
7077
7078 static const char
7079 *PNGNote=
7080 {
7081 "See http://www.libpng.org/ for details about the PNG format."
7082 },
glennrp47b9dd52010-11-24 18:12:06 +00007083
cristy3ed852e2009-09-05 21:47:34 +00007084 *JNGNote=
7085 {
7086 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7087 "format."
7088 },
glennrp47b9dd52010-11-24 18:12:06 +00007089
cristy3ed852e2009-09-05 21:47:34 +00007090 *MNGNote=
7091 {
7092 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7093 "format."
7094 };
7095
7096 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007097
cristy3ed852e2009-09-05 21:47:34 +00007098#if defined(PNG_LIBPNG_VER_STRING)
7099 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7100 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007101
cristy3ed852e2009-09-05 21:47:34 +00007102 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7103 {
7104 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7105 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7106 MaxTextExtent);
7107 }
7108#endif
glennrp47b9dd52010-11-24 18:12:06 +00007109
cristy3ed852e2009-09-05 21:47:34 +00007110 entry=SetMagickInfo("MNG");
7111 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007112
cristy3ed852e2009-09-05 21:47:34 +00007113#if defined(MAGICKCORE_PNG_DELEGATE)
7114 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7115 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7116#endif
glennrp47b9dd52010-11-24 18:12:06 +00007117
cristy3ed852e2009-09-05 21:47:34 +00007118 entry->magick=(IsImageFormatHandler *) IsMNG;
7119 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007120
cristy3ed852e2009-09-05 21:47:34 +00007121 if (*version != '\0')
7122 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007123
cristy3ed852e2009-09-05 21:47:34 +00007124 entry->module=ConstantString("PNG");
7125 entry->note=ConstantString(MNGNote);
7126 (void) RegisterMagickInfo(entry);
7127
7128 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007129
cristy3ed852e2009-09-05 21:47:34 +00007130#if defined(MAGICKCORE_PNG_DELEGATE)
7131 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7132 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7133#endif
glennrp47b9dd52010-11-24 18:12:06 +00007134
cristy3ed852e2009-09-05 21:47:34 +00007135 entry->magick=(IsImageFormatHandler *) IsPNG;
7136 entry->adjoin=MagickFalse;
7137 entry->description=ConstantString("Portable Network Graphics");
7138 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007139
cristy3ed852e2009-09-05 21:47:34 +00007140 if (*version != '\0')
7141 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007142
cristy3ed852e2009-09-05 21:47:34 +00007143 entry->note=ConstantString(PNGNote);
7144 (void) RegisterMagickInfo(entry);
7145
7146 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007147
cristy3ed852e2009-09-05 21:47:34 +00007148#if defined(MAGICKCORE_PNG_DELEGATE)
7149 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7150 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7151#endif
glennrp47b9dd52010-11-24 18:12:06 +00007152
cristy3ed852e2009-09-05 21:47:34 +00007153 entry->magick=(IsImageFormatHandler *) IsPNG;
7154 entry->adjoin=MagickFalse;
7155 entry->description=ConstantString(
7156 "8-bit indexed with optional binary transparency");
7157 entry->module=ConstantString("PNG");
7158 (void) RegisterMagickInfo(entry);
7159
7160 entry=SetMagickInfo("PNG24");
7161 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007162
cristy3ed852e2009-09-05 21:47:34 +00007163#if defined(ZLIB_VERSION)
7164 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7165 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007166
cristy3ed852e2009-09-05 21:47:34 +00007167 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7168 {
7169 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7170 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7171 }
7172#endif
glennrp47b9dd52010-11-24 18:12:06 +00007173
cristy3ed852e2009-09-05 21:47:34 +00007174 if (*version != '\0')
7175 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007176
cristy3ed852e2009-09-05 21:47:34 +00007177#if defined(MAGICKCORE_PNG_DELEGATE)
7178 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7179 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7180#endif
glennrp47b9dd52010-11-24 18:12:06 +00007181
cristy3ed852e2009-09-05 21:47:34 +00007182 entry->magick=(IsImageFormatHandler *) IsPNG;
7183 entry->adjoin=MagickFalse;
7184 entry->description=ConstantString("opaque 24-bit RGB");
7185 entry->module=ConstantString("PNG");
7186 (void) RegisterMagickInfo(entry);
7187
7188 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007189
cristy3ed852e2009-09-05 21:47:34 +00007190#if defined(MAGICKCORE_PNG_DELEGATE)
7191 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7192 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7193#endif
glennrp47b9dd52010-11-24 18:12:06 +00007194
cristy3ed852e2009-09-05 21:47:34 +00007195 entry->magick=(IsImageFormatHandler *) IsPNG;
7196 entry->adjoin=MagickFalse;
7197 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7198 entry->module=ConstantString("PNG");
7199 (void) RegisterMagickInfo(entry);
7200
7201 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007202
cristy3ed852e2009-09-05 21:47:34 +00007203#if defined(JNG_SUPPORTED)
7204#if defined(MAGICKCORE_PNG_DELEGATE)
7205 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7206 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7207#endif
7208#endif
glennrp47b9dd52010-11-24 18:12:06 +00007209
cristy3ed852e2009-09-05 21:47:34 +00007210 entry->magick=(IsImageFormatHandler *) IsJNG;
7211 entry->adjoin=MagickFalse;
7212 entry->description=ConstantString("JPEG Network Graphics");
7213 entry->module=ConstantString("PNG");
7214 entry->note=ConstantString(JNGNote);
7215 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007216
cristy18b17442009-10-25 18:36:48 +00007217#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007218 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007219#endif
glennrp47b9dd52010-11-24 18:12:06 +00007220
cristy3ed852e2009-09-05 21:47:34 +00007221 return(MagickImageCoderSignature);
7222}
7223
7224/*
7225%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7226% %
7227% %
7228% %
7229% U n r e g i s t e r P N G I m a g e %
7230% %
7231% %
7232% %
7233%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7234%
7235% UnregisterPNGImage() removes format registrations made by the
7236% PNG module from the list of supported formats.
7237%
7238% The format of the UnregisterPNGImage method is:
7239%
7240% UnregisterPNGImage(void)
7241%
7242*/
7243ModuleExport void UnregisterPNGImage(void)
7244{
7245 (void) UnregisterMagickInfo("MNG");
7246 (void) UnregisterMagickInfo("PNG");
7247 (void) UnregisterMagickInfo("PNG8");
7248 (void) UnregisterMagickInfo("PNG24");
7249 (void) UnregisterMagickInfo("PNG32");
7250 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007251
cristy3ed852e2009-09-05 21:47:34 +00007252#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007253 if (ping_semaphore != (SemaphoreInfo *) NULL)
7254 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007255#endif
7256}
7257
7258#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007259#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007260/*
7261%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7262% %
7263% %
7264% %
7265% W r i t e M N G I m a g e %
7266% %
7267% %
7268% %
7269%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7270%
7271% WriteMNGImage() writes an image in the Portable Network Graphics
7272% Group's "Multiple-image Network Graphics" encoded image format.
7273%
7274% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7275%
7276% The format of the WriteMNGImage method is:
7277%
cristy1e178e72011-08-28 19:44:34 +00007278% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7279% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007280%
7281% A description of each parameter follows.
7282%
7283% o image_info: the image info.
7284%
7285% o image: The image.
7286%
cristy1e178e72011-08-28 19:44:34 +00007287% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007288%
7289% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7290% "To do" under ReadPNGImage):
7291%
cristy3ed852e2009-09-05 21:47:34 +00007292% Preserve all unknown and not-yet-handled known chunks found in input
7293% PNG file and copy them into output PNG files according to the PNG
7294% copying rules.
7295%
7296% Write the iCCP chunk at MNG level when (icc profile length > 0)
7297%
7298% Improve selection of color type (use indexed-colour or indexed-colour
7299% with tRNS when 256 or fewer unique RGBA values are present).
7300%
7301% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7302% This will be complicated if we limit ourselves to generating MNG-LC
7303% files. For now we ignore disposal method 3 and simply overlay the next
7304% image on it.
7305%
7306% Check for identical PLTE's or PLTE/tRNS combinations and use a
7307% global MNG PLTE or PLTE/tRNS combination when appropriate.
7308% [mostly done 15 June 1999 but still need to take care of tRNS]
7309%
7310% Check for identical sRGB and replace with a global sRGB (and remove
7311% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7312% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7313% local gAMA/cHRM with local sRGB if appropriate).
7314%
7315% Check for identical sBIT chunks and write global ones.
7316%
7317% Provide option to skip writing the signature tEXt chunks.
7318%
7319% Use signatures to detect identical objects and reuse the first
7320% instance of such objects instead of writing duplicate objects.
7321%
7322% Use a smaller-than-32k value of compression window size when
7323% appropriate.
7324%
7325% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7326% ancillary text chunks and save profiles.
7327%
7328% Provide an option to force LC files (to ensure exact framing rate)
7329% instead of VLC.
7330%
7331% Provide an option to force VLC files instead of LC, even when offsets
7332% are present. This will involve expanding the embedded images with a
7333% transparent region at the top and/or left.
7334*/
7335
cristy3ed852e2009-09-05 21:47:34 +00007336static void
glennrpcf002022011-01-30 02:38:15 +00007337Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007338 png_info *ping_info, unsigned char *profile_type, unsigned char
7339 *profile_description, unsigned char *profile_data, png_uint_32 length)
7340{
cristy3ed852e2009-09-05 21:47:34 +00007341 png_textp
7342 text;
7343
cristybb503372010-05-27 20:51:26 +00007344 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007345 i;
7346
7347 unsigned char
7348 *sp;
7349
7350 png_charp
7351 dp;
7352
7353 png_uint_32
7354 allocated_length,
7355 description_length;
7356
7357 unsigned char
7358 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007359
cristy3ed852e2009-09-05 21:47:34 +00007360 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7361 return;
7362
7363 if (image_info->verbose)
7364 {
glennrp0fe50b42010-11-16 03:52:51 +00007365 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7366 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007367 }
glennrp0fe50b42010-11-16 03:52:51 +00007368
cristy3ed852e2009-09-05 21:47:34 +00007369 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7370 description_length=(png_uint_32) strlen((const char *) profile_description);
7371 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7372 + description_length);
7373 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7374 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7375 text[0].key[0]='\0';
7376 (void) ConcatenateMagickString(text[0].key,
7377 "Raw profile type ",MaxTextExtent);
7378 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7379 sp=profile_data;
7380 dp=text[0].text;
7381 *dp++='\n';
7382 (void) CopyMagickString(dp,(const char *) profile_description,
7383 allocated_length);
7384 dp+=description_length;
7385 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007386 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007387 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007388 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007389
cristybb503372010-05-27 20:51:26 +00007390 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007391 {
7392 if (i%36 == 0)
7393 *dp++='\n';
7394 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7395 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7396 }
glennrp47b9dd52010-11-24 18:12:06 +00007397
cristy3ed852e2009-09-05 21:47:34 +00007398 *dp++='\n';
7399 *dp='\0';
7400 text[0].text_length=(png_size_t) (dp-text[0].text);
7401 text[0].compression=image_info->compression == NoCompression ||
7402 (image_info->compression == UndefinedCompression &&
7403 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007404
cristy3ed852e2009-09-05 21:47:34 +00007405 if (text[0].text_length <= allocated_length)
7406 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007407
cristy3ed852e2009-09-05 21:47:34 +00007408 png_free(ping,text[0].text);
7409 png_free(ping,text[0].key);
7410 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007411}
7412
glennrpcf002022011-01-30 02:38:15 +00007413static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007414 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007415{
7416 char
7417 *name;
7418
7419 const StringInfo
7420 *profile;
7421
7422 unsigned char
7423 *data;
7424
7425 png_uint_32 length;
7426
7427 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007428
7429 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7430 {
cristy3ed852e2009-09-05 21:47:34 +00007431 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007432
cristy3ed852e2009-09-05 21:47:34 +00007433 if (profile != (const StringInfo *) NULL)
7434 {
7435 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007436 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007437
glennrp47b9dd52010-11-24 18:12:06 +00007438 if (LocaleNCompare(name,string,11) == 0)
7439 {
7440 if (logging != MagickFalse)
7441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7442 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007443
glennrpcf002022011-01-30 02:38:15 +00007444 ping_profile=CloneStringInfo(profile);
7445 data=GetStringInfoDatum(ping_profile),
7446 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007447 data[4]=data[3];
7448 data[3]=data[2];
7449 data[2]=data[1];
7450 data[1]=data[0];
7451 (void) WriteBlobMSBULong(image,length-5); /* data length */
7452 (void) WriteBlob(image,length-1,data+1);
7453 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007454 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007455 }
cristy3ed852e2009-09-05 21:47:34 +00007456 }
glennrp47b9dd52010-11-24 18:12:06 +00007457
cristy3ed852e2009-09-05 21:47:34 +00007458 name=GetNextImageProfile(image);
7459 }
glennrp47b9dd52010-11-24 18:12:06 +00007460
cristy3ed852e2009-09-05 21:47:34 +00007461 return(MagickTrue);
7462}
7463
glennrpb9cfe272010-12-21 15:08:06 +00007464
cristy3ed852e2009-09-05 21:47:34 +00007465/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007466static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +00007467 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007468{
7469 Image
7470 *image;
7471
7472 ImageInfo
7473 *image_info;
7474
cristy3ed852e2009-09-05 21:47:34 +00007475 char
7476 s[2];
7477
7478 const char
7479 *name,
7480 *property,
7481 *value;
7482
7483 const StringInfo
7484 *profile;
7485
cristy3ed852e2009-09-05 21:47:34 +00007486 int
cristy3ed852e2009-09-05 21:47:34 +00007487 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007488 pass;
7489
glennrpe9c26dc2010-05-30 01:56:35 +00007490 png_byte
7491 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007492
glennrp39992b42010-11-14 00:03:43 +00007493 png_color
7494 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007495
glennrp5af765f2010-03-30 11:12:18 +00007496 png_color_16
7497 ping_background,
7498 ping_trans_color;
7499
cristy3ed852e2009-09-05 21:47:34 +00007500 png_info
7501 *ping_info;
7502
7503 png_struct
7504 *ping;
7505
glennrp5af765f2010-03-30 11:12:18 +00007506 png_uint_32
7507 ping_height,
7508 ping_width;
7509
cristybb503372010-05-27 20:51:26 +00007510 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007511 y;
7512
7513 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007514 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007515 logging,
glennrp58e01762011-01-07 15:28:54 +00007516 matte,
7517
glennrpda8f3a72011-02-27 23:54:12 +00007518 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007519 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007520 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007521 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007522 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007523 ping_have_bKGD,
7524 ping_have_pHYs,
7525 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007526
7527 ping_exclude_bKGD,
7528 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007529 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007530 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007531 ping_exclude_gAMA,
7532 ping_exclude_iCCP,
7533 /* ping_exclude_iTXt, */
7534 ping_exclude_oFFs,
7535 ping_exclude_pHYs,
7536 ping_exclude_sRGB,
7537 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007538 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007539 ping_exclude_vpAg,
7540 ping_exclude_zCCP, /* hex-encoded iCCP */
7541 ping_exclude_zTXt,
7542
glennrp8d3d6e52011-04-19 04:39:51 +00007543 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007544 ping_need_colortype_warning,
7545
glennrp82b3c532011-03-22 19:20:54 +00007546 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007547 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007548 tried_333,
7549 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007550
7551 QuantumInfo
7552 *quantum_info;
7553
cristyc82a27b2011-10-21 01:07:16 +00007554 PNGErrorInfo
7555 error_info;
7556
cristybb503372010-05-27 20:51:26 +00007557 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007558 i,
7559 x;
7560
7561 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007562 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007563
glennrp5af765f2010-03-30 11:12:18 +00007564 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007565 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007566 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007567 ping_color_type,
7568 ping_interlace_method,
7569 ping_compression_method,
7570 ping_filter_method,
7571 ping_num_trans;
7572
cristybb503372010-05-27 20:51:26 +00007573 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007574 image_depth,
7575 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007576
cristybb503372010-05-27 20:51:26 +00007577 size_t
cristy3ed852e2009-09-05 21:47:34 +00007578 quality,
7579 rowbytes,
7580 save_image_depth;
7581
glennrpdfd70802010-11-14 01:23:35 +00007582 int
glennrpfd05d622011-02-25 04:10:33 +00007583 j,
glennrpf09bded2011-01-08 01:15:59 +00007584 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007585 number_opaque,
7586 number_semitransparent,
7587 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007588 ping_pHYs_unit_type;
7589
7590 png_uint_32
7591 ping_pHYs_x_resolution,
7592 ping_pHYs_y_resolution;
7593
cristy3ed852e2009-09-05 21:47:34 +00007594 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007595 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007596
cristyc82a27b2011-10-21 01:07:16 +00007597 image = CloneImage(IMimage,0,0,MagickFalse,exception);
glennrpb9cfe272010-12-21 15:08:06 +00007598 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007599 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007600 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007601
cristy3ed852e2009-09-05 21:47:34 +00007602#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007603 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007604#endif
7605
glennrp5af765f2010-03-30 11:12:18 +00007606 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007607 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007608 ping_color_type=0,
7609 ping_interlace_method=0,
7610 ping_compression_method=0,
7611 ping_filter_method=0,
7612 ping_num_trans = 0;
7613
7614 ping_background.red = 0;
7615 ping_background.green = 0;
7616 ping_background.blue = 0;
7617 ping_background.gray = 0;
7618 ping_background.index = 0;
7619
7620 ping_trans_color.red=0;
7621 ping_trans_color.green=0;
7622 ping_trans_color.blue=0;
7623 ping_trans_color.gray=0;
7624
glennrpdfd70802010-11-14 01:23:35 +00007625 ping_pHYs_unit_type = 0;
7626 ping_pHYs_x_resolution = 0;
7627 ping_pHYs_y_resolution = 0;
7628
glennrpda8f3a72011-02-27 23:54:12 +00007629 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007630 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007631 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007632 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007633 ping_have_bKGD=MagickFalse;
7634 ping_have_pHYs=MagickFalse;
7635 ping_have_tRNS=MagickFalse;
7636
glennrp0e8ea192010-12-24 18:00:33 +00007637 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7638 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007639 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007640 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007641 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007642 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7643 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7644 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7645 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7646 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7647 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007648 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007649 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7650 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7651 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7652
glennrp8d3d6e52011-04-19 04:39:51 +00007653 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007654 ping_need_colortype_warning = MagickFalse;
7655
cristy0d57eec2011-09-04 22:13:56 +00007656 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7657 * i.e., eliminate the ICC profile and set image->rendering_intent.
7658 * Note that this will not involve any changes to the actual pixels
7659 * but merely passes information to applications that read the resulting
7660 * PNG image.
7661 */
7662 if (ping_exclude_sRGB == MagickFalse)
7663 {
7664 char
7665 *name;
7666
7667 const StringInfo
7668 *profile;
7669
7670 ResetImageProfileIterator(image);
7671 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7672 {
7673 profile=GetImageProfile(image,name);
7674
7675 if (profile != (StringInfo *) NULL)
7676 {
7677 if ((LocaleCompare(name,"ICC") == 0) ||
glennrp29a106e2011-09-06 17:11:42 +00007678 (LocaleCompare(name,"ICM") == 0))
7679 {
glennrpee7b4c02011-10-04 01:21:09 +00007680 int
7681 icheck;
7682
7683 /* 0: not a known sRGB profile
7684 * 1: HP-Microsoft sRGB v2
7685 * 2: ICC sRGB v4 perceptual
7686 * 3: ICC sRGB v2 perceptual no black-compensation
7687 */
7688 png_uint_32
7689 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7690 check_len[4] = {0, 3144, 60960, 3052};
7691
7692 png_uint_32
7693 length,
7694 profile_crc;
7695
cristy0d57eec2011-09-04 22:13:56 +00007696 unsigned char
7697 *data;
7698
glennrp29a106e2011-09-06 17:11:42 +00007699 length=(png_uint_32) GetStringInfoLength(profile);
7700
glennrpee7b4c02011-10-04 01:21:09 +00007701 for (icheck=3; icheck > 0; icheck--)
cristy0d57eec2011-09-04 22:13:56 +00007702 {
glennrpee7b4c02011-10-04 01:21:09 +00007703 if (length == check_len[icheck])
glennrp29a106e2011-09-06 17:11:42 +00007704 {
glennrpee7b4c02011-10-04 01:21:09 +00007705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7706 " Got a %lu-byte ICC profile (potentially sRGB)",
7707 (unsigned long) length);
glennrp29a106e2011-09-06 17:11:42 +00007708
glennrpee7b4c02011-10-04 01:21:09 +00007709 data=GetStringInfoDatum(profile);
7710 profile_crc=crc32(0,data,length);
glennrp29a106e2011-09-06 17:11:42 +00007711
glennrpee7b4c02011-10-04 01:21:09 +00007712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe54cd4b2011-10-04 01:31:35 +00007713 " with crc=%8x",(unsigned int) profile_crc);
glennrpee7b4c02011-10-04 01:21:09 +00007714
7715 if (profile_crc == check_crc[icheck])
7716 {
7717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7718 " It is sRGB.");
7719 if (image->rendering_intent==UndefinedIntent)
7720 image->rendering_intent=PerceptualIntent;
7721 break;
7722 }
glennrp29a106e2011-09-06 17:11:42 +00007723 }
glennrp29a106e2011-09-06 17:11:42 +00007724 }
glennrpee7b4c02011-10-04 01:21:09 +00007725 if (icheck == 0)
glennrp29a106e2011-09-06 17:11:42 +00007726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpee7b4c02011-10-04 01:21:09 +00007727 " Got a %lu-byte ICC profile",
glennrp29a106e2011-09-06 17:11:42 +00007728 (unsigned long) length);
7729 }
cristy0d57eec2011-09-04 22:13:56 +00007730 }
7731 name=GetNextImageProfile(image);
7732 }
7733 }
7734
glennrp8bb3a022010-12-13 20:40:04 +00007735 number_opaque = 0;
7736 number_semitransparent = 0;
7737 number_transparent = 0;
7738
glennrpfd05d622011-02-25 04:10:33 +00007739 if (logging != MagickFalse)
7740 {
7741 if (image->storage_class == UndefinedClass)
7742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7743 " storage_class=UndefinedClass");
7744 if (image->storage_class == DirectClass)
7745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7746 " storage_class=DirectClass");
7747 if (image->storage_class == PseudoClass)
7748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7749 " storage_class=PseudoClass");
7750 }
glennrp28af3712011-04-06 18:07:30 +00007751
glennrp7e65e932011-08-19 02:31:16 +00007752 if (image->storage_class == PseudoClass &&
7753 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7754 (mng_info->write_png_colortype != 0 &&
7755 mng_info->write_png_colortype != 4)))
7756 {
cristyea1a8aa2011-10-20 13:24:06 +00007757 (void) SyncImage(image,exception);
glennrp7e65e932011-08-19 02:31:16 +00007758 image->storage_class = DirectClass;
7759 }
7760
glennrpc6c391a2011-04-27 02:23:56 +00007761 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007762 {
glennrpc6c391a2011-04-27 02:23:56 +00007763 if (image->storage_class != PseudoClass && image->colormap != NULL)
7764 {
7765 /* Free the bogus colormap; it can cause trouble later */
7766 if (logging != MagickFalse)
7767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7768 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00007769 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00007770 image->colormap=NULL;
7771 }
glennrp28af3712011-04-06 18:07:30 +00007772 }
glennrpbb4f99d2011-05-22 11:13:17 +00007773
cristy510d06a2011-07-06 23:43:54 +00007774 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristye941a752011-10-15 01:52:48 +00007775 (void) TransformImageColorspace(image,RGBColorspace,exception);
glennrp0fe50b42010-11-16 03:52:51 +00007776
glennrp3241bd02010-12-12 04:36:28 +00007777 /*
7778 Sometimes we get PseudoClass images whose RGB values don't match
7779 the colors in the colormap. This code syncs the RGB values.
7780 */
7781 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
cristyea1a8aa2011-10-20 13:24:06 +00007782 (void) SyncImage(image,exception);
glennrp3241bd02010-12-12 04:36:28 +00007783
glennrpa6a06632011-01-19 15:15:34 +00007784#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7785 if (image->depth > 8)
7786 {
7787 if (logging != MagickFalse)
7788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7789 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7790
7791 image->depth=8;
7792 }
7793#endif
7794
glennrp8e58efd2011-05-20 12:16:29 +00007795 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007796 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7797 {
cristy4c08aed2011-07-01 19:47:50 +00007798 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007799 *r;
7800
glennrp8e58efd2011-05-20 12:16:29 +00007801 if (image->depth > 8)
7802 {
7803#if MAGICKCORE_QUANTUM_DEPTH > 16
7804 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007805 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007806
7807 for (y=0; y < (ssize_t) image->rows; y++)
7808 {
cristy8a20fa02011-12-27 15:54:31 +00007809 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007810
cristy4c08aed2011-07-01 19:47:50 +00007811 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007812 break;
7813
7814 for (x=0; x < (ssize_t) image->columns; x++)
7815 {
glennrp54cf7972011-08-06 14:28:09 +00007816 LBR16PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007817 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007818 }
glennrpbb4f99d2011-05-22 11:13:17 +00007819
glennrp8e58efd2011-05-20 12:16:29 +00007820 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7821 break;
7822 }
7823
7824 if (image->storage_class == PseudoClass && image->colormap != NULL)
7825 {
cristy3e08f112011-05-24 13:19:30 +00007826 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007827 {
glennrp91d99252011-06-25 14:30:13 +00007828 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007829 }
7830 }
7831#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7832 }
7833
7834 else if (image->depth > 4)
7835 {
7836#if MAGICKCORE_QUANTUM_DEPTH > 8
7837 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007838 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007839
7840 for (y=0; y < (ssize_t) image->rows; y++)
7841 {
cristyc82a27b2011-10-21 01:07:16 +00007842 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007843
cristy4c08aed2011-07-01 19:47:50 +00007844 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007845 break;
7846
7847 for (x=0; x < (ssize_t) image->columns; x++)
7848 {
glennrp54cf7972011-08-06 14:28:09 +00007849 LBR08PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007850 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007851 }
glennrpbb4f99d2011-05-22 11:13:17 +00007852
glennrp8e58efd2011-05-20 12:16:29 +00007853 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7854 break;
7855 }
7856
7857 if (image->storage_class == PseudoClass && image->colormap != NULL)
7858 {
cristy3e08f112011-05-24 13:19:30 +00007859 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007860 {
glennrp91d99252011-06-25 14:30:13 +00007861 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007862 }
7863 }
7864#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7865 }
7866 else
7867 if (image->depth > 2)
7868 {
7869 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007870 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007871
7872 for (y=0; y < (ssize_t) image->rows; y++)
7873 {
cristy8a20fa02011-12-27 15:54:31 +00007874 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007875
cristy4c08aed2011-07-01 19:47:50 +00007876 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007877 break;
7878
7879 for (x=0; x < (ssize_t) image->columns; x++)
7880 {
glennrp54cf7972011-08-06 14:28:09 +00007881 LBR04PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007882 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007883 }
glennrpbb4f99d2011-05-22 11:13:17 +00007884
glennrp8e58efd2011-05-20 12:16:29 +00007885 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7886 break;
7887 }
7888
7889 if (image->storage_class == PseudoClass && image->colormap != NULL)
7890 {
cristy3e08f112011-05-24 13:19:30 +00007891 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007892 {
glennrp91d99252011-06-25 14:30:13 +00007893 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007894 }
7895 }
7896 }
7897
7898 else if (image->depth > 1)
7899 {
7900 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007901 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007902
7903 for (y=0; y < (ssize_t) image->rows; y++)
7904 {
cristy8a20fa02011-12-27 15:54:31 +00007905 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007906
cristy4c08aed2011-07-01 19:47:50 +00007907 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007908 break;
7909
7910 for (x=0; x < (ssize_t) image->columns; x++)
7911 {
glennrp54cf7972011-08-06 14:28:09 +00007912 LBR02PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007913 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007914 }
glennrpbb4f99d2011-05-22 11:13:17 +00007915
glennrp8e58efd2011-05-20 12:16:29 +00007916 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7917 break;
7918 }
7919
7920 if (image->storage_class == PseudoClass && image->colormap != NULL)
7921 {
cristy3e08f112011-05-24 13:19:30 +00007922 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007923 {
glennrp91d99252011-06-25 14:30:13 +00007924 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007925 }
7926 }
7927 }
7928 else
7929 {
7930 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007931 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007932
7933 for (y=0; y < (ssize_t) image->rows; y++)
7934 {
cristy8a20fa02011-12-27 15:54:31 +00007935 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007936
cristy4c08aed2011-07-01 19:47:50 +00007937 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007938 break;
7939
7940 for (x=0; x < (ssize_t) image->columns; x++)
7941 {
glennrp54cf7972011-08-06 14:28:09 +00007942 LBR01PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007943 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007944 }
glennrpbb4f99d2011-05-22 11:13:17 +00007945
glennrp8e58efd2011-05-20 12:16:29 +00007946 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7947 break;
7948 }
7949
7950 if (image->storage_class == PseudoClass && image->colormap != NULL)
7951 {
cristy3e08f112011-05-24 13:19:30 +00007952 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007953 {
glennrp91d99252011-06-25 14:30:13 +00007954 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007955 }
7956 }
7957 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007958 }
7959
glennrp67b9c1a2011-04-22 18:47:36 +00007960 /* To do: set to next higher multiple of 8 */
7961 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007962 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007963
glennrp2b013e42010-11-24 16:55:50 +00007964#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7965 /* PNG does not handle depths greater than 16 so reduce it even
7966 * if lossy
7967 */
glennrp8e58efd2011-05-20 12:16:29 +00007968 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007969 image->depth=16;
7970#endif
7971
glennrp3faa9a32011-04-23 14:00:25 +00007972#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00007973 if (image->depth > 8)
7974 {
7975 /* To do: fill low byte properly */
7976 image->depth=16;
7977 }
7978
glennrpc722dd82011-02-24 05:13:21 +00007979 if (image->depth == 16 && mng_info->write_png_depth != 16)
cristyc82a27b2011-10-21 01:07:16 +00007980 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007981 image->depth = 8;
7982#endif
7983
glennrpc8c2f062011-02-25 19:00:33 +00007984 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007985 * we reduce the transparency to binary and run again, then if there
7986 * 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 +00007987 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7988 * palette. Then (To do) we take care of a final reduction that is only
7989 * needed if there are still 256 colors present and one of them has both
7990 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007991 */
glennrp82b3c532011-03-22 19:20:54 +00007992
glennrp8ca51ad2011-05-12 21:22:32 +00007993 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007994 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007995 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007996
glennrp8ca51ad2011-05-12 21:22:32 +00007997 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007998 {
7999 /* BUILD_PALETTE
8000 *
8001 * Sometimes we get DirectClass images that have 256 colors or fewer.
8002 * This code will build a colormap.
8003 *
8004 * Also, sometimes we get PseudoClass images with an out-of-date
8005 * colormap. This code will replace the colormap with a new one.
8006 * Sometimes we get PseudoClass images that have more than 256 colors.
8007 * This code will delete the colormap and change the image to
8008 * DirectClass.
8009 *
cristy4c08aed2011-07-01 19:47:50 +00008010 * If image->matte is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00008011 * even though it sometimes contains left-over non-opaque values.
8012 *
8013 * Also we gather some information (number of opaque, transparent,
8014 * and semitransparent pixels, and whether the image has any non-gray
8015 * pixels or only black-and-white pixels) that we might need later.
8016 *
8017 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8018 * we need to check for bogus non-opaque values, at least.
8019 */
glennrp3c218112010-11-27 15:31:26 +00008020
glennrpd71e86a2011-02-24 01:28:37 +00008021 int
8022 n;
glennrp3c218112010-11-27 15:31:26 +00008023
cristy101ab702011-10-13 13:06:32 +00008024 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008025 opaque[260],
8026 semitransparent[260],
8027 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00008028
cristy4c08aed2011-07-01 19:47:50 +00008029 register const Quantum
8030 *s;
glennrp8bb3a022010-12-13 20:40:04 +00008031
cristy4c08aed2011-07-01 19:47:50 +00008032 register Quantum
8033 *q,
glennrpfd05d622011-02-25 04:10:33 +00008034 *r;
8035
glennrpd71e86a2011-02-24 01:28:37 +00008036 if (logging != MagickFalse)
8037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8038 " Enter BUILD_PALETTE:");
8039
8040 if (logging != MagickFalse)
8041 {
glennrp03812ae2010-12-24 01:31:34 +00008042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008043 " image->columns=%.20g",(double) image->columns);
8044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8045 " image->rows=%.20g",(double) image->rows);
8046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8047 " image->matte=%.20g",(double) image->matte);
8048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8049 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008050
glennrpfd05d622011-02-25 04:10:33 +00008051 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008052 {
8053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008054 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008056 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008057
glennrpd71e86a2011-02-24 01:28:37 +00008058 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008059 {
glennrpd71e86a2011-02-24 01:28:37 +00008060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8061 " %d (%d,%d,%d,%d)",
8062 (int) i,
8063 (int) image->colormap[i].red,
8064 (int) image->colormap[i].green,
8065 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008066 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008067 }
glennrp2cc891a2010-12-24 13:44:32 +00008068
glennrpd71e86a2011-02-24 01:28:37 +00008069 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8070 {
8071 if (i > 255)
8072 {
8073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8074 " %d (%d,%d,%d,%d)",
8075 (int) i,
8076 (int) image->colormap[i].red,
8077 (int) image->colormap[i].green,
8078 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008079 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008080 }
8081 }
glennrp03812ae2010-12-24 01:31:34 +00008082 }
glennrp7ddcc222010-12-11 05:01:05 +00008083
glennrpd71e86a2011-02-24 01:28:37 +00008084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8085 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008086
glennrpd71e86a2011-02-24 01:28:37 +00008087 if (image->colors == 0)
cristyc458f912011-12-27 20:26:40 +00008088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8089 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008090
glennrp8d3d6e52011-04-19 04:39:51 +00008091 if (ping_preserve_colormap == MagickFalse)
8092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8093 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008094 }
8095
glennrpd71e86a2011-02-24 01:28:37 +00008096 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008097 number_opaque = 0;
8098 number_semitransparent = 0;
8099 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008100
8101 for (y=0; y < (ssize_t) image->rows; y++)
8102 {
8103 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8104
cristyacd2ed22011-08-30 01:44:23 +00008105 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008106 break;
8107
8108 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008109 {
glennrp4737d522011-04-29 03:33:42 +00008110 if (image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008111 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008112 {
8113 if (number_opaque < 259)
8114 {
8115 if (number_opaque == 0)
8116 {
cristy101ab702011-10-13 13:06:32 +00008117 GetPixelInfoPixel(image, q, opaque);
cristy4c08aed2011-07-01 19:47:50 +00008118 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008119 number_opaque=1;
8120 }
glennrp2cc891a2010-12-24 13:44:32 +00008121
glennrpd71e86a2011-02-24 01:28:37 +00008122 for (i=0; i< (ssize_t) number_opaque; i++)
8123 {
cristy4c08aed2011-07-01 19:47:50 +00008124 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008125 break;
8126 }
glennrp7ddcc222010-12-11 05:01:05 +00008127
cristyc458f912011-12-27 20:26:40 +00008128 if (i == (ssize_t) number_opaque && number_opaque < 259)
glennrpd71e86a2011-02-24 01:28:37 +00008129 {
8130 number_opaque++;
cristy101ab702011-10-13 13:06:32 +00008131 GetPixelInfoPixel(image, q, opaque+i);
cristy4c08aed2011-07-01 19:47:50 +00008132 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008133 }
8134 }
8135 }
cristy4c08aed2011-07-01 19:47:50 +00008136 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008137 {
8138 if (number_transparent < 259)
8139 {
8140 if (number_transparent == 0)
8141 {
cristy101ab702011-10-13 13:06:32 +00008142 GetPixelInfoPixel(image, q, transparent);
cristy4c08aed2011-07-01 19:47:50 +00008143 ping_trans_color.red=(unsigned short)
8144 GetPixelRed(image,q);
8145 ping_trans_color.green=(unsigned short)
8146 GetPixelGreen(image,q);
8147 ping_trans_color.blue=(unsigned short)
8148 GetPixelBlue(image,q);
8149 ping_trans_color.gray=(unsigned short)
8150 GetPixelRed(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008151 number_transparent = 1;
8152 }
8153
8154 for (i=0; i< (ssize_t) number_transparent; i++)
8155 {
cristy4c08aed2011-07-01 19:47:50 +00008156 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008157 break;
8158 }
8159
8160 if (i == (ssize_t) number_transparent &&
8161 number_transparent < 259)
8162 {
8163 number_transparent++;
cristy101ab702011-10-13 13:06:32 +00008164 GetPixelInfoPixel(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008165 }
8166 }
8167 }
8168 else
8169 {
8170 if (number_semitransparent < 259)
8171 {
8172 if (number_semitransparent == 0)
8173 {
cristy101ab702011-10-13 13:06:32 +00008174 GetPixelInfoPixel(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008175 number_semitransparent = 1;
8176 }
8177
8178 for (i=0; i< (ssize_t) number_semitransparent; i++)
8179 {
cristy4c08aed2011-07-01 19:47:50 +00008180 if (IsPixelEquivalent(image,q, semitransparent+i)
8181 && GetPixelAlpha(image,q) ==
8182 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008183 break;
8184 }
8185
8186 if (i == (ssize_t) number_semitransparent &&
8187 number_semitransparent < 259)
8188 {
8189 number_semitransparent++;
cristy101ab702011-10-13 13:06:32 +00008190 GetPixelInfoPixel(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008191 }
8192 }
8193 }
cristyed231572011-07-14 02:18:59 +00008194 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008195 }
8196 }
8197
cristy4054bfb2011-08-29 23:41:39 +00008198 if (mng_info->write_png8 == MagickFalse &&
8199 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008200 {
8201 /* Add the background color to the palette, if it
8202 * isn't already there.
8203 */
glennrpc6c391a2011-04-27 02:23:56 +00008204 if (logging != MagickFalse)
8205 {
8206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8207 " Check colormap for background (%d,%d,%d)",
8208 (int) image->background_color.red,
8209 (int) image->background_color.green,
8210 (int) image->background_color.blue);
8211 }
glennrpd71e86a2011-02-24 01:28:37 +00008212 for (i=0; i<number_opaque; i++)
8213 {
glennrpca7ad3a2011-04-26 04:44:54 +00008214 if (opaque[i].red == image->background_color.red &&
8215 opaque[i].green == image->background_color.green &&
8216 opaque[i].blue == image->background_color.blue)
8217 break;
glennrpd71e86a2011-02-24 01:28:37 +00008218 }
glennrpd71e86a2011-02-24 01:28:37 +00008219 if (number_opaque < 259 && i == number_opaque)
8220 {
glennrp8e045c82011-04-27 16:40:27 +00008221 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008222 ping_background.index = i;
8223 if (logging != MagickFalse)
8224 {
8225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8226 " background_color index is %d",(int) i);
8227 }
8228
glennrpd71e86a2011-02-24 01:28:37 +00008229 }
glennrpa080bc32011-03-11 18:03:44 +00008230 else if (logging != MagickFalse)
8231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8232 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008233 }
8234
8235 image_colors=number_opaque+number_transparent+number_semitransparent;
8236
glennrpa080bc32011-03-11 18:03:44 +00008237 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8238 {
8239 /* No room for the background color; remove it. */
8240 number_opaque--;
8241 image_colors--;
8242 }
8243
glennrpd71e86a2011-02-24 01:28:37 +00008244 if (logging != MagickFalse)
8245 {
8246 if (image_colors > 256)
8247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8248 " image has more than 256 colors");
8249
8250 else
8251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8252 " image has %d colors",image_colors);
8253 }
8254
glennrp8d3d6e52011-04-19 04:39:51 +00008255 if (ping_preserve_colormap != MagickFalse)
8256 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008257
glennrpfd05d622011-02-25 04:10:33 +00008258 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008259 {
8260 ping_have_color=MagickFalse;
8261 ping_have_non_bw=MagickFalse;
8262
8263 if(image_colors > 256)
8264 {
8265 for (y=0; y < (ssize_t) image->rows; y++)
8266 {
8267 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8268
cristyacd2ed22011-08-30 01:44:23 +00008269 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008270 break;
8271
glennrpe5e6b802011-07-20 14:44:40 +00008272 s=q;
8273 for (x=0; x < (ssize_t) image->columns; x++)
8274 {
8275 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8276 GetPixelRed(image,s) != GetPixelBlue(image,s))
8277 {
8278 ping_have_color=MagickTrue;
8279 ping_have_non_bw=MagickTrue;
8280 break;
8281 }
8282 s+=GetPixelChannels(image);
8283 }
8284
8285 if (ping_have_color != MagickFalse)
8286 break;
8287
glennrpd71e86a2011-02-24 01:28:37 +00008288 /* Worst case is black-and-white; we are looking at every
8289 * pixel twice.
8290 */
8291
glennrpd71e86a2011-02-24 01:28:37 +00008292 if (ping_have_non_bw == MagickFalse)
8293 {
8294 s=q;
8295 for (x=0; x < (ssize_t) image->columns; x++)
8296 {
cristy4c08aed2011-07-01 19:47:50 +00008297 if (GetPixelRed(image,s) != 0 &&
8298 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008299 {
8300 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008301 break;
glennrpd71e86a2011-02-24 01:28:37 +00008302 }
cristyed231572011-07-14 02:18:59 +00008303 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008304 }
glennrpe5e6b802011-07-20 14:44:40 +00008305 }
glennrpd71e86a2011-02-24 01:28:37 +00008306 }
glennrpbb4f99d2011-05-22 11:13:17 +00008307 }
8308 }
glennrpd71e86a2011-02-24 01:28:37 +00008309
8310 if (image_colors < 257)
8311 {
cristy101ab702011-10-13 13:06:32 +00008312 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008313 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008314
glennrpd71e86a2011-02-24 01:28:37 +00008315 /*
8316 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008317 */
8318
glennrpd71e86a2011-02-24 01:28:37 +00008319 if (logging != MagickFalse)
8320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8321 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008322
glennrpd71e86a2011-02-24 01:28:37 +00008323 /* Sort palette, transparent first */;
8324
8325 n = 0;
8326
8327 for (i=0; i<number_transparent; i++)
8328 colormap[n++] = transparent[i];
8329
8330 for (i=0; i<number_semitransparent; i++)
8331 colormap[n++] = semitransparent[i];
8332
8333 for (i=0; i<number_opaque; i++)
8334 colormap[n++] = opaque[i];
8335
glennrpc6c391a2011-04-27 02:23:56 +00008336 ping_background.index +=
8337 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008338
glennrpd71e86a2011-02-24 01:28:37 +00008339 /* image_colors < 257; search the colormap instead of the pixels
8340 * to get ping_have_color and ping_have_non_bw
8341 */
8342 for (i=0; i<n; i++)
8343 {
8344 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008345 {
glennrpd71e86a2011-02-24 01:28:37 +00008346 if (colormap[i].red != colormap[i].green ||
8347 colormap[i].red != colormap[i].blue)
8348 {
8349 ping_have_color=MagickTrue;
8350 ping_have_non_bw=MagickTrue;
8351 break;
8352 }
8353 }
8354
8355 if (ping_have_non_bw == MagickFalse)
8356 {
8357 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008358 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008359 }
glennrp8bb3a022010-12-13 20:40:04 +00008360 }
8361
glennrpd71e86a2011-02-24 01:28:37 +00008362 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8363 (number_transparent == 0 && number_semitransparent == 0)) &&
8364 (((mng_info->write_png_colortype-1) ==
8365 PNG_COLOR_TYPE_PALETTE) ||
8366 (mng_info->write_png_colortype == 0)))
8367 {
glennrp6185c532011-01-14 17:58:40 +00008368 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008369 {
glennrpd71e86a2011-02-24 01:28:37 +00008370 if (n != (ssize_t) image_colors)
8371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8372 " image_colors (%d) and n (%d) don't match",
8373 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008374
glennrpd71e86a2011-02-24 01:28:37 +00008375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8376 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008377 }
glennrp03812ae2010-12-24 01:31:34 +00008378
glennrpd71e86a2011-02-24 01:28:37 +00008379 image->colors = image_colors;
8380
cristy018f07f2011-09-04 21:15:19 +00008381 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008382 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008383 ThrowWriterException(ResourceLimitError,
8384 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008385
8386 for (i=0; i< (ssize_t) image_colors; i++)
8387 image->colormap[i] = colormap[i];
8388
8389 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008390 {
glennrpd71e86a2011-02-24 01:28:37 +00008391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8392 " image->colors=%d (%d)",
8393 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008394
glennrpd71e86a2011-02-24 01:28:37 +00008395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8396 " Update the pixel indexes");
8397 }
glennrp6185c532011-01-14 17:58:40 +00008398
glennrpfd05d622011-02-25 04:10:33 +00008399 /* Sync the pixel indices with the new colormap */
8400
glennrpd71e86a2011-02-24 01:28:37 +00008401 for (y=0; y < (ssize_t) image->rows; y++)
8402 {
cristy97707062011-12-27 18:25:00 +00008403 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp6185c532011-01-14 17:58:40 +00008404
cristyacd2ed22011-08-30 01:44:23 +00008405 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008406 break;
glennrp6185c532011-01-14 17:58:40 +00008407
glennrpd71e86a2011-02-24 01:28:37 +00008408 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008409 {
glennrpd71e86a2011-02-24 01:28:37 +00008410 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008411 {
glennrpd71e86a2011-02-24 01:28:37 +00008412 if ((image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008413 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8414 image->colormap[i].red == GetPixelRed(image,q) &&
8415 image->colormap[i].green == GetPixelGreen(image,q) &&
8416 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008417 {
cristy4c08aed2011-07-01 19:47:50 +00008418 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008419 break;
glennrp6185c532011-01-14 17:58:40 +00008420 }
glennrp6185c532011-01-14 17:58:40 +00008421 }
cristyed231572011-07-14 02:18:59 +00008422 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008423 }
glennrp6185c532011-01-14 17:58:40 +00008424
glennrpd71e86a2011-02-24 01:28:37 +00008425 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8426 break;
8427 }
8428 }
8429 }
8430
8431 if (logging != MagickFalse)
8432 {
8433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8434 " image->colors=%d", (int) image->colors);
8435
8436 if (image->colormap != NULL)
8437 {
8438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008439 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008440
8441 for (i=0; i < (ssize_t) image->colors; i++)
8442 {
cristy72988482011-03-29 16:34:38 +00008443 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008444 {
8445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8446 " %d (%d,%d,%d,%d)",
8447 (int) i,
8448 (int) image->colormap[i].red,
8449 (int) image->colormap[i].green,
8450 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008451 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008452 }
glennrp6185c532011-01-14 17:58:40 +00008453 }
8454 }
glennrp03812ae2010-12-24 01:31:34 +00008455
glennrpd71e86a2011-02-24 01:28:37 +00008456 if (number_transparent < 257)
8457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8458 " number_transparent = %d",
8459 number_transparent);
8460 else
glennrp03812ae2010-12-24 01:31:34 +00008461
glennrpd71e86a2011-02-24 01:28:37 +00008462 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8463 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008464
glennrpd71e86a2011-02-24 01:28:37 +00008465 if (number_opaque < 257)
8466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8467 " number_opaque = %d",
8468 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008469
glennrpd71e86a2011-02-24 01:28:37 +00008470 else
8471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8472 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008473
glennrpd71e86a2011-02-24 01:28:37 +00008474 if (number_semitransparent < 257)
8475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8476 " number_semitransparent = %d",
8477 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008478
glennrpd71e86a2011-02-24 01:28:37 +00008479 else
8480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8481 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008482
glennrpd71e86a2011-02-24 01:28:37 +00008483 if (ping_have_non_bw == MagickFalse)
8484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8485 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008486
glennrpd71e86a2011-02-24 01:28:37 +00008487 else if (ping_have_color == MagickFalse)
8488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8489 " All pixels and the background are gray");
8490
8491 else
8492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8493 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008494
glennrp03812ae2010-12-24 01:31:34 +00008495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8496 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008497 }
glennrpfd05d622011-02-25 04:10:33 +00008498
glennrpc8c2f062011-02-25 19:00:33 +00008499 if (mng_info->write_png8 == MagickFalse)
8500 break;
glennrpfd05d622011-02-25 04:10:33 +00008501
glennrpc8c2f062011-02-25 19:00:33 +00008502 /* Make any reductions necessary for the PNG8 format */
8503 if (image_colors <= 256 &&
8504 image_colors != 0 && image->colormap != NULL &&
8505 number_semitransparent == 0 &&
8506 number_transparent <= 1)
8507 break;
8508
8509 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008510 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8511 * transparent color so if more than one is transparent we merge
8512 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008513 */
glennrp130fc452011-08-20 03:43:18 +00008514 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008515 {
8516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8517 " Thresholding the alpha channel to binary");
8518
8519 for (y=0; y < (ssize_t) image->rows; y++)
8520 {
cristy8a20fa02011-12-27 15:54:31 +00008521 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008522
cristy4c08aed2011-07-01 19:47:50 +00008523 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008524 break;
8525
8526 for (x=0; x < (ssize_t) image->columns; x++)
8527 {
glennrpf73547f2011-08-20 04:40:26 +00008528 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008529 {
cristy803640d2011-11-17 02:11:32 +00008530 SetPixelInfoPixel(image,&image->background_color,r);
cristy4c08aed2011-07-01 19:47:50 +00008531 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008532 }
8533 else
cristy4c08aed2011-07-01 19:47:50 +00008534 SetPixelAlpha(image,OpaqueAlpha,r);
cristyed231572011-07-14 02:18:59 +00008535 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008536 }
glennrpbb4f99d2011-05-22 11:13:17 +00008537
glennrpc8c2f062011-02-25 19:00:33 +00008538 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8539 break;
8540
8541 if (image_colors != 0 && image_colors <= 256 &&
8542 image->colormap != NULL)
8543 for (i=0; i<image_colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00008544 image->colormap[i].alpha =
8545 (image->colormap[i].alpha > TransparentAlpha/2 ?
8546 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008547 }
8548 continue;
8549 }
8550
8551 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008552 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8553 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8554 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008555 */
glennrpd3371642011-03-22 19:42:23 +00008556 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8557 {
8558 if (logging != MagickFalse)
8559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8560 " Quantizing the background color to 4-4-4");
8561
8562 tried_444 = MagickTrue;
8563
glennrp91d99252011-06-25 14:30:13 +00008564 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008565
8566 if (logging != MagickFalse)
8567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8568 " Quantizing the pixel colors to 4-4-4");
8569
8570 if (image->colormap == NULL)
8571 {
8572 for (y=0; y < (ssize_t) image->rows; y++)
8573 {
cristy8a20fa02011-12-27 15:54:31 +00008574 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpd3371642011-03-22 19:42:23 +00008575
cristy4c08aed2011-07-01 19:47:50 +00008576 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008577 break;
8578
8579 for (x=0; x < (ssize_t) image->columns; x++)
8580 {
cristy4c08aed2011-07-01 19:47:50 +00008581 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008582 LBR04PixelRGB(r);
cristy8a20fa02011-12-27 15:54:31 +00008583 r+=GetPixelChannels(image);
glennrpd3371642011-03-22 19:42:23 +00008584 }
glennrpbb4f99d2011-05-22 11:13:17 +00008585
glennrpd3371642011-03-22 19:42:23 +00008586 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8587 break;
8588 }
8589 }
8590
8591 else /* Should not reach this; colormap already exists and
8592 must be <= 256 */
8593 {
8594 if (logging != MagickFalse)
8595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8596 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008597
glennrpd3371642011-03-22 19:42:23 +00008598 for (i=0; i<image_colors; i++)
8599 {
glennrp91d99252011-06-25 14:30:13 +00008600 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008601 }
8602 }
8603 continue;
8604 }
8605
glennrp82b3c532011-03-22 19:20:54 +00008606 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8607 {
8608 if (logging != MagickFalse)
8609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8610 " Quantizing the background color to 3-3-3");
8611
8612 tried_333 = MagickTrue;
8613
glennrp91d99252011-06-25 14:30:13 +00008614 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008615
8616 if (logging != MagickFalse)
8617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008618 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008619
8620 if (image->colormap == NULL)
8621 {
8622 for (y=0; y < (ssize_t) image->rows; y++)
8623 {
cristy8a20fa02011-12-27 15:54:31 +00008624 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp82b3c532011-03-22 19:20:54 +00008625
cristy4c08aed2011-07-01 19:47:50 +00008626 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008627 break;
8628
8629 for (x=0; x < (ssize_t) image->columns; x++)
8630 {
cristy4c08aed2011-07-01 19:47:50 +00008631 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8632 LBR03RGB(r);
cristy8a20fa02011-12-27 15:54:31 +00008633 r+=GetPixelChannels(image);
glennrp82b3c532011-03-22 19:20:54 +00008634 }
glennrpbb4f99d2011-05-22 11:13:17 +00008635
glennrp82b3c532011-03-22 19:20:54 +00008636 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8637 break;
8638 }
8639 }
8640
8641 else /* Should not reach this; colormap already exists and
8642 must be <= 256 */
8643 {
8644 if (logging != MagickFalse)
8645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008646 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008647 for (i=0; i<image_colors; i++)
8648 {
glennrp91d99252011-06-25 14:30:13 +00008649 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008650 }
glennrpd3371642011-03-22 19:42:23 +00008651 }
8652 continue;
glennrp82b3c532011-03-22 19:20:54 +00008653 }
glennrpc8c2f062011-02-25 19:00:33 +00008654
glennrp8ca51ad2011-05-12 21:22:32 +00008655 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008656 {
8657 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008659 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008660
glennrp8ca51ad2011-05-12 21:22:32 +00008661 tried_332 = MagickTrue;
8662
glennrp3faa9a32011-04-23 14:00:25 +00008663 /* Red and green were already done so we only quantize the blue
8664 * channel
8665 */
8666
glennrp91d99252011-06-25 14:30:13 +00008667 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008668
glennrpc8c2f062011-02-25 19:00:33 +00008669 if (logging != MagickFalse)
8670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008671 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008672
glennrpc8c2f062011-02-25 19:00:33 +00008673 if (image->colormap == NULL)
8674 {
8675 for (y=0; y < (ssize_t) image->rows; y++)
8676 {
cristy8a20fa02011-12-27 15:54:31 +00008677 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008678
cristy4c08aed2011-07-01 19:47:50 +00008679 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008680 break;
8681
8682 for (x=0; x < (ssize_t) image->columns; x++)
8683 {
cristy4c08aed2011-07-01 19:47:50 +00008684 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008685 LBR02PixelBlue(r);
cristy8a20fa02011-12-27 15:54:31 +00008686 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008687 }
glennrpbb4f99d2011-05-22 11:13:17 +00008688
glennrpc8c2f062011-02-25 19:00:33 +00008689 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8690 break;
8691 }
8692 }
glennrpfd05d622011-02-25 04:10:33 +00008693
glennrpc8c2f062011-02-25 19:00:33 +00008694 else /* Should not reach this; colormap already exists and
8695 must be <= 256 */
8696 {
8697 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008699 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008700 for (i=0; i<image_colors; i++)
8701 {
glennrp91d99252011-06-25 14:30:13 +00008702 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008703 }
8704 }
8705 continue;
8706 }
8707 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008708
8709 if (image_colors == 0 || image_colors > 256)
8710 {
8711 /* Take care of special case with 256 colors + 1 transparent
8712 * color. We don't need to quantize to 2-3-2-1; we only need to
8713 * eliminate one color, so we'll merge the two darkest red
8714 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8715 */
8716 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8717 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8718 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8719 {
8720 image->background_color.red=ScaleCharToQuantum(0x24);
8721 }
glennrpbb4f99d2011-05-22 11:13:17 +00008722
glennrp8ca51ad2011-05-12 21:22:32 +00008723 if (image->colormap == NULL)
8724 {
8725 for (y=0; y < (ssize_t) image->rows; y++)
8726 {
cristy8a20fa02011-12-27 15:54:31 +00008727 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008728
cristy4c08aed2011-07-01 19:47:50 +00008729 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008730 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008731
glennrp8ca51ad2011-05-12 21:22:32 +00008732 for (x=0; x < (ssize_t) image->columns; x++)
8733 {
cristy4c08aed2011-07-01 19:47:50 +00008734 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8735 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8736 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8737 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008738 {
cristy4c08aed2011-07-01 19:47:50 +00008739 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008740 }
cristyed231572011-07-14 02:18:59 +00008741 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008742 }
glennrpbb4f99d2011-05-22 11:13:17 +00008743
glennrp8ca51ad2011-05-12 21:22:32 +00008744 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8745 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008746
glennrp8ca51ad2011-05-12 21:22:32 +00008747 }
8748 }
8749
8750 else
8751 {
8752 for (i=0; i<image_colors; i++)
8753 {
8754 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8755 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8756 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8757 {
8758 image->colormap[i].red=ScaleCharToQuantum(0x24);
8759 }
8760 }
8761 }
8762 }
glennrpd71e86a2011-02-24 01:28:37 +00008763 }
glennrpfd05d622011-02-25 04:10:33 +00008764 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008765
glennrpfd05d622011-02-25 04:10:33 +00008766 /* If we are excluding the tRNS chunk and there is transparency,
8767 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8768 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008769 */
glennrp0e8ea192010-12-24 18:00:33 +00008770 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8771 (number_transparent != 0 || number_semitransparent != 0))
8772 {
glennrpd17915c2011-04-29 14:24:22 +00008773 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008774
8775 if (ping_have_color == MagickFalse)
8776 mng_info->write_png_colortype = 5;
8777
8778 else
8779 mng_info->write_png_colortype = 7;
8780
glennrp8d579662011-02-23 02:05:02 +00008781 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008782 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008783 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008784
glennrp0e8ea192010-12-24 18:00:33 +00008785 }
8786
glennrpfd05d622011-02-25 04:10:33 +00008787 /* See if cheap transparency is possible. It is only possible
8788 * when there is a single transparent color, no semitransparent
8789 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008790 * as the transparent color. We only need this information if
8791 * we are writing a PNG with colortype 0 or 2, and we have not
8792 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008793 */
glennrp5a39f372011-02-25 04:52:16 +00008794 if (number_transparent == 1 &&
8795 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008796 {
8797 ping_have_cheap_transparency = MagickTrue;
8798
8799 if (number_semitransparent != 0)
8800 ping_have_cheap_transparency = MagickFalse;
8801
8802 else if (image_colors == 0 || image_colors > 256 ||
8803 image->colormap == NULL)
8804 {
cristy4c08aed2011-07-01 19:47:50 +00008805 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008806 *q;
8807
glennrpfd05d622011-02-25 04:10:33 +00008808 for (y=0; y < (ssize_t) image->rows; y++)
8809 {
8810 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8811
cristyacd2ed22011-08-30 01:44:23 +00008812 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008813 break;
8814
8815 for (x=0; x < (ssize_t) image->columns; x++)
8816 {
cristy4c08aed2011-07-01 19:47:50 +00008817 if (GetPixelAlpha(image,q) != TransparentAlpha &&
glennrp847370c2011-07-05 17:37:15 +00008818 (unsigned short) GetPixelRed(image,q) ==
8819 ping_trans_color.red &&
8820 (unsigned short) GetPixelGreen(image,q) ==
8821 ping_trans_color.green &&
8822 (unsigned short) GetPixelBlue(image,q) ==
8823 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008824 {
8825 ping_have_cheap_transparency = MagickFalse;
8826 break;
8827 }
8828
cristyed231572011-07-14 02:18:59 +00008829 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008830 }
glennrpbb4f99d2011-05-22 11:13:17 +00008831
glennrpfd05d622011-02-25 04:10:33 +00008832 if (ping_have_cheap_transparency == MagickFalse)
8833 break;
8834 }
8835 }
8836 else
8837 {
glennrp67b9c1a2011-04-22 18:47:36 +00008838 /* Assuming that image->colormap[0] is the one transparent color
8839 * and that all others are opaque.
8840 */
glennrpfd05d622011-02-25 04:10:33 +00008841 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008842 for (i=1; i<image_colors; i++)
8843 if (image->colormap[i].red == image->colormap[0].red &&
8844 image->colormap[i].green == image->colormap[0].green &&
8845 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008846 {
glennrp67b9c1a2011-04-22 18:47:36 +00008847 ping_have_cheap_transparency = MagickFalse;
8848 break;
glennrpfd05d622011-02-25 04:10:33 +00008849 }
8850 }
glennrpbb4f99d2011-05-22 11:13:17 +00008851
glennrpfd05d622011-02-25 04:10:33 +00008852 if (logging != MagickFalse)
8853 {
8854 if (ping_have_cheap_transparency == MagickFalse)
8855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8856 " Cheap transparency is not possible.");
8857
8858 else
8859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8860 " Cheap transparency is possible.");
8861 }
8862 }
8863 else
8864 ping_have_cheap_transparency = MagickFalse;
8865
glennrp8640fb52010-11-23 15:48:26 +00008866 image_depth=image->depth;
8867
glennrp26c990a2010-11-23 02:23:20 +00008868 quantum_info = (QuantumInfo *) NULL;
8869 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008870 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008871 image_matte=image->matte;
8872
glennrp0fe50b42010-11-16 03:52:51 +00008873 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008874 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008875
glennrp52a479c2011-02-26 21:14:38 +00008876 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8877 (image->colors == 0 || image->colormap == NULL))
8878 {
glennrp52a479c2011-02-26 21:14:38 +00008879 image_info=DestroyImageInfo(image_info);
8880 image=DestroyImage(image);
cristyc82a27b2011-10-21 01:07:16 +00008881 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
glennrp15e01552011-03-06 23:29:17 +00008882 "Cannot write PNG8 or color-type 3; colormap is NULL",
8883 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008884#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8885 UnlockSemaphoreInfo(ping_semaphore);
8886#endif
8887 return(MagickFalse);
8888 }
8889
cristy3ed852e2009-09-05 21:47:34 +00008890 /*
8891 Allocate the PNG structures
8892 */
8893#ifdef PNG_USER_MEM_SUPPORTED
cristyc82a27b2011-10-21 01:07:16 +00008894 error_info.image=image;
8895 error_info.exception=exception;
8896 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008897 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8898 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008899
cristy3ed852e2009-09-05 21:47:34 +00008900#else
cristyc82a27b2011-10-21 01:07:16 +00008901 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008902 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008903
cristy3ed852e2009-09-05 21:47:34 +00008904#endif
8905 if (ping == (png_struct *) NULL)
8906 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008907
cristy3ed852e2009-09-05 21:47:34 +00008908 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008909
cristy3ed852e2009-09-05 21:47:34 +00008910 if (ping_info == (png_info *) NULL)
8911 {
8912 png_destroy_write_struct(&ping,(png_info **) NULL);
8913 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8914 }
glennrp0fe50b42010-11-16 03:52:51 +00008915
cristy3ed852e2009-09-05 21:47:34 +00008916 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008917 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008918
glennrp5af765f2010-03-30 11:12:18 +00008919 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008920 {
8921 /*
8922 PNG write failed.
8923 */
8924#ifdef PNG_DEBUG
8925 if (image_info->verbose)
8926 (void) printf("PNG write has failed.\n");
8927#endif
8928 png_destroy_write_struct(&ping,&ping_info);
8929#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008930 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008931#endif
glennrpda8f3a72011-02-27 23:54:12 +00008932 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008933 (void) CloseBlob(image);
8934 image_info=DestroyImageInfo(image_info);
8935 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008936 return(MagickFalse);
8937 }
8938 /*
8939 Prepare PNG for writing.
8940 */
8941#if defined(PNG_MNG_FEATURES_SUPPORTED)
8942 if (mng_info->write_mng)
8943 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008944
cristy3ed852e2009-09-05 21:47:34 +00008945#else
8946# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8947 if (mng_info->write_mng)
8948 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008949
cristy3ed852e2009-09-05 21:47:34 +00008950# endif
8951#endif
glennrp2b013e42010-11-24 16:55:50 +00008952
cristy3ed852e2009-09-05 21:47:34 +00008953 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008954
cristy4e5bc842010-06-09 13:56:01 +00008955 ping_width=(png_uint_32) image->columns;
8956 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008957
cristy3ed852e2009-09-05 21:47:34 +00008958 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8959 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008960
cristy3ed852e2009-09-05 21:47:34 +00008961 if (mng_info->write_png_depth != 0)
8962 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008963
cristy3ed852e2009-09-05 21:47:34 +00008964 /* Adjust requested depth to next higher valid depth if necessary */
8965 if (image_depth > 8)
8966 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008967
cristy3ed852e2009-09-05 21:47:34 +00008968 if ((image_depth > 4) && (image_depth < 8))
8969 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008970
cristy3ed852e2009-09-05 21:47:34 +00008971 if (image_depth == 3)
8972 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008973
cristy3ed852e2009-09-05 21:47:34 +00008974 if (logging != MagickFalse)
8975 {
8976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008977 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008979 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008981 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008983 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008985 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008986 }
glennrp8640fb52010-11-23 15:48:26 +00008987
cristy3ed852e2009-09-05 21:47:34 +00008988 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008989 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008990
glennrp26f37912010-12-23 16:22:42 +00008991
cristy3ed852e2009-09-05 21:47:34 +00008992#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008993 if (ping_exclude_pHYs == MagickFalse)
8994 {
cristy2a11bef2011-10-28 18:33:11 +00008995 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00008996 (!mng_info->write_mng || !mng_info->equal_physs))
8997 {
glennrp0fe50b42010-11-16 03:52:51 +00008998 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9000 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00009001
9002 if (image->units == PixelsPerInchResolution)
9003 {
glennrpdfd70802010-11-14 01:23:35 +00009004 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00009005 ping_pHYs_x_resolution=
cristy2a11bef2011-10-28 18:33:11 +00009006 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
glennrp823b55c2011-03-14 18:46:46 +00009007 ping_pHYs_y_resolution=
cristy2a11bef2011-10-28 18:33:11 +00009008 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00009009 }
glennrpdfd70802010-11-14 01:23:35 +00009010
cristy3ed852e2009-09-05 21:47:34 +00009011 else if (image->units == PixelsPerCentimeterResolution)
9012 {
glennrpdfd70802010-11-14 01:23:35 +00009013 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
cristy2a11bef2011-10-28 18:33:11 +00009014 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9015 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00009016 }
glennrp991d11d2010-11-12 21:55:28 +00009017
cristy3ed852e2009-09-05 21:47:34 +00009018 else
9019 {
glennrpdfd70802010-11-14 01:23:35 +00009020 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
cristy2a11bef2011-10-28 18:33:11 +00009021 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9022 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00009023 }
glennrp991d11d2010-11-12 21:55:28 +00009024
glennrp823b55c2011-03-14 18:46:46 +00009025 if (logging != MagickFalse)
9026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9027 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9028 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9029 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009030 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009031 }
glennrp26f37912010-12-23 16:22:42 +00009032 }
cristy3ed852e2009-09-05 21:47:34 +00009033#endif
glennrpa521b2f2010-10-29 04:11:03 +00009034
glennrp26f37912010-12-23 16:22:42 +00009035 if (ping_exclude_bKGD == MagickFalse)
9036 {
glennrpa521b2f2010-10-29 04:11:03 +00009037 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009038 {
glennrpa521b2f2010-10-29 04:11:03 +00009039 unsigned int
9040 mask;
cristy3ed852e2009-09-05 21:47:34 +00009041
glennrpa521b2f2010-10-29 04:11:03 +00009042 mask=0xffff;
9043 if (ping_bit_depth == 8)
9044 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009045
glennrpa521b2f2010-10-29 04:11:03 +00009046 if (ping_bit_depth == 4)
9047 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009048
glennrpa521b2f2010-10-29 04:11:03 +00009049 if (ping_bit_depth == 2)
9050 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009051
glennrpa521b2f2010-10-29 04:11:03 +00009052 if (ping_bit_depth == 1)
9053 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009054
glennrpa521b2f2010-10-29 04:11:03 +00009055 ping_background.red=(png_uint_16)
9056 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009057
glennrpa521b2f2010-10-29 04:11:03 +00009058 ping_background.green=(png_uint_16)
9059 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009060
glennrpa521b2f2010-10-29 04:11:03 +00009061 ping_background.blue=(png_uint_16)
9062 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009063
9064 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009065 }
cristy3ed852e2009-09-05 21:47:34 +00009066
glennrp0fe50b42010-11-16 03:52:51 +00009067 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009068 {
9069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9070 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9072 " background_color index is %d",
9073 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009074
9075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9076 " ping_bit_depth=%d",ping_bit_depth);
9077 }
glennrp0fe50b42010-11-16 03:52:51 +00009078
9079 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009080 }
glennrp0fe50b42010-11-16 03:52:51 +00009081
cristy3ed852e2009-09-05 21:47:34 +00009082 /*
9083 Select the color type.
9084 */
9085 matte=image_matte;
9086 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009087
glennrp1273f7b2011-02-24 03:20:30 +00009088 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009089 {
glennrp0fe50b42010-11-16 03:52:51 +00009090
glennrpfd05d622011-02-25 04:10:33 +00009091 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009092 for reducing the sample depth from 8. */
9093
glennrp0fe50b42010-11-16 03:52:51 +00009094 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009095
glennrp8bb3a022010-12-13 20:40:04 +00009096 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009097
9098 /*
9099 Set image palette.
9100 */
9101 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9102
glennrp0fe50b42010-11-16 03:52:51 +00009103 if (logging != MagickFalse)
9104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9105 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009106 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009107
9108 for (i=0; i < (ssize_t) number_colors; i++)
9109 {
9110 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9111 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9112 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9113 if (logging != MagickFalse)
9114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009115#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009116 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009117#else
9118 " %5ld (%5d,%5d,%5d)",
9119#endif
glennrp0fe50b42010-11-16 03:52:51 +00009120 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9121
9122 }
glennrp2b013e42010-11-24 16:55:50 +00009123
glennrp8bb3a022010-12-13 20:40:04 +00009124 ping_have_PLTE=MagickTrue;
9125 image_depth=ping_bit_depth;
9126 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009127
glennrp58e01762011-01-07 15:28:54 +00009128 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009129 {
glennrp0fe50b42010-11-16 03:52:51 +00009130 /*
9131 Identify which colormap entry is transparent.
9132 */
9133 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009134 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009135
glennrp8bb3a022010-12-13 20:40:04 +00009136 for (i=0; i < (ssize_t) number_transparent; i++)
9137 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009138
glennrp0fe50b42010-11-16 03:52:51 +00009139
glennrp2cc891a2010-12-24 13:44:32 +00009140 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009141 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009142
9143 if (ping_num_trans == 0)
9144 ping_have_tRNS=MagickFalse;
9145
glennrp8bb3a022010-12-13 20:40:04 +00009146 else
9147 ping_have_tRNS=MagickTrue;
9148 }
glennrp0fe50b42010-11-16 03:52:51 +00009149
glennrp1273f7b2011-02-24 03:20:30 +00009150 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009151 {
glennrp1273f7b2011-02-24 03:20:30 +00009152 /*
9153 * Identify which colormap entry is the background color.
9154 */
9155
glennrp4f25bd02011-01-01 18:51:28 +00009156 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9157 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9158 break;
glennrp0fe50b42010-11-16 03:52:51 +00009159
glennrp4f25bd02011-01-01 18:51:28 +00009160 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009161
9162 if (logging != MagickFalse)
9163 {
9164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9165 " background_color index is %d",
9166 (int) ping_background.index);
9167 }
glennrp4f25bd02011-01-01 18:51:28 +00009168 }
cristy3ed852e2009-09-05 21:47:34 +00009169 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009170
glennrp7e65e932011-08-19 02:31:16 +00009171 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009172 {
9173 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009174 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009175 }
glennrp0fe50b42010-11-16 03:52:51 +00009176
glennrp7e65e932011-08-19 02:31:16 +00009177 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009178 {
9179 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009180 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009181 }
glennrp0fe50b42010-11-16 03:52:51 +00009182
glennrp8bb3a022010-12-13 20:40:04 +00009183 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009184 {
glennrp5af765f2010-03-30 11:12:18 +00009185 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009186
glennrp8bb3a022010-12-13 20:40:04 +00009187 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009188 {
glennrp5af765f2010-03-30 11:12:18 +00009189 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009190
glennrp5af765f2010-03-30 11:12:18 +00009191 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9192 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009193 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009194
glennrp8bb3a022010-12-13 20:40:04 +00009195 else
9196 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009197
9198 if (logging != MagickFalse)
9199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9200 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009201 }
glennrp0fe50b42010-11-16 03:52:51 +00009202
glennrp7c4c9e62011-03-21 20:23:32 +00009203 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009204 {
9205 if (logging != MagickFalse)
9206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009207 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009208
glennrpd6bf1612010-12-17 17:28:54 +00009209 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009210 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009211
glennrpd6bf1612010-12-17 17:28:54 +00009212 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009213 {
glennrp5af765f2010-03-30 11:12:18 +00009214 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009215 image_matte=MagickFalse;
9216 }
glennrp0fe50b42010-11-16 03:52:51 +00009217
glennrpd6bf1612010-12-17 17:28:54 +00009218 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009219 {
glennrp5af765f2010-03-30 11:12:18 +00009220 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009221 image_matte=MagickTrue;
9222 }
glennrp0fe50b42010-11-16 03:52:51 +00009223
glennrp5aa37f62011-01-02 03:07:57 +00009224 if (image_info->type == PaletteType ||
9225 image_info->type == PaletteMatteType)
9226 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9227
glennrp7c4c9e62011-03-21 20:23:32 +00009228 if (mng_info->write_png_colortype == 0 &&
9229 (image_info->type == UndefinedType ||
9230 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009231 {
glennrp5aa37f62011-01-02 03:07:57 +00009232 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009233 {
glennrp5aa37f62011-01-02 03:07:57 +00009234 if (image_matte == MagickFalse)
9235 {
9236 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9237 image_matte=MagickFalse;
9238 }
glennrp0fe50b42010-11-16 03:52:51 +00009239
glennrp0b206f52011-01-07 04:55:32 +00009240 else
glennrp5aa37f62011-01-02 03:07:57 +00009241 {
9242 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9243 image_matte=MagickTrue;
9244 }
9245 }
9246 else
glennrp8bb3a022010-12-13 20:40:04 +00009247 {
glennrp5aa37f62011-01-02 03:07:57 +00009248 if (image_matte == MagickFalse)
9249 {
9250 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9251 image_matte=MagickFalse;
9252 }
glennrp8bb3a022010-12-13 20:40:04 +00009253
glennrp0b206f52011-01-07 04:55:32 +00009254 else
glennrp5aa37f62011-01-02 03:07:57 +00009255 {
9256 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9257 image_matte=MagickTrue;
9258 }
9259 }
glennrp0fe50b42010-11-16 03:52:51 +00009260 }
glennrp5aa37f62011-01-02 03:07:57 +00009261
cristy3ed852e2009-09-05 21:47:34 +00009262 }
glennrp0fe50b42010-11-16 03:52:51 +00009263
cristy3ed852e2009-09-05 21:47:34 +00009264 if (logging != MagickFalse)
9265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009266 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009267
glennrp5af765f2010-03-30 11:12:18 +00009268 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009269 {
9270 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9271 ping_color_type == PNG_COLOR_TYPE_RGB ||
9272 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9273 ping_bit_depth=8;
9274 }
cristy3ed852e2009-09-05 21:47:34 +00009275
glennrpd6bf1612010-12-17 17:28:54 +00009276 old_bit_depth=ping_bit_depth;
9277
glennrp5af765f2010-03-30 11:12:18 +00009278 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009279 {
glennrp8d579662011-02-23 02:05:02 +00009280 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9281 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009282 }
glennrp8640fb52010-11-23 15:48:26 +00009283
glennrp5af765f2010-03-30 11:12:18 +00009284 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009285 {
cristy35ef8242010-06-03 16:24:13 +00009286 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009287 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009288
9289 if (image->colors == 0)
9290 {
glennrp0fe50b42010-11-16 03:52:51 +00009291 /* DO SOMETHING */
cristyc82a27b2011-10-21 01:07:16 +00009292 (void) ThrowMagickException(exception,
glennrp0f111982010-07-07 20:18:33 +00009293 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009294 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009295 }
9296
cristy35ef8242010-06-03 16:24:13 +00009297 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009298 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009299 }
glennrp2b013e42010-11-24 16:55:50 +00009300
glennrpd6bf1612010-12-17 17:28:54 +00009301 if (logging != MagickFalse)
9302 {
9303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9304 " Number of colors: %.20g",(double) image_colors);
9305
9306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9307 " Tentative PNG bit depth: %d",ping_bit_depth);
9308 }
9309
9310 if (ping_bit_depth < (int) mng_info->write_png_depth)
9311 ping_bit_depth = mng_info->write_png_depth;
9312 }
glennrp2cc891a2010-12-24 13:44:32 +00009313
glennrp5af765f2010-03-30 11:12:18 +00009314 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009315
cristy3ed852e2009-09-05 21:47:34 +00009316 if (logging != MagickFalse)
9317 {
9318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009319 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009320
cristy3ed852e2009-09-05 21:47:34 +00009321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009322 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009323
cristy3ed852e2009-09-05 21:47:34 +00009324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009325 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009326
cristy3ed852e2009-09-05 21:47:34 +00009327 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009328
glennrp8640fb52010-11-23 15:48:26 +00009329 " image->depth: %.20g",(double) image->depth);
9330
9331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009332 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009333 }
9334
glennrp58e01762011-01-07 15:28:54 +00009335 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009336 {
glennrp4f25bd02011-01-01 18:51:28 +00009337 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009338 {
glennrp7c4c9e62011-03-21 20:23:32 +00009339 if (mng_info->write_png_colortype == 0)
9340 {
9341 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009342
glennrp7c4c9e62011-03-21 20:23:32 +00009343 if (ping_have_color != MagickFalse)
9344 ping_color_type=PNG_COLOR_TYPE_RGBA;
9345 }
glennrp4f25bd02011-01-01 18:51:28 +00009346
9347 /*
9348 * Determine if there is any transparent color.
9349 */
9350 if (number_transparent + number_semitransparent == 0)
9351 {
9352 /*
9353 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9354 */
glennrpa6a06632011-01-19 15:15:34 +00009355
glennrp4f25bd02011-01-01 18:51:28 +00009356 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009357
9358 if (mng_info->write_png_colortype == 0)
9359 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009360 }
9361
9362 else
9363 {
9364 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009365 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009366
9367 mask=0xffff;
9368
9369 if (ping_bit_depth == 8)
9370 mask=0x00ff;
9371
9372 if (ping_bit_depth == 4)
9373 mask=0x000f;
9374
9375 if (ping_bit_depth == 2)
9376 mask=0x0003;
9377
9378 if (ping_bit_depth == 1)
9379 mask=0x0001;
9380
9381 ping_trans_color.red=(png_uint_16)
9382 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9383
9384 ping_trans_color.green=(png_uint_16)
9385 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9386
9387 ping_trans_color.blue=(png_uint_16)
9388 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9389
9390 ping_trans_color.gray=(png_uint_16)
cristy101ab702011-10-13 13:06:32 +00009391 (ScaleQuantumToShort(GetPixelInfoIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009392 image->colormap)) & mask);
9393
9394 ping_trans_color.index=(png_byte) 0;
9395
9396 ping_have_tRNS=MagickTrue;
9397 }
9398
9399 if (ping_have_tRNS != MagickFalse)
9400 {
9401 /*
glennrpfd05d622011-02-25 04:10:33 +00009402 * Determine if there is one and only one transparent color
9403 * and if so if it is fully transparent.
9404 */
9405 if (ping_have_cheap_transparency == MagickFalse)
9406 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009407 }
9408
9409 if (ping_have_tRNS != MagickFalse)
9410 {
glennrp7c4c9e62011-03-21 20:23:32 +00009411 if (mng_info->write_png_colortype == 0)
9412 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009413
9414 if (image_depth == 8)
9415 {
9416 ping_trans_color.red&=0xff;
9417 ping_trans_color.green&=0xff;
9418 ping_trans_color.blue&=0xff;
9419 ping_trans_color.gray&=0xff;
9420 }
9421 }
9422 }
cristy3ed852e2009-09-05 21:47:34 +00009423 else
9424 {
cristy3ed852e2009-09-05 21:47:34 +00009425 if (image_depth == 8)
9426 {
glennrp5af765f2010-03-30 11:12:18 +00009427 ping_trans_color.red&=0xff;
9428 ping_trans_color.green&=0xff;
9429 ping_trans_color.blue&=0xff;
9430 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009431 }
9432 }
9433 }
glennrp8640fb52010-11-23 15:48:26 +00009434
cristy3ed852e2009-09-05 21:47:34 +00009435 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009436
glennrp2e09f552010-11-14 00:38:48 +00009437 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009438 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009439
glennrp39992b42010-11-14 00:03:43 +00009440 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009441 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009442 ping_have_color == MagickFalse &&
9443 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009444 {
cristy35ef8242010-06-03 16:24:13 +00009445 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009446
cristy3ed852e2009-09-05 21:47:34 +00009447 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009448 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009449
glennrp7c4c9e62011-03-21 20:23:32 +00009450 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009451 {
glennrp5af765f2010-03-30 11:12:18 +00009452 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009453
cristy3ed852e2009-09-05 21:47:34 +00009454 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009455 {
9456 if (logging != MagickFalse)
9457 {
9458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9459 " Scaling ping_trans_color (0)");
9460 }
9461 ping_trans_color.gray*=0x0101;
9462 }
cristy3ed852e2009-09-05 21:47:34 +00009463 }
glennrp0fe50b42010-11-16 03:52:51 +00009464
cristy3ed852e2009-09-05 21:47:34 +00009465 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9466 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009467
glennrp136ee3a2011-04-27 15:47:45 +00009468 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009469 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009470 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009471
cristy3ed852e2009-09-05 21:47:34 +00009472 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009473 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009474
cristy3ed852e2009-09-05 21:47:34 +00009475 else
9476 {
glennrp5af765f2010-03-30 11:12:18 +00009477 ping_bit_depth=8;
9478 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009479 {
9480 if(!mng_info->write_png_depth)
9481 {
glennrp5af765f2010-03-30 11:12:18 +00009482 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009483
cristy35ef8242010-06-03 16:24:13 +00009484 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009485 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009486 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009487 }
9488 }
glennrp2b013e42010-11-24 16:55:50 +00009489
glennrp0fe50b42010-11-16 03:52:51 +00009490 else if (ping_color_type ==
9491 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009492 mng_info->IsPalette)
9493 {
cristy3ed852e2009-09-05 21:47:34 +00009494 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009495
cristy3ed852e2009-09-05 21:47:34 +00009496 int
9497 depth_4_ok=MagickTrue,
9498 depth_2_ok=MagickTrue,
9499 depth_1_ok=MagickTrue;
9500
cristybb503372010-05-27 20:51:26 +00009501 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009502 {
9503 unsigned char
9504 intensity;
9505
9506 intensity=ScaleQuantumToChar(image->colormap[i].red);
9507
9508 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9509 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9510 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9511 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009512 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009513 depth_1_ok=MagickFalse;
9514 }
glennrp2b013e42010-11-24 16:55:50 +00009515
cristy3ed852e2009-09-05 21:47:34 +00009516 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009517 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009518
cristy3ed852e2009-09-05 21:47:34 +00009519 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009520 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009521
cristy3ed852e2009-09-05 21:47:34 +00009522 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009523 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009524 }
9525 }
glennrp2b013e42010-11-24 16:55:50 +00009526
glennrp5af765f2010-03-30 11:12:18 +00009527 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009528 }
glennrp0fe50b42010-11-16 03:52:51 +00009529
cristy3ed852e2009-09-05 21:47:34 +00009530 else
glennrp0fe50b42010-11-16 03:52:51 +00009531
cristy3ed852e2009-09-05 21:47:34 +00009532 if (mng_info->IsPalette)
9533 {
glennrp17a14852010-05-10 03:01:59 +00009534 number_colors=image_colors;
9535
cristy3ed852e2009-09-05 21:47:34 +00009536 if (image_depth <= 8)
9537 {
cristy3ed852e2009-09-05 21:47:34 +00009538 /*
9539 Set image palette.
9540 */
glennrp5af765f2010-03-30 11:12:18 +00009541 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009542
glennrp58e01762011-01-07 15:28:54 +00009543 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009544 {
glennrp9c1eb072010-06-06 22:19:15 +00009545 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009546
glennrp3b51f0e2010-11-27 18:14:08 +00009547 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9549 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009550 }
glennrp0fe50b42010-11-16 03:52:51 +00009551
cristy3ed852e2009-09-05 21:47:34 +00009552 else
9553 {
cristybb503372010-05-27 20:51:26 +00009554 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009555 {
9556 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9557 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9558 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9559 }
glennrp0fe50b42010-11-16 03:52:51 +00009560
glennrp3b51f0e2010-11-27 18:14:08 +00009561 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009563 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009564 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009565
glennrp39992b42010-11-14 00:03:43 +00009566 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009567 }
glennrp0fe50b42010-11-16 03:52:51 +00009568
cristy3ed852e2009-09-05 21:47:34 +00009569 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009570 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009571 {
cristybefe4d22010-06-07 01:18:58 +00009572 size_t
9573 one;
9574
glennrp5af765f2010-03-30 11:12:18 +00009575 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009576 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009577
cristy94b11832011-09-08 19:46:03 +00009578 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009579 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009580 }
glennrp0fe50b42010-11-16 03:52:51 +00009581
glennrp5af765f2010-03-30 11:12:18 +00009582 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009583
glennrp58e01762011-01-07 15:28:54 +00009584 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009585 {
glennrp0fe50b42010-11-16 03:52:51 +00009586 /*
glennrpd6bf1612010-12-17 17:28:54 +00009587 * Set up trans_colors array.
9588 */
glennrp0fe50b42010-11-16 03:52:51 +00009589 assert(number_colors <= 256);
9590
glennrpd6bf1612010-12-17 17:28:54 +00009591 ping_num_trans=(unsigned short) (number_transparent +
9592 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009593
9594 if (ping_num_trans == 0)
9595 ping_have_tRNS=MagickFalse;
9596
glennrpd6bf1612010-12-17 17:28:54 +00009597 else
glennrp0fe50b42010-11-16 03:52:51 +00009598 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009599 if (logging != MagickFalse)
9600 {
9601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9602 " Scaling ping_trans_color (1)");
9603 }
glennrpd6bf1612010-12-17 17:28:54 +00009604 ping_have_tRNS=MagickTrue;
9605
9606 for (i=0; i < ping_num_trans; i++)
9607 {
cristy4c08aed2011-07-01 19:47:50 +00009608 ping_trans_alpha[i]= (png_byte)
9609 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009610 }
glennrp0fe50b42010-11-16 03:52:51 +00009611 }
9612 }
cristy3ed852e2009-09-05 21:47:34 +00009613 }
9614 }
glennrp0fe50b42010-11-16 03:52:51 +00009615
cristy3ed852e2009-09-05 21:47:34 +00009616 else
9617 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009618
cristy3ed852e2009-09-05 21:47:34 +00009619 if (image_depth < 8)
9620 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009621
cristy3ed852e2009-09-05 21:47:34 +00009622 if ((save_image_depth == 16) && (image_depth == 8))
9623 {
glennrp4f25bd02011-01-01 18:51:28 +00009624 if (logging != MagickFalse)
9625 {
9626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9627 " Scaling ping_trans_color from (%d,%d,%d)",
9628 (int) ping_trans_color.red,
9629 (int) ping_trans_color.green,
9630 (int) ping_trans_color.blue);
9631 }
9632
glennrp5af765f2010-03-30 11:12:18 +00009633 ping_trans_color.red*=0x0101;
9634 ping_trans_color.green*=0x0101;
9635 ping_trans_color.blue*=0x0101;
9636 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009637
9638 if (logging != MagickFalse)
9639 {
9640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9641 " to (%d,%d,%d)",
9642 (int) ping_trans_color.red,
9643 (int) ping_trans_color.green,
9644 (int) ping_trans_color.blue);
9645 }
cristy3ed852e2009-09-05 21:47:34 +00009646 }
9647 }
9648
cristy4383ec82011-01-05 15:42:32 +00009649 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9650 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009651
cristy3ed852e2009-09-05 21:47:34 +00009652 /*
9653 Adjust background and transparency samples in sub-8-bit grayscale files.
9654 */
glennrp5af765f2010-03-30 11:12:18 +00009655 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009656 PNG_COLOR_TYPE_GRAY)
9657 {
9658 png_uint_16
9659 maxval;
9660
cristy35ef8242010-06-03 16:24:13 +00009661 size_t
9662 one=1;
9663
cristy22ffd972010-06-03 16:51:47 +00009664 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009665
glennrp4f25bd02011-01-01 18:51:28 +00009666 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009667 {
cristy3ed852e2009-09-05 21:47:34 +00009668
glennrp9f0fa852011-12-15 12:20:50 +00009669 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9670 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9671 &image->background_color))) +.5)));
cristy3ed852e2009-09-05 21:47:34 +00009672
9673 if (logging != MagickFalse)
9674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009675 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9677 " background_color index is %d",
9678 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009679
glennrp991d11d2010-11-12 21:55:28 +00009680 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009681 }
cristy3ed852e2009-09-05 21:47:34 +00009682
glennrp3e3e20f2011-06-09 04:21:43 +00009683 if (logging != MagickFalse)
9684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9685 " Scaling ping_trans_color.gray from %d",
9686 (int)ping_trans_color.gray);
9687
glennrp9be9b1c2011-06-09 12:21:45 +00009688 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009689 ping_trans_color.gray)+.5);
9690
9691 if (logging != MagickFalse)
9692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9693 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009694 }
glennrp17a14852010-05-10 03:01:59 +00009695
glennrp26f37912010-12-23 16:22:42 +00009696 if (ping_exclude_bKGD == MagickFalse)
9697 {
glennrp1273f7b2011-02-24 03:20:30 +00009698 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009699 {
9700 /*
9701 Identify which colormap entry is the background color.
9702 */
9703
glennrp17a14852010-05-10 03:01:59 +00009704 number_colors=image_colors;
9705
glennrpa521b2f2010-10-29 04:11:03 +00009706 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9707 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009708 break;
9709
9710 ping_background.index=(png_byte) i;
9711
glennrp3b51f0e2010-11-27 18:14:08 +00009712 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009713 {
9714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009715 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009716 }
glennrp0fe50b42010-11-16 03:52:51 +00009717
cristy13d07042010-11-21 20:56:18 +00009718 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009719 {
9720 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009721
9722 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009723 {
9724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9725 " background =(%d,%d,%d)",
9726 (int) ping_background.red,
9727 (int) ping_background.green,
9728 (int) ping_background.blue);
9729 }
9730 }
glennrpa521b2f2010-10-29 04:11:03 +00009731
glennrpd6bf1612010-12-17 17:28:54 +00009732 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009733 {
glennrp3b51f0e2010-11-27 18:14:08 +00009734 if (logging != MagickFalse)
9735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9736 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009737 ping_have_bKGD = MagickFalse;
9738 }
glennrp17a14852010-05-10 03:01:59 +00009739 }
glennrp26f37912010-12-23 16:22:42 +00009740 }
glennrp17a14852010-05-10 03:01:59 +00009741
cristy3ed852e2009-09-05 21:47:34 +00009742 if (logging != MagickFalse)
9743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009744 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009745 /*
9746 Initialize compression level and filtering.
9747 */
9748 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009749 {
9750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9751 " Setting up deflate compression");
9752
9753 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9754 " Compression buffer size: 32768");
9755 }
9756
cristy3ed852e2009-09-05 21:47:34 +00009757 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009758
cristy3ed852e2009-09-05 21:47:34 +00009759 if (logging != MagickFalse)
9760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9761 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009762
cristy4054bfb2011-08-29 23:41:39 +00009763 png_set_compression_mem_level(ping, 9);
9764
glennrp10d739e2011-06-29 18:00:52 +00009765 /* Untangle the "-quality" setting:
9766
9767 Undefined is 0; the default is used.
9768 Default is 75
9769
9770 10's digit:
9771
9772 0: Use Z_HUFFMAN_ONLY strategy with the
9773 zlib default compression level
9774
9775 1-9: the zlib compression level
9776
9777 1's digit:
9778
9779 0-4: the PNG filter method
9780
9781 5: libpng adaptive filtering if compression level > 5
9782 libpng filter type "none" if compression level <= 5
9783 or if image is grayscale or palette
9784
9785 6: libpng adaptive filtering
9786
9787 7: "LOCO" filtering (intrapixel differing) if writing
9788 a MNG, othewise "none". Did not work in IM-6.7.0-9
9789 and earlier because of a missing "else".
9790
9791 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009792 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009793
9794 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009795 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009796
9797 Note that using the -quality option, not all combinations of
9798 PNG filter type, zlib compression level, and zlib compression
cristy5e6be1e2011-07-16 01:23:39 +00009799 strategy are possible. This will be addressed soon in a
cristy5d6fc9c2011-12-27 03:10:42 +00009800 release that accomodates "-define png:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +00009801
9802 */
9803
cristy3ed852e2009-09-05 21:47:34 +00009804 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9805 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009806
glennrp18682582011-06-30 18:11:47 +00009807 if (quality <= 9)
9808 {
9809 if (mng_info->write_png_compression_strategy == 0)
9810 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9811 }
9812
9813 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009814 {
9815 int
9816 level;
9817
cristybb503372010-05-27 20:51:26 +00009818 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009819
glennrp18682582011-06-30 18:11:47 +00009820 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009821 }
glennrp0fe50b42010-11-16 03:52:51 +00009822
glennrp18682582011-06-30 18:11:47 +00009823 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009824 {
glennrp18682582011-06-30 18:11:47 +00009825 if ((quality %10) == 8 || (quality %10) == 9)
9826 mng_info->write_png_compression_strategy=Z_RLE;
cristy3ed852e2009-09-05 21:47:34 +00009827 }
glennrp0fe50b42010-11-16 03:52:51 +00009828
glennrp18682582011-06-30 18:11:47 +00009829 if (mng_info->write_png_compression_filter == 0)
9830 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9831
cristy3ed852e2009-09-05 21:47:34 +00009832 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009833 {
glennrp18682582011-06-30 18:11:47 +00009834 if (mng_info->write_png_compression_level)
9835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9836 " Compression level: %d",
9837 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009838
glennrp18682582011-06-30 18:11:47 +00009839 if (mng_info->write_png_compression_strategy)
9840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9841 " Compression strategy: %d",
9842 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009843
glennrp18682582011-06-30 18:11:47 +00009844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9845 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009846
cristy4054bfb2011-08-29 23:41:39 +00009847 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +00009848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9849 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +00009850 else if (mng_info->write_png_compression_filter == 0 ||
9851 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +00009852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9853 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009854 else
9855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9856 " Base filter method: %d",
9857 (int) mng_info->write_png_compression_filter-1);
9858 }
glennrp2b013e42010-11-24 16:55:50 +00009859
glennrp18682582011-06-30 18:11:47 +00009860 if (mng_info->write_png_compression_level != 0)
9861 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9862
9863 if (mng_info->write_png_compression_filter == 6)
9864 {
9865 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9866 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9867 (quality < 50))
9868 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9869 else
9870 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9871 }
cristy4054bfb2011-08-29 23:41:39 +00009872 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +00009873 mng_info->write_png_compression_filter == 10)
9874 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9875
9876 else if (mng_info->write_png_compression_filter == 8)
9877 {
9878#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9879 if (mng_info->write_mng)
9880 {
9881 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9882 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9883 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9884 }
9885#endif
cristy4054bfb2011-08-29 23:41:39 +00009886 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +00009887 }
9888
9889 else if (mng_info->write_png_compression_filter == 9)
9890 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9891
9892 else if (mng_info->write_png_compression_filter != 0)
9893 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9894 mng_info->write_png_compression_filter-1);
9895
9896 if (mng_info->write_png_compression_strategy != 0)
9897 png_set_compression_strategy(ping,
9898 mng_info->write_png_compression_strategy-1);
9899
cristy0d57eec2011-09-04 22:13:56 +00009900 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9901 if (ping_exclude_sRGB != MagickFalse ||
9902 (image->rendering_intent == UndefinedIntent))
9903 {
9904 if ((ping_exclude_tEXt == MagickFalse ||
9905 ping_exclude_zTXt == MagickFalse) &&
9906 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009907 {
9908 ResetImageProfileIterator(image);
9909 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009910 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009911 profile=GetImageProfile(image,name);
9912
9913 if (profile != (StringInfo *) NULL)
9914 {
glennrp5af765f2010-03-30 11:12:18 +00009915#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009916 if ((LocaleCompare(name,"ICC") == 0) ||
9917 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009918 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009919
9920 if (ping_exclude_iCCP == MagickFalse)
9921 {
cristy9f027d12011-09-21 01:17:17 +00009922 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009923#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009924 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009925#else
9926 (png_const_bytep) GetStringInfoDatum(profile),
9927#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009928 (png_uint_32) GetStringInfoLength(profile));
9929 }
glennrp26f37912010-12-23 16:22:42 +00009930 }
glennrp0fe50b42010-11-16 03:52:51 +00009931
glennrpc8cbc5d2011-01-01 00:12:34 +00009932 else
cristy3ed852e2009-09-05 21:47:34 +00009933#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009934 if (ping_exclude_zCCP == MagickFalse)
9935 {
glennrpcf002022011-01-30 02:38:15 +00009936 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009937 (unsigned char *) name,(unsigned char *) name,
9938 GetStringInfoDatum(profile),
9939 (png_uint_32) GetStringInfoLength(profile));
9940 }
9941 }
glennrp0b206f52011-01-07 04:55:32 +00009942
glennrpc8cbc5d2011-01-01 00:12:34 +00009943 if (logging != MagickFalse)
9944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9945 " Setting up text chunk with %s profile",name);
9946
9947 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009948 }
cristy0d57eec2011-09-04 22:13:56 +00009949 }
cristy3ed852e2009-09-05 21:47:34 +00009950 }
9951
9952#if defined(PNG_WRITE_sRGB_SUPPORTED)
9953 if ((mng_info->have_write_global_srgb == 0) &&
9954 ((image->rendering_intent != UndefinedIntent) ||
9955 (image->colorspace == sRGBColorspace)))
9956 {
glennrp26f37912010-12-23 16:22:42 +00009957 if (ping_exclude_sRGB == MagickFalse)
9958 {
9959 /*
9960 Note image rendering intent.
9961 */
9962 if (logging != MagickFalse)
9963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9964 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009965
glennrp26f37912010-12-23 16:22:42 +00009966 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009967 Magick_RenderingIntent_to_PNG_RenderingIntent(
9968 image->rendering_intent)));
glennrp26f37912010-12-23 16:22:42 +00009969 }
cristy3ed852e2009-09-05 21:47:34 +00009970 }
glennrp26f37912010-12-23 16:22:42 +00009971
glennrp5af765f2010-03-30 11:12:18 +00009972 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009973#endif
9974 {
glennrp2cc891a2010-12-24 13:44:32 +00009975 if (ping_exclude_gAMA == MagickFalse &&
9976 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009977 (image->gamma < .45 || image->gamma > .46)))
9978 {
cristy3ed852e2009-09-05 21:47:34 +00009979 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9980 {
9981 /*
9982 Note image gamma.
9983 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9984 */
9985 if (logging != MagickFalse)
9986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9987 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009988
cristy3ed852e2009-09-05 21:47:34 +00009989 png_set_gAMA(ping,ping_info,image->gamma);
9990 }
glennrp26f37912010-12-23 16:22:42 +00009991 }
glennrp2b013e42010-11-24 16:55:50 +00009992
glennrp26f37912010-12-23 16:22:42 +00009993 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009994 {
glennrp26f37912010-12-23 16:22:42 +00009995 if ((mng_info->have_write_global_chrm == 0) &&
9996 (image->chromaticity.red_primary.x != 0.0))
9997 {
9998 /*
9999 Note image chromaticity.
10000 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10001 */
10002 PrimaryInfo
10003 bp,
10004 gp,
10005 rp,
10006 wp;
cristy3ed852e2009-09-05 21:47:34 +000010007
glennrp26f37912010-12-23 16:22:42 +000010008 wp=image->chromaticity.white_point;
10009 rp=image->chromaticity.red_primary;
10010 gp=image->chromaticity.green_primary;
10011 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +000010012
glennrp26f37912010-12-23 16:22:42 +000010013 if (logging != MagickFalse)
10014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10015 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010016
glennrp26f37912010-12-23 16:22:42 +000010017 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10018 bp.x,bp.y);
10019 }
10020 }
cristy3ed852e2009-09-05 21:47:34 +000010021 }
glennrpdfd70802010-11-14 01:23:35 +000010022
glennrp5af765f2010-03-30 11:12:18 +000010023 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +000010024
10025 if (mng_info->write_mng)
10026 png_set_sig_bytes(ping,8);
10027
cristy5d6fc9c2011-12-27 03:10:42 +000010028 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
cristy3ed852e2009-09-05 21:47:34 +000010029
glennrpd6bf1612010-12-17 17:28:54 +000010030 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +000010031 {
10032 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +000010033 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010034 {
glennrp5af765f2010-03-30 11:12:18 +000010035 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +000010036
glennrp5af765f2010-03-30 11:12:18 +000010037 if (ping_bit_depth < 8)
10038 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +000010039 }
glennrp0fe50b42010-11-16 03:52:51 +000010040
cristy3ed852e2009-09-05 21:47:34 +000010041 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +000010042 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +000010043 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +000010044 }
10045
glennrp0e8ea192010-12-24 18:00:33 +000010046 if (ping_need_colortype_warning != MagickFalse ||
10047 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +000010048 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +000010049 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +000010050 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +000010051 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +000010052 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +000010053 {
10054 if (logging != MagickFalse)
10055 {
glennrp0e8ea192010-12-24 18:00:33 +000010056 if (ping_need_colortype_warning != MagickFalse)
10057 {
10058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10059 " Image has transparency but tRNS chunk was excluded");
10060 }
10061
cristy3ed852e2009-09-05 21:47:34 +000010062 if (mng_info->write_png_depth)
10063 {
10064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010065 " Defined png:bit-depth=%u, Computed depth=%u",
cristy3ed852e2009-09-05 21:47:34 +000010066 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +000010067 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +000010068 }
glennrp0e8ea192010-12-24 18:00:33 +000010069
cristy3ed852e2009-09-05 21:47:34 +000010070 if (mng_info->write_png_colortype)
10071 {
10072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010073 " Defined png:color-type=%u, Computed color type=%u",
cristy3ed852e2009-09-05 21:47:34 +000010074 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +000010075 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010076 }
10077 }
glennrp0e8ea192010-12-24 18:00:33 +000010078
glennrp3bd2e412010-08-10 13:34:52 +000010079 png_warning(ping,
cristy5d6fc9c2011-12-27 03:10:42 +000010080 "Cannot write image with defined png:bit-depth or png:color-type.");
cristy3ed852e2009-09-05 21:47:34 +000010081 }
10082
glennrp58e01762011-01-07 15:28:54 +000010083 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010084 {
10085 /* Add an opaque matte channel */
10086 image->matte = MagickTrue;
cristye941a752011-10-15 01:52:48 +000010087 (void) SetImageAlpha(image,OpaqueAlpha,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010088
glennrpb4a13412010-05-05 12:47:19 +000010089 if (logging != MagickFalse)
10090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10091 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +000010092 }
10093
glennrp0e319732011-01-25 21:53:13 +000010094 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +000010095 {
glennrp991d11d2010-11-12 21:55:28 +000010096 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +000010097 {
glennrp991d11d2010-11-12 21:55:28 +000010098 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010099 if (logging != MagickFalse)
10100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10101 " Setting ping_have_tRNS=MagickTrue.");
10102 }
glennrpe9c26dc2010-05-30 01:56:35 +000010103 }
10104
cristy3ed852e2009-09-05 21:47:34 +000010105 if (logging != MagickFalse)
10106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10107 " Writing PNG header chunks");
10108
glennrp5af765f2010-03-30 11:12:18 +000010109 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10110 ping_bit_depth,ping_color_type,
10111 ping_interlace_method,ping_compression_method,
10112 ping_filter_method);
10113
glennrp39992b42010-11-14 00:03:43 +000010114 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10115 {
glennrpf09bded2011-01-08 01:15:59 +000010116 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010117
glennrp3b51f0e2010-11-27 18:14:08 +000010118 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +000010119 {
glennrp8640fb52010-11-23 15:48:26 +000010120 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +000010121 {
glennrpd6bf1612010-12-17 17:28:54 +000010122 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +000010123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010124 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10125 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010126 (int) palette[i].red,
10127 (int) palette[i].green,
10128 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +000010129 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010130 (int) ping_trans_alpha[i]);
10131 else
10132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010133 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010134 (int) i,
10135 (int) palette[i].red,
10136 (int) palette[i].green,
10137 (int) palette[i].blue);
10138 }
glennrp39992b42010-11-14 00:03:43 +000010139 }
glennrp39992b42010-11-14 00:03:43 +000010140 }
10141
glennrp26f37912010-12-23 16:22:42 +000010142 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010143 {
glennrp26f37912010-12-23 16:22:42 +000010144 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010145 {
glennrp26f37912010-12-23 16:22:42 +000010146 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010147 if (logging)
10148 {
10149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10150 " Setting up bKGD chunk");
10151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10152 " background color = (%d,%d,%d)",
10153 (int) ping_background.red,
10154 (int) ping_background.green,
10155 (int) ping_background.blue);
10156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10157 " index = %d, gray=%d",
10158 (int) ping_background.index,
10159 (int) ping_background.gray);
10160 }
10161 }
glennrp26f37912010-12-23 16:22:42 +000010162 }
10163
10164 if (ping_exclude_pHYs == MagickFalse)
10165 {
10166 if (ping_have_pHYs != MagickFalse)
10167 {
10168 png_set_pHYs(ping,ping_info,
10169 ping_pHYs_x_resolution,
10170 ping_pHYs_y_resolution,
10171 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010172
10173 if (logging)
10174 {
10175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10176 " Setting up pHYs chunk");
10177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10178 " x_resolution=%lu",
10179 (unsigned long) ping_pHYs_x_resolution);
10180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10181 " y_resolution=%lu",
10182 (unsigned long) ping_pHYs_y_resolution);
10183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10184 " unit_type=%lu",
10185 (unsigned long) ping_pHYs_unit_type);
10186 }
glennrp26f37912010-12-23 16:22:42 +000010187 }
glennrpdfd70802010-11-14 01:23:35 +000010188 }
10189
10190#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010191 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010192 {
glennrp26f37912010-12-23 16:22:42 +000010193 if (image->page.x || image->page.y)
10194 {
10195 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10196 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010197
glennrp26f37912010-12-23 16:22:42 +000010198 if (logging != MagickFalse)
10199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10200 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10201 (int) image->page.x, (int) image->page.y);
10202 }
glennrpdfd70802010-11-14 01:23:35 +000010203 }
10204#endif
10205
glennrpda8f3a72011-02-27 23:54:12 +000010206 if (mng_info->need_blob != MagickFalse)
10207 {
cristyc82a27b2011-10-21 01:07:16 +000010208 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
glennrpda8f3a72011-02-27 23:54:12 +000010209 MagickFalse)
10210 png_error(ping,"WriteBlob Failed");
10211
10212 ping_have_blob=MagickTrue;
10213 }
10214
cristy3ed852e2009-09-05 21:47:34 +000010215 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010216
glennrp39992b42010-11-14 00:03:43 +000010217 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010218 {
glennrp3b51f0e2010-11-27 18:14:08 +000010219 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010220 {
10221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10222 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10223 }
10224
10225 if (ping_color_type == 3)
10226 (void) png_set_tRNS(ping, ping_info,
10227 ping_trans_alpha,
10228 ping_num_trans,
10229 NULL);
10230
10231 else
10232 {
10233 (void) png_set_tRNS(ping, ping_info,
10234 NULL,
10235 0,
10236 &ping_trans_color);
10237
glennrp3b51f0e2010-11-27 18:14:08 +000010238 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010239 {
10240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010241 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010242 (int) ping_trans_color.red,
10243 (int) ping_trans_color.green,
10244 (int) ping_trans_color.blue);
10245 }
10246 }
glennrp991d11d2010-11-12 21:55:28 +000010247 }
10248
cristy3ed852e2009-09-05 21:47:34 +000010249 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010250 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010251
cristy3ed852e2009-09-05 21:47:34 +000010252 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010253
cristy3ed852e2009-09-05 21:47:34 +000010254 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010255 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010256
glennrp26f37912010-12-23 16:22:42 +000010257 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010258 {
glennrp4f25bd02011-01-01 18:51:28 +000010259 if ((image->page.width != 0 && image->page.width != image->columns) ||
10260 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010261 {
10262 unsigned char
10263 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010264
glennrp26f37912010-12-23 16:22:42 +000010265 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10266 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010267 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010268 PNGLong(chunk+4,(png_uint_32) image->page.width);
10269 PNGLong(chunk+8,(png_uint_32) image->page.height);
10270 chunk[12]=0; /* unit = pixels */
10271 (void) WriteBlob(image,13,chunk);
10272 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10273 }
cristy3ed852e2009-09-05 21:47:34 +000010274 }
10275
10276#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010277 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010278#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010279 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010280#undef PNG_HAVE_IDAT
10281#endif
10282
10283 png_set_packing(ping);
10284 /*
10285 Allocate memory.
10286 */
10287 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010288 if (image_depth > 8)
10289 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010290 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010291 {
glennrpb4a13412010-05-05 12:47:19 +000010292 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010293 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010294 break;
glennrp0fe50b42010-11-16 03:52:51 +000010295
glennrpb4a13412010-05-05 12:47:19 +000010296 case PNG_COLOR_TYPE_GRAY_ALPHA:
10297 rowbytes*=2;
10298 break;
glennrp0fe50b42010-11-16 03:52:51 +000010299
glennrpb4a13412010-05-05 12:47:19 +000010300 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010301 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010302 break;
glennrp0fe50b42010-11-16 03:52:51 +000010303
glennrpb4a13412010-05-05 12:47:19 +000010304 default:
10305 break;
cristy3ed852e2009-09-05 21:47:34 +000010306 }
glennrp3b51f0e2010-11-27 18:14:08 +000010307
10308 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010309 {
10310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10311 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010312
glennrpb4a13412010-05-05 12:47:19 +000010313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010314 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010315 }
glennrpcf002022011-01-30 02:38:15 +000010316 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10317 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010318
glennrpcf002022011-01-30 02:38:15 +000010319 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010320 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010321
cristy3ed852e2009-09-05 21:47:34 +000010322 /*
10323 Initialize image scanlines.
10324 */
glennrp5af765f2010-03-30 11:12:18 +000010325 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010326 {
10327 /*
10328 PNG write failed.
10329 */
10330#ifdef PNG_DEBUG
10331 if (image_info->verbose)
10332 (void) printf("PNG write has failed.\n");
10333#endif
10334 png_destroy_write_struct(&ping,&ping_info);
10335 if (quantum_info != (QuantumInfo *) NULL)
10336 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010337 if (ping_pixels != (unsigned char *) NULL)
10338 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010339#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010340 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010341#endif
glennrpda8f3a72011-02-27 23:54:12 +000010342 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010343 (void) CloseBlob(image);
10344 image_info=DestroyImageInfo(image_info);
10345 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010346 return(MagickFalse);
10347 }
cristyed552522009-10-16 14:04:35 +000010348 quantum_info=AcquireQuantumInfo(image_info,image);
10349 if (quantum_info == (QuantumInfo *) NULL)
10350 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010351 quantum_info->format=UndefinedQuantumFormat;
10352 quantum_info->depth=image_depth;
10353 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010354
cristy3ed852e2009-09-05 21:47:34 +000010355 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010356 !mng_info->write_png32) &&
10357 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010358 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010359 image_matte == MagickFalse &&
10360 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010361 {
glennrp8bb3a022010-12-13 20:40:04 +000010362 /* Palette, Bilevel, or Opaque Monochrome */
cristy4c08aed2011-07-01 19:47:50 +000010363 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010364 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010365
cristy3ed852e2009-09-05 21:47:34 +000010366 quantum_info->depth=8;
10367 for (pass=0; pass < num_passes; pass++)
10368 {
10369 /*
10370 Convert PseudoClass image to a PNG monochrome image.
10371 */
cristybb503372010-05-27 20:51:26 +000010372 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010373 {
glennrpd71e86a2011-02-24 01:28:37 +000010374 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10376 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010377
cristyc82a27b2011-10-21 01:07:16 +000010378 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010379
cristy4c08aed2011-07-01 19:47:50 +000010380 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010381 break;
glennrp0fe50b42010-11-16 03:52:51 +000010382
cristy3ed852e2009-09-05 21:47:34 +000010383 if (mng_info->IsPalette)
10384 {
cristy4c08aed2011-07-01 19:47:50 +000010385 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010386 quantum_info,GrayQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010387 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10388 mng_info->write_png_depth &&
10389 mng_info->write_png_depth != old_bit_depth)
10390 {
10391 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010392 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010393 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010394 >> (8-old_bit_depth));
10395 }
10396 }
glennrp0fe50b42010-11-16 03:52:51 +000010397
cristy3ed852e2009-09-05 21:47:34 +000010398 else
10399 {
cristy4c08aed2011-07-01 19:47:50 +000010400 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010401 quantum_info,RedQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010402 }
glennrp0fe50b42010-11-16 03:52:51 +000010403
cristy3ed852e2009-09-05 21:47:34 +000010404 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010405 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010406 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010407 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010408
glennrp3b51f0e2010-11-27 18:14:08 +000010409 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10411 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010412
glennrpcf002022011-01-30 02:38:15 +000010413 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010414 }
10415 if (image->previous == (Image *) NULL)
10416 {
10417 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10418 if (status == MagickFalse)
10419 break;
10420 }
10421 }
10422 }
glennrp0fe50b42010-11-16 03:52:51 +000010423
glennrp8bb3a022010-12-13 20:40:04 +000010424 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010425 {
glennrp0fe50b42010-11-16 03:52:51 +000010426 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010427 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010428 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010429 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010430 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010431 {
cristy4c08aed2011-07-01 19:47:50 +000010432 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010433 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010434
glennrp8bb3a022010-12-13 20:40:04 +000010435 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010436 {
glennrp8bb3a022010-12-13 20:40:04 +000010437
cristybb503372010-05-27 20:51:26 +000010438 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010439 {
cristyc82a27b2011-10-21 01:07:16 +000010440 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010441
cristy4c08aed2011-07-01 19:47:50 +000010442 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010443 break;
glennrp2cc891a2010-12-24 13:44:32 +000010444
glennrp5af765f2010-03-30 11:12:18 +000010445 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010446 {
glennrp8bb3a022010-12-13 20:40:04 +000010447 if (mng_info->IsPalette)
cristy4c08aed2011-07-01 19:47:50 +000010448 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010449 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010450
glennrp8bb3a022010-12-13 20:40:04 +000010451 else
cristy4c08aed2011-07-01 19:47:50 +000010452 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010453 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010454
glennrp3b51f0e2010-11-27 18:14:08 +000010455 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010457 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010458 }
glennrp2cc891a2010-12-24 13:44:32 +000010459
glennrp8bb3a022010-12-13 20:40:04 +000010460 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10461 {
10462 if (logging != MagickFalse && y == 0)
10463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10464 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010465
cristy4c08aed2011-07-01 19:47:50 +000010466 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010467 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010468 }
glennrp2cc891a2010-12-24 13:44:32 +000010469
glennrp3b51f0e2010-11-27 18:14:08 +000010470 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010472 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010473
glennrpcf002022011-01-30 02:38:15 +000010474 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010475 }
glennrp2cc891a2010-12-24 13:44:32 +000010476
glennrp8bb3a022010-12-13 20:40:04 +000010477 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010478 {
glennrp8bb3a022010-12-13 20:40:04 +000010479 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10480 if (status == MagickFalse)
10481 break;
cristy3ed852e2009-09-05 21:47:34 +000010482 }
cristy3ed852e2009-09-05 21:47:34 +000010483 }
10484 }
glennrp8bb3a022010-12-13 20:40:04 +000010485
10486 else
10487 {
cristy4c08aed2011-07-01 19:47:50 +000010488 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010489 *p;
10490
10491 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010492 {
glennrp8bb3a022010-12-13 20:40:04 +000010493 if ((image_depth > 8) || (mng_info->write_png24 ||
10494 mng_info->write_png32 ||
10495 (!mng_info->write_png8 && !mng_info->IsPalette)))
10496 {
10497 for (y=0; y < (ssize_t) image->rows; y++)
10498 {
10499 p=GetVirtualPixels(image,0,y,image->columns,1,
cristyc82a27b2011-10-21 01:07:16 +000010500 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010501
cristy4c08aed2011-07-01 19:47:50 +000010502 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010503 break;
glennrp2cc891a2010-12-24 13:44:32 +000010504
glennrp8bb3a022010-12-13 20:40:04 +000010505 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10506 {
10507 if (image->storage_class == DirectClass)
cristy4c08aed2011-07-01 19:47:50 +000010508 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010509 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010510
glennrp8bb3a022010-12-13 20:40:04 +000010511 else
cristy4c08aed2011-07-01 19:47:50 +000010512 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010513 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010514 }
glennrp2cc891a2010-12-24 13:44:32 +000010515
glennrp8bb3a022010-12-13 20:40:04 +000010516 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10517 {
cristy4c08aed2011-07-01 19:47:50 +000010518 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010519 quantum_info,GrayAlphaQuantum,ping_pixels,
cristyc82a27b2011-10-21 01:07:16 +000010520 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010521
glennrp8bb3a022010-12-13 20:40:04 +000010522 if (logging != MagickFalse && y == 0)
10523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10524 " Writing GRAY_ALPHA PNG pixels (3)");
10525 }
glennrp2cc891a2010-12-24 13:44:32 +000010526
glennrp8bb3a022010-12-13 20:40:04 +000010527 else if (image_matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +000010528 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010529 quantum_info,RGBAQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010530
glennrp8bb3a022010-12-13 20:40:04 +000010531 else
cristy4c08aed2011-07-01 19:47:50 +000010532 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010533 quantum_info,RGBQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010534
glennrp8bb3a022010-12-13 20:40:04 +000010535 if (logging != MagickFalse && y == 0)
10536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10537 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010538
glennrpcf002022011-01-30 02:38:15 +000010539 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010540 }
10541 }
glennrp2cc891a2010-12-24 13:44:32 +000010542
glennrp8bb3a022010-12-13 20:40:04 +000010543 else
10544 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10545 mng_info->write_png32 ||
10546 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10547 {
10548 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10549 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10550 {
10551 if (logging != MagickFalse)
10552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10553 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010554
glennrp8bb3a022010-12-13 20:40:04 +000010555 quantum_info->depth=8;
10556 image_depth=8;
10557 }
glennrp2cc891a2010-12-24 13:44:32 +000010558
glennrp8bb3a022010-12-13 20:40:04 +000010559 for (y=0; y < (ssize_t) image->rows; y++)
10560 {
10561 if (logging != MagickFalse && y == 0)
10562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10563 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010564
cristy97707062011-12-27 18:25:00 +000010565 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000010566
cristy4c08aed2011-07-01 19:47:50 +000010567 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010568 break;
glennrp2cc891a2010-12-24 13:44:32 +000010569
glennrp8bb3a022010-12-13 20:40:04 +000010570 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010571 {
glennrp4bf89732011-03-21 13:48:28 +000010572 quantum_info->depth=image->depth;
10573
cristy4c08aed2011-07-01 19:47:50 +000010574 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010575 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp44757ab2011-03-17 12:57:03 +000010576 }
glennrp2cc891a2010-12-24 13:44:32 +000010577
glennrp8bb3a022010-12-13 20:40:04 +000010578 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10579 {
10580 if (logging != MagickFalse && y == 0)
10581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10582 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010583
cristy4c08aed2011-07-01 19:47:50 +000010584 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010585 quantum_info,GrayAlphaQuantum,ping_pixels,
cristyc82a27b2011-10-21 01:07:16 +000010586 exception);
glennrp8bb3a022010-12-13 20:40:04 +000010587 }
glennrp2cc891a2010-12-24 13:44:32 +000010588
glennrp8bb3a022010-12-13 20:40:04 +000010589 else
glennrp8bb3a022010-12-13 20:40:04 +000010590 {
cristy4c08aed2011-07-01 19:47:50 +000010591 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010592 quantum_info,IndexQuantum,ping_pixels,exception);
glennrp5eae7602011-02-22 15:21:32 +000010593
10594 if (logging != MagickFalse && y <= 2)
10595 {
10596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010597 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010598
10599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10600 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10601 (int)ping_pixels[0],(int)ping_pixels[1]);
10602 }
glennrp8bb3a022010-12-13 20:40:04 +000010603 }
glennrpcf002022011-01-30 02:38:15 +000010604 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010605 }
10606 }
glennrp2cc891a2010-12-24 13:44:32 +000010607
glennrp8bb3a022010-12-13 20:40:04 +000010608 if (image->previous == (Image *) NULL)
10609 {
10610 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10611 if (status == MagickFalse)
10612 break;
10613 }
cristy3ed852e2009-09-05 21:47:34 +000010614 }
glennrp8bb3a022010-12-13 20:40:04 +000010615 }
10616 }
10617
cristyb32b90a2009-09-07 21:45:48 +000010618 if (quantum_info != (QuantumInfo *) NULL)
10619 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010620
10621 if (logging != MagickFalse)
10622 {
10623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010624 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010625
cristy3ed852e2009-09-05 21:47:34 +000010626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010627 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010628
cristy3ed852e2009-09-05 21:47:34 +000010629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010630 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010631
cristy3ed852e2009-09-05 21:47:34 +000010632 if (mng_info->write_png_depth)
10633 {
10634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010635 " Defined png:bit-depth: %d",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010636 }
glennrp0fe50b42010-11-16 03:52:51 +000010637
cristy3ed852e2009-09-05 21:47:34 +000010638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010639 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010640
cristy3ed852e2009-09-05 21:47:34 +000010641 if (mng_info->write_png_colortype)
10642 {
10643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010644 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010645 }
glennrp0fe50b42010-11-16 03:52:51 +000010646
cristy3ed852e2009-09-05 21:47:34 +000010647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010648 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010649
cristy3ed852e2009-09-05 21:47:34 +000010650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010651 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010652 }
10653 /*
glennrpa0ed0092011-04-18 16:36:29 +000010654 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010655 */
glennrp823b55c2011-03-14 18:46:46 +000010656 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010657 {
glennrp26f37912010-12-23 16:22:42 +000010658 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010659 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010660 while (property != (const char *) NULL)
10661 {
10662 png_textp
10663 text;
glennrp2cc891a2010-12-24 13:44:32 +000010664
cristyd15e6592011-10-15 00:13:06 +000010665 value=GetImageProperty(image,property,exception);
glennrpa0ed0092011-04-18 16:36:29 +000010666
10667 /* Don't write any "png:" properties; those are just for "identify" */
10668 if (LocaleNCompare(property,"png:",4) != 0 &&
10669
10670 /* Suppress density and units if we wrote a pHYs chunk */
10671 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010672 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010673 LocaleCompare(property,"units") != 0) &&
10674
10675 /* Suppress the IM-generated Date:create and Date:modify */
10676 (ping_exclude_date == MagickFalse ||
10677 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010678 {
glennrpc70af4a2011-03-07 00:08:23 +000010679 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010680 {
glennrpc70af4a2011-03-07 00:08:23 +000010681 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10682 text[0].key=(char *) property;
10683 text[0].text=(char *) value;
10684 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010685
glennrpc70af4a2011-03-07 00:08:23 +000010686 if (ping_exclude_tEXt != MagickFalse)
10687 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10688
10689 else if (ping_exclude_zTXt != MagickFalse)
10690 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10691
10692 else
glennrp26f37912010-12-23 16:22:42 +000010693 {
glennrpc70af4a2011-03-07 00:08:23 +000010694 text[0].compression=image_info->compression == NoCompression ||
10695 (image_info->compression == UndefinedCompression &&
10696 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10697 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010698 }
glennrp2cc891a2010-12-24 13:44:32 +000010699
glennrpc70af4a2011-03-07 00:08:23 +000010700 if (logging != MagickFalse)
10701 {
10702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10703 " Setting up text chunk");
10704
10705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10706 " keyword: %s",text[0].key);
10707 }
10708
10709 png_set_text(ping,ping_info,text,1);
10710 png_free(ping,text);
10711 }
glennrp26f37912010-12-23 16:22:42 +000010712 }
10713 property=GetNextImageProperty(image);
10714 }
cristy3ed852e2009-09-05 21:47:34 +000010715 }
10716
10717 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010718 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010719
10720 if (logging != MagickFalse)
10721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10722 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010723
cristy3ed852e2009-09-05 21:47:34 +000010724 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010725
cristy3ed852e2009-09-05 21:47:34 +000010726 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10727 {
10728 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010729 (ping_width != mng_info->page.width) ||
10730 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010731 {
10732 unsigned char
10733 chunk[32];
10734
10735 /*
10736 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10737 */
10738 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10739 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010740 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010741 chunk[4]=4;
10742 chunk[5]=0; /* frame name separator (no name) */
10743 chunk[6]=1; /* flag for changing delay, for next frame only */
10744 chunk[7]=0; /* flag for changing frame timeout */
10745 chunk[8]=1; /* flag for changing frame clipping for next frame */
10746 chunk[9]=0; /* flag for changing frame sync_id */
10747 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10748 chunk[14]=0; /* clipping boundaries delta type */
10749 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10750 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010751 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010752 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10753 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010754 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010755 (void) WriteBlob(image,31,chunk);
10756 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10757 mng_info->old_framing_mode=4;
10758 mng_info->framing_mode=1;
10759 }
glennrp0fe50b42010-11-16 03:52:51 +000010760
cristy3ed852e2009-09-05 21:47:34 +000010761 else
10762 mng_info->framing_mode=3;
10763 }
10764 if (mng_info->write_mng && !mng_info->need_fram &&
10765 ((int) image->dispose == 3))
cristyc82a27b2011-10-21 01:07:16 +000010766 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010767 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010768 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010769
cristy3ed852e2009-09-05 21:47:34 +000010770 /*
10771 Free PNG resources.
10772 */
glennrp5af765f2010-03-30 11:12:18 +000010773
cristy3ed852e2009-09-05 21:47:34 +000010774 png_destroy_write_struct(&ping,&ping_info);
10775
glennrpcf002022011-01-30 02:38:15 +000010776 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010777
10778#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010779 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010780#endif
10781
glennrpda8f3a72011-02-27 23:54:12 +000010782 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010783 (void) CloseBlob(image);
10784
10785 image_info=DestroyImageInfo(image_info);
10786 image=DestroyImage(image);
10787
10788 /* Store bit depth actually written */
10789 s[0]=(char) ping_bit_depth;
10790 s[1]='\0';
10791
cristyd15e6592011-10-15 00:13:06 +000010792 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
glennrpb9cfe272010-12-21 15:08:06 +000010793
cristy3ed852e2009-09-05 21:47:34 +000010794 if (logging != MagickFalse)
10795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10796 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010797
cristy3ed852e2009-09-05 21:47:34 +000010798 return(MagickTrue);
10799/* End write one PNG image */
10800}
10801
10802/*
10803%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10804% %
10805% %
10806% %
10807% W r i t e P N G I m a g e %
10808% %
10809% %
10810% %
10811%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10812%
10813% WritePNGImage() writes a Portable Network Graphics (PNG) or
10814% Multiple-image Network Graphics (MNG) image file.
10815%
10816% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10817%
10818% The format of the WritePNGImage method is:
10819%
cristy1e178e72011-08-28 19:44:34 +000010820% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10821% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010822%
10823% A description of each parameter follows:
10824%
10825% o image_info: the image info.
10826%
10827% o image: The image.
10828%
cristy1e178e72011-08-28 19:44:34 +000010829% o exception: return any errors or warnings in this structure.
10830%
cristy3ed852e2009-09-05 21:47:34 +000010831% Returns MagickTrue on success, MagickFalse on failure.
10832%
10833% Communicating with the PNG encoder:
10834%
10835% While the datastream written is always in PNG format and normally would
10836% be given the "png" file extension, this method also writes the following
cristy5d6fc9c2011-12-27 03:10:42 +000010837% pseudo-formats which are subsets of png:
cristy3ed852e2009-09-05 21:47:34 +000010838%
glennrp5a39f372011-02-25 04:52:16 +000010839% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10840% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010841% is present, the tRNS chunk must only have values 0 and 255
10842% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010843% transparent). If other values are present they will be
10844% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010845% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000010846% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10847% of any resulting fully-transparent pixels is changed to
10848% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000010849%
10850% If you want better quantization or dithering of the colors
10851% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010852% PNG encoder. The pixels contain 8-bit indices even if
10853% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010854% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010855% PNG grayscale type might be slightly more efficient. Please
10856% note that writing to the PNG8 format may result in loss
10857% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010858%
10859% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10860% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010861% one of the colors as transparent. The only loss incurred
10862% is reduction of sample depth to 8. If the image has more
10863% than one transparent color, has semitransparent pixels, or
10864% has an opaque pixel with the same RGB components as the
10865% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010866%
10867% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10868% transparency is permitted, i.e., the alpha sample for
10869% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010870% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010871% The only loss in data is the reduction of the sample depth
10872% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010873%
10874% o -define: For more precise control of the PNG output, you can use the
10875% Image options "png:bit-depth" and "png:color-type". These
10876% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010877% from the application programming interfaces. The options
10878% are case-independent and are converted to lowercase before
10879% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010880%
10881% png:color-type can be 0, 2, 3, 4, or 6.
10882%
10883% When png:color-type is 0 (Grayscale), png:bit-depth can
10884% be 1, 2, 4, 8, or 16.
10885%
10886% When png:color-type is 2 (RGB), png:bit-depth can
10887% be 8 or 16.
10888%
10889% When png:color-type is 3 (Indexed), png:bit-depth can
10890% be 1, 2, 4, or 8. This refers to the number of bits
10891% used to store the index. The color samples always have
10892% bit-depth 8 in indexed PNG files.
10893%
10894% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10895% png:bit-depth can be 8 or 16.
10896%
glennrp5a39f372011-02-25 04:52:16 +000010897% If the image cannot be written without loss with the requested bit-depth
10898% and color-type, a PNG file will not be written, and the encoder will
10899% return MagickFalse.
10900%
cristy3ed852e2009-09-05 21:47:34 +000010901% Since image encoders should not be responsible for the "heavy lifting",
10902% the user should make sure that ImageMagick has already reduced the
10903% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010904% transparency prior to attempting to write the image with depth, color,
glennrp54dc0692011-07-01 19:02:13 +000010905% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010906%
cristy3ed852e2009-09-05 21:47:34 +000010907% Note that another definition, "png:bit-depth-written" exists, but it
10908% is not intended for external use. It is only used internally by the
10909% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10910%
10911% It is possible to request that the PNG encoder write previously-formatted
10912% ancillary chunks in the output PNG file, using the "-profile" commandline
10913% option as shown below or by setting the profile via a programming
10914% interface:
10915%
10916% -profile PNG-chunk-x:<file>
10917%
10918% where x is a location flag and <file> is a file containing the chunk
10919% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010920% This encoder will compute the chunk length and CRC, so those must not
10921% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010922%
10923% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10924% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10925% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010926% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010927%
glennrpbb8a7332010-11-13 15:17:35 +000010928% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010929%
glennrp3241bd02010-12-12 04:36:28 +000010930% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010931%
glennrpd6afd542010-11-19 01:53:05 +000010932% o 32-bit depth is reduced to 16.
10933% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10934% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010935% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010936% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010937% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010938% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10939% this can be done without loss and a larger bit depth N was not
cristy5d6fc9c2011-12-27 03:10:42 +000010940% requested via the "-define png:bit-depth=N" option.
glennrpd6afd542010-11-19 01:53:05 +000010941% o If matte channel is present but only one transparent color is
10942% present, RGB+tRNS is written instead of RGBA
10943% o Opaque matte channel is removed (or added, if color-type 4 or 6
10944% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010945%
cristy3ed852e2009-09-05 21:47:34 +000010946%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10947*/
10948static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +000010949 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010950{
10951 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010952 excluding,
10953 logging,
10954 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010955 status;
10956
10957 MngInfo
10958 *mng_info;
10959
10960 const char
10961 *value;
10962
10963 int
glennrp21f0e622011-01-07 16:20:57 +000010964 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010965 source;
10966
cristy3ed852e2009-09-05 21:47:34 +000010967 /*
10968 Open image file.
10969 */
10970 assert(image_info != (const ImageInfo *) NULL);
10971 assert(image_info->signature == MagickSignature);
10972 assert(image != (Image *) NULL);
10973 assert(image->signature == MagickSignature);
10974 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010975 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010976 /*
10977 Allocate a MngInfo structure.
10978 */
10979 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010980 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010981
cristy3ed852e2009-09-05 21:47:34 +000010982 if (mng_info == (MngInfo *) NULL)
10983 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010984
cristy3ed852e2009-09-05 21:47:34 +000010985 /*
10986 Initialize members of the MngInfo structure.
10987 */
10988 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10989 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010990 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010991 have_mng_structure=MagickTrue;
10992
10993 /* See if user has requested a specific PNG subformat */
10994
10995 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10996 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10997 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10998
10999 if (mng_info->write_png8)
11000 {
glennrp9c1eb072010-06-06 22:19:15 +000011001 mng_info->write_png_colortype = /* 3 */ 4;
11002 mng_info->write_png_depth = 8;
11003 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000011004 }
11005
11006 if (mng_info->write_png24)
11007 {
glennrp9c1eb072010-06-06 22:19:15 +000011008 mng_info->write_png_colortype = /* 2 */ 3;
11009 mng_info->write_png_depth = 8;
11010 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011011
glennrp9c1eb072010-06-06 22:19:15 +000011012 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011013 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011014
glennrp9c1eb072010-06-06 22:19:15 +000011015 else
cristy018f07f2011-09-04 21:15:19 +000011016 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011017
cristyea1a8aa2011-10-20 13:24:06 +000011018 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011019 }
11020
11021 if (mng_info->write_png32)
11022 {
glennrp9c1eb072010-06-06 22:19:15 +000011023 mng_info->write_png_colortype = /* 6 */ 7;
11024 mng_info->write_png_depth = 8;
11025 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011026
glennrp9c1eb072010-06-06 22:19:15 +000011027 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011028 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011029
glennrp9c1eb072010-06-06 22:19:15 +000011030 else
cristy018f07f2011-09-04 21:15:19 +000011031 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011032
cristyea1a8aa2011-10-20 13:24:06 +000011033 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011034 }
11035
11036 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011037
cristy3ed852e2009-09-05 21:47:34 +000011038 if (value != (char *) NULL)
11039 {
11040 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011041 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011042
cristy3ed852e2009-09-05 21:47:34 +000011043 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011044 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011045
cristy3ed852e2009-09-05 21:47:34 +000011046 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011047 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011048
cristy3ed852e2009-09-05 21:47:34 +000011049 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011050 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011051
cristy3ed852e2009-09-05 21:47:34 +000011052 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011053 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011054
glennrpbb8a7332010-11-13 15:17:35 +000011055 else
cristyc82a27b2011-10-21 01:07:16 +000011056 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011057 GetMagickModule(),CoderWarning,
11058 "ignoring invalid defined png:bit-depth",
11059 "=%s",value);
11060
cristy3ed852e2009-09-05 21:47:34 +000011061 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011063 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011064 }
glennrp0fe50b42010-11-16 03:52:51 +000011065
cristy3ed852e2009-09-05 21:47:34 +000011066 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011067
cristy3ed852e2009-09-05 21:47:34 +000011068 if (value != (char *) NULL)
11069 {
11070 /* We must store colortype+1 because 0 is a valid colortype */
11071 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011072 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011073
glennrpaa9c3c72012-01-30 21:14:50 +000011074 else if (LocaleCompare(value,"1") == 0)
11075 mng_info->write_png_colortype = 2;
11076
cristy3ed852e2009-09-05 21:47:34 +000011077 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011078 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011079
cristy3ed852e2009-09-05 21:47:34 +000011080 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011081 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011082
cristy3ed852e2009-09-05 21:47:34 +000011083 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011084 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011085
cristy3ed852e2009-09-05 21:47:34 +000011086 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011087 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011088
glennrpbb8a7332010-11-13 15:17:35 +000011089 else
cristyc82a27b2011-10-21 01:07:16 +000011090 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011091 GetMagickModule(),CoderWarning,
11092 "ignoring invalid defined png:color-type",
11093 "=%s",value);
11094
cristy3ed852e2009-09-05 21:47:34 +000011095 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011097 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011098 }
11099
glennrp0e8ea192010-12-24 18:00:33 +000011100 /* Check for chunks to be excluded:
11101 *
glennrp0dff56c2011-01-29 19:10:02 +000011102 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011103 * listed in the "unused_chunks" array, above.
11104 *
cristy5d6fc9c2011-12-27 03:10:42 +000011105 * Chunks can be listed for exclusion via a "png:exclude-chunk"
glennrp0e8ea192010-12-24 18:00:33 +000011106 * define (in the image properties or in the image artifacts)
11107 * or via a mng_info member. For convenience, in addition
11108 * to or instead of a comma-separated list of chunks, the
11109 * "exclude-chunk" string can be simply "all" or "none".
11110 *
11111 * The exclude-chunk define takes priority over the mng_info.
11112 *
cristy5d6fc9c2011-12-27 03:10:42 +000011113 * A "png:include-chunk" define takes priority over both the
11114 * mng_info and the "png:exclude-chunk" define. Like the
glennrp0e8ea192010-12-24 18:00:33 +000011115 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011116 * well as a comma-separated list. Chunks that are unknown to
11117 * ImageMagick are always excluded, regardless of their "copy-safe"
11118 * status according to the PNG specification, and even if they
glennrpaa192b12012-01-17 21:35:21 +000011119 * appear in the "include-chunk" list. Such defines appearing among
11120 * the image options take priority over those found among the image
11121 * artifacts.
glennrp0e8ea192010-12-24 18:00:33 +000011122 *
11123 * Finally, all chunks listed in the "unused_chunks" array are
11124 * automatically excluded, regardless of the other instructions
11125 * or lack thereof.
11126 *
11127 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11128 * will not be written and the gAMA chunk will only be written if it
11129 * is not between .45 and .46, or approximately (1.0/2.2).
11130 *
11131 * If you exclude tRNS and the image has transparency, the colortype
11132 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11133 *
11134 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011135 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011136 */
11137
glennrp26f37912010-12-23 16:22:42 +000011138 mng_info->ping_exclude_bKGD=MagickFalse;
11139 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011140 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011141 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11142 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011143 mng_info->ping_exclude_iCCP=MagickFalse;
11144 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11145 mng_info->ping_exclude_oFFs=MagickFalse;
11146 mng_info->ping_exclude_pHYs=MagickFalse;
11147 mng_info->ping_exclude_sRGB=MagickFalse;
11148 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011149 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011150 mng_info->ping_exclude_vpAg=MagickFalse;
11151 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11152 mng_info->ping_exclude_zTXt=MagickFalse;
11153
glennrp8d3d6e52011-04-19 04:39:51 +000011154 mng_info->ping_preserve_colormap=MagickFalse;
11155
11156 value=GetImageArtifact(image,"png:preserve-colormap");
11157 if (value == NULL)
11158 value=GetImageOption(image_info,"png:preserve-colormap");
11159 if (value != NULL)
11160 mng_info->ping_preserve_colormap=MagickTrue;
11161
glennrp18682582011-06-30 18:11:47 +000011162 /* Thes compression-level, compression-strategy, and compression-filter
11163 * defines take precedence over values from the -quality option.
11164 */
11165 value=GetImageArtifact(image,"png:compression-level");
11166 if (value == NULL)
11167 value=GetImageOption(image_info,"png:compression-level");
11168 if (value != NULL)
11169 {
glennrp18682582011-06-30 18:11:47 +000011170 /* We have to add 1 to everything because 0 is a valid input,
11171 * and we want to use 0 (the default) to mean undefined.
11172 */
11173 if (LocaleCompare(value,"0") == 0)
11174 mng_info->write_png_compression_level = 1;
11175
glennrpaa9c3c72012-01-30 21:14:50 +000011176 if (LocaleCompare(value,"1") == 0)
glennrp18682582011-06-30 18:11:47 +000011177 mng_info->write_png_compression_level = 2;
11178
11179 else if (LocaleCompare(value,"2") == 0)
11180 mng_info->write_png_compression_level = 3;
11181
11182 else if (LocaleCompare(value,"3") == 0)
11183 mng_info->write_png_compression_level = 4;
11184
11185 else if (LocaleCompare(value,"4") == 0)
11186 mng_info->write_png_compression_level = 5;
11187
11188 else if (LocaleCompare(value,"5") == 0)
11189 mng_info->write_png_compression_level = 6;
11190
11191 else if (LocaleCompare(value,"6") == 0)
11192 mng_info->write_png_compression_level = 7;
11193
11194 else if (LocaleCompare(value,"7") == 0)
11195 mng_info->write_png_compression_level = 8;
11196
11197 else if (LocaleCompare(value,"8") == 0)
11198 mng_info->write_png_compression_level = 9;
11199
11200 else if (LocaleCompare(value,"9") == 0)
11201 mng_info->write_png_compression_level = 10;
11202
11203 else
cristyc82a27b2011-10-21 01:07:16 +000011204 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011205 GetMagickModule(),CoderWarning,
11206 "ignoring invalid defined png:compression-level",
11207 "=%s",value);
11208 }
11209
11210 value=GetImageArtifact(image,"png:compression-strategy");
11211 if (value == NULL)
11212 value=GetImageOption(image_info,"png:compression-strategy");
11213 if (value != NULL)
11214 {
11215
11216 if (LocaleCompare(value,"0") == 0)
11217 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11218
11219 else if (LocaleCompare(value,"1") == 0)
11220 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11221
11222 else if (LocaleCompare(value,"2") == 0)
11223 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11224
11225 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011226#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011227 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011228#else
11229 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11230#endif
glennrp18682582011-06-30 18:11:47 +000011231
11232 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011233#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011234 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011235#else
11236 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11237#endif
glennrp18682582011-06-30 18:11:47 +000011238
11239 else
cristyc82a27b2011-10-21 01:07:16 +000011240 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011241 GetMagickModule(),CoderWarning,
11242 "ignoring invalid defined png:compression-strategy",
11243 "=%s",value);
11244 }
11245
11246 value=GetImageArtifact(image,"png:compression-filter");
11247 if (value == NULL)
11248 value=GetImageOption(image_info,"png:compression-filter");
11249 if (value != NULL)
11250 {
11251
11252 /* To do: combinations of filters allowed by libpng
11253 * masks 0x08 through 0xf8
11254 *
11255 * Implement this as a comma-separated list of 0,1,2,3,4,5
11256 * where 5 is a special case meaning PNG_ALL_FILTERS.
11257 */
11258
11259 if (LocaleCompare(value,"0") == 0)
11260 mng_info->write_png_compression_filter = 1;
11261
11262 if (LocaleCompare(value,"1") == 0)
11263 mng_info->write_png_compression_filter = 2;
11264
11265 else if (LocaleCompare(value,"2") == 0)
11266 mng_info->write_png_compression_filter = 3;
11267
11268 else if (LocaleCompare(value,"3") == 0)
11269 mng_info->write_png_compression_filter = 4;
11270
11271 else if (LocaleCompare(value,"4") == 0)
11272 mng_info->write_png_compression_filter = 5;
11273
11274 else if (LocaleCompare(value,"5") == 0)
11275 mng_info->write_png_compression_filter = 6;
11276
glennrp18682582011-06-30 18:11:47 +000011277 else
cristyc82a27b2011-10-21 01:07:16 +000011278 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011279 GetMagickModule(),CoderWarning,
11280 "ignoring invalid defined png:compression-filter",
11281 "=%s",value);
11282 }
11283
glennrp03812ae2010-12-24 01:31:34 +000011284 excluding=MagickFalse;
11285
glennrp5c7cf4e2010-12-24 00:30:00 +000011286 for (source=0; source<1; source++)
11287 {
11288 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011289 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011290 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011291
11292 if (value == NULL)
11293 value=GetImageArtifact(image,"png:exclude-chunks");
11294 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011295 else
glennrpacba0042010-12-24 14:27:26 +000011296 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011297 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011298
glennrpacba0042010-12-24 14:27:26 +000011299 if (value == NULL)
11300 value=GetImageOption(image_info,"png:exclude-chunks");
11301 }
11302
glennrp03812ae2010-12-24 01:31:34 +000011303 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011304 {
glennrp03812ae2010-12-24 01:31:34 +000011305
11306 size_t
11307 last;
11308
11309 excluding=MagickTrue;
11310
11311 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011312 {
11313 if (source == 0)
11314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11315 " png:exclude-chunk=%s found in image artifacts.\n", value);
11316 else
11317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11318 " png:exclude-chunk=%s found in image properties.\n", value);
11319 }
glennrp03812ae2010-12-24 01:31:34 +000011320
11321 last=strlen(value);
11322
11323 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011324 {
glennrp03812ae2010-12-24 01:31:34 +000011325
11326 if (LocaleNCompare(value+i,"all",3) == 0)
11327 {
11328 mng_info->ping_exclude_bKGD=MagickTrue;
11329 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011330 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011331 mng_info->ping_exclude_EXIF=MagickTrue;
11332 mng_info->ping_exclude_gAMA=MagickTrue;
11333 mng_info->ping_exclude_iCCP=MagickTrue;
11334 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11335 mng_info->ping_exclude_oFFs=MagickTrue;
11336 mng_info->ping_exclude_pHYs=MagickTrue;
11337 mng_info->ping_exclude_sRGB=MagickTrue;
11338 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011339 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011340 mng_info->ping_exclude_vpAg=MagickTrue;
11341 mng_info->ping_exclude_zCCP=MagickTrue;
11342 mng_info->ping_exclude_zTXt=MagickTrue;
11343 i--;
11344 }
glennrp2cc891a2010-12-24 13:44:32 +000011345
glennrp03812ae2010-12-24 01:31:34 +000011346 if (LocaleNCompare(value+i,"none",4) == 0)
11347 {
11348 mng_info->ping_exclude_bKGD=MagickFalse;
11349 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011350 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011351 mng_info->ping_exclude_EXIF=MagickFalse;
11352 mng_info->ping_exclude_gAMA=MagickFalse;
11353 mng_info->ping_exclude_iCCP=MagickFalse;
11354 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11355 mng_info->ping_exclude_oFFs=MagickFalse;
11356 mng_info->ping_exclude_pHYs=MagickFalse;
11357 mng_info->ping_exclude_sRGB=MagickFalse;
11358 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011359 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011360 mng_info->ping_exclude_vpAg=MagickFalse;
11361 mng_info->ping_exclude_zCCP=MagickFalse;
11362 mng_info->ping_exclude_zTXt=MagickFalse;
11363 }
glennrp2cc891a2010-12-24 13:44:32 +000011364
glennrp03812ae2010-12-24 01:31:34 +000011365 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11366 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011367
glennrp03812ae2010-12-24 01:31:34 +000011368 if (LocaleNCompare(value+i,"chrm",4) == 0)
11369 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011370
glennrpa0ed0092011-04-18 16:36:29 +000011371 if (LocaleNCompare(value+i,"date",4) == 0)
11372 mng_info->ping_exclude_date=MagickTrue;
11373
glennrp03812ae2010-12-24 01:31:34 +000011374 if (LocaleNCompare(value+i,"exif",4) == 0)
11375 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011376
glennrp03812ae2010-12-24 01:31:34 +000011377 if (LocaleNCompare(value+i,"gama",4) == 0)
11378 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011379
glennrp03812ae2010-12-24 01:31:34 +000011380 if (LocaleNCompare(value+i,"iccp",4) == 0)
11381 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011382
glennrp03812ae2010-12-24 01:31:34 +000011383 /*
11384 if (LocaleNCompare(value+i,"itxt",4) == 0)
11385 mng_info->ping_exclude_iTXt=MagickTrue;
11386 */
glennrp2cc891a2010-12-24 13:44:32 +000011387
glennrp03812ae2010-12-24 01:31:34 +000011388 if (LocaleNCompare(value+i,"gama",4) == 0)
11389 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011390
glennrp03812ae2010-12-24 01:31:34 +000011391 if (LocaleNCompare(value+i,"offs",4) == 0)
11392 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011393
glennrp03812ae2010-12-24 01:31:34 +000011394 if (LocaleNCompare(value+i,"phys",4) == 0)
11395 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011396
glennrpa1e3b7b2010-12-24 16:37:33 +000011397 if (LocaleNCompare(value+i,"srgb",4) == 0)
11398 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011399
glennrp03812ae2010-12-24 01:31:34 +000011400 if (LocaleNCompare(value+i,"text",4) == 0)
11401 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011402
glennrpa1e3b7b2010-12-24 16:37:33 +000011403 if (LocaleNCompare(value+i,"trns",4) == 0)
11404 mng_info->ping_exclude_tRNS=MagickTrue;
11405
glennrp03812ae2010-12-24 01:31:34 +000011406 if (LocaleNCompare(value+i,"vpag",4) == 0)
11407 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011408
glennrp03812ae2010-12-24 01:31:34 +000011409 if (LocaleNCompare(value+i,"zccp",4) == 0)
11410 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011411
glennrp03812ae2010-12-24 01:31:34 +000011412 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11413 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011414
glennrp03812ae2010-12-24 01:31:34 +000011415 }
glennrpce91ed52010-12-23 22:37:49 +000011416 }
glennrp26f37912010-12-23 16:22:42 +000011417 }
11418
glennrp5c7cf4e2010-12-24 00:30:00 +000011419 for (source=0; source<1; source++)
11420 {
11421 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011422 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011423 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011424
11425 if (value == NULL)
11426 value=GetImageArtifact(image,"png:include-chunks");
11427 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011428 else
glennrpacba0042010-12-24 14:27:26 +000011429 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011430 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011431
glennrpacba0042010-12-24 14:27:26 +000011432 if (value == NULL)
11433 value=GetImageOption(image_info,"png:include-chunks");
11434 }
11435
glennrp03812ae2010-12-24 01:31:34 +000011436 if (value != NULL)
11437 {
11438 size_t
11439 last;
glennrp26f37912010-12-23 16:22:42 +000011440
glennrp03812ae2010-12-24 01:31:34 +000011441 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011442
glennrp03812ae2010-12-24 01:31:34 +000011443 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011444 {
11445 if (source == 0)
11446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11447 " png:include-chunk=%s found in image artifacts.\n", value);
11448 else
11449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11450 " png:include-chunk=%s found in image properties.\n", value);
11451 }
glennrp03812ae2010-12-24 01:31:34 +000011452
11453 last=strlen(value);
11454
11455 for (i=0; i<(int) last; i+=5)
11456 {
11457 if (LocaleNCompare(value+i,"all",3) == 0)
11458 {
11459 mng_info->ping_exclude_bKGD=MagickFalse;
11460 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011461 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011462 mng_info->ping_exclude_EXIF=MagickFalse;
11463 mng_info->ping_exclude_gAMA=MagickFalse;
11464 mng_info->ping_exclude_iCCP=MagickFalse;
11465 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11466 mng_info->ping_exclude_oFFs=MagickFalse;
11467 mng_info->ping_exclude_pHYs=MagickFalse;
11468 mng_info->ping_exclude_sRGB=MagickFalse;
11469 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011470 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011471 mng_info->ping_exclude_vpAg=MagickFalse;
11472 mng_info->ping_exclude_zCCP=MagickFalse;
11473 mng_info->ping_exclude_zTXt=MagickFalse;
11474 i--;
11475 }
glennrp2cc891a2010-12-24 13:44:32 +000011476
glennrp03812ae2010-12-24 01:31:34 +000011477 if (LocaleNCompare(value+i,"none",4) == 0)
11478 {
11479 mng_info->ping_exclude_bKGD=MagickTrue;
11480 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011481 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011482 mng_info->ping_exclude_EXIF=MagickTrue;
11483 mng_info->ping_exclude_gAMA=MagickTrue;
11484 mng_info->ping_exclude_iCCP=MagickTrue;
11485 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11486 mng_info->ping_exclude_oFFs=MagickTrue;
11487 mng_info->ping_exclude_pHYs=MagickTrue;
11488 mng_info->ping_exclude_sRGB=MagickTrue;
11489 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011490 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011491 mng_info->ping_exclude_vpAg=MagickTrue;
11492 mng_info->ping_exclude_zCCP=MagickTrue;
11493 mng_info->ping_exclude_zTXt=MagickTrue;
11494 }
glennrp2cc891a2010-12-24 13:44:32 +000011495
glennrp03812ae2010-12-24 01:31:34 +000011496 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11497 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011498
glennrp03812ae2010-12-24 01:31:34 +000011499 if (LocaleNCompare(value+i,"chrm",4) == 0)
11500 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011501
glennrpa0ed0092011-04-18 16:36:29 +000011502 if (LocaleNCompare(value+i,"date",4) == 0)
11503 mng_info->ping_exclude_date=MagickFalse;
11504
glennrp03812ae2010-12-24 01:31:34 +000011505 if (LocaleNCompare(value+i,"exif",4) == 0)
11506 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011507
glennrp03812ae2010-12-24 01:31:34 +000011508 if (LocaleNCompare(value+i,"gama",4) == 0)
11509 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011510
glennrp03812ae2010-12-24 01:31:34 +000011511 if (LocaleNCompare(value+i,"iccp",4) == 0)
11512 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011513
glennrp03812ae2010-12-24 01:31:34 +000011514 /*
11515 if (LocaleNCompare(value+i,"itxt",4) == 0)
11516 mng_info->ping_exclude_iTXt=MagickFalse;
11517 */
glennrp2cc891a2010-12-24 13:44:32 +000011518
glennrp03812ae2010-12-24 01:31:34 +000011519 if (LocaleNCompare(value+i,"gama",4) == 0)
11520 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011521
glennrp03812ae2010-12-24 01:31:34 +000011522 if (LocaleNCompare(value+i,"offs",4) == 0)
11523 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011524
glennrp03812ae2010-12-24 01:31:34 +000011525 if (LocaleNCompare(value+i,"phys",4) == 0)
11526 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011527
glennrpa1e3b7b2010-12-24 16:37:33 +000011528 if (LocaleNCompare(value+i,"srgb",4) == 0)
11529 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011530
glennrp03812ae2010-12-24 01:31:34 +000011531 if (LocaleNCompare(value+i,"text",4) == 0)
11532 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011533
glennrpa1e3b7b2010-12-24 16:37:33 +000011534 if (LocaleNCompare(value+i,"trns",4) == 0)
11535 mng_info->ping_exclude_tRNS=MagickFalse;
11536
glennrp03812ae2010-12-24 01:31:34 +000011537 if (LocaleNCompare(value+i,"vpag",4) == 0)
11538 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011539
glennrp03812ae2010-12-24 01:31:34 +000011540 if (LocaleNCompare(value+i,"zccp",4) == 0)
11541 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011542
glennrp03812ae2010-12-24 01:31:34 +000011543 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11544 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011545
glennrp03812ae2010-12-24 01:31:34 +000011546 }
glennrpce91ed52010-12-23 22:37:49 +000011547 }
glennrp26f37912010-12-23 16:22:42 +000011548 }
11549
glennrp03812ae2010-12-24 01:31:34 +000011550 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011551 {
11552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011553 " Chunks to be excluded from the output png:");
glennrp26f37912010-12-23 16:22:42 +000011554 if (mng_info->ping_exclude_bKGD != MagickFalse)
11555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11556 " bKGD");
11557 if (mng_info->ping_exclude_cHRM != MagickFalse)
11558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11559 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011560 if (mng_info->ping_exclude_date != MagickFalse)
11561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11562 " date");
glennrp26f37912010-12-23 16:22:42 +000011563 if (mng_info->ping_exclude_EXIF != MagickFalse)
11564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11565 " EXIF");
11566 if (mng_info->ping_exclude_gAMA != MagickFalse)
11567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11568 " gAMA");
11569 if (mng_info->ping_exclude_iCCP != MagickFalse)
11570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11571 " iCCP");
11572/*
11573 if (mng_info->ping_exclude_iTXt != MagickFalse)
11574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11575 " iTXt");
11576*/
11577 if (mng_info->ping_exclude_oFFs != MagickFalse)
11578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11579 " oFFs");
11580 if (mng_info->ping_exclude_pHYs != MagickFalse)
11581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11582 " pHYs");
11583 if (mng_info->ping_exclude_sRGB != MagickFalse)
11584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11585 " sRGB");
11586 if (mng_info->ping_exclude_tEXt != MagickFalse)
11587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11588 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011589 if (mng_info->ping_exclude_tRNS != MagickFalse)
11590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11591 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011592 if (mng_info->ping_exclude_vpAg != MagickFalse)
11593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11594 " vpAg");
11595 if (mng_info->ping_exclude_zCCP != MagickFalse)
11596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11597 " zCCP");
11598 if (mng_info->ping_exclude_zTXt != MagickFalse)
11599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11600 " zTXt");
11601 }
11602
glennrpb9cfe272010-12-21 15:08:06 +000011603 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011604
cristy018f07f2011-09-04 21:15:19 +000011605 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011606
11607 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011608
cristy3ed852e2009-09-05 21:47:34 +000011609 if (logging != MagickFalse)
11610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011611
cristy3ed852e2009-09-05 21:47:34 +000011612 return(status);
11613}
11614
11615#if defined(JNG_SUPPORTED)
11616
11617/* Write one JNG image */
11618static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +000011619 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011620{
11621 Image
11622 *jpeg_image;
11623
11624 ImageInfo
11625 *jpeg_image_info;
11626
11627 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011628 logging,
cristy3ed852e2009-09-05 21:47:34 +000011629 status;
11630
11631 size_t
11632 length;
11633
11634 unsigned char
11635 *blob,
11636 chunk[80],
11637 *p;
11638
11639 unsigned int
11640 jng_alpha_compression_method,
11641 jng_alpha_sample_depth,
11642 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011643 transparent;
11644
cristybb503372010-05-27 20:51:26 +000011645 size_t
glennrp59575fa2011-12-31 21:31:39 +000011646 jng_alpha_quality,
cristy3ed852e2009-09-05 21:47:34 +000011647 jng_quality;
11648
11649 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011650 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011651
11652 blob=(unsigned char *) NULL;
11653 jpeg_image=(Image *) NULL;
11654 jpeg_image_info=(ImageInfo *) NULL;
11655
11656 status=MagickTrue;
11657 transparent=image_info->type==GrayscaleMatteType ||
glennrp59575fa2011-12-31 21:31:39 +000011658 image_info->type==TrueColorMatteType || image->matte != MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +000011659
glennrp59575fa2011-12-31 21:31:39 +000011660 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
11661
11662 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
11663
11664 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
11665 image_info->quality;
11666
11667 if (jng_alpha_quality >= 1000)
11668 jng_alpha_quality /= 1000;
cristy3ed852e2009-09-05 21:47:34 +000011669
11670 if (transparent)
11671 {
11672 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011673
cristy3ed852e2009-09-05 21:47:34 +000011674 /* Create JPEG blob, image, and image_info */
11675 if (logging != MagickFalse)
11676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +000011677 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011678
cristy3ed852e2009-09-05 21:47:34 +000011679 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011680
cristy3ed852e2009-09-05 21:47:34 +000011681 if (jpeg_image_info == (ImageInfo *) NULL)
11682 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011683
cristy3ed852e2009-09-05 21:47:34 +000011684 if (logging != MagickFalse)
11685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11686 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011687
cristy262346f2012-01-11 19:34:10 +000011688 jpeg_image=SeparateImage(image,AlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +000011689 if (jpeg_image == (Image *) NULL)
11690 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11691 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +000011692 jpeg_image->matte=MagickFalse;
glennrp59575fa2011-12-31 21:31:39 +000011693 jpeg_image->quality=jng_alpha_quality;
cristy3ed852e2009-09-05 21:47:34 +000011694 jpeg_image_info->type=GrayscaleType;
cristy018f07f2011-09-04 21:15:19 +000011695 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000011696 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011697 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011698 "%s",jpeg_image->filename);
11699 }
glennrp59575fa2011-12-31 21:31:39 +000011700 else
11701 {
11702 jng_alpha_compression_method=0;
11703 jng_color_type=10;
11704 jng_alpha_sample_depth=0;
11705 }
cristy3ed852e2009-09-05 21:47:34 +000011706
11707 /* To do: check bit depth of PNG alpha channel */
11708
11709 /* Check if image is grayscale. */
11710 if (image_info->type != TrueColorMatteType && image_info->type !=
cristyc82a27b2011-10-21 01:07:16 +000011711 TrueColorType && ImageIsGray(image,exception))
cristy3ed852e2009-09-05 21:47:34 +000011712 jng_color_type-=2;
11713
glennrp59575fa2011-12-31 21:31:39 +000011714 if (logging != MagickFalse)
11715 {
11716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11717 " JNG Quality = %d",(int) jng_quality);
11718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11719 " JNG Color Type = %d",jng_color_type);
11720 if (transparent)
11721 {
11722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11723 " JNG Alpha Compression = %d",jng_alpha_compression_method);
11724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11725 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
11726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11727 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
11728 }
11729 }
11730
cristy3ed852e2009-09-05 21:47:34 +000011731 if (transparent)
11732 {
11733 if (jng_alpha_compression_method==0)
11734 {
11735 const char
11736 *value;
11737
cristy4c08aed2011-07-01 19:47:50 +000011738 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011739 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000011740 exception);
cristy3ed852e2009-09-05 21:47:34 +000011741 if (logging != MagickFalse)
11742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11743 " Creating PNG blob.");
11744 length=0;
11745
11746 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11747 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11748 jpeg_image_info->interlace=NoInterlace;
11749
glennrpcc5d45b2012-01-06 04:06:10 +000011750 /* Exclude all ancillary chunks */
11751 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
11752
cristy3ed852e2009-09-05 21:47:34 +000011753 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyc82a27b2011-10-21 01:07:16 +000011754 exception);
cristy3ed852e2009-09-05 21:47:34 +000011755
11756 /* Retrieve sample depth used */
cristyd15e6592011-10-15 00:13:06 +000011757 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
cristy3ed852e2009-09-05 21:47:34 +000011758 if (value != (char *) NULL)
11759 jng_alpha_sample_depth= (unsigned int) value[0];
11760 }
11761 else
11762 {
cristy4c08aed2011-07-01 19:47:50 +000011763 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011764
11765 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000011766 exception);
cristy3ed852e2009-09-05 21:47:34 +000011767
11768 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11769 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11770 jpeg_image_info->interlace=NoInterlace;
11771 if (logging != MagickFalse)
11772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11773 " Creating blob.");
11774 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyc82a27b2011-10-21 01:07:16 +000011775 exception);
cristy3ed852e2009-09-05 21:47:34 +000011776 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011777
cristy3ed852e2009-09-05 21:47:34 +000011778 if (logging != MagickFalse)
11779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011780 " Successfully read jpeg_image into a blob, length=%.20g.",
11781 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011782
11783 }
11784 /* Destroy JPEG image and image_info */
11785 jpeg_image=DestroyImage(jpeg_image);
11786 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11787 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11788 }
11789
11790 /* Write JHDR chunk */
11791 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11792 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011793 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011794 PNGLong(chunk+4,(png_uint_32) image->columns);
11795 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011796 chunk[12]=jng_color_type;
11797 chunk[13]=8; /* sample depth */
11798 chunk[14]=8; /*jng_image_compression_method */
11799 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11800 chunk[16]=jng_alpha_sample_depth;
11801 chunk[17]=jng_alpha_compression_method;
11802 chunk[18]=0; /*jng_alpha_filter_method */
11803 chunk[19]=0; /*jng_alpha_interlace_method */
11804 (void) WriteBlob(image,20,chunk);
11805 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11806 if (logging != MagickFalse)
11807 {
11808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011809 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011810
cristy3ed852e2009-09-05 21:47:34 +000011811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011812 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011813
cristy3ed852e2009-09-05 21:47:34 +000011814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11815 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011816
cristy3ed852e2009-09-05 21:47:34 +000011817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11818 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011819
cristy3ed852e2009-09-05 21:47:34 +000011820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11821 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011822
cristy3ed852e2009-09-05 21:47:34 +000011823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11824 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011825
cristy3ed852e2009-09-05 21:47:34 +000011826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11827 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011828
cristy3ed852e2009-09-05 21:47:34 +000011829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11830 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011831
cristy3ed852e2009-09-05 21:47:34 +000011832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11833 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011834
cristy3ed852e2009-09-05 21:47:34 +000011835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11836 " JNG alpha interlace:%5d",0);
11837 }
11838
glennrp0fe50b42010-11-16 03:52:51 +000011839 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011840 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011841
11842 /*
11843 Write leading ancillary chunks
11844 */
11845
11846 if (transparent)
11847 {
11848 /*
11849 Write JNG bKGD chunk
11850 */
11851
11852 unsigned char
11853 blue,
11854 green,
11855 red;
11856
cristybb503372010-05-27 20:51:26 +000011857 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011858 num_bytes;
11859
11860 if (jng_color_type == 8 || jng_color_type == 12)
11861 num_bytes=6L;
11862 else
11863 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011864 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011865 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011866 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011867 red=ScaleQuantumToChar(image->background_color.red);
11868 green=ScaleQuantumToChar(image->background_color.green);
11869 blue=ScaleQuantumToChar(image->background_color.blue);
11870 *(chunk+4)=0;
11871 *(chunk+5)=red;
11872 *(chunk+6)=0;
11873 *(chunk+7)=green;
11874 *(chunk+8)=0;
11875 *(chunk+9)=blue;
11876 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11877 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11878 }
11879
11880 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11881 {
11882 /*
11883 Write JNG sRGB chunk
11884 */
11885 (void) WriteBlobMSBULong(image,1L);
11886 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011887 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011888
cristy3ed852e2009-09-05 21:47:34 +000011889 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011890 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011891 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011892 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011893
cristy3ed852e2009-09-05 21:47:34 +000011894 else
glennrpe610a072010-08-05 17:08:46 +000011895 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011896 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011897 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011898
cristy3ed852e2009-09-05 21:47:34 +000011899 (void) WriteBlob(image,5,chunk);
11900 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11901 }
11902 else
11903 {
11904 if (image->gamma != 0.0)
11905 {
11906 /*
11907 Write JNG gAMA chunk
11908 */
11909 (void) WriteBlobMSBULong(image,4L);
11910 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011911 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011912 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011913 (void) WriteBlob(image,8,chunk);
11914 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11915 }
glennrp0fe50b42010-11-16 03:52:51 +000011916
cristy3ed852e2009-09-05 21:47:34 +000011917 if ((mng_info->equal_chrms == MagickFalse) &&
11918 (image->chromaticity.red_primary.x != 0.0))
11919 {
11920 PrimaryInfo
11921 primary;
11922
11923 /*
11924 Write JNG cHRM chunk
11925 */
11926 (void) WriteBlobMSBULong(image,32L);
11927 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011928 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011929 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011930 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11931 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011932 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011933 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11934 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011935 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011936 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11937 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011938 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011939 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11940 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011941 (void) WriteBlob(image,36,chunk);
11942 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11943 }
11944 }
glennrp0fe50b42010-11-16 03:52:51 +000011945
cristy2a11bef2011-10-28 18:33:11 +000011946 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000011947 {
11948 /*
11949 Write JNG pHYs chunk
11950 */
11951 (void) WriteBlobMSBULong(image,9L);
11952 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011953 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011954 if (image->units == PixelsPerInchResolution)
11955 {
cristy35ef8242010-06-03 16:24:13 +000011956 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011957 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011958
cristy35ef8242010-06-03 16:24:13 +000011959 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011960 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011961
cristy3ed852e2009-09-05 21:47:34 +000011962 chunk[12]=1;
11963 }
glennrp0fe50b42010-11-16 03:52:51 +000011964
cristy3ed852e2009-09-05 21:47:34 +000011965 else
11966 {
11967 if (image->units == PixelsPerCentimeterResolution)
11968 {
cristy35ef8242010-06-03 16:24:13 +000011969 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011970 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011971
cristy35ef8242010-06-03 16:24:13 +000011972 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011973 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011974
cristy3ed852e2009-09-05 21:47:34 +000011975 chunk[12]=1;
11976 }
glennrp0fe50b42010-11-16 03:52:51 +000011977
cristy3ed852e2009-09-05 21:47:34 +000011978 else
11979 {
cristy2a11bef2011-10-28 18:33:11 +000011980 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
11981 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011982 chunk[12]=0;
11983 }
11984 }
11985 (void) WriteBlob(image,13,chunk);
11986 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11987 }
11988
11989 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11990 {
11991 /*
11992 Write JNG oFFs chunk
11993 */
11994 (void) WriteBlobMSBULong(image,9L);
11995 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011996 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011997 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11998 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011999 chunk[12]=0;
12000 (void) WriteBlob(image,13,chunk);
12001 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12002 }
12003 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12004 {
12005 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12006 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000012007 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000012008 PNGLong(chunk+4,(png_uint_32) image->page.width);
12009 PNGLong(chunk+8,(png_uint_32) image->page.height);
12010 chunk[12]=0; /* unit = pixels */
12011 (void) WriteBlob(image,13,chunk);
12012 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12013 }
12014
12015
12016 if (transparent)
12017 {
12018 if (jng_alpha_compression_method==0)
12019 {
cristybb503372010-05-27 20:51:26 +000012020 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012021 i;
12022
cristybb503372010-05-27 20:51:26 +000012023 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012024 len;
12025
12026 /* Write IDAT chunk header */
12027 if (logging != MagickFalse)
12028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012029 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000012030 length);
cristy3ed852e2009-09-05 21:47:34 +000012031
12032 /* Copy IDAT chunks */
12033 len=0;
12034 p=blob+8;
cristybb503372010-05-27 20:51:26 +000012035 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000012036 {
12037 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12038 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012039
cristy3ed852e2009-09-05 21:47:34 +000012040 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12041 {
12042 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000012043 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000012044 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000012045 (void) WriteBlob(image,(size_t) len+4,p);
12046 (void) WriteBlobMSBULong(image,
12047 crc32(0,p,(uInt) len+4));
12048 }
glennrp0fe50b42010-11-16 03:52:51 +000012049
cristy3ed852e2009-09-05 21:47:34 +000012050 else
12051 {
12052 if (logging != MagickFalse)
12053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012054 " Skipping %c%c%c%c chunk, length=%.20g.",
12055 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012056 }
12057 p+=(8+len);
12058 }
12059 }
12060 else
12061 {
12062 /* Write JDAA chunk header */
12063 if (logging != MagickFalse)
12064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012065 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012066 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012067 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012068 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012069 /* Write JDAT chunk(s) data */
12070 (void) WriteBlob(image,4,chunk);
12071 (void) WriteBlob(image,length,blob);
12072 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12073 (uInt) length));
12074 }
12075 blob=(unsigned char *) RelinquishMagickMemory(blob);
12076 }
12077
12078 /* Encode image as a JPEG blob */
12079 if (logging != MagickFalse)
12080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12081 " Creating jpeg_image_info.");
12082 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12083 if (jpeg_image_info == (ImageInfo *) NULL)
12084 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12085
12086 if (logging != MagickFalse)
12087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12088 " Creating jpeg_image.");
12089
cristyc82a27b2011-10-21 01:07:16 +000012090 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012091 if (jpeg_image == (Image *) NULL)
12092 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12093 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12094
12095 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012096 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012097 jpeg_image->filename);
12098
12099 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000012100 exception);
cristy3ed852e2009-09-05 21:47:34 +000012101
12102 if (logging != MagickFalse)
12103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012104 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12105 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012106
12107 if (jng_color_type == 8 || jng_color_type == 12)
12108 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012109
glennrp59575fa2011-12-31 21:31:39 +000012110 jpeg_image_info->quality=jng_quality;
12111 jpeg_image->quality=jng_quality;
cristy3ed852e2009-09-05 21:47:34 +000012112 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12113 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012114
cristy3ed852e2009-09-05 21:47:34 +000012115 if (logging != MagickFalse)
12116 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12117 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012118
cristyc82a27b2011-10-21 01:07:16 +000012119 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
glennrp0fe50b42010-11-16 03:52:51 +000012120
cristy3ed852e2009-09-05 21:47:34 +000012121 if (logging != MagickFalse)
12122 {
12123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012124 " Successfully read jpeg_image into a blob, length=%.20g.",
12125 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012126
12127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012128 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012129 }
glennrp0fe50b42010-11-16 03:52:51 +000012130
cristy3ed852e2009-09-05 21:47:34 +000012131 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012132 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012133 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012134 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012135 (void) WriteBlob(image,4,chunk);
12136 (void) WriteBlob(image,length,blob);
12137 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12138
12139 jpeg_image=DestroyImage(jpeg_image);
12140 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12141 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12142 blob=(unsigned char *) RelinquishMagickMemory(blob);
12143
12144 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012145 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012146
12147 /* Write IEND chunk */
12148 (void) WriteBlobMSBULong(image,0L);
12149 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012150 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012151 (void) WriteBlob(image,4,chunk);
12152 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12153
12154 if (logging != MagickFalse)
12155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12156 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012157
cristy3ed852e2009-09-05 21:47:34 +000012158 return(status);
12159}
12160
12161
12162/*
12163%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12164% %
12165% %
12166% %
12167% W r i t e J N G I m a g e %
12168% %
12169% %
12170% %
12171%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12172%
12173% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12174%
12175% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12176%
12177% The format of the WriteJNGImage method is:
12178%
cristy1e178e72011-08-28 19:44:34 +000012179% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12180% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012181%
12182% A description of each parameter follows:
12183%
12184% o image_info: the image info.
12185%
12186% o image: The image.
12187%
cristy1e178e72011-08-28 19:44:34 +000012188% o exception: return any errors or warnings in this structure.
12189%
cristy3ed852e2009-09-05 21:47:34 +000012190%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12191*/
cristy1e178e72011-08-28 19:44:34 +000012192static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12193 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012194{
12195 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012196 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012197 logging,
cristy3ed852e2009-09-05 21:47:34 +000012198 status;
12199
12200 MngInfo
12201 *mng_info;
12202
cristy3ed852e2009-09-05 21:47:34 +000012203 /*
12204 Open image file.
12205 */
12206 assert(image_info != (const ImageInfo *) NULL);
12207 assert(image_info->signature == MagickSignature);
12208 assert(image != (Image *) NULL);
12209 assert(image->signature == MagickSignature);
12210 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012211 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristyc82a27b2011-10-21 01:07:16 +000012212 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012213 if (status == MagickFalse)
12214 return(status);
12215
12216 /*
12217 Allocate a MngInfo structure.
12218 */
12219 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012220 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012221 if (mng_info == (MngInfo *) NULL)
12222 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12223 /*
12224 Initialize members of the MngInfo structure.
12225 */
12226 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12227 mng_info->image=image;
12228 have_mng_structure=MagickTrue;
12229
12230 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12231
cristy018f07f2011-09-04 21:15:19 +000012232 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012233 (void) CloseBlob(image);
12234
12235 (void) CatchImageException(image);
12236 MngInfoFreeStruct(mng_info,&have_mng_structure);
12237 if (logging != MagickFalse)
12238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12239 return(status);
12240}
12241#endif
12242
cristy1e178e72011-08-28 19:44:34 +000012243static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12244 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012245{
12246 const char
12247 *option;
12248
12249 Image
12250 *next_image;
12251
12252 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012253 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012254 status;
12255
glennrp03812ae2010-12-24 01:31:34 +000012256 volatile MagickBooleanType
12257 logging;
12258
cristy3ed852e2009-09-05 21:47:34 +000012259 MngInfo
12260 *mng_info;
12261
12262 int
cristy3ed852e2009-09-05 21:47:34 +000012263 image_count,
12264 need_iterations,
12265 need_matte;
12266
12267 volatile int
12268#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12269 defined(PNG_MNG_FEATURES_SUPPORTED)
12270 need_local_plte,
12271#endif
12272 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012273 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012274 use_global_plte;
12275
cristybb503372010-05-27 20:51:26 +000012276 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012277 i;
12278
12279 unsigned char
12280 chunk[800];
12281
12282 volatile unsigned int
12283 write_jng,
12284 write_mng;
12285
cristybb503372010-05-27 20:51:26 +000012286 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012287 scene;
12288
cristybb503372010-05-27 20:51:26 +000012289 size_t
cristy3ed852e2009-09-05 21:47:34 +000012290 final_delay=0,
12291 initial_delay;
12292
glennrpd5045b42010-03-24 12:40:35 +000012293#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012294 if (image_info->verbose)
12295 printf("Your PNG library (libpng-%s) is rather old.\n",
12296 PNG_LIBPNG_VER_STRING);
12297#endif
12298
12299 /*
12300 Open image file.
12301 */
12302 assert(image_info != (const ImageInfo *) NULL);
12303 assert(image_info->signature == MagickSignature);
12304 assert(image != (Image *) NULL);
12305 assert(image->signature == MagickSignature);
12306 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012307 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristyc82a27b2011-10-21 01:07:16 +000012308 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012309 if (status == MagickFalse)
12310 return(status);
12311
12312 /*
12313 Allocate a MngInfo structure.
12314 */
12315 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012316 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012317 if (mng_info == (MngInfo *) NULL)
12318 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12319 /*
12320 Initialize members of the MngInfo structure.
12321 */
12322 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12323 mng_info->image=image;
12324 have_mng_structure=MagickTrue;
12325 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12326
12327 /*
12328 * See if user has requested a specific PNG subformat to be used
12329 * for all of the PNGs in the MNG being written, e.g.,
12330 *
12331 * convert *.png png8:animation.mng
12332 *
12333 * To do: check -define png:bit_depth and png:color_type as well,
12334 * or perhaps use mng:bit_depth and mng:color_type instead for
12335 * global settings.
12336 */
12337
12338 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12339 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12340 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12341
12342 write_jng=MagickFalse;
12343 if (image_info->compression == JPEGCompression)
12344 write_jng=MagickTrue;
12345
12346 mng_info->adjoin=image_info->adjoin &&
12347 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12348
cristy3ed852e2009-09-05 21:47:34 +000012349 if (logging != MagickFalse)
12350 {
12351 /* Log some info about the input */
12352 Image
12353 *p;
12354
12355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12356 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012357
cristy3ed852e2009-09-05 21:47:34 +000012358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012359 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012360
cristy3ed852e2009-09-05 21:47:34 +000012361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12362 " Type: %d",image_info->type);
12363
12364 scene=0;
12365 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12366 {
12367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012368 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012369
cristy3ed852e2009-09-05 21:47:34 +000012370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012371 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012372
cristy3ed852e2009-09-05 21:47:34 +000012373 if (p->matte)
12374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12375 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012376
cristy3ed852e2009-09-05 21:47:34 +000012377 else
12378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12379 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012380
cristy3ed852e2009-09-05 21:47:34 +000012381 if (p->storage_class == PseudoClass)
12382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12383 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012384
cristy3ed852e2009-09-05 21:47:34 +000012385 else
12386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12387 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012388
cristy3ed852e2009-09-05 21:47:34 +000012389 if (p->colors)
12390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012391 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012392
cristy3ed852e2009-09-05 21:47:34 +000012393 else
12394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12395 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012396
cristy3ed852e2009-09-05 21:47:34 +000012397 if (mng_info->adjoin == MagickFalse)
12398 break;
12399 }
12400 }
12401
cristy3ed852e2009-09-05 21:47:34 +000012402 use_global_plte=MagickFalse;
12403 all_images_are_gray=MagickFalse;
12404#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12405 need_local_plte=MagickTrue;
12406#endif
12407 need_defi=MagickFalse;
12408 need_matte=MagickFalse;
12409 mng_info->framing_mode=1;
12410 mng_info->old_framing_mode=1;
12411
12412 if (write_mng)
12413 if (image_info->page != (char *) NULL)
12414 {
12415 /*
12416 Determine image bounding box.
12417 */
12418 SetGeometry(image,&mng_info->page);
12419 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12420 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12421 }
12422 if (write_mng)
12423 {
12424 unsigned int
12425 need_geom;
12426
12427 unsigned short
12428 red,
12429 green,
12430 blue;
12431
12432 mng_info->page=image->page;
12433 need_geom=MagickTrue;
12434 if (mng_info->page.width || mng_info->page.height)
12435 need_geom=MagickFalse;
12436 /*
12437 Check all the scenes.
12438 */
12439 initial_delay=image->delay;
12440 need_iterations=MagickFalse;
12441 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12442 mng_info->equal_physs=MagickTrue,
12443 mng_info->equal_gammas=MagickTrue;
12444 mng_info->equal_srgbs=MagickTrue;
12445 mng_info->equal_backgrounds=MagickTrue;
12446 image_count=0;
12447#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12448 defined(PNG_MNG_FEATURES_SUPPORTED)
12449 all_images_are_gray=MagickTrue;
12450 mng_info->equal_palettes=MagickFalse;
12451 need_local_plte=MagickFalse;
12452#endif
12453 for (next_image=image; next_image != (Image *) NULL; )
12454 {
12455 if (need_geom)
12456 {
12457 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12458 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012459
cristy3ed852e2009-09-05 21:47:34 +000012460 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12461 mng_info->page.height=next_image->rows+next_image->page.y;
12462 }
glennrp0fe50b42010-11-16 03:52:51 +000012463
cristy3ed852e2009-09-05 21:47:34 +000012464 if (next_image->page.x || next_image->page.y)
12465 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012466
cristy3ed852e2009-09-05 21:47:34 +000012467 if (next_image->matte)
12468 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012469
cristy3ed852e2009-09-05 21:47:34 +000012470 if ((int) next_image->dispose >= BackgroundDispose)
12471 if (next_image->matte || next_image->page.x || next_image->page.y ||
12472 ((next_image->columns < mng_info->page.width) &&
12473 (next_image->rows < mng_info->page.height)))
12474 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012475
cristy3ed852e2009-09-05 21:47:34 +000012476 if (next_image->iterations)
12477 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012478
cristy3ed852e2009-09-05 21:47:34 +000012479 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012480
cristy3ed852e2009-09-05 21:47:34 +000012481 if (final_delay != initial_delay || final_delay > 1UL*
12482 next_image->ticks_per_second)
12483 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012484
cristy3ed852e2009-09-05 21:47:34 +000012485#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12486 defined(PNG_MNG_FEATURES_SUPPORTED)
12487 /*
12488 check for global palette possibility.
12489 */
12490 if (image->matte != MagickFalse)
12491 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012492
cristy3ed852e2009-09-05 21:47:34 +000012493 if (need_local_plte == 0)
12494 {
cristyc82a27b2011-10-21 01:07:16 +000012495 if (ImageIsGray(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012496 all_images_are_gray=MagickFalse;
12497 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12498 if (use_global_plte == 0)
12499 use_global_plte=mng_info->equal_palettes;
12500 need_local_plte=!mng_info->equal_palettes;
12501 }
12502#endif
12503 if (GetNextImageInList(next_image) != (Image *) NULL)
12504 {
12505 if (next_image->background_color.red !=
12506 next_image->next->background_color.red ||
12507 next_image->background_color.green !=
12508 next_image->next->background_color.green ||
12509 next_image->background_color.blue !=
12510 next_image->next->background_color.blue)
12511 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012512
cristy3ed852e2009-09-05 21:47:34 +000012513 if (next_image->gamma != next_image->next->gamma)
12514 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012515
cristy3ed852e2009-09-05 21:47:34 +000012516 if (next_image->rendering_intent !=
12517 next_image->next->rendering_intent)
12518 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012519
cristy3ed852e2009-09-05 21:47:34 +000012520 if ((next_image->units != next_image->next->units) ||
cristy2a11bef2011-10-28 18:33:11 +000012521 (next_image->resolution.x != next_image->next->resolution.x) ||
12522 (next_image->resolution.y != next_image->next->resolution.y))
cristy3ed852e2009-09-05 21:47:34 +000012523 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012524
cristy3ed852e2009-09-05 21:47:34 +000012525 if (mng_info->equal_chrms)
12526 {
12527 if (next_image->chromaticity.red_primary.x !=
12528 next_image->next->chromaticity.red_primary.x ||
12529 next_image->chromaticity.red_primary.y !=
12530 next_image->next->chromaticity.red_primary.y ||
12531 next_image->chromaticity.green_primary.x !=
12532 next_image->next->chromaticity.green_primary.x ||
12533 next_image->chromaticity.green_primary.y !=
12534 next_image->next->chromaticity.green_primary.y ||
12535 next_image->chromaticity.blue_primary.x !=
12536 next_image->next->chromaticity.blue_primary.x ||
12537 next_image->chromaticity.blue_primary.y !=
12538 next_image->next->chromaticity.blue_primary.y ||
12539 next_image->chromaticity.white_point.x !=
12540 next_image->next->chromaticity.white_point.x ||
12541 next_image->chromaticity.white_point.y !=
12542 next_image->next->chromaticity.white_point.y)
12543 mng_info->equal_chrms=MagickFalse;
12544 }
12545 }
12546 image_count++;
12547 next_image=GetNextImageInList(next_image);
12548 }
12549 if (image_count < 2)
12550 {
12551 mng_info->equal_backgrounds=MagickFalse;
12552 mng_info->equal_chrms=MagickFalse;
12553 mng_info->equal_gammas=MagickFalse;
12554 mng_info->equal_srgbs=MagickFalse;
12555 mng_info->equal_physs=MagickFalse;
12556 use_global_plte=MagickFalse;
12557#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12558 need_local_plte=MagickTrue;
12559#endif
12560 need_iterations=MagickFalse;
12561 }
glennrp0fe50b42010-11-16 03:52:51 +000012562
cristy3ed852e2009-09-05 21:47:34 +000012563 if (mng_info->need_fram == MagickFalse)
12564 {
12565 /*
12566 Only certain framing rates 100/n are exactly representable without
12567 the FRAM chunk but we'll allow some slop in VLC files
12568 */
12569 if (final_delay == 0)
12570 {
12571 if (need_iterations != MagickFalse)
12572 {
12573 /*
12574 It's probably a GIF with loop; don't run it *too* fast.
12575 */
glennrp02617122010-07-28 13:07:35 +000012576 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012577 {
12578 final_delay=10;
cristyc82a27b2011-10-21 01:07:16 +000012579 (void) ThrowMagickException(exception,GetMagickModule(),
12580 CoderWarning,
glennrpd908de42010-07-28 13:28:27 +000012581 "input has zero delay between all frames; assuming",
12582 " 10 cs `%s'","");
12583 }
cristy3ed852e2009-09-05 21:47:34 +000012584 }
12585 else
12586 mng_info->ticks_per_second=0;
12587 }
12588 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012589 mng_info->ticks_per_second=(png_uint_32)
12590 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012591 if (final_delay > 50)
12592 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012593
cristy3ed852e2009-09-05 21:47:34 +000012594 if (final_delay > 75)
12595 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012596
cristy3ed852e2009-09-05 21:47:34 +000012597 if (final_delay > 125)
12598 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012599
cristy3ed852e2009-09-05 21:47:34 +000012600 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12601 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12602 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12603 1UL*image->ticks_per_second))
12604 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12605 }
glennrp0fe50b42010-11-16 03:52:51 +000012606
cristy3ed852e2009-09-05 21:47:34 +000012607 if (mng_info->need_fram != MagickFalse)
12608 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12609 /*
12610 If pseudocolor, we should also check to see if all the
12611 palettes are identical and write a global PLTE if they are.
12612 ../glennrp Feb 99.
12613 */
12614 /*
12615 Write the MNG version 1.0 signature and MHDR chunk.
12616 */
12617 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12618 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12619 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012620 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012621 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12622 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012623 PNGLong(chunk+12,mng_info->ticks_per_second);
12624 PNGLong(chunk+16,0L); /* layer count=unknown */
12625 PNGLong(chunk+20,0L); /* frame count=unknown */
12626 PNGLong(chunk+24,0L); /* play time=unknown */
12627 if (write_jng)
12628 {
12629 if (need_matte)
12630 {
12631 if (need_defi || mng_info->need_fram || use_global_plte)
12632 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012633
cristy3ed852e2009-09-05 21:47:34 +000012634 else
12635 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12636 }
glennrp0fe50b42010-11-16 03:52:51 +000012637
cristy3ed852e2009-09-05 21:47:34 +000012638 else
12639 {
12640 if (need_defi || mng_info->need_fram || use_global_plte)
12641 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012642
cristy3ed852e2009-09-05 21:47:34 +000012643 else
12644 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12645 }
12646 }
glennrp0fe50b42010-11-16 03:52:51 +000012647
cristy3ed852e2009-09-05 21:47:34 +000012648 else
12649 {
12650 if (need_matte)
12651 {
12652 if (need_defi || mng_info->need_fram || use_global_plte)
12653 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012654
cristy3ed852e2009-09-05 21:47:34 +000012655 else
12656 PNGLong(chunk+28,9L); /* simplicity=VLC */
12657 }
glennrp0fe50b42010-11-16 03:52:51 +000012658
cristy3ed852e2009-09-05 21:47:34 +000012659 else
12660 {
12661 if (need_defi || mng_info->need_fram || use_global_plte)
12662 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012663
cristy3ed852e2009-09-05 21:47:34 +000012664 else
12665 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12666 }
12667 }
12668 (void) WriteBlob(image,32,chunk);
12669 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12670 option=GetImageOption(image_info,"mng:need-cacheoff");
12671 if (option != (const char *) NULL)
12672 {
12673 size_t
12674 length;
12675
12676 /*
12677 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12678 */
12679 PNGType(chunk,mng_nEED);
12680 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012681 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012682 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012683 length+=4;
12684 (void) WriteBlob(image,length,chunk);
12685 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12686 }
12687 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12688 (GetNextImageInList(image) != (Image *) NULL) &&
12689 (image->iterations != 1))
12690 {
12691 /*
12692 Write MNG TERM chunk
12693 */
12694 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12695 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012696 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012697 chunk[4]=3; /* repeat animation */
12698 chunk[5]=0; /* show last frame when done */
12699 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12700 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012701
cristy3ed852e2009-09-05 21:47:34 +000012702 if (image->iterations == 0)
12703 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012704
cristy3ed852e2009-09-05 21:47:34 +000012705 else
12706 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012707
cristy3ed852e2009-09-05 21:47:34 +000012708 if (logging != MagickFalse)
12709 {
12710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012711 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12712 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012713
cristy3ed852e2009-09-05 21:47:34 +000012714 if (image->iterations == 0)
12715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012716 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012717
cristy3ed852e2009-09-05 21:47:34 +000012718 else
12719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012720 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012721 }
12722 (void) WriteBlob(image,14,chunk);
12723 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12724 }
12725 /*
12726 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12727 */
12728 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12729 mng_info->equal_srgbs)
12730 {
12731 /*
12732 Write MNG sRGB chunk
12733 */
12734 (void) WriteBlobMSBULong(image,1L);
12735 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012736 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012737
cristy3ed852e2009-09-05 21:47:34 +000012738 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012739 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012740 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012741 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012742
cristy3ed852e2009-09-05 21:47:34 +000012743 else
glennrpe610a072010-08-05 17:08:46 +000012744 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012745 Magick_RenderingIntent_to_PNG_RenderingIntent(
12746 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012747
cristy3ed852e2009-09-05 21:47:34 +000012748 (void) WriteBlob(image,5,chunk);
12749 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12750 mng_info->have_write_global_srgb=MagickTrue;
12751 }
glennrp0fe50b42010-11-16 03:52:51 +000012752
cristy3ed852e2009-09-05 21:47:34 +000012753 else
12754 {
12755 if (image->gamma && mng_info->equal_gammas)
12756 {
12757 /*
12758 Write MNG gAMA chunk
12759 */
12760 (void) WriteBlobMSBULong(image,4L);
12761 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012762 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012763 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012764 (void) WriteBlob(image,8,chunk);
12765 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12766 mng_info->have_write_global_gama=MagickTrue;
12767 }
12768 if (mng_info->equal_chrms)
12769 {
12770 PrimaryInfo
12771 primary;
12772
12773 /*
12774 Write MNG cHRM chunk
12775 */
12776 (void) WriteBlobMSBULong(image,32L);
12777 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012778 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012779 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012780 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12781 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012782 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012783 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12784 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012785 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012786 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12787 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012788 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012789 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12790 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012791 (void) WriteBlob(image,36,chunk);
12792 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12793 mng_info->have_write_global_chrm=MagickTrue;
12794 }
12795 }
cristy2a11bef2011-10-28 18:33:11 +000012796 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012797 {
12798 /*
12799 Write MNG pHYs chunk
12800 */
12801 (void) WriteBlobMSBULong(image,9L);
12802 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012803 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012804
cristy3ed852e2009-09-05 21:47:34 +000012805 if (image->units == PixelsPerInchResolution)
12806 {
cristy35ef8242010-06-03 16:24:13 +000012807 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012808 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012809
cristy35ef8242010-06-03 16:24:13 +000012810 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012811 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012812
cristy3ed852e2009-09-05 21:47:34 +000012813 chunk[12]=1;
12814 }
glennrp0fe50b42010-11-16 03:52:51 +000012815
cristy3ed852e2009-09-05 21:47:34 +000012816 else
12817 {
12818 if (image->units == PixelsPerCentimeterResolution)
12819 {
cristy35ef8242010-06-03 16:24:13 +000012820 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012821 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012822
cristy35ef8242010-06-03 16:24:13 +000012823 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012824 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012825
cristy3ed852e2009-09-05 21:47:34 +000012826 chunk[12]=1;
12827 }
glennrp0fe50b42010-11-16 03:52:51 +000012828
cristy3ed852e2009-09-05 21:47:34 +000012829 else
12830 {
cristy2a11bef2011-10-28 18:33:11 +000012831 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12832 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012833 chunk[12]=0;
12834 }
12835 }
12836 (void) WriteBlob(image,13,chunk);
12837 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12838 }
12839 /*
12840 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12841 or does not cover the entire frame.
12842 */
12843 if (write_mng && (image->matte || image->page.x > 0 ||
12844 image->page.y > 0 || (image->page.width &&
12845 (image->page.width+image->page.x < mng_info->page.width))
12846 || (image->page.height && (image->page.height+image->page.y
12847 < mng_info->page.height))))
12848 {
12849 (void) WriteBlobMSBULong(image,6L);
12850 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012851 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012852 red=ScaleQuantumToShort(image->background_color.red);
12853 green=ScaleQuantumToShort(image->background_color.green);
12854 blue=ScaleQuantumToShort(image->background_color.blue);
12855 PNGShort(chunk+4,red);
12856 PNGShort(chunk+6,green);
12857 PNGShort(chunk+8,blue);
12858 (void) WriteBlob(image,10,chunk);
12859 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12860 if (mng_info->equal_backgrounds)
12861 {
12862 (void) WriteBlobMSBULong(image,6L);
12863 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012864 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012865 (void) WriteBlob(image,10,chunk);
12866 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12867 }
12868 }
12869
12870#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12871 if ((need_local_plte == MagickFalse) &&
12872 (image->storage_class == PseudoClass) &&
12873 (all_images_are_gray == MagickFalse))
12874 {
cristybb503372010-05-27 20:51:26 +000012875 size_t
cristy3ed852e2009-09-05 21:47:34 +000012876 data_length;
12877
12878 /*
12879 Write MNG PLTE chunk
12880 */
12881 data_length=3*image->colors;
12882 (void) WriteBlobMSBULong(image,data_length);
12883 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012884 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012885
cristybb503372010-05-27 20:51:26 +000012886 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012887 {
cristy5f07f702011-09-26 17:29:10 +000012888 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12889 image->colormap[i].red) & 0xff);
12890 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12891 image->colormap[i].green) & 0xff);
12892 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12893 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000012894 }
glennrp0fe50b42010-11-16 03:52:51 +000012895
cristy3ed852e2009-09-05 21:47:34 +000012896 (void) WriteBlob(image,data_length+4,chunk);
12897 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12898 mng_info->have_write_global_plte=MagickTrue;
12899 }
12900#endif
12901 }
12902 scene=0;
12903 mng_info->delay=0;
12904#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12905 defined(PNG_MNG_FEATURES_SUPPORTED)
12906 mng_info->equal_palettes=MagickFalse;
12907#endif
12908 do
12909 {
12910 if (mng_info->adjoin)
12911 {
12912#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12913 defined(PNG_MNG_FEATURES_SUPPORTED)
12914 /*
12915 If we aren't using a global palette for the entire MNG, check to
12916 see if we can use one for two or more consecutive images.
12917 */
12918 if (need_local_plte && use_global_plte && !all_images_are_gray)
12919 {
12920 if (mng_info->IsPalette)
12921 {
12922 /*
12923 When equal_palettes is true, this image has the same palette
12924 as the previous PseudoClass image
12925 */
12926 mng_info->have_write_global_plte=mng_info->equal_palettes;
12927 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12928 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12929 {
12930 /*
12931 Write MNG PLTE chunk
12932 */
cristybb503372010-05-27 20:51:26 +000012933 size_t
cristy3ed852e2009-09-05 21:47:34 +000012934 data_length;
12935
12936 data_length=3*image->colors;
12937 (void) WriteBlobMSBULong(image,data_length);
12938 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012939 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012940
cristybb503372010-05-27 20:51:26 +000012941 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012942 {
12943 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12944 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12945 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12946 }
glennrp0fe50b42010-11-16 03:52:51 +000012947
cristy3ed852e2009-09-05 21:47:34 +000012948 (void) WriteBlob(image,data_length+4,chunk);
12949 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12950 (uInt) (data_length+4)));
12951 mng_info->have_write_global_plte=MagickTrue;
12952 }
12953 }
12954 else
12955 mng_info->have_write_global_plte=MagickFalse;
12956 }
12957#endif
12958 if (need_defi)
12959 {
cristybb503372010-05-27 20:51:26 +000012960 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012961 previous_x,
12962 previous_y;
12963
12964 if (scene)
12965 {
12966 previous_x=mng_info->page.x;
12967 previous_y=mng_info->page.y;
12968 }
12969 else
12970 {
12971 previous_x=0;
12972 previous_y=0;
12973 }
12974 mng_info->page=image->page;
12975 if ((mng_info->page.x != previous_x) ||
12976 (mng_info->page.y != previous_y))
12977 {
12978 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12979 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012980 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012981 chunk[4]=0; /* object 0 MSB */
12982 chunk[5]=0; /* object 0 LSB */
12983 chunk[6]=0; /* visible */
12984 chunk[7]=0; /* abstract */
12985 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12986 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12987 (void) WriteBlob(image,16,chunk);
12988 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12989 }
12990 }
12991 }
12992
12993 mng_info->write_mng=write_mng;
12994
12995 if ((int) image->dispose >= 3)
12996 mng_info->framing_mode=3;
12997
12998 if (mng_info->need_fram && mng_info->adjoin &&
12999 ((image->delay != mng_info->delay) ||
13000 (mng_info->framing_mode != mng_info->old_framing_mode)))
13001 {
13002 if (image->delay == mng_info->delay)
13003 {
13004 /*
13005 Write a MNG FRAM chunk with the new framing mode.
13006 */
13007 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13008 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013009 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000013010 chunk[4]=(unsigned char) mng_info->framing_mode;
13011 (void) WriteBlob(image,5,chunk);
13012 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13013 }
13014 else
13015 {
13016 /*
13017 Write a MNG FRAM chunk with the delay.
13018 */
13019 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13020 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013021 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013022 chunk[4]=(unsigned char) mng_info->framing_mode;
13023 chunk[5]=0; /* frame name separator (no name) */
13024 chunk[6]=2; /* flag for changing default delay */
13025 chunk[7]=0; /* flag for changing frame timeout */
13026 chunk[8]=0; /* flag for changing frame clipping */
13027 chunk[9]=0; /* flag for changing frame sync_id */
13028 PNGLong(chunk+10,(png_uint_32)
13029 ((mng_info->ticks_per_second*
13030 image->delay)/MagickMax(image->ticks_per_second,1)));
13031 (void) WriteBlob(image,14,chunk);
13032 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000013033 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000013034 }
13035 mng_info->old_framing_mode=mng_info->framing_mode;
13036 }
13037
13038#if defined(JNG_SUPPORTED)
13039 if (image_info->compression == JPEGCompression)
13040 {
13041 ImageInfo
13042 *write_info;
13043
13044 if (logging != MagickFalse)
13045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13046 " Writing JNG object.");
13047 /* To do: specify the desired alpha compression method. */
13048 write_info=CloneImageInfo(image_info);
13049 write_info->compression=UndefinedCompression;
cristy018f07f2011-09-04 21:15:19 +000013050 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013051 write_info=DestroyImageInfo(write_info);
13052 }
13053 else
13054#endif
13055 {
13056 if (logging != MagickFalse)
13057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13058 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013059
glennrpb9cfe272010-12-21 15:08:06 +000013060 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013061 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013062
13063 /* We don't want any ancillary chunks written */
13064 mng_info->ping_exclude_bKGD=MagickTrue;
13065 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013066 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013067 mng_info->ping_exclude_EXIF=MagickTrue;
13068 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013069 mng_info->ping_exclude_iCCP=MagickTrue;
13070 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13071 mng_info->ping_exclude_oFFs=MagickTrue;
13072 mng_info->ping_exclude_pHYs=MagickTrue;
13073 mng_info->ping_exclude_sRGB=MagickTrue;
13074 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013075 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013076 mng_info->ping_exclude_vpAg=MagickTrue;
13077 mng_info->ping_exclude_zCCP=MagickTrue;
13078 mng_info->ping_exclude_zTXt=MagickTrue;
13079
cristy018f07f2011-09-04 21:15:19 +000013080 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013081 }
13082
13083 if (status == MagickFalse)
13084 {
13085 MngInfoFreeStruct(mng_info,&have_mng_structure);
13086 (void) CloseBlob(image);
13087 return(MagickFalse);
13088 }
13089 (void) CatchImageException(image);
13090 if (GetNextImageInList(image) == (Image *) NULL)
13091 break;
13092 image=SyncNextImageInList(image);
13093 status=SetImageProgress(image,SaveImagesTag,scene++,
13094 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013095
cristy3ed852e2009-09-05 21:47:34 +000013096 if (status == MagickFalse)
13097 break;
glennrp0fe50b42010-11-16 03:52:51 +000013098
cristy3ed852e2009-09-05 21:47:34 +000013099 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013100
cristy3ed852e2009-09-05 21:47:34 +000013101 if (write_mng)
13102 {
13103 while (GetPreviousImageInList(image) != (Image *) NULL)
13104 image=GetPreviousImageInList(image);
13105 /*
13106 Write the MEND chunk.
13107 */
13108 (void) WriteBlobMSBULong(image,0x00000000L);
13109 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013110 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013111 (void) WriteBlob(image,4,chunk);
13112 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13113 }
13114 /*
13115 Relinquish resources.
13116 */
13117 (void) CloseBlob(image);
13118 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013119
cristy3ed852e2009-09-05 21:47:34 +000013120 if (logging != MagickFalse)
13121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013122
cristy3ed852e2009-09-05 21:47:34 +000013123 return(MagickTrue);
13124}
glennrpd5045b42010-03-24 12:40:35 +000013125#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013126
cristy3ed852e2009-09-05 21:47:34 +000013127static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13128{
glennrp3bd393f2011-12-21 18:54:53 +000013129 (void) image;
cristy3ed852e2009-09-05 21:47:34 +000013130 printf("Your PNG library is too old: You have libpng-%s\n",
13131 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013132
cristy3ed852e2009-09-05 21:47:34 +000013133 ThrowBinaryException(CoderError,"PNG library is too old",
13134 image_info->filename);
13135}
glennrp39992b42010-11-14 00:03:43 +000013136
cristy3ed852e2009-09-05 21:47:34 +000013137static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13138{
13139 return(WritePNGImage(image_info,image));
13140}
glennrpd5045b42010-03-24 12:40:35 +000013141#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013142#endif