blob: 0783900a3618ad26ea4369234946d374cc302746 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP N N GGGG %
7% P P NN N G %
8% PPPP N N N G GG %
9% P N NN G G %
10% P N N GGG %
11% %
12% %
13% Read/Write Portable Network Graphics Image Format %
14% %
15% Software Design %
16% John Cristy %
17% Glenn Randers-Pehrson %
18% November 1997 %
19% %
20% %
cristy1454be72011-12-19 01:52:48 +000021% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000022% dedicated to making software imaging solutions freely available. %
23% %
24% You may not use this file except in compliance with the License. You may %
25% obtain a copy of the License at %
26% %
27% http://www.imagemagick.org/script/license.php %
28% %
29% Unless required by applicable law or agreed to in writing, software %
30% distributed under the License is distributed on an "AS IS" BASIS, %
31% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32% See the License for the specific language governing permissions and %
33% limitations under the License. %
34% %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38*/
39
40
41/*
42 Include declarations.
43*/
cristy4c08aed2011-07-01 19:47:50 +000044#include "MagickCore/studio.h"
45#include "MagickCore/artifact.h"
46#include "MagickCore/attribute.h"
47#include "MagickCore/blob.h"
48#include "MagickCore/blob-private.h"
49#include "MagickCore/cache.h"
50#include "MagickCore/color.h"
51#include "MagickCore/color-private.h"
52#include "MagickCore/colormap.h"
53#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000054#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000055#include "MagickCore/constitute.h"
56#include "MagickCore/enhance.h"
57#include "MagickCore/exception.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/geometry.h"
60#include "MagickCore/histogram.h"
61#include "MagickCore/image.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/layer.h"
64#include "MagickCore/list.h"
65#include "MagickCore/log.h"
66#include "MagickCore/MagickCore.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/module.h"
69#include "MagickCore/monitor.h"
70#include "MagickCore/monitor-private.h"
71#include "MagickCore/option.h"
72#include "MagickCore/pixel.h"
73#include "MagickCore/pixel-accessor.h"
74#include "MagickCore/profile.h"
75#include "MagickCore/property.h"
76#include "MagickCore/quantum-private.h"
77#include "MagickCore/resource_.h"
78#include "MagickCore/semaphore.h"
79#include "MagickCore/quantum-private.h"
80#include "MagickCore/static.h"
81#include "MagickCore/statistic.h"
82#include "MagickCore/string_.h"
83#include "MagickCore/string-private.h"
84#include "MagickCore/transform.h"
85#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000086#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp286a6352009-11-09 02:58:50 +000087
glennrp7ef138c2009-11-10 13:50:20 +000088/* Suppress libpng pedantic warnings that were added in
89 * libpng-1.2.41 and libpng-1.4.0. If you are working on
glennrpfaa852b2010-03-30 12:17:00 +000090 * migration to libpng-1.5, remove these defines and then
glennrp7ef138c2009-11-10 13:50:20 +000091 * fix any code that generates warnings.
92 */
glennrp991e92a2010-01-28 03:09:00 +000093/* #define PNG_DEPRECATED Use of this function is deprecated */
glennrpfaa852b2010-03-30 12:17:00 +000094/* #define PNG_USE_RESULT The result of this function must be checked */
95/* #define PNG_NORETURN This function does not return */
96/* #define PNG_ALLOCATED The result of the function is new memory */
glennrp8371ecc2011-02-19 19:15:38 +000097/* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
glennrp5b927342011-04-14 14:31:48 +000098
99/* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
cristy75cfe702011-04-14 14:27:17 +0000100#define PNG_PTR_NORETURN
glennrp286a6352009-11-09 02:58:50 +0000101
cristy3ed852e2009-09-05 21:47:34 +0000102#include "png.h"
103#include "zlib.h"
104
105/* ImageMagick differences */
106#define first_scene scene
107
glennrpd5045b42010-03-24 12:40:35 +0000108#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000109/*
110 Optional declarations. Define or undefine them as you like.
111*/
112/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
113
114/*
115 Features under construction. Define these to work on them.
116*/
117#undef MNG_OBJECT_BUFFERS
118#undef MNG_BASI_SUPPORTED
119#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
120#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
cristy3ed852e2009-09-05 21:47:34 +0000121#if defined(MAGICKCORE_JPEG_DELEGATE)
122# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
123#endif
124#if !defined(RGBColorMatchExact)
125#define IsPNGColorEqual(color,target) \
glennrp8e045c82011-04-27 16:40:27 +0000126 (((color).red == (target).red) && \
127 ((color).green == (target).green) && \
128 ((color).blue == (target).blue))
cristy3ed852e2009-09-05 21:47:34 +0000129#endif
130
glennrp8e58efd2011-05-20 12:16:29 +0000131/* Macros for left-bit-replication to ensure that pixels
glennrp1a2061f2011-10-18 12:30:45 +0000132 * and PixelInfos all have the same image->depth, and for use
glennrp8e58efd2011-05-20 12:16:29 +0000133 * in PNG8 quantization.
134 */
135
136
137/* LBR01: Replicate top bit */
138
glennrp05001c32011-08-06 13:04:16 +0000139#define LBR01PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000140 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
141 0 : QuantumRange);
142
glennrp91d99252011-06-25 14:30:13 +0000143#define LBR01PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000144 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
145 0 : QuantumRange);
146
glennrp91d99252011-06-25 14:30:13 +0000147#define LBR01PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000148 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
149 0 : QuantumRange);
150
cristy4c08aed2011-07-01 19:47:50 +0000151#define LBR01PacketAlpha(pixelpacket) \
152 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000153 0 : QuantumRange);
154
glennrp91d99252011-06-25 14:30:13 +0000155#define LBR01PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000156 { \
glennrp05001c32011-08-06 13:04:16 +0000157 LBR01PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000158 LBR01PacketGreen((pixelpacket)); \
159 LBR01PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000160 }
glennrp8e58efd2011-05-20 12:16:29 +0000161
glennrp91d99252011-06-25 14:30:13 +0000162#define LBR01PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000163 { \
glennrp91d99252011-06-25 14:30:13 +0000164 LBR01PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000165 LBR01PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000166 }
glennrp8e58efd2011-05-20 12:16:29 +0000167
cristyef618312011-06-25 12:26:44 +0000168#define LBR01PixelRed(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000169 (ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000170 0 : QuantumRange);
171
glennrp54cf7972011-08-06 14:28:09 +0000172#define LBR01PixelGreen(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000173 (ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000174 0 : QuantumRange);
175
glennrp54cf7972011-08-06 14:28:09 +0000176#define LBR01PixelBlue(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000177 (ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000178 0 : QuantumRange);
179
glennrp54cf7972011-08-06 14:28:09 +0000180#define LBR01PixelAlpha(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000181 (ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000182 0 : QuantumRange);
183
glennrp54cf7972011-08-06 14:28:09 +0000184#define LBR01PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000185 { \
cristyef618312011-06-25 12:26:44 +0000186 LBR01PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000187 LBR01PixelGreen((pixel)); \
188 LBR01PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000189 }
glennrp8e58efd2011-05-20 12:16:29 +0000190
glennrp54cf7972011-08-06 14:28:09 +0000191#define LBR01PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000192 { \
glennrp54cf7972011-08-06 14:28:09 +0000193 LBR01PixelRGB((pixel)); \
194 LBR01PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000195 }
glennrp8e58efd2011-05-20 12:16:29 +0000196
197/* LBR02: Replicate top 2 bits */
198
glennrp05001c32011-08-06 13:04:16 +0000199#define LBR02PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000200 { \
201 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
202 (pixelpacket).red=ScaleCharToQuantum( \
203 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
204 }
glennrp91d99252011-06-25 14:30:13 +0000205#define LBR02PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000206 { \
207 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
208 (pixelpacket).green=ScaleCharToQuantum( \
209 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
210 }
glennrp91d99252011-06-25 14:30:13 +0000211#define LBR02PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000212 { \
213 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
214 (pixelpacket).blue=ScaleCharToQuantum( \
215 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
216 }
cristy4c08aed2011-07-01 19:47:50 +0000217#define LBR02PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000218 { \
cristy4c08aed2011-07-01 19:47:50 +0000219 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
220 (pixelpacket).alpha=ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000221 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
222 }
223
glennrp91d99252011-06-25 14:30:13 +0000224#define LBR02PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000225 { \
glennrp05001c32011-08-06 13:04:16 +0000226 LBR02PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000227 LBR02PacketGreen((pixelpacket)); \
228 LBR02PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000229 }
glennrp8e58efd2011-05-20 12:16:29 +0000230
glennrp91d99252011-06-25 14:30:13 +0000231#define LBR02PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000232 { \
glennrp91d99252011-06-25 14:30:13 +0000233 LBR02PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000234 LBR02PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000235 }
glennrp8e58efd2011-05-20 12:16:29 +0000236
cristyef618312011-06-25 12:26:44 +0000237#define LBR02PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000238 { \
cristy4c08aed2011-07-01 19:47:50 +0000239 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000240 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000241 SetPixelRed(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000242 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
243 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000244 }
glennrp54cf7972011-08-06 14:28:09 +0000245#define LBR02PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000246 { \
cristy4c08aed2011-07-01 19:47:50 +0000247 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000248 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000249 SetPixelGreen(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000250 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
251 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000252 }
glennrp54cf7972011-08-06 14:28:09 +0000253#define LBR02PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000254 { \
255 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000256 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
257 SetPixelBlue(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000258 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
259 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000260 }
glennrp54cf7972011-08-06 14:28:09 +0000261#define LBR02PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000262 { \
263 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000264 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
265 SetPixelAlpha(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000266 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
267 (pixel) ); \
glennrp8e58efd2011-05-20 12:16:29 +0000268 }
269
glennrp54cf7972011-08-06 14:28:09 +0000270#define LBR02PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000271 { \
cristyef618312011-06-25 12:26:44 +0000272 LBR02PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000273 LBR02PixelGreen((pixel)); \
274 LBR02PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000275 }
glennrp8e58efd2011-05-20 12:16:29 +0000276
glennrp54cf7972011-08-06 14:28:09 +0000277#define LBR02PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000278 { \
glennrp54cf7972011-08-06 14:28:09 +0000279 LBR02PixelRGB((pixel)); \
280 LBR02PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000281 }
glennrp8e58efd2011-05-20 12:16:29 +0000282
283/* LBR03: Replicate top 3 bits (only used with opaque pixels during
284 PNG8 quantization) */
285
glennrp05001c32011-08-06 13:04:16 +0000286#define LBR03PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000287 { \
288 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
289 (pixelpacket).red=ScaleCharToQuantum( \
290 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
291 }
glennrp91d99252011-06-25 14:30:13 +0000292#define LBR03PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000293 { \
294 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
295 (pixelpacket).green=ScaleCharToQuantum( \
296 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
297 }
glennrp91d99252011-06-25 14:30:13 +0000298#define LBR03PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000299 { \
300 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
301 (pixelpacket).blue=ScaleCharToQuantum( \
302 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
303 }
304
glennrp91d99252011-06-25 14:30:13 +0000305#define LBR03PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000306 { \
glennrp05001c32011-08-06 13:04:16 +0000307 LBR03PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000308 LBR03PacketGreen((pixelpacket)); \
309 LBR03PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000310 }
glennrp8e58efd2011-05-20 12:16:29 +0000311
cristyef618312011-06-25 12:26:44 +0000312#define LBR03PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000313 { \
cristy4c08aed2011-07-01 19:47:50 +0000314 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000315 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000316 SetPixelRed(image, ScaleCharToQuantum( \
317 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000318 }
cristy4c08aed2011-07-01 19:47:50 +0000319#define LBR03Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000320 { \
cristy4c08aed2011-07-01 19:47:50 +0000321 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000322 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000323 SetPixelGreen(image, ScaleCharToQuantum( \
324 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000325 }
cristy4c08aed2011-07-01 19:47:50 +0000326#define LBR03Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000327 { \
cristy4c08aed2011-07-01 19:47:50 +0000328 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000329 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000330 SetPixelBlue(image, ScaleCharToQuantum( \
331 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000332 }
333
cristy4c08aed2011-07-01 19:47:50 +0000334#define LBR03RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000335 { \
cristyef618312011-06-25 12:26:44 +0000336 LBR03PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000337 LBR03Green((pixel)); \
338 LBR03Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000339 }
glennrp8e58efd2011-05-20 12:16:29 +0000340
341/* LBR04: Replicate top 4 bits */
342
glennrp05001c32011-08-06 13:04:16 +0000343#define LBR04PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000344 { \
345 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
346 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
347 }
glennrp91d99252011-06-25 14:30:13 +0000348#define LBR04PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000349 { \
350 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
351 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
352 }
glennrp91d99252011-06-25 14:30:13 +0000353#define LBR04PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000354 { \
355 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
356 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
357 }
cristy4c08aed2011-07-01 19:47:50 +0000358#define LBR04PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000359 { \
cristy4c08aed2011-07-01 19:47:50 +0000360 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
361 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
glennrp8e58efd2011-05-20 12:16:29 +0000362 }
363
glennrp91d99252011-06-25 14:30:13 +0000364#define LBR04PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000365 { \
glennrp05001c32011-08-06 13:04:16 +0000366 LBR04PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000367 LBR04PacketGreen((pixelpacket)); \
368 LBR04PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000369 }
glennrp8e58efd2011-05-20 12:16:29 +0000370
glennrp91d99252011-06-25 14:30:13 +0000371#define LBR04PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000372 { \
glennrp91d99252011-06-25 14:30:13 +0000373 LBR04PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000374 LBR04PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000375 }
glennrp8e58efd2011-05-20 12:16:29 +0000376
cristyef618312011-06-25 12:26:44 +0000377#define LBR04PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000378 { \
cristy4c08aed2011-07-01 19:47:50 +0000379 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000380 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000381 SetPixelRed(image,\
382 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000383 }
glennrp54cf7972011-08-06 14:28:09 +0000384#define LBR04PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000385 { \
cristy4c08aed2011-07-01 19:47:50 +0000386 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000387 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000388 SetPixelGreen(image,\
389 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000390 }
glennrp54cf7972011-08-06 14:28:09 +0000391#define LBR04PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000392 { \
393 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000394 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
395 SetPixelBlue(image,\
396 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000397 }
glennrp54cf7972011-08-06 14:28:09 +0000398#define LBR04PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000399 { \
400 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000401 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
402 SetPixelAlpha(image,\
403 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000404 }
405
glennrp54cf7972011-08-06 14:28:09 +0000406#define LBR04PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000407 { \
cristyef618312011-06-25 12:26:44 +0000408 LBR04PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000409 LBR04PixelGreen((pixel)); \
410 LBR04PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000411 }
glennrp8e58efd2011-05-20 12:16:29 +0000412
glennrp54cf7972011-08-06 14:28:09 +0000413#define LBR04PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000414 { \
glennrp54cf7972011-08-06 14:28:09 +0000415 LBR04PixelRGB((pixel)); \
416 LBR04PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000417 }
glennrp8e58efd2011-05-20 12:16:29 +0000418
419
420/* LBR08: Replicate top 8 bits */
421
glennrp05001c32011-08-06 13:04:16 +0000422#define LBR08PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000423 { \
424 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
425 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
426 }
glennrp91d99252011-06-25 14:30:13 +0000427#define LBR08PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000428 { \
429 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
430 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
431 }
glennrp91d99252011-06-25 14:30:13 +0000432#define LBR08PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000433 { \
434 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
435 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
436 }
cristy4c08aed2011-07-01 19:47:50 +0000437#define LBR08PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000438 { \
cristy4c08aed2011-07-01 19:47:50 +0000439 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
440 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000441 }
442
glennrp91d99252011-06-25 14:30:13 +0000443#define LBR08PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000444 { \
glennrp05001c32011-08-06 13:04:16 +0000445 LBR08PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000446 LBR08PacketGreen((pixelpacket)); \
447 LBR08PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000448 }
glennrp8e58efd2011-05-20 12:16:29 +0000449
glennrp91d99252011-06-25 14:30:13 +0000450#define LBR08PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000451 { \
glennrp91d99252011-06-25 14:30:13 +0000452 LBR08PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000453 LBR08PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000454 }
glennrp8e58efd2011-05-20 12:16:29 +0000455
cristyef618312011-06-25 12:26:44 +0000456#define LBR08PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000457 { \
458 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000459 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
460 SetPixelRed(image,\
461 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000462 }
glennrp54cf7972011-08-06 14:28:09 +0000463#define LBR08PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000464 { \
465 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000466 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
467 SetPixelGreen(image,\
468 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000469 }
glennrp54cf7972011-08-06 14:28:09 +0000470#define LBR08PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000471 { \
472 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000473 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
474 SetPixelBlue(image,\
475 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000476 }
glennrp54cf7972011-08-06 14:28:09 +0000477#define LBR08PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000478 { \
479 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000480 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
481 SetPixelAlpha(image,\
482 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000483 }
484
glennrp54cf7972011-08-06 14:28:09 +0000485#define LBR08PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000486 { \
cristyef618312011-06-25 12:26:44 +0000487 LBR08PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000488 LBR08PixelGreen((pixel)); \
489 LBR08PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000490 }
glennrp8e58efd2011-05-20 12:16:29 +0000491
glennrp54cf7972011-08-06 14:28:09 +0000492#define LBR08PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000493 { \
glennrp54cf7972011-08-06 14:28:09 +0000494 LBR08PixelRGB((pixel)); \
495 LBR08PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000496 }
glennrp8e58efd2011-05-20 12:16:29 +0000497
498
499/* LBR16: Replicate top 16 bits */
500
glennrp05001c32011-08-06 13:04:16 +0000501#define LBR16PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000502 { \
503 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
504 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
505 }
glennrp91d99252011-06-25 14:30:13 +0000506#define LBR16PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000507 { \
508 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
509 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
510 }
glennrp91d99252011-06-25 14:30:13 +0000511#define LBR16PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000512 { \
513 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
514 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
515 }
cristy4c08aed2011-07-01 19:47:50 +0000516#define LBR16PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000517 { \
cristy4c08aed2011-07-01 19:47:50 +0000518 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
519 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000520 }
521
glennrp91d99252011-06-25 14:30:13 +0000522#define LBR16PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000523 { \
glennrp05001c32011-08-06 13:04:16 +0000524 LBR16PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000525 LBR16PacketGreen((pixelpacket)); \
526 LBR16PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000527 }
glennrp8e58efd2011-05-20 12:16:29 +0000528
glennrp91d99252011-06-25 14:30:13 +0000529#define LBR16PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000530 { \
glennrp91d99252011-06-25 14:30:13 +0000531 LBR16PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000532 LBR16PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000533 }
glennrp8e58efd2011-05-20 12:16:29 +0000534
cristyef618312011-06-25 12:26:44 +0000535#define LBR16PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000536 { \
537 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000538 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
539 SetPixelRed(image,\
540 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000541 }
glennrp54cf7972011-08-06 14:28:09 +0000542#define LBR16PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000543 { \
544 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000545 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
546 SetPixelGreen(image,\
547 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000548 }
glennrp54cf7972011-08-06 14:28:09 +0000549#define LBR16PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000550 { \
551 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000552 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
553 SetPixelBlue(image,\
554 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000555 }
glennrp54cf7972011-08-06 14:28:09 +0000556#define LBR16PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000557 { \
558 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000559 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
560 SetPixelAlpha(image,\
561 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000562 }
563
glennrp54cf7972011-08-06 14:28:09 +0000564#define LBR16PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000565 { \
cristyef618312011-06-25 12:26:44 +0000566 LBR16PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000567 LBR16PixelGreen((pixel)); \
568 LBR16PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000569 }
glennrp8e58efd2011-05-20 12:16:29 +0000570
glennrp54cf7972011-08-06 14:28:09 +0000571#define LBR16PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000572 { \
glennrp54cf7972011-08-06 14:28:09 +0000573 LBR16PixelRGB((pixel)); \
574 LBR16PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000575 }
glennrp8e58efd2011-05-20 12:16:29 +0000576
cristy3ed852e2009-09-05 21:47:34 +0000577/*
578 Establish thread safety.
579 setjmp/longjmp is claimed to be safe on these platforms:
580 setjmp/longjmp is alleged to be unsafe on these platforms:
581*/
582#ifndef SETJMP_IS_THREAD_SAFE
583#define PNG_SETJMP_NOT_THREAD_SAFE
584#endif
585
586#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
587static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000588 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000589#endif
590
591/*
592 This temporary until I set up malloc'ed object attributes array.
593 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
594 waste more memory.
595*/
596#define MNG_MAX_OBJECTS 256
597
598/*
599 If this not defined, spec is interpreted strictly. If it is
600 defined, an attempt will be made to recover from some errors,
601 including
602 o global PLTE too short
603*/
604#undef MNG_LOOSE
605
606/*
607 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
608 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
609 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
610 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
611 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
612 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
613 will be enabled by default in libpng-1.2.0.
614*/
cristy3ed852e2009-09-05 21:47:34 +0000615#ifdef PNG_MNG_FEATURES_SUPPORTED
616# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
617# define PNG_READ_EMPTY_PLTE_SUPPORTED
618# endif
619# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
620# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
621# endif
622#endif
623
624/*
cristybb503372010-05-27 20:51:26 +0000625 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000626 This macro is only defined in libpng-1.0.3 and later.
627 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
628*/
629#ifndef PNG_UINT_31_MAX
630#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
631#endif
632
633/*
634 Constant strings for known chunk types. If you need to add a chunk,
635 add a string holding the name here. To make the code more
636 portable, we use ASCII numbers like this, not characters.
637*/
638
glennrp85dcf872011-12-07 02:51:47 +0000639static png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
640static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
641static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
642static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
643static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
644static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
645static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
646static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
647static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
648static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
649static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
650static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
651static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
652static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
653static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
654static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
655static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
656static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
657static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
658static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
659static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
660static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
661static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
662static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
663static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
664static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
665static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
666static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
667static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
668static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
669static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
670static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
671static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
672static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000673
674#if defined(JNG_SUPPORTED)
glennrp85dcf872011-12-07 02:51:47 +0000675static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
676static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
677static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
678static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
679static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
680static png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000681#endif
682
683/*
684Other known chunks that are not yet supported by ImageMagick:
glennrp85dcf872011-12-07 02:51:47 +0000685static png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
686static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
687static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
688static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
689static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
690static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
691static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
692static png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000693*/
694
695typedef struct _MngBox
696{
cristy8182b072010-05-30 20:10:53 +0000697 long
cristy3ed852e2009-09-05 21:47:34 +0000698 left,
699 right,
700 top,
701 bottom;
702} MngBox;
703
704typedef struct _MngPair
705{
cristy8182b072010-05-30 20:10:53 +0000706 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000707 a,
708 b;
709} MngPair;
710
711#ifdef MNG_OBJECT_BUFFERS
712typedef struct _MngBuffer
713{
714
cristybb503372010-05-27 20:51:26 +0000715 size_t
cristy3ed852e2009-09-05 21:47:34 +0000716 height,
717 width;
718
719 Image
720 *image;
721
722 png_color
723 plte[256];
724
725 int
726 reference_count;
727
728 unsigned char
729 alpha_sample_depth,
730 compression_method,
731 color_type,
732 concrete,
733 filter_method,
734 frozen,
735 image_type,
736 interlace_method,
737 pixel_sample_depth,
738 plte_length,
739 sample_depth,
740 viewable;
741} MngBuffer;
742#endif
743
744typedef struct _MngInfo
745{
746
747#ifdef MNG_OBJECT_BUFFERS
748 MngBuffer
749 *ob[MNG_MAX_OBJECTS];
750#endif
751
752 Image *
753 image;
754
755 RectangleInfo
756 page;
757
758 int
759 adjoin,
760#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
761 bytes_in_read_buffer,
762 found_empty_plte,
763#endif
764 equal_backgrounds,
765 equal_chrms,
766 equal_gammas,
767#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
768 defined(PNG_MNG_FEATURES_SUPPORTED)
769 equal_palettes,
770#endif
771 equal_physs,
772 equal_srgbs,
773 framing_mode,
774 have_global_bkgd,
775 have_global_chrm,
776 have_global_gama,
777 have_global_phys,
778 have_global_sbit,
779 have_global_srgb,
780 have_saved_bkgd_index,
781 have_write_global_chrm,
782 have_write_global_gama,
783 have_write_global_plte,
784 have_write_global_srgb,
785 need_fram,
786 object_id,
787 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000788 saved_bkgd_index;
789
790 int
791 new_number_colors;
792
cristybb503372010-05-27 20:51:26 +0000793 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000794 image_found,
795 loop_count[256],
796 loop_iteration[256],
797 scenes_found,
798 x_off[MNG_MAX_OBJECTS],
799 y_off[MNG_MAX_OBJECTS];
800
801 MngBox
802 clip,
803 frame,
804 image_box,
805 object_clip[MNG_MAX_OBJECTS];
806
807 unsigned char
808 /* These flags could be combined into one byte */
809 exists[MNG_MAX_OBJECTS],
810 frozen[MNG_MAX_OBJECTS],
811 loop_active[256],
812 invisible[MNG_MAX_OBJECTS],
813 viewable[MNG_MAX_OBJECTS];
814
815 MagickOffsetType
816 loop_jump[256];
817
818 png_colorp
819 global_plte;
820
821 png_color_8
822 global_sbit;
823
824 png_byte
825#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
826 read_buffer[8],
827#endif
828 global_trns[256];
829
830 float
831 global_gamma;
832
833 ChromaticityInfo
834 global_chrm;
835
836 RenderingIntent
837 global_srgb_intent;
838
cristy35ef8242010-06-03 16:24:13 +0000839 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000840 delay,
841 global_plte_length,
842 global_trns_length,
843 global_x_pixels_per_unit,
844 global_y_pixels_per_unit,
845 mng_width,
846 mng_height,
847 ticks_per_second;
848
glennrpb9cfe272010-12-21 15:08:06 +0000849 MagickBooleanType
850 need_blob;
851
cristy3ed852e2009-09-05 21:47:34 +0000852 unsigned int
853 IsPalette,
854 global_phys_unit_type,
855 basi_warning,
856 clon_warning,
857 dhdr_warning,
858 jhdr_warning,
859 magn_warning,
860 past_warning,
861 phyg_warning,
862 phys_warning,
863 sbit_warning,
864 show_warning,
865 mng_type,
866 write_mng,
867 write_png_colortype,
868 write_png_depth,
glennrp18682582011-06-30 18:11:47 +0000869 write_png_compression_level,
870 write_png_compression_strategy,
871 write_png_compression_filter,
cristy3ed852e2009-09-05 21:47:34 +0000872 write_png8,
873 write_png24,
874 write_png32;
875
876#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000877 size_t
cristy3ed852e2009-09-05 21:47:34 +0000878 basi_width,
879 basi_height;
880
881 unsigned int
882 basi_depth,
883 basi_color_type,
884 basi_compression_method,
885 basi_filter_type,
886 basi_interlace_method,
887 basi_red,
888 basi_green,
889 basi_blue,
890 basi_alpha,
891 basi_viewable;
892#endif
893
894 png_uint_16
895 magn_first,
896 magn_last,
897 magn_mb,
898 magn_ml,
899 magn_mr,
900 magn_mt,
901 magn_mx,
902 magn_my,
903 magn_methx,
904 magn_methy;
905
cristy101ab702011-10-13 13:06:32 +0000906 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000907 mng_global_bkgd;
908
glennrp26f37912010-12-23 16:22:42 +0000909 /* Added at version 6.6.6-7 */
910 MagickBooleanType
911 ping_exclude_bKGD,
912 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000913 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000914 ping_exclude_EXIF,
915 ping_exclude_gAMA,
916 ping_exclude_iCCP,
917 /* ping_exclude_iTXt, */
918 ping_exclude_oFFs,
919 ping_exclude_pHYs,
920 ping_exclude_sRGB,
921 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000922 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000923 ping_exclude_vpAg,
924 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000925 ping_exclude_zTXt,
926 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000927
cristy3ed852e2009-09-05 21:47:34 +0000928} MngInfo;
929#endif /* VER */
930
931/*
932 Forward declarations.
933*/
934static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000935 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000936
cristy3ed852e2009-09-05 21:47:34 +0000937static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000938 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000939
cristy3ed852e2009-09-05 21:47:34 +0000940#if defined(JNG_SUPPORTED)
941static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000942 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000943#endif
944
glennrp0c3e06b2010-11-19 13:45:02 +0000945#if PNG_LIBPNG_VER > 10011
946
glennrpfd05d622011-02-25 04:10:33 +0000947
glennrp0c3e06b2010-11-19 13:45:02 +0000948#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
949static MagickBooleanType
cristyc82a27b2011-10-21 01:07:16 +0000950LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
glennrp0c3e06b2010-11-19 13:45:02 +0000951{
glennrp67b9c1a2011-04-22 18:47:36 +0000952 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
953 *
954 * This is true if the high byte and the next highest byte of
955 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000956 * are equal to each other. We check this by seeing if the samples
957 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000958 *
959 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000960 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000961 */
962
glennrp3faa9a32011-04-23 14:00:25 +0000963#define QuantumToCharToQuantumEqQuantum(quantum) \
964 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
965
glennrp0c3e06b2010-11-19 13:45:02 +0000966 MagickBooleanType
967 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000968
glennrp03e11f62011-04-22 13:30:16 +0000969 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000970 {
971
cristy4c08aed2011-07-01 19:47:50 +0000972 const Quantum
glennrp0c3e06b2010-11-19 13:45:02 +0000973 *p;
974
975 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000976 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
977 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
978 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
979 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000980
981 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
982 {
983 int indx;
984
985 for (indx=0; indx < (ssize_t) image->colors; indx++)
986 {
glennrp3faa9a32011-04-23 14:00:25 +0000987 ok_to_reduce=(
988 QuantumToCharToQuantumEqQuantum(
989 image->colormap[indx].red) &&
990 QuantumToCharToQuantumEqQuantum(
991 image->colormap[indx].green) &&
992 QuantumToCharToQuantumEqQuantum(
993 image->colormap[indx].blue)) ?
994 MagickTrue : MagickFalse;
995
glennrp0c3e06b2010-11-19 13:45:02 +0000996 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000997 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000998 }
999 }
1000
1001 if ((ok_to_reduce != MagickFalse) &&
1002 (image->storage_class != PseudoClass))
1003 {
1004 ssize_t
1005 y;
1006
1007 register ssize_t
1008 x;
1009
1010 for (y=0; y < (ssize_t) image->rows; y++)
1011 {
cristyc82a27b2011-10-21 01:07:16 +00001012 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0c3e06b2010-11-19 13:45:02 +00001013
cristy4c08aed2011-07-01 19:47:50 +00001014 if (p == (const Quantum *) NULL)
glennrp0c3e06b2010-11-19 13:45:02 +00001015 {
1016 ok_to_reduce = MagickFalse;
1017 break;
1018 }
1019
1020 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1021 {
glennrp3faa9a32011-04-23 14:00:25 +00001022 ok_to_reduce=
cristy4c08aed2011-07-01 19:47:50 +00001023 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1024 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1025 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
glennrp3faa9a32011-04-23 14:00:25 +00001026 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001027
1028 if (ok_to_reduce == MagickFalse)
1029 break;
1030
cristyed231572011-07-14 02:18:59 +00001031 p+=GetPixelChannels(image);
glennrp0c3e06b2010-11-19 13:45:02 +00001032 }
glennrp8640fb52010-11-23 15:48:26 +00001033 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +00001034 break;
1035 }
1036 }
1037
1038 if (ok_to_reduce != MagickFalse)
1039 {
glennrp0c3e06b2010-11-19 13:45:02 +00001040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001041 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +00001042 }
glennrpa6a06632011-01-19 15:15:34 +00001043 else
1044 {
1045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001046 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +00001047 }
glennrp0c3e06b2010-11-19 13:45:02 +00001048 }
1049
1050 return ok_to_reduce;
1051}
1052#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1053
glennrpe610a072010-08-05 17:08:46 +00001054static int
glennrpcf002022011-01-30 02:38:15 +00001055Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +00001056{
glennrpe610a072010-08-05 17:08:46 +00001057 switch (intent)
1058 {
1059 case PerceptualIntent:
1060 return 0;
glennrp0fe50b42010-11-16 03:52:51 +00001061
glennrpe610a072010-08-05 17:08:46 +00001062 case RelativeIntent:
1063 return 1;
glennrp0fe50b42010-11-16 03:52:51 +00001064
glennrpe610a072010-08-05 17:08:46 +00001065 case SaturationIntent:
1066 return 2;
glennrp0fe50b42010-11-16 03:52:51 +00001067
glennrpe610a072010-08-05 17:08:46 +00001068 case AbsoluteIntent:
1069 return 3;
glennrp0fe50b42010-11-16 03:52:51 +00001070
glennrpe610a072010-08-05 17:08:46 +00001071 default:
1072 return -1;
1073 }
1074}
1075
1076static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +00001077Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +00001078{
glennrpcf002022011-01-30 02:38:15 +00001079 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001080 {
1081 case 0:
1082 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001083
glennrpe610a072010-08-05 17:08:46 +00001084 case 1:
1085 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001086
glennrpe610a072010-08-05 17:08:46 +00001087 case 2:
1088 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001089
glennrpe610a072010-08-05 17:08:46 +00001090 case 3:
1091 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001092
glennrpe610a072010-08-05 17:08:46 +00001093 default:
1094 return UndefinedIntent;
1095 }
1096}
1097
cristybb503372010-05-27 20:51:26 +00001098static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001099{
1100 if (x > y)
1101 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001102
cristy3ed852e2009-09-05 21:47:34 +00001103 return(y);
1104}
glennrp0c3e06b2010-11-19 13:45:02 +00001105
cristybb503372010-05-27 20:51:26 +00001106static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001107{
1108 if (x < y)
1109 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001110
cristy3ed852e2009-09-05 21:47:34 +00001111 return(y);
1112}
glennrp0c3e06b2010-11-19 13:45:02 +00001113
cristy3ed852e2009-09-05 21:47:34 +00001114
1115/*
1116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1117% %
1118% %
1119% %
1120% I m a g e I s G r a y %
1121% %
1122% %
1123% %
1124%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1125% %
cristy4c08aed2011-07-01 19:47:50 +00001126% Like IsImageGray except does not change DirectClass to PseudoClass %
cristy3ed852e2009-09-05 21:47:34 +00001127% %
1128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1129*/
cristyc82a27b2011-10-21 01:07:16 +00001130static MagickBooleanType ImageIsGray(Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001131{
cristy4c08aed2011-07-01 19:47:50 +00001132 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001133 *p;
1134
cristybb503372010-05-27 20:51:26 +00001135 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001136 i,
1137 x,
1138 y;
1139
1140 assert(image != (Image *) NULL);
1141 assert(image->signature == MagickSignature);
1142 if (image->debug != MagickFalse)
1143 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1144
1145 if (image->storage_class == PseudoClass)
1146 {
cristybb503372010-05-27 20:51:26 +00001147 for (i=0; i < (ssize_t) image->colors; i++)
cristy101ab702011-10-13 13:06:32 +00001148 if (IsPixelInfoGray(image->colormap+i) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001149 return(MagickFalse);
1150 return(MagickTrue);
1151 }
cristybb503372010-05-27 20:51:26 +00001152 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001153 {
cristyc82a27b2011-10-21 01:07:16 +00001154 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001155 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001156 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +00001157 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001158 {
cristy4c08aed2011-07-01 19:47:50 +00001159 if (IsPixelGray(image,p) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001160 return(MagickFalse);
cristyed231572011-07-14 02:18:59 +00001161 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001162 }
1163 }
1164 return(MagickTrue);
1165}
glennrpd5045b42010-03-24 12:40:35 +00001166#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001167#endif /* MAGICKCORE_PNG_DELEGATE */
1168
1169/*
1170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1171% %
1172% %
1173% %
1174% I s M N G %
1175% %
1176% %
1177% %
1178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1179%
1180% IsMNG() returns MagickTrue if the image format type, identified by the
1181% magick string, is MNG.
1182%
1183% The format of the IsMNG method is:
1184%
1185% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1186%
1187% A description of each parameter follows:
1188%
1189% o magick: compare image format pattern against these bytes.
1190%
1191% o length: Specifies the length of the magick string.
1192%
1193%
1194*/
1195static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1196{
1197 if (length < 8)
1198 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001199
cristy3ed852e2009-09-05 21:47:34 +00001200 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1201 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001202
cristy3ed852e2009-09-05 21:47:34 +00001203 return(MagickFalse);
1204}
1205
1206/*
1207%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1208% %
1209% %
1210% %
1211% I s J N G %
1212% %
1213% %
1214% %
1215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1216%
1217% IsJNG() returns MagickTrue if the image format type, identified by the
1218% magick string, is JNG.
1219%
1220% The format of the IsJNG method is:
1221%
1222% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1223%
1224% A description of each parameter follows:
1225%
1226% o magick: compare image format pattern against these bytes.
1227%
1228% o length: Specifies the length of the magick string.
1229%
1230%
1231*/
1232static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1233{
1234 if (length < 8)
1235 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001236
cristy3ed852e2009-09-05 21:47:34 +00001237 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1238 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001239
cristy3ed852e2009-09-05 21:47:34 +00001240 return(MagickFalse);
1241}
1242
1243/*
1244%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245% %
1246% %
1247% %
1248% I s P N G %
1249% %
1250% %
1251% %
1252%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1253%
1254% IsPNG() returns MagickTrue if the image format type, identified by the
1255% magick string, is PNG.
1256%
1257% The format of the IsPNG method is:
1258%
1259% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1260%
1261% A description of each parameter follows:
1262%
1263% o magick: compare image format pattern against these bytes.
1264%
1265% o length: Specifies the length of the magick string.
1266%
1267*/
1268static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1269{
1270 if (length < 8)
1271 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001272
cristy3ed852e2009-09-05 21:47:34 +00001273 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1274 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001275
cristy3ed852e2009-09-05 21:47:34 +00001276 return(MagickFalse);
1277}
1278
1279#if defined(MAGICKCORE_PNG_DELEGATE)
1280#if defined(__cplusplus) || defined(c_plusplus)
1281extern "C" {
1282#endif
1283
glennrpd5045b42010-03-24 12:40:35 +00001284#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001285static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001286{
1287 unsigned char
1288 buffer[4];
1289
1290 assert(image != (Image *) NULL);
1291 assert(image->signature == MagickSignature);
1292 buffer[0]=(unsigned char) (value >> 24);
1293 buffer[1]=(unsigned char) (value >> 16);
1294 buffer[2]=(unsigned char) (value >> 8);
1295 buffer[3]=(unsigned char) value;
1296 return((size_t) WriteBlob(image,4,buffer));
1297}
1298
1299static void PNGLong(png_bytep p,png_uint_32 value)
1300{
1301 *p++=(png_byte) ((value >> 24) & 0xff);
1302 *p++=(png_byte) ((value >> 16) & 0xff);
1303 *p++=(png_byte) ((value >> 8) & 0xff);
1304 *p++=(png_byte) (value & 0xff);
1305}
1306
glennrpa521b2f2010-10-29 04:11:03 +00001307#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001308static void PNGsLong(png_bytep p,png_int_32 value)
1309{
1310 *p++=(png_byte) ((value >> 24) & 0xff);
1311 *p++=(png_byte) ((value >> 16) & 0xff);
1312 *p++=(png_byte) ((value >> 8) & 0xff);
1313 *p++=(png_byte) (value & 0xff);
1314}
glennrpa521b2f2010-10-29 04:11:03 +00001315#endif
cristy3ed852e2009-09-05 21:47:34 +00001316
1317static void PNGShort(png_bytep p,png_uint_16 value)
1318{
1319 *p++=(png_byte) ((value >> 8) & 0xff);
1320 *p++=(png_byte) (value & 0xff);
1321}
1322
1323static void PNGType(png_bytep p,png_bytep type)
1324{
1325 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1326}
1327
glennrp03812ae2010-12-24 01:31:34 +00001328static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1329 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001330{
1331 if (logging != MagickFalse)
1332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001333 " Writing %c%c%c%c chunk, length: %.20g",
1334 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001335}
glennrpd5045b42010-03-24 12:40:35 +00001336#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001337
1338#if defined(__cplusplus) || defined(c_plusplus)
1339}
1340#endif
1341
glennrpd5045b42010-03-24 12:40:35 +00001342#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001343/*
1344%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1345% %
1346% %
1347% %
1348% R e a d P N G I m a g e %
1349% %
1350% %
1351% %
1352%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1353%
1354% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1355% Multiple-image Network Graphics (MNG) image file and returns it. It
1356% allocates the memory necessary for the new Image structure and returns a
1357% pointer to the new image or set of images.
1358%
1359% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1360%
1361% The format of the ReadPNGImage method is:
1362%
1363% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1364%
1365% A description of each parameter follows:
1366%
1367% o image_info: the image info.
1368%
1369% o exception: return any errors or warnings in this structure.
1370%
1371% To do, more or less in chronological order (as of version 5.5.2,
1372% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1373%
1374% Get 16-bit cheap transparency working.
1375%
1376% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1377%
1378% Preserve all unknown and not-yet-handled known chunks found in input
1379% PNG file and copy them into output PNG files according to the PNG
1380% copying rules.
1381%
1382% (At this point, PNG encoding should be in full MNG compliance)
1383%
1384% Provide options for choice of background to use when the MNG BACK
1385% chunk is not present or is not mandatory (i.e., leave transparent,
1386% user specified, MNG BACK, PNG bKGD)
1387%
1388% Implement LOOP/ENDL [done, but could do discretionary loops more
1389% efficiently by linking in the duplicate frames.].
1390%
1391% Decode and act on the MHDR simplicity profile (offer option to reject
1392% files or attempt to process them anyway when the profile isn't LC or VLC).
1393%
1394% Upgrade to full MNG without Delta-PNG.
1395%
1396% o BACK [done a while ago except for background image ID]
1397% o MOVE [done 15 May 1999]
1398% o CLIP [done 15 May 1999]
1399% o DISC [done 19 May 1999]
1400% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1401% o SEEK [partially done 19 May 1999 (discard function only)]
1402% o SHOW
1403% o PAST
1404% o BASI
1405% o MNG-level tEXt/iTXt/zTXt
1406% o pHYg
1407% o pHYs
1408% o sBIT
1409% o bKGD
1410% o iTXt (wait for libpng implementation).
1411%
1412% Use the scene signature to discover when an identical scene is
1413% being reused, and just point to the original image->exception instead
1414% of storing another set of pixels. This not specific to MNG
1415% but could be applied generally.
1416%
1417% Upgrade to full MNG with Delta-PNG.
1418%
1419% JNG tEXt/iTXt/zTXt
1420%
1421% We will not attempt to read files containing the CgBI chunk.
1422% They are really Xcode files meant for display on the iPhone.
1423% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001424% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001425% since irretrievable loss of color data has occurred due to the
1426% use of premultiplied alpha.
1427*/
1428
1429#if defined(__cplusplus) || defined(c_plusplus)
1430extern "C" {
1431#endif
1432
1433/*
1434 This the function that does the actual reading of data. It is
1435 the same as the one supplied in libpng, except that it receives the
1436 datastream from the ReadBlob() function instead of standard input.
1437*/
1438static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1439{
1440 Image
1441 *image;
1442
1443 image=(Image *) png_get_io_ptr(png_ptr);
1444 if (length)
1445 {
1446 png_size_t
1447 check;
1448
1449 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1450 if (check != length)
1451 {
1452 char
1453 msg[MaxTextExtent];
1454
cristy3b6fd2e2011-05-20 12:53:50 +00001455 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001456 "Expected %.20g bytes; found %.20g bytes",(double) length,
1457 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001458 png_warning(png_ptr,msg);
1459 png_error(png_ptr,"Read Exception");
1460 }
1461 }
1462}
1463
1464#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1465 !defined(PNG_MNG_FEATURES_SUPPORTED)
1466/* We use mng_get_data() instead of png_get_data() if we have a libpng
1467 * older than libpng-1.0.3a, which was the first to allow the empty
1468 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1469 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1470 * encountered after an empty PLTE, so we have to look ahead for bKGD
1471 * chunks and remove them from the datastream that is passed to libpng,
1472 * and store their contents for later use.
1473 */
1474static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1475{
1476 MngInfo
1477 *mng_info;
1478
1479 Image
1480 *image;
1481
1482 png_size_t
1483 check;
1484
cristybb503372010-05-27 20:51:26 +00001485 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001486 i;
1487
1488 i=0;
1489 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1490 image=(Image *) mng_info->image;
1491 while (mng_info->bytes_in_read_buffer && length)
1492 {
1493 data[i]=mng_info->read_buffer[i];
1494 mng_info->bytes_in_read_buffer--;
1495 length--;
1496 i++;
1497 }
1498 if (length)
1499 {
1500 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001501
cristy3ed852e2009-09-05 21:47:34 +00001502 if (check != length)
1503 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001504
cristy3ed852e2009-09-05 21:47:34 +00001505 if (length == 4)
1506 {
1507 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1508 (data[3] == 0))
1509 {
1510 check=(png_size_t) ReadBlob(image,(size_t) length,
1511 (char *) mng_info->read_buffer);
1512 mng_info->read_buffer[4]=0;
1513 mng_info->bytes_in_read_buffer=4;
1514 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1515 mng_info->found_empty_plte=MagickTrue;
1516 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1517 {
1518 mng_info->found_empty_plte=MagickFalse;
1519 mng_info->have_saved_bkgd_index=MagickFalse;
1520 }
1521 }
glennrp0fe50b42010-11-16 03:52:51 +00001522
cristy3ed852e2009-09-05 21:47:34 +00001523 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1524 (data[3] == 1))
1525 {
1526 check=(png_size_t) ReadBlob(image,(size_t) length,
1527 (char *) mng_info->read_buffer);
1528 mng_info->read_buffer[4]=0;
1529 mng_info->bytes_in_read_buffer=4;
1530 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1531 if (mng_info->found_empty_plte)
1532 {
1533 /*
1534 Skip the bKGD data byte and CRC.
1535 */
1536 check=(png_size_t)
1537 ReadBlob(image,5,(char *) mng_info->read_buffer);
1538 check=(png_size_t) ReadBlob(image,(size_t) length,
1539 (char *) mng_info->read_buffer);
1540 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1541 mng_info->have_saved_bkgd_index=MagickTrue;
1542 mng_info->bytes_in_read_buffer=0;
1543 }
1544 }
1545 }
1546 }
1547}
1548#endif
1549
1550static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1551{
1552 Image
1553 *image;
1554
1555 image=(Image *) png_get_io_ptr(png_ptr);
1556 if (length)
1557 {
1558 png_size_t
1559 check;
1560
cristybb503372010-05-27 20:51:26 +00001561 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001562
cristy3ed852e2009-09-05 21:47:34 +00001563 if (check != length)
1564 png_error(png_ptr,"WriteBlob Failed");
1565 }
1566}
1567
1568static void png_flush_data(png_structp png_ptr)
1569{
1570 (void) png_ptr;
1571}
1572
1573#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1574static int PalettesAreEqual(Image *a,Image *b)
1575{
cristybb503372010-05-27 20:51:26 +00001576 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001577 i;
1578
1579 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1580 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001581
cristy3ed852e2009-09-05 21:47:34 +00001582 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1583 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001584
cristy3ed852e2009-09-05 21:47:34 +00001585 if (a->colors != b->colors)
1586 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001587
cristybb503372010-05-27 20:51:26 +00001588 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001589 {
1590 if ((a->colormap[i].red != b->colormap[i].red) ||
1591 (a->colormap[i].green != b->colormap[i].green) ||
1592 (a->colormap[i].blue != b->colormap[i].blue))
1593 return((int) MagickFalse);
1594 }
glennrp0fe50b42010-11-16 03:52:51 +00001595
cristy3ed852e2009-09-05 21:47:34 +00001596 return((int) MagickTrue);
1597}
1598#endif
1599
1600static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1601{
1602 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1603 mng_info->exists[i] && !mng_info->frozen[i])
1604 {
1605#ifdef MNG_OBJECT_BUFFERS
1606 if (mng_info->ob[i] != (MngBuffer *) NULL)
1607 {
1608 if (mng_info->ob[i]->reference_count > 0)
1609 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001610
cristy3ed852e2009-09-05 21:47:34 +00001611 if (mng_info->ob[i]->reference_count == 0)
1612 {
1613 if (mng_info->ob[i]->image != (Image *) NULL)
1614 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001615
cristy3ed852e2009-09-05 21:47:34 +00001616 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1617 }
1618 }
1619 mng_info->ob[i]=(MngBuffer *) NULL;
1620#endif
1621 mng_info->exists[i]=MagickFalse;
1622 mng_info->invisible[i]=MagickFalse;
1623 mng_info->viewable[i]=MagickFalse;
1624 mng_info->frozen[i]=MagickFalse;
1625 mng_info->x_off[i]=0;
1626 mng_info->y_off[i]=0;
1627 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001628 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001629 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001630 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001631 }
1632}
1633
glennrp21f0e622011-01-07 16:20:57 +00001634static void MngInfoFreeStruct(MngInfo *mng_info,
1635 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001636{
glennrp21f0e622011-01-07 16:20:57 +00001637 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001638 {
cristybb503372010-05-27 20:51:26 +00001639 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001640 i;
1641
1642 for (i=1; i < MNG_MAX_OBJECTS; i++)
1643 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001644
cristy3ed852e2009-09-05 21:47:34 +00001645 if (mng_info->global_plte != (png_colorp) NULL)
1646 mng_info->global_plte=(png_colorp)
1647 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001648
cristy3ed852e2009-09-05 21:47:34 +00001649 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1650 *have_mng_structure=MagickFalse;
1651 }
1652}
1653
1654static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1655{
1656 MngBox
1657 box;
1658
1659 box=box1;
1660 if (box.left < box2.left)
1661 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001662
cristy3ed852e2009-09-05 21:47:34 +00001663 if (box.top < box2.top)
1664 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001665
cristy3ed852e2009-09-05 21:47:34 +00001666 if (box.right > box2.right)
1667 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001668
cristy3ed852e2009-09-05 21:47:34 +00001669 if (box.bottom > box2.bottom)
1670 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001671
cristy3ed852e2009-09-05 21:47:34 +00001672 return box;
1673}
1674
1675static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1676{
1677 MngBox
1678 box;
1679
1680 /*
1681 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1682 */
cristybb503372010-05-27 20:51:26 +00001683 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1684 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1685 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1686 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001687 if (delta_type != 0)
1688 {
1689 box.left+=previous_box.left;
1690 box.right+=previous_box.right;
1691 box.top+=previous_box.top;
1692 box.bottom+=previous_box.bottom;
1693 }
glennrp0fe50b42010-11-16 03:52:51 +00001694
cristy3ed852e2009-09-05 21:47:34 +00001695 return(box);
1696}
1697
1698static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1699 unsigned char *p)
1700{
1701 MngPair
1702 pair;
1703 /*
cristybb503372010-05-27 20:51:26 +00001704 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001705 */
cristy8182b072010-05-30 20:10:53 +00001706 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1707 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001708
cristy3ed852e2009-09-05 21:47:34 +00001709 if (delta_type != 0)
1710 {
1711 pair.a+=previous_pair.a;
1712 pair.b+=previous_pair.b;
1713 }
glennrp0fe50b42010-11-16 03:52:51 +00001714
cristy3ed852e2009-09-05 21:47:34 +00001715 return(pair);
1716}
1717
cristy8182b072010-05-30 20:10:53 +00001718static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001719{
cristy8182b072010-05-30 20:10:53 +00001720 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001721}
1722
cristyc82a27b2011-10-21 01:07:16 +00001723typedef struct _PNGErrorInfo
cristy3ed852e2009-09-05 21:47:34 +00001724{
1725 Image
1726 *image;
1727
cristyc82a27b2011-10-21 01:07:16 +00001728 ExceptionInfo
1729 *exception;
1730} PNGErrorInfo;
glennrp0fe50b42010-11-16 03:52:51 +00001731
cristyc82a27b2011-10-21 01:07:16 +00001732static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1733{
1734 ExceptionInfo
1735 *exception;
glennrp0fe50b42010-11-16 03:52:51 +00001736
cristyc82a27b2011-10-21 01:07:16 +00001737 Image
1738 *image;
1739
1740 PNGErrorInfo
1741 *error_info;
1742
1743 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1744 image=error_info->image;
1745 exception=error_info->exception;
1746
1747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1748 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1749
1750 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1751 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001752
glennrpe4017e32011-01-08 17:16:09 +00001753#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001754 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1755 * are building with libpng-1.4.x and can be ignored.
1756 */
cristy3ed852e2009-09-05 21:47:34 +00001757 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001758#else
1759 png_longjmp(ping,1);
1760#endif
cristy3ed852e2009-09-05 21:47:34 +00001761}
1762
glennrpcf002022011-01-30 02:38:15 +00001763static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001764{
cristyc82a27b2011-10-21 01:07:16 +00001765 ExceptionInfo
1766 *exception;
1767
cristy3ed852e2009-09-05 21:47:34 +00001768 Image
1769 *image;
1770
cristyc82a27b2011-10-21 01:07:16 +00001771 PNGErrorInfo
1772 *error_info;
1773
cristy3ed852e2009-09-05 21:47:34 +00001774 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1775 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001776
cristyc82a27b2011-10-21 01:07:16 +00001777 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1778 image=error_info->image;
1779 exception=error_info->exception;
1780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1781 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001782
cristyc82a27b2011-10-21 01:07:16 +00001783 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
cristy3ed852e2009-09-05 21:47:34 +00001784 message,"`%s'",image->filename);
1785}
1786
1787#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001788static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001789{
1790#if (PNG_LIBPNG_VER < 10011)
1791 png_voidp
1792 ret;
1793
cristydf0d90e2011-12-12 01:03:55 +00001794 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001795 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001796
cristy3ed852e2009-09-05 21:47:34 +00001797 if (ret == NULL)
1798 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001799
cristy3ed852e2009-09-05 21:47:34 +00001800 return(ret);
1801#else
cristydf0d90e2011-12-12 01:03:55 +00001802 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001803 return((png_voidp) AcquireMagickMemory((size_t) size));
1804#endif
1805}
1806
1807/*
1808 Free a pointer. It is removed from the list at the same time.
1809*/
glennrpcf002022011-01-30 02:38:15 +00001810static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001811{
glennrp3bd393f2011-12-21 18:54:53 +00001812 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001813 ptr=RelinquishMagickMemory(ptr);
1814 return((png_free_ptr) NULL);
1815}
1816#endif
1817
1818#if defined(__cplusplus) || defined(c_plusplus)
1819}
1820#endif
1821
1822static int
glennrpcf002022011-01-30 02:38:15 +00001823Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristyd15e6592011-10-15 00:13:06 +00001824 png_textp text,int ii,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001825{
cristybb503372010-05-27 20:51:26 +00001826 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001827 i;
1828
1829 register unsigned char
1830 *dp;
1831
1832 register png_charp
1833 sp;
1834
1835 png_uint_32
1836 length,
1837 nibbles;
1838
1839 StringInfo
1840 *profile;
1841
glennrp0c3e06b2010-11-19 13:45:02 +00001842 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001843 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1844 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1845 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1846 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1847 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1848 13,14,15};
1849
1850 sp=text[ii].text+1;
1851 /* look for newline */
1852 while (*sp != '\n')
1853 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001854
cristy3ed852e2009-09-05 21:47:34 +00001855 /* look for length */
1856 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1857 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001858
cristyf2f27272009-12-17 14:48:46 +00001859 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001860
glennrp97f90e22011-02-22 05:47:58 +00001861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1862 " length: %lu",(unsigned long) length);
1863
cristy3ed852e2009-09-05 21:47:34 +00001864 while (*sp != ' ' && *sp != '\n')
1865 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001866
cristy3ed852e2009-09-05 21:47:34 +00001867 /* allocate space */
1868 if (length == 0)
1869 {
cristyc82a27b2011-10-21 01:07:16 +00001870 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001871 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1872 return(MagickFalse);
1873 }
glennrp0fe50b42010-11-16 03:52:51 +00001874
cristy8723e4b2011-09-01 13:11:19 +00001875 profile=BlobToStringInfo((const void *) NULL,length);
glennrp0fe50b42010-11-16 03:52:51 +00001876
cristy3ed852e2009-09-05 21:47:34 +00001877 if (profile == (StringInfo *) NULL)
1878 {
cristyc82a27b2011-10-21 01:07:16 +00001879 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001880 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1881 "unable to copy profile");
1882 return(MagickFalse);
1883 }
glennrp0fe50b42010-11-16 03:52:51 +00001884
cristy3ed852e2009-09-05 21:47:34 +00001885 /* copy profile, skipping white space and column 1 "=" signs */
1886 dp=GetStringInfoDatum(profile);
1887 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001888
cristybb503372010-05-27 20:51:26 +00001889 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001890 {
1891 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1892 {
1893 if (*sp == '\0')
1894 {
cristyc82a27b2011-10-21 01:07:16 +00001895 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00001896 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1897 profile=DestroyStringInfo(profile);
1898 return(MagickFalse);
1899 }
1900 sp++;
1901 }
glennrp0fe50b42010-11-16 03:52:51 +00001902
cristy3ed852e2009-09-05 21:47:34 +00001903 if (i%2 == 0)
1904 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001905
cristy3ed852e2009-09-05 21:47:34 +00001906 else
1907 (*dp++)+=unhex[(int) *sp++];
1908 }
1909 /*
1910 We have already read "Raw profile type.
1911 */
cristyd15e6592011-10-15 00:13:06 +00001912 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001913 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001914
cristy3ed852e2009-09-05 21:47:34 +00001915 if (image_info->verbose)
1916 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001917
cristy3ed852e2009-09-05 21:47:34 +00001918 return MagickTrue;
1919}
1920
1921#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1922static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1923{
1924 Image
1925 *image;
1926
1927
1928 /* The unknown chunk structure contains the chunk data:
1929 png_byte name[5];
1930 png_byte *data;
1931 png_size_t size;
1932
1933 Note that libpng has already taken care of the CRC handling.
1934 */
1935
1936
1937 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1938 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1939 return(0); /* Did not recognize */
1940
1941 /* recognized vpAg */
1942
1943 if (chunk->size != 9)
1944 return(-1); /* Error return */
1945
1946 if (chunk->data[8] != 0)
1947 return(0); /* ImageMagick requires pixel units */
1948
1949 image=(Image *) png_get_user_chunk_ptr(ping);
1950
cristybb503372010-05-27 20:51:26 +00001951 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001952 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001953
cristybb503372010-05-27 20:51:26 +00001954 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001955 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1956
1957 /* Return one of the following: */
1958 /* return(-n); chunk had an error */
1959 /* return(0); did not recognize */
1960 /* return(n); success */
1961
1962 return(1);
1963
1964}
1965#endif
1966
1967/*
1968%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1969% %
1970% %
1971% %
1972% R e a d O n e P N G I m a g e %
1973% %
1974% %
1975% %
1976%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1977%
1978% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1979% (minus the 8-byte signature) and returns it. It allocates the memory
1980% necessary for the new Image structure and returns a pointer to the new
1981% image.
1982%
1983% The format of the ReadOnePNGImage method is:
1984%
1985% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1986% ExceptionInfo *exception)
1987%
1988% A description of each parameter follows:
1989%
1990% o mng_info: Specifies a pointer to a MngInfo structure.
1991%
1992% o image_info: the image info.
1993%
1994% o exception: return any errors or warnings in this structure.
1995%
1996*/
1997static Image *ReadOnePNGImage(MngInfo *mng_info,
1998 const ImageInfo *image_info, ExceptionInfo *exception)
1999{
2000 /* Read one PNG image */
2001
glennrpcc95c3f2011-04-18 16:46:48 +00002002 /* To do: Read the tIME chunk into the date:modify property */
2003 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2004
cristy3ed852e2009-09-05 21:47:34 +00002005 Image
2006 *image;
2007
2008 int
glennrp4eb39312011-03-30 21:34:55 +00002009 intent,
glennrpcb395ac2011-03-30 19:50:23 +00002010 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00002011 num_text,
glennrp4eb39312011-03-30 21:34:55 +00002012 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00002013 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00002014 pass,
2015 ping_bit_depth,
2016 ping_color_type,
2017 ping_interlace_method,
2018 ping_compression_method,
2019 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00002020 ping_num_trans,
2021 unit_type;
2022
2023 double
2024 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002025
2026 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002027 logging,
cristy3ed852e2009-09-05 21:47:34 +00002028 status;
2029
cristyc82a27b2011-10-21 01:07:16 +00002030 PixelInfo
2031 transparent_color;
2032
2033 PNGErrorInfo
2034 error_info;
2035
glennrpfaa852b2010-03-30 12:17:00 +00002036 png_bytep
2037 ping_trans_alpha;
2038
2039 png_color_16p
2040 ping_background,
2041 ping_trans_color;
2042
cristy3ed852e2009-09-05 21:47:34 +00002043 png_info
2044 *end_info,
2045 *ping_info;
2046
2047 png_struct
2048 *ping;
2049
2050 png_textp
2051 text;
2052
glennrpfaa852b2010-03-30 12:17:00 +00002053 png_uint_32
2054 ping_height,
2055 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002056 x_resolution,
2057 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002058
cristy3ed852e2009-09-05 21:47:34 +00002059 QuantumInfo
2060 *quantum_info;
2061
2062 unsigned char
glennrpcf002022011-01-30 02:38:15 +00002063 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002064
cristybb503372010-05-27 20:51:26 +00002065 ssize_t
cristy756ae432011-11-19 02:18:25 +00002066 ping_rowbytes,
cristy3ed852e2009-09-05 21:47:34 +00002067 y;
2068
2069 register unsigned char
2070 *p;
2071
cristybb503372010-05-27 20:51:26 +00002072 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002073 i,
2074 x;
2075
cristy4c08aed2011-07-01 19:47:50 +00002076 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002077 *q;
2078
2079 size_t
glennrp39992b42010-11-14 00:03:43 +00002080 length,
cristy3ed852e2009-09-05 21:47:34 +00002081 row_offset;
2082
cristyeb3b22a2011-03-31 20:16:11 +00002083 ssize_t
2084 j;
2085
cristy3ed852e2009-09-05 21:47:34 +00002086#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2087 png_byte unused_chunks[]=
2088 {
2089 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2090 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2091 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2092 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2093 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2094 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2095 };
2096#endif
2097
2098 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002099 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002100
2101#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002102 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002103#endif
2104
glennrp25c1e2b2010-03-25 01:39:56 +00002105#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002106 if (image_info->verbose)
2107 printf("Your PNG library (libpng-%s) is rather old.\n",
2108 PNG_LIBPNG_VER_STRING);
2109#endif
2110
glennrp61b4c952009-11-10 20:40:41 +00002111#if (PNG_LIBPNG_VER >= 10400)
2112# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2113 if (image_info->verbose)
2114 {
2115 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2116 PNG_LIBPNG_VER_STRING);
2117 printf("Please update it.\n");
2118 }
2119# endif
2120#endif
2121
2122
cristyed552522009-10-16 14:04:35 +00002123 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002124 image=mng_info->image;
2125
glennrpa6a06632011-01-19 15:15:34 +00002126 if (logging != MagickFalse)
2127 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2128 " image->matte=%d",(int) image->matte);
2129
glennrp0e319732011-01-25 21:53:13 +00002130 /* Set to an out-of-range color unless tRNS chunk is present */
2131 transparent_color.red=65537;
2132 transparent_color.green=65537;
2133 transparent_color.blue=65537;
cristy4c08aed2011-07-01 19:47:50 +00002134 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002135
glennrpcb395ac2011-03-30 19:50:23 +00002136 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002137 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002138 num_raw_profiles = 0;
2139
cristy3ed852e2009-09-05 21:47:34 +00002140 /*
2141 Allocate the PNG structures
2142 */
2143#ifdef PNG_USER_MEM_SUPPORTED
cristyc82a27b2011-10-21 01:07:16 +00002144 error_info.image=image;
2145 error_info.exception=exception;
2146 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002147 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2148 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002149#else
cristyc82a27b2011-10-21 01:07:16 +00002150 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002151 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002152#endif
2153 if (ping == (png_struct *) NULL)
2154 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002155
cristy3ed852e2009-09-05 21:47:34 +00002156 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002157
cristy3ed852e2009-09-05 21:47:34 +00002158 if (ping_info == (png_info *) NULL)
2159 {
2160 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2161 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2162 }
glennrp0fe50b42010-11-16 03:52:51 +00002163
cristy3ed852e2009-09-05 21:47:34 +00002164 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002165
cristy3ed852e2009-09-05 21:47:34 +00002166 if (end_info == (png_info *) NULL)
2167 {
2168 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2169 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2170 }
glennrp0fe50b42010-11-16 03:52:51 +00002171
glennrpcf002022011-01-30 02:38:15 +00002172 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002173
glennrpfaa852b2010-03-30 12:17:00 +00002174 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002175 {
2176 /*
2177 PNG image is corrupt.
2178 */
2179 png_destroy_read_struct(&ping,&ping_info,&end_info);
2180#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002181 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002182#endif
2183 if (logging != MagickFalse)
2184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2185 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002186
cristy3ed852e2009-09-05 21:47:34 +00002187 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002188 {
cristyc82a27b2011-10-21 01:07:16 +00002189 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002190 image->columns=0;
2191 }
glennrp0fe50b42010-11-16 03:52:51 +00002192
cristy3ed852e2009-09-05 21:47:34 +00002193 return(GetFirstImageInList(image));
2194 }
2195 /*
2196 Prepare PNG for reading.
2197 */
glennrpfaa852b2010-03-30 12:17:00 +00002198
cristy3ed852e2009-09-05 21:47:34 +00002199 mng_info->image_found++;
2200 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002201
cristy3ed852e2009-09-05 21:47:34 +00002202 if (LocaleCompare(image_info->magick,"MNG") == 0)
2203 {
2204#if defined(PNG_MNG_FEATURES_SUPPORTED)
2205 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2206 png_set_read_fn(ping,image,png_get_data);
2207#else
2208#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2209 png_permit_empty_plte(ping,MagickTrue);
2210 png_set_read_fn(ping,image,png_get_data);
2211#else
2212 mng_info->image=image;
2213 mng_info->bytes_in_read_buffer=0;
2214 mng_info->found_empty_plte=MagickFalse;
2215 mng_info->have_saved_bkgd_index=MagickFalse;
2216 png_set_read_fn(ping,mng_info,mng_get_data);
2217#endif
2218#endif
2219 }
glennrp0fe50b42010-11-16 03:52:51 +00002220
cristy3ed852e2009-09-05 21:47:34 +00002221 else
2222 png_set_read_fn(ping,image,png_get_data);
2223
2224#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2225 /* Ignore unused chunks and all unknown chunks except for vpAg */
2226 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2227 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2228 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2229 (int)sizeof(unused_chunks)/5);
2230 /* Callback for other unknown chunks */
2231 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2232#endif
2233
glennrp991e92a2010-01-28 03:09:00 +00002234#if (PNG_LIBPNG_VER < 10400)
2235# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2236 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002237 /* Disable thread-unsafe features of pnggccrd */
2238 if (png_access_version_number() >= 10200)
2239 {
2240 png_uint_32 mmx_disable_mask=0;
2241 png_uint_32 asm_flags;
2242
2243 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2244 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2245 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2246 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2247 asm_flags=png_get_asm_flags(ping);
2248 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2249 }
glennrp991e92a2010-01-28 03:09:00 +00002250# endif
cristy3ed852e2009-09-05 21:47:34 +00002251#endif
2252
2253 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002254
2255 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2256 &ping_bit_depth,&ping_color_type,
2257 &ping_interlace_method,&ping_compression_method,
2258 &ping_filter_method);
2259
2260 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2261 &ping_trans_color);
2262
2263 (void) png_get_bKGD(ping, ping_info, &ping_background);
2264
2265 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002266 {
glennrpfaa852b2010-03-30 12:17:00 +00002267 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2268 {
2269 png_set_packing(ping);
2270 ping_bit_depth = 8;
2271 }
cristy3ed852e2009-09-05 21:47:34 +00002272 }
glennrpfaa852b2010-03-30 12:17:00 +00002273
2274 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002275 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002276 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00002277 if (logging != MagickFalse)
2278 {
2279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002280 " PNG width: %.20g, height: %.20g",
2281 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002282
cristy3ed852e2009-09-05 21:47:34 +00002283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2284 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002285 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002286
cristy3ed852e2009-09-05 21:47:34 +00002287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2288 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002289 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002290
cristy3ed852e2009-09-05 21:47:34 +00002291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2292 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002293 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002294 }
2295
glennrpfaa852b2010-03-30 12:17:00 +00002296#ifdef PNG_READ_iCCP_SUPPORTED
2297 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002298 {
2299 int
2300 compression;
2301
glennrpe4017e32011-01-08 17:16:09 +00002302#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002303 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002304 info;
2305#else
2306 png_bytep
2307 info;
2308#endif
2309
2310 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002311 name;
2312
2313 png_uint_32
2314 profile_length;
2315
2316 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2317 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002318
cristy3ed852e2009-09-05 21:47:34 +00002319 if (profile_length != 0)
2320 {
2321 StringInfo
2322 *profile;
2323
2324 if (logging != MagickFalse)
2325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2326 " Reading PNG iCCP chunk.");
cristye8f8f382011-09-01 13:32:37 +00002327 profile=BlobToStringInfo(info,profile_length);
2328 if (profile == (StringInfo *) NULL)
2329 {
cristyc82a27b2011-10-21 01:07:16 +00002330 (void) ThrowMagickException(exception,GetMagickModule(),
cristye8f8f382011-09-01 13:32:37 +00002331 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2332 "unable to copy profile");
cristyad5b0852011-09-08 02:01:21 +00002333 return((Image *) NULL);
cristye8f8f382011-09-01 13:32:37 +00002334 }
cristy3ed852e2009-09-05 21:47:34 +00002335 SetStringInfoDatum(profile,(const unsigned char *) info);
cristyd15e6592011-10-15 00:13:06 +00002336 (void) SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00002337 profile=DestroyStringInfo(profile);
2338 }
2339 }
2340#endif
2341#if defined(PNG_READ_sRGB_SUPPORTED)
2342 {
cristy3ed852e2009-09-05 21:47:34 +00002343 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00002344 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2345 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00002346
cristy3ed852e2009-09-05 21:47:34 +00002347 if (png_get_sRGB(ping,ping_info,&intent))
2348 {
glennrpcf002022011-01-30 02:38:15 +00002349 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2350 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002351
cristy3ed852e2009-09-05 21:47:34 +00002352 if (logging != MagickFalse)
2353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002354 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002355 }
2356 }
2357#endif
2358 {
glennrpfaa852b2010-03-30 12:17:00 +00002359 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2360 if (mng_info->have_global_gama)
2361 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002362
cristy3ed852e2009-09-05 21:47:34 +00002363 if (png_get_gAMA(ping,ping_info,&file_gamma))
2364 {
2365 image->gamma=(float) file_gamma;
2366 if (logging != MagickFalse)
2367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2368 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2369 }
2370 }
glennrpfaa852b2010-03-30 12:17:00 +00002371 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2372 {
2373 if (mng_info->have_global_chrm != MagickFalse)
2374 {
2375 (void) png_set_cHRM(ping,ping_info,
2376 mng_info->global_chrm.white_point.x,
2377 mng_info->global_chrm.white_point.y,
2378 mng_info->global_chrm.red_primary.x,
2379 mng_info->global_chrm.red_primary.y,
2380 mng_info->global_chrm.green_primary.x,
2381 mng_info->global_chrm.green_primary.y,
2382 mng_info->global_chrm.blue_primary.x,
2383 mng_info->global_chrm.blue_primary.y);
2384 }
2385 }
glennrp0fe50b42010-11-16 03:52:51 +00002386
glennrpfaa852b2010-03-30 12:17:00 +00002387 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002388 {
2389 (void) png_get_cHRM(ping,ping_info,
2390 &image->chromaticity.white_point.x,
2391 &image->chromaticity.white_point.y,
2392 &image->chromaticity.red_primary.x,
2393 &image->chromaticity.red_primary.y,
2394 &image->chromaticity.green_primary.x,
2395 &image->chromaticity.green_primary.y,
2396 &image->chromaticity.blue_primary.x,
2397 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002398
cristy3ed852e2009-09-05 21:47:34 +00002399 if (logging != MagickFalse)
2400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2401 " Reading PNG cHRM chunk.");
2402 }
glennrp0fe50b42010-11-16 03:52:51 +00002403
glennrpe610a072010-08-05 17:08:46 +00002404 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002405 {
glennrpe610a072010-08-05 17:08:46 +00002406 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002407 Magick_RenderingIntent_to_PNG_RenderingIntent
2408 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00002409 png_set_gAMA(ping,ping_info,0.45455f);
2410 png_set_cHRM(ping,ping_info,
2411 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2412 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002413 }
cristy3ed852e2009-09-05 21:47:34 +00002414#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002415 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002416 {
cristy905ef802011-02-23 00:29:18 +00002417 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2418 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002419
cristy3ed852e2009-09-05 21:47:34 +00002420 if (logging != MagickFalse)
2421 if (image->page.x || image->page.y)
2422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002423 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2424 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002425 }
2426#endif
2427#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002428 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2429 {
2430 if (mng_info->have_global_phys)
2431 {
2432 png_set_pHYs(ping,ping_info,
2433 mng_info->global_x_pixels_per_unit,
2434 mng_info->global_y_pixels_per_unit,
2435 mng_info->global_phys_unit_type);
2436 }
2437 }
2438
2439 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002440 {
cristy3ed852e2009-09-05 21:47:34 +00002441 /*
2442 Set image resolution.
2443 */
2444 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002445 &unit_type);
cristy2a11bef2011-10-28 18:33:11 +00002446 image->resolution.x=(double) x_resolution;
2447 image->resolution.y=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002448
cristy3ed852e2009-09-05 21:47:34 +00002449 if (unit_type == PNG_RESOLUTION_METER)
2450 {
2451 image->units=PixelsPerCentimeterResolution;
cristy2a11bef2011-10-28 18:33:11 +00002452 image->resolution.x=(double) x_resolution/100.0;
2453 image->resolution.y=(double) y_resolution/100.0;
cristy3ed852e2009-09-05 21:47:34 +00002454 }
glennrp0fe50b42010-11-16 03:52:51 +00002455
cristy3ed852e2009-09-05 21:47:34 +00002456 if (logging != MagickFalse)
2457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002458 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2459 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002460 }
cristy3ed852e2009-09-05 21:47:34 +00002461#endif
glennrp823b55c2011-03-14 18:46:46 +00002462
glennrpfaa852b2010-03-30 12:17:00 +00002463 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002464 {
2465 int
2466 number_colors;
2467
2468 png_colorp
2469 palette;
2470
2471 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002472
cristy3ed852e2009-09-05 21:47:34 +00002473 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002474 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002475 {
2476 if (mng_info->global_plte_length)
2477 {
2478 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2479 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002480
glennrpfaa852b2010-03-30 12:17:00 +00002481 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002482 if (mng_info->global_trns_length)
2483 {
2484 if (mng_info->global_trns_length >
2485 mng_info->global_plte_length)
cristyc82a27b2011-10-21 01:07:16 +00002486 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00002487 GetMagickModule(),CoderError,
2488 "global tRNS has more entries than global PLTE",
2489 "`%s'",image_info->filename);
2490 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2491 (int) mng_info->global_trns_length,NULL);
2492 }
glennrpbfd9e612011-04-22 14:02:20 +00002493#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002494 if (
2495#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2496 mng_info->have_saved_bkgd_index ||
2497#endif
glennrpfaa852b2010-03-30 12:17:00 +00002498 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002499 {
2500 png_color_16
2501 background;
2502
2503#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2504 if (mng_info->have_saved_bkgd_index)
2505 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002506#endif
glennrpfaa852b2010-03-30 12:17:00 +00002507 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2508 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002509
cristy3ed852e2009-09-05 21:47:34 +00002510 background.red=(png_uint_16)
2511 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002512
cristy3ed852e2009-09-05 21:47:34 +00002513 background.green=(png_uint_16)
2514 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002515
cristy3ed852e2009-09-05 21:47:34 +00002516 background.blue=(png_uint_16)
2517 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002518
glennrpc6c391a2011-04-27 02:23:56 +00002519 background.gray=(png_uint_16)
2520 mng_info->global_plte[background.index].green;
2521
cristy3ed852e2009-09-05 21:47:34 +00002522 png_set_bKGD(ping,ping_info,&background);
2523 }
2524#endif
2525 }
2526 else
cristyc82a27b2011-10-21 01:07:16 +00002527 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00002528 CoderError,"No global PLTE in file","`%s'",
2529 image_info->filename);
2530 }
2531 }
2532
glennrpbfd9e612011-04-22 14:02:20 +00002533#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002534 if (mng_info->have_global_bkgd &&
2535 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002536 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002537
glennrpfaa852b2010-03-30 12:17:00 +00002538 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002539 {
glennrpbfd9e612011-04-22 14:02:20 +00002540 unsigned int
2541 bkgd_scale;
2542
cristy3ed852e2009-09-05 21:47:34 +00002543 /*
2544 Set image background color.
2545 */
2546 if (logging != MagickFalse)
2547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2548 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002549
glennrpbfd9e612011-04-22 14:02:20 +00002550 /* Scale background components to 16-bit, then scale
2551 * to quantum depth
2552 */
2553 if (logging != MagickFalse)
2554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2555 " raw ping_background=(%d,%d,%d).",ping_background->red,
2556 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002557
glennrpbfd9e612011-04-22 14:02:20 +00002558 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002559
glennrpbfd9e612011-04-22 14:02:20 +00002560 if (ping_bit_depth == 1)
2561 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002562
glennrpbfd9e612011-04-22 14:02:20 +00002563 else if (ping_bit_depth == 2)
2564 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002565
glennrpbfd9e612011-04-22 14:02:20 +00002566 else if (ping_bit_depth == 4)
2567 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002568
glennrpbfd9e612011-04-22 14:02:20 +00002569 if (ping_bit_depth <= 8)
2570 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002571
glennrpbfd9e612011-04-22 14:02:20 +00002572 ping_background->red *= bkgd_scale;
2573 ping_background->green *= bkgd_scale;
2574 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002575
glennrpbfd9e612011-04-22 14:02:20 +00002576 if (logging != MagickFalse)
2577 {
glennrp2cbb4482010-06-02 04:37:24 +00002578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2579 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002580
glennrp2cbb4482010-06-02 04:37:24 +00002581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2582 " ping_background=(%d,%d,%d).",ping_background->red,
2583 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002584 }
glennrp2cbb4482010-06-02 04:37:24 +00002585
glennrpbfd9e612011-04-22 14:02:20 +00002586 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002587 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002588
glennrpbfd9e612011-04-22 14:02:20 +00002589 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002590 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002591
glennrpbfd9e612011-04-22 14:02:20 +00002592 image->background_color.blue=
2593 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002594
cristy4c08aed2011-07-01 19:47:50 +00002595 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002596
glennrpbfd9e612011-04-22 14:02:20 +00002597 if (logging != MagickFalse)
2598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2599 " image->background_color=(%.20g,%.20g,%.20g).",
2600 (double) image->background_color.red,
2601 (double) image->background_color.green,
2602 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002603 }
glennrpbfd9e612011-04-22 14:02:20 +00002604#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002605
glennrpfaa852b2010-03-30 12:17:00 +00002606 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002607 {
2608 /*
glennrpa6a06632011-01-19 15:15:34 +00002609 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002610 */
2611 int
2612 max_sample;
2613
cristy35ef8242010-06-03 16:24:13 +00002614 size_t
2615 one=1;
2616
cristy3ed852e2009-09-05 21:47:34 +00002617 if (logging != MagickFalse)
2618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2619 " Reading PNG tRNS chunk.");
2620
cristyf9cca6a2010-06-04 23:49:28 +00002621 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002622
glennrpfaa852b2010-03-30 12:17:00 +00002623 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2624 (int)ping_trans_color->gray > max_sample) ||
2625 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2626 ((int)ping_trans_color->red > max_sample ||
2627 (int)ping_trans_color->green > max_sample ||
2628 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002629 {
2630 if (logging != MagickFalse)
2631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2632 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002633 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002634 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002635 image->matte=MagickFalse;
2636 }
2637 else
2638 {
glennrpa6a06632011-01-19 15:15:34 +00002639 int
2640 scale_to_short;
2641
2642 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2643
2644 /* Scale transparent_color to short */
2645 transparent_color.red= scale_to_short*ping_trans_color->red;
2646 transparent_color.green= scale_to_short*ping_trans_color->green;
2647 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy4c08aed2011-07-01 19:47:50 +00002648 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002649
glennrpfaa852b2010-03-30 12:17:00 +00002650 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002651 {
glennrp0f111982010-07-07 20:18:33 +00002652 if (logging != MagickFalse)
2653 {
2654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2655 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002656
glennrp0f111982010-07-07 20:18:33 +00002657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy101ab702011-10-13 13:06:32 +00002658 " scaled graylevel is %.20g.",transparent_color.alpha);
glennrp0f111982010-07-07 20:18:33 +00002659 }
cristy4c08aed2011-07-01 19:47:50 +00002660 transparent_color.red=transparent_color.alpha;
2661 transparent_color.green=transparent_color.alpha;
2662 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002663 }
2664 }
2665 }
2666#if defined(PNG_READ_sBIT_SUPPORTED)
2667 if (mng_info->have_global_sbit)
2668 {
glennrpfaa852b2010-03-30 12:17:00 +00002669 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002670 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2671 }
2672#endif
2673 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002674
cristy3ed852e2009-09-05 21:47:34 +00002675 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002676
2677 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2678
cristy3ed852e2009-09-05 21:47:34 +00002679 /*
2680 Initialize image structure.
2681 */
2682 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002683 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002684 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002685 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002686 if (mng_info->mng_type == 0)
2687 {
glennrpfaa852b2010-03-30 12:17:00 +00002688 mng_info->mng_width=ping_width;
2689 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002690 mng_info->frame=mng_info->image_box;
2691 mng_info->clip=mng_info->image_box;
2692 }
glennrp0fe50b42010-11-16 03:52:51 +00002693
cristy3ed852e2009-09-05 21:47:34 +00002694 else
2695 {
2696 image->page.y=mng_info->y_off[mng_info->object_id];
2697 }
glennrp0fe50b42010-11-16 03:52:51 +00002698
cristy3ed852e2009-09-05 21:47:34 +00002699 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002700 image->columns=ping_width;
2701 image->rows=ping_height;
2702 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002703 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002704 {
cristybefe4d22010-06-07 01:18:58 +00002705 size_t
2706 one;
2707
cristy3ed852e2009-09-05 21:47:34 +00002708 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002709 one=1;
2710 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002711#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2712 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002713 image->colors=256;
2714#else
2715 if (image->colors > 65536L)
2716 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002717#endif
glennrpfaa852b2010-03-30 12:17:00 +00002718 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002719 {
2720 int
2721 number_colors;
2722
2723 png_colorp
2724 palette;
2725
2726 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002727 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002728
cristy3ed852e2009-09-05 21:47:34 +00002729 if (logging != MagickFalse)
2730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2731 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2732 }
2733 }
2734
2735 if (image->storage_class == PseudoClass)
2736 {
2737 /*
2738 Initialize image colormap.
2739 */
cristy018f07f2011-09-04 21:15:19 +00002740 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002741 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002742
glennrpfaa852b2010-03-30 12:17:00 +00002743 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002744 {
2745 int
2746 number_colors;
2747
2748 png_colorp
2749 palette;
2750
2751 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002752
glennrp6af6cf12011-04-22 13:05:16 +00002753 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002754 {
2755 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2756 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2757 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2758 }
glennrp6af6cf12011-04-22 13:05:16 +00002759
glennrp67b9c1a2011-04-22 18:47:36 +00002760 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002761 {
2762 image->colormap[i].red=0;
2763 image->colormap[i].green=0;
2764 image->colormap[i].blue=0;
2765 }
cristy3ed852e2009-09-05 21:47:34 +00002766 }
glennrp0fe50b42010-11-16 03:52:51 +00002767
cristy3ed852e2009-09-05 21:47:34 +00002768 else
2769 {
cristybb503372010-05-27 20:51:26 +00002770 size_t
cristy3ed852e2009-09-05 21:47:34 +00002771 scale;
2772
glennrpfaa852b2010-03-30 12:17:00 +00002773 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002774
cristy3ed852e2009-09-05 21:47:34 +00002775 if (scale < 1)
2776 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002777
cristybb503372010-05-27 20:51:26 +00002778 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002779 {
2780 image->colormap[i].red=(Quantum) (i*scale);
2781 image->colormap[i].green=(Quantum) (i*scale);
2782 image->colormap[i].blue=(Quantum) (i*scale);
2783 }
2784 }
2785 }
glennrp147bc912011-03-30 18:47:21 +00002786
glennrpcb395ac2011-03-30 19:50:23 +00002787 /* Set some properties for reporting by "identify" */
2788 {
glennrp147bc912011-03-30 18:47:21 +00002789 char
2790 msg[MaxTextExtent];
2791
2792 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2793 ping_interlace_method in value */
2794
cristy3b6fd2e2011-05-20 12:53:50 +00002795 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002796 "%d, %d",(int) ping_width, (int) ping_height);
cristy5d6fc9c2011-12-27 03:10:42 +00002797 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002798
cristy3b6fd2e2011-05-20 12:53:50 +00002799 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
cristy5d6fc9c2011-12-27 03:10:42 +00002800 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002801
cristy3b6fd2e2011-05-20 12:53:50 +00002802 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
cristy5d6fc9c2011-12-27 03:10:42 +00002803 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002804
cristy3b6fd2e2011-05-20 12:53:50 +00002805 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002806 (int) ping_interlace_method);
cristy5d6fc9c2011-12-27 03:10:42 +00002807 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
glennrpcb395ac2011-03-30 19:50:23 +00002808 }
glennrp147bc912011-03-30 18:47:21 +00002809
cristy3ed852e2009-09-05 21:47:34 +00002810 /*
2811 Read image scanlines.
2812 */
2813 if (image->delay != 0)
2814 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002815
glennrp0ca69b12010-07-26 01:57:52 +00002816 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002817 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2818 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002819 {
2820 if (logging != MagickFalse)
2821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002822 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002823 mng_info->scenes_found-1);
2824 png_destroy_read_struct(&ping,&ping_info,&end_info);
2825#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002826 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002827#endif
2828 if (logging != MagickFalse)
2829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2830 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002831
cristy3ed852e2009-09-05 21:47:34 +00002832 return(image);
2833 }
glennrp0fe50b42010-11-16 03:52:51 +00002834
cristy3ed852e2009-09-05 21:47:34 +00002835 if (logging != MagickFalse)
2836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2837 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002838
cristy3ed852e2009-09-05 21:47:34 +00002839 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002840 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2841 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002842
cristy3ed852e2009-09-05 21:47:34 +00002843 else
glennrpcf002022011-01-30 02:38:15 +00002844 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2845 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002846
glennrpcf002022011-01-30 02:38:15 +00002847 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002848 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002849
cristy3ed852e2009-09-05 21:47:34 +00002850 if (logging != MagickFalse)
2851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2852 " Converting PNG pixels to pixel packets");
2853 /*
2854 Convert PNG pixels to pixel packets.
2855 */
glennrpfaa852b2010-03-30 12:17:00 +00002856 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002857 {
2858 /*
2859 PNG image is corrupt.
2860 */
2861 png_destroy_read_struct(&ping,&ping_info,&end_info);
2862#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002863 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002864#endif
2865 if (quantum_info != (QuantumInfo *) NULL)
2866 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002867
glennrpcf002022011-01-30 02:38:15 +00002868 if (ping_pixels != (unsigned char *) NULL)
2869 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002870
cristy3ed852e2009-09-05 21:47:34 +00002871 if (logging != MagickFalse)
2872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2873 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002874
cristy3ed852e2009-09-05 21:47:34 +00002875 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002876 {
cristyc82a27b2011-10-21 01:07:16 +00002877 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002878 image->columns=0;
2879 }
glennrp0fe50b42010-11-16 03:52:51 +00002880
cristy3ed852e2009-09-05 21:47:34 +00002881 return(GetFirstImageInList(image));
2882 }
glennrp0fe50b42010-11-16 03:52:51 +00002883
cristyed552522009-10-16 14:04:35 +00002884 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002885
cristyed552522009-10-16 14:04:35 +00002886 if (quantum_info == (QuantumInfo *) NULL)
2887 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002888
glennrpc8cbc5d2011-01-01 00:12:34 +00002889 {
2890
2891 MagickBooleanType
2892 found_transparent_pixel;
2893
2894 found_transparent_pixel=MagickFalse;
2895
cristy3ed852e2009-09-05 21:47:34 +00002896 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002897 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002898 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002899 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002900 /*
2901 Convert image to DirectClass pixel packets.
2902 */
glennrp67b9c1a2011-04-22 18:47:36 +00002903#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2904 int
2905 depth;
2906
2907 depth=(ssize_t) ping_bit_depth;
2908#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002909 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2910 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2911 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2912 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002913
glennrpc8cbc5d2011-01-01 00:12:34 +00002914 for (y=0; y < (ssize_t) image->rows; y++)
2915 {
2916 if (num_passes > 1)
2917 row_offset=ping_rowbytes*y;
2918
2919 else
2920 row_offset=0;
2921
glennrpcf002022011-01-30 02:38:15 +00002922 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002923 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2924
cristyacd2ed22011-08-30 01:44:23 +00002925 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00002926 break;
2927
glennrpc8cbc5d2011-01-01 00:12:34 +00002928 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2929 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002930 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002931
2932 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2933 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002934 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002935
2936 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2937 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002938 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002939
2940 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2941 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002942 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002943
2944 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2945 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002946 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002947
glennrpc8cbc5d2011-01-01 00:12:34 +00002948 if (found_transparent_pixel == MagickFalse)
2949 {
2950 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002951 if (y== 0 && logging != MagickFalse)
2952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2953 " Looking for cheap transparent pixel");
2954
glennrpc8cbc5d2011-01-01 00:12:34 +00002955 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2956 {
glennrp5aa37f62011-01-02 03:07:57 +00002957 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2958 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy4c08aed2011-07-01 19:47:50 +00002959 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00002960 {
glennrpa6a06632011-01-19 15:15:34 +00002961 if (logging != MagickFalse)
2962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2963 " ...got one.");
2964
glennrpc8cbc5d2011-01-01 00:12:34 +00002965 found_transparent_pixel = MagickTrue;
2966 break;
2967 }
glennrp4f25bd02011-01-01 18:51:28 +00002968 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2969 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp847370c2011-07-05 17:37:15 +00002970 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2971 transparent_color.red &&
2972 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2973 transparent_color.green &&
2974 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2975 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002976 {
glennrpa6a06632011-01-19 15:15:34 +00002977 if (logging != MagickFalse)
2978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2979 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002980 found_transparent_pixel = MagickTrue;
2981 break;
2982 }
cristyed231572011-07-14 02:18:59 +00002983 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00002984 }
2985 }
2986
2987 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2988 {
2989 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2990 image->rows);
2991
2992 if (status == MagickFalse)
2993 break;
2994 }
2995 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2996 break;
2997 }
2998
2999 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3000 {
3001 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00003002 if (status == MagickFalse)
3003 break;
3004 }
cristy3ed852e2009-09-05 21:47:34 +00003005 }
cristy3ed852e2009-09-05 21:47:34 +00003006 }
glennrp0fe50b42010-11-16 03:52:51 +00003007
cristy3ed852e2009-09-05 21:47:34 +00003008 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00003009
cristy3ed852e2009-09-05 21:47:34 +00003010 for (pass=0; pass < num_passes; pass++)
3011 {
3012 Quantum
3013 *quantum_scanline;
3014
3015 register Quantum
3016 *r;
3017
3018 /*
3019 Convert grayscale image to PseudoClass pixel packets.
3020 */
glennrpc17d96f2011-06-27 01:20:11 +00003021 if (logging != MagickFalse)
3022 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3023 " Converting grayscale pixels to pixel packets");
cristy4c08aed2011-07-01 19:47:50 +00003024
glennrpfaa852b2010-03-30 12:17:00 +00003025 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00003026 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00003027
cristy3ed852e2009-09-05 21:47:34 +00003028 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3029 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003030
cristy3ed852e2009-09-05 21:47:34 +00003031 if (quantum_scanline == (Quantum *) NULL)
3032 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003033
cristybb503372010-05-27 20:51:26 +00003034 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003035 {
3036 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003037 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003038
cristy3ed852e2009-09-05 21:47:34 +00003039 else
3040 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003041
glennrpcf002022011-01-30 02:38:15 +00003042 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00003043 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003044
cristyacd2ed22011-08-30 01:44:23 +00003045 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003046 break;
glennrp0fe50b42010-11-16 03:52:51 +00003047
glennrpcf002022011-01-30 02:38:15 +00003048 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003049 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003050
glennrpfaa852b2010-03-30 12:17:00 +00003051 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003052 {
3053 case 1:
3054 {
cristybb503372010-05-27 20:51:26 +00003055 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003056 bit;
3057
cristybb503372010-05-27 20:51:26 +00003058 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003059 {
3060 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003061 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003062 p++;
3063 }
glennrp0fe50b42010-11-16 03:52:51 +00003064
cristy3ed852e2009-09-05 21:47:34 +00003065 if ((image->columns % 8) != 0)
3066 {
cristybb503372010-05-27 20:51:26 +00003067 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003068 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003069 }
glennrp0fe50b42010-11-16 03:52:51 +00003070
cristy3ed852e2009-09-05 21:47:34 +00003071 break;
3072 }
glennrp47b9dd52010-11-24 18:12:06 +00003073
cristy3ed852e2009-09-05 21:47:34 +00003074 case 2:
3075 {
cristybb503372010-05-27 20:51:26 +00003076 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003077 {
glennrpa18d5bc2011-04-23 14:51:34 +00003078 *r++=(*p >> 6) & 0x03;
3079 *r++=(*p >> 4) & 0x03;
3080 *r++=(*p >> 2) & 0x03;
3081 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003082 }
glennrp0fe50b42010-11-16 03:52:51 +00003083
cristy3ed852e2009-09-05 21:47:34 +00003084 if ((image->columns % 4) != 0)
3085 {
cristybb503372010-05-27 20:51:26 +00003086 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003087 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003088 }
glennrp0fe50b42010-11-16 03:52:51 +00003089
cristy3ed852e2009-09-05 21:47:34 +00003090 break;
3091 }
glennrp47b9dd52010-11-24 18:12:06 +00003092
cristy3ed852e2009-09-05 21:47:34 +00003093 case 4:
3094 {
cristybb503372010-05-27 20:51:26 +00003095 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003096 {
glennrpa18d5bc2011-04-23 14:51:34 +00003097 *r++=(*p >> 4) & 0x0f;
3098 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003099 }
glennrp0fe50b42010-11-16 03:52:51 +00003100
cristy3ed852e2009-09-05 21:47:34 +00003101 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003102 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003103
cristy3ed852e2009-09-05 21:47:34 +00003104 break;
3105 }
glennrp47b9dd52010-11-24 18:12:06 +00003106
cristy3ed852e2009-09-05 21:47:34 +00003107 case 8:
3108 {
glennrpfaa852b2010-03-30 12:17:00 +00003109 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003110 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003111 {
glennrpa18d5bc2011-04-23 14:51:34 +00003112 *r++=*p++;
cristy4c08aed2011-07-01 19:47:50 +00003113 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3114 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003115 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003116 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003117 }
glennrp0fe50b42010-11-16 03:52:51 +00003118
cristy3ed852e2009-09-05 21:47:34 +00003119 else
cristybb503372010-05-27 20:51:26 +00003120 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003121 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003122
cristy3ed852e2009-09-05 21:47:34 +00003123 break;
3124 }
glennrp47b9dd52010-11-24 18:12:06 +00003125
cristy3ed852e2009-09-05 21:47:34 +00003126 case 16:
3127 {
cristybb503372010-05-27 20:51:26 +00003128 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003129 {
glennrpc17d96f2011-06-27 01:20:11 +00003130#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003131 size_t
3132 quantum;
3133
3134 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003135 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003136
3137 else
glennrpc17d96f2011-06-27 01:20:11 +00003138 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003139
glennrp58f77c72011-04-23 14:09:09 +00003140 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003141 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003142 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003143
3144 if (ping_color_type == 4)
3145 {
glennrpc17d96f2011-06-27 01:20:11 +00003146 if (image->colors > 256)
3147 quantum=((*p++) << 8);
3148 else
3149 quantum=0;
3150
glennrp58f77c72011-04-23 14:09:09 +00003151 quantum|=(*p++);
cristy4c08aed2011-07-01 19:47:50 +00003152 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3153 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003154 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003155 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003156 }
glennrp58f77c72011-04-23 14:09:09 +00003157
3158#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3159 *r++=(*p++);
3160 p++; /* strip low byte */
3161
3162 if (ping_color_type == 4)
3163 {
cristy4c08aed2011-07-01 19:47:50 +00003164 SetPixelAlpha(image,*p++,q);
3165 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003166 found_transparent_pixel = MagickTrue;
3167 p++;
cristyed231572011-07-14 02:18:59 +00003168 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003169 }
cristy3ed852e2009-09-05 21:47:34 +00003170#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003171 }
glennrp47b9dd52010-11-24 18:12:06 +00003172
cristy3ed852e2009-09-05 21:47:34 +00003173 break;
3174 }
glennrp47b9dd52010-11-24 18:12:06 +00003175
cristy3ed852e2009-09-05 21:47:34 +00003176 default:
3177 break;
3178 }
glennrp3faa9a32011-04-23 14:00:25 +00003179
cristy3ed852e2009-09-05 21:47:34 +00003180 /*
3181 Transfer image scanline.
3182 */
3183 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003184
cristy4c08aed2011-07-01 19:47:50 +00003185 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3186
cristyacd2ed22011-08-30 01:44:23 +00003187 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00003188 break;
cristybb503372010-05-27 20:51:26 +00003189 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00003190 {
3191 SetPixelIndex(image,*r++,q);
cristyed231572011-07-14 02:18:59 +00003192 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00003193 }
glennrp0fe50b42010-11-16 03:52:51 +00003194
cristy3ed852e2009-09-05 21:47:34 +00003195 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3196 break;
glennrp0fe50b42010-11-16 03:52:51 +00003197
cristy7a287bf2010-02-14 02:18:09 +00003198 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3199 {
cristycee97112010-05-28 00:44:52 +00003200 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003201 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003202
cristy7a287bf2010-02-14 02:18:09 +00003203 if (status == MagickFalse)
3204 break;
3205 }
cristy3ed852e2009-09-05 21:47:34 +00003206 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003207
cristy7a287bf2010-02-14 02:18:09 +00003208 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003209 {
3210 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003211
cristy3ed852e2009-09-05 21:47:34 +00003212 if (status == MagickFalse)
3213 break;
3214 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003215
cristy3ed852e2009-09-05 21:47:34 +00003216 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3217 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003218
3219 image->matte=found_transparent_pixel;
3220
3221 if (logging != MagickFalse)
3222 {
3223 if (found_transparent_pixel != MagickFalse)
3224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3225 " Found transparent pixel");
3226 else
glennrp5aa37f62011-01-02 03:07:57 +00003227 {
3228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3229 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003230
glennrp5aa37f62011-01-02 03:07:57 +00003231 ping_color_type&=0x03;
3232 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003233 }
3234 }
3235
cristyb32b90a2009-09-07 21:45:48 +00003236 if (quantum_info != (QuantumInfo *) NULL)
3237 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00003238
cristy5c6f7892010-05-05 22:53:29 +00003239 if (image->storage_class == PseudoClass)
3240 {
cristyaeb2cbc2010-05-07 13:28:58 +00003241 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003242 matte;
3243
3244 matte=image->matte;
3245 image->matte=MagickFalse;
cristyea1a8aa2011-10-20 13:24:06 +00003246 (void) SyncImage(image,exception);
cristyaeb2cbc2010-05-07 13:28:58 +00003247 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003248 }
glennrp47b9dd52010-11-24 18:12:06 +00003249
glennrp4eb39312011-03-30 21:34:55 +00003250 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003251
3252 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003253 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003254 {
3255 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003256 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003257 image->colors=2;
cristyea1a8aa2011-10-20 13:24:06 +00003258 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003259#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003260 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003261#endif
3262 if (logging != MagickFalse)
3263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3264 " exit ReadOnePNGImage() early.");
3265 return(image);
3266 }
glennrp47b9dd52010-11-24 18:12:06 +00003267
glennrpfaa852b2010-03-30 12:17:00 +00003268 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003269 {
3270 ClassType
3271 storage_class;
3272
3273 /*
3274 Image has a transparent background.
3275 */
3276 storage_class=image->storage_class;
3277 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003278
glennrp3c218112010-11-27 15:31:26 +00003279/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003280
glennrp0fe50b42010-11-16 03:52:51 +00003281 if (storage_class == PseudoClass)
3282 {
3283 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003284 {
glennrp0fe50b42010-11-16 03:52:51 +00003285 for (x=0; x < ping_num_trans; x++)
3286 {
cristy4c08aed2011-07-01 19:47:50 +00003287 image->colormap[x].alpha =
3288 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003289 }
glennrpc11cf6a2010-03-20 16:46:19 +00003290 }
glennrp47b9dd52010-11-24 18:12:06 +00003291
glennrp0fe50b42010-11-16 03:52:51 +00003292 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3293 {
3294 for (x=0; x < (int) image->colors; x++)
3295 {
3296 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy4c08aed2011-07-01 19:47:50 +00003297 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003298 {
cristy4c08aed2011-07-01 19:47:50 +00003299 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003300 }
3301 }
3302 }
cristyea1a8aa2011-10-20 13:24:06 +00003303 (void) SyncImage(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003304 }
glennrp47b9dd52010-11-24 18:12:06 +00003305
glennrpa6a06632011-01-19 15:15:34 +00003306#if 1 /* Should have already been done above, but glennrp problem P10
3307 * needs this.
3308 */
glennrp0fe50b42010-11-16 03:52:51 +00003309 else
3310 {
3311 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003312 {
glennrp0fe50b42010-11-16 03:52:51 +00003313 image->storage_class=storage_class;
3314 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3315
cristyacd2ed22011-08-30 01:44:23 +00003316 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003317 break;
3318
glennrp0fe50b42010-11-16 03:52:51 +00003319
glennrpa6a06632011-01-19 15:15:34 +00003320 /* Caution: on a Q8 build, this does not distinguish between
3321 * 16-bit colors that differ only in the low byte
3322 */
glennrp0fe50b42010-11-16 03:52:51 +00003323 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3324 {
glennrp847370c2011-07-05 17:37:15 +00003325 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3326 transparent_color.red &&
3327 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3328 transparent_color.green &&
3329 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3330 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003331 {
cristy4c08aed2011-07-01 19:47:50 +00003332 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003333 }
glennrp0fe50b42010-11-16 03:52:51 +00003334
glennrp67b9c1a2011-04-22 18:47:36 +00003335#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003336 else
glennrp4f25bd02011-01-01 18:51:28 +00003337 {
cristy4c08aed2011-07-01 19:47:50 +00003338 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003339 }
glennrpa6a06632011-01-19 15:15:34 +00003340#endif
glennrp0fe50b42010-11-16 03:52:51 +00003341
cristyed231572011-07-14 02:18:59 +00003342 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003343 }
3344
3345 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3346 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003347 }
glennrp0fe50b42010-11-16 03:52:51 +00003348 }
glennrpa6a06632011-01-19 15:15:34 +00003349#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003350
cristy3ed852e2009-09-05 21:47:34 +00003351 image->storage_class=DirectClass;
3352 }
glennrp3c218112010-11-27 15:31:26 +00003353
cristyb40fc462010-08-08 00:49:49 +00003354 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3355 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3356 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00003357
cristyeb3b22a2011-03-31 20:16:11 +00003358 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003359 {
3360 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003361 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3362 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003363 else
glennrpa0ed0092011-04-18 16:36:29 +00003364 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3365 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003366
glennrp4eb39312011-03-30 21:34:55 +00003367 if (status != MagickFalse)
3368 for (i=0; i < (ssize_t) num_text; i++)
3369 {
3370 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003371
glennrp4eb39312011-03-30 21:34:55 +00003372 if (logging != MagickFalse)
3373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3374 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003375
glennrp4eb39312011-03-30 21:34:55 +00003376 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003377 {
cristyd15e6592011-10-15 00:13:06 +00003378 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i,
3379 exception);
glennrp4eb39312011-03-30 21:34:55 +00003380 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003381 }
glennrp0fe50b42010-11-16 03:52:51 +00003382
glennrp4eb39312011-03-30 21:34:55 +00003383 else
3384 {
3385 char
3386 *value;
3387
3388 length=text[i].text_length;
3389 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3390 sizeof(*value));
3391 if (value == (char *) NULL)
3392 {
cristyc82a27b2011-10-21 01:07:16 +00003393 (void) ThrowMagickException(exception,GetMagickModule(),
glennrp4eb39312011-03-30 21:34:55 +00003394 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3395 image->filename);
3396 break;
3397 }
3398 *value='\0';
3399 (void) ConcatenateMagickString(value,text[i].text,length+2);
3400
3401 /* Don't save "density" or "units" property if we have a pHYs
3402 * chunk
3403 */
3404 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3405 (LocaleCompare(text[i].key,"density") != 0 &&
3406 LocaleCompare(text[i].key,"units") != 0))
cristyd15e6592011-10-15 00:13:06 +00003407 (void) SetImageProperty(image,text[i].key,value,exception);
glennrp4eb39312011-03-30 21:34:55 +00003408
3409 if (logging != MagickFalse)
3410 {
3411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3412 " length: %lu",(unsigned long) length);
3413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3414 " Keyword: %s",text[i].key);
3415 }
3416
3417 value=DestroyString(value);
3418 }
3419 }
3420 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003421 }
glennrp3c218112010-11-27 15:31:26 +00003422
cristy3ed852e2009-09-05 21:47:34 +00003423#ifdef MNG_OBJECT_BUFFERS
3424 /*
3425 Store the object if necessary.
3426 */
3427 if (object_id && !mng_info->frozen[object_id])
3428 {
3429 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3430 {
3431 /*
3432 create a new object buffer.
3433 */
3434 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003435 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003436
cristy3ed852e2009-09-05 21:47:34 +00003437 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3438 {
3439 mng_info->ob[object_id]->image=(Image *) NULL;
3440 mng_info->ob[object_id]->reference_count=1;
3441 }
3442 }
glennrp47b9dd52010-11-24 18:12:06 +00003443
cristy3ed852e2009-09-05 21:47:34 +00003444 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3445 mng_info->ob[object_id]->frozen)
3446 {
3447 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
cristyc82a27b2011-10-21 01:07:16 +00003448 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003449 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3450 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003451
cristy3ed852e2009-09-05 21:47:34 +00003452 if (mng_info->ob[object_id]->frozen)
cristyc82a27b2011-10-21 01:07:16 +00003453 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003454 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3455 "`%s'",image->filename);
3456 }
glennrp0fe50b42010-11-16 03:52:51 +00003457
cristy3ed852e2009-09-05 21:47:34 +00003458 else
3459 {
cristy3ed852e2009-09-05 21:47:34 +00003460
3461 if (mng_info->ob[object_id]->image != (Image *) NULL)
3462 mng_info->ob[object_id]->image=DestroyImage
3463 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003464
cristy3ed852e2009-09-05 21:47:34 +00003465 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
cristyc82a27b2011-10-21 01:07:16 +00003466 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003467
cristy3ed852e2009-09-05 21:47:34 +00003468 if (mng_info->ob[object_id]->image != (Image *) NULL)
3469 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003470
cristy3ed852e2009-09-05 21:47:34 +00003471 else
cristyc82a27b2011-10-21 01:07:16 +00003472 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00003473 ResourceLimitError,"Cloning image for object buffer failed",
3474 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003475
glennrpfaa852b2010-03-30 12:17:00 +00003476 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003477 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003478
glennrpfaa852b2010-03-30 12:17:00 +00003479 mng_info->ob[object_id]->width=ping_width;
3480 mng_info->ob[object_id]->height=ping_height;
3481 mng_info->ob[object_id]->color_type=ping_color_type;
3482 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3483 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3484 mng_info->ob[object_id]->compression_method=
3485 ping_compression_method;
3486 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003487
glennrpfaa852b2010-03-30 12:17:00 +00003488 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003489 {
3490 int
3491 number_colors;
3492
3493 png_colorp
3494 plte;
3495
3496 /*
3497 Copy the PLTE to the object buffer.
3498 */
3499 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3500 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003501
cristy3ed852e2009-09-05 21:47:34 +00003502 for (i=0; i < number_colors; i++)
3503 {
3504 mng_info->ob[object_id]->plte[i]=plte[i];
3505 }
3506 }
glennrp47b9dd52010-11-24 18:12:06 +00003507
cristy3ed852e2009-09-05 21:47:34 +00003508 else
3509 mng_info->ob[object_id]->plte_length=0;
3510 }
3511 }
3512#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003513
3514 /* Set image->matte to MagickTrue if the input colortype supports
3515 * alpha or if a valid tRNS chunk is present, no matter whether there
3516 * is actual transparency present.
3517 */
3518 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3519 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3520 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3521 MagickTrue : MagickFalse;
3522
glennrpcb395ac2011-03-30 19:50:23 +00003523 /* Set more properties for identify to retrieve */
3524 {
3525 char
3526 msg[MaxTextExtent];
3527
glennrp4eb39312011-03-30 21:34:55 +00003528 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003529 {
3530 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003531 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003532 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
cristy5d6fc9c2011-12-27 03:10:42 +00003533 (void) SetImageProperty(image,"png:text ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003534 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003535 }
3536
3537 if (num_raw_profiles != 0)
3538 {
cristy3b6fd2e2011-05-20 12:53:50 +00003539 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003540 "%d were found", num_raw_profiles);
cristy5d6fc9c2011-12-27 03:10:42 +00003541 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003542 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003543 }
3544
glennrpcb395ac2011-03-30 19:50:23 +00003545 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003546 {
cristy3b6fd2e2011-05-20 12:53:50 +00003547 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003548 "chunk was found (see Chromaticity, above)");
cristy5d6fc9c2011-12-27 03:10:42 +00003549 (void) SetImageProperty(image,"png:cHRM ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003550 exception);
glennrp59612252011-03-30 21:45:21 +00003551 }
glennrpcb395ac2011-03-30 19:50:23 +00003552
3553 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003554 {
cristy3b6fd2e2011-05-20 12:53:50 +00003555 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003556 "chunk was found (see Background color, above)");
cristy5d6fc9c2011-12-27 03:10:42 +00003557 (void) SetImageProperty(image,"png:bKGD ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003558 exception);
glennrp59612252011-03-30 21:45:21 +00003559 }
3560
cristy3b6fd2e2011-05-20 12:53:50 +00003561 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003562 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003563
3564 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy5d6fc9c2011-12-27 03:10:42 +00003565 (void) SetImageProperty(image,"png:iCCP ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003566 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003567
glennrpcb395ac2011-03-30 19:50:23 +00003568 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy5d6fc9c2011-12-27 03:10:42 +00003569 (void) SetImageProperty(image,"png:tRNS ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003570 exception);
glennrp4eb39312011-03-30 21:34:55 +00003571
3572#if defined(PNG_sRGB_SUPPORTED)
3573 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3574 {
cristy3b6fd2e2011-05-20 12:53:50 +00003575 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003576 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003577 (int) intent);
cristy5d6fc9c2011-12-27 03:10:42 +00003578 (void) SetImageProperty(image,"png:sRGB ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003579 exception);
glennrp4eb39312011-03-30 21:34:55 +00003580 }
3581#endif
3582
3583 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3584 {
cristy3b6fd2e2011-05-20 12:53:50 +00003585 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003586 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003587 file_gamma);
cristy5d6fc9c2011-12-27 03:10:42 +00003588 (void) SetImageProperty(image,"png:gAMA ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003589 exception);
glennrp4eb39312011-03-30 21:34:55 +00003590 }
3591
3592#if defined(PNG_pHYs_SUPPORTED)
3593 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3594 {
cristy3b6fd2e2011-05-20 12:53:50 +00003595 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003596 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003597 (double) x_resolution,(double) y_resolution, unit_type);
cristy5d6fc9c2011-12-27 03:10:42 +00003598 (void) SetImageProperty(image,"png:pHYs ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003599 exception);
glennrp4eb39312011-03-30 21:34:55 +00003600 }
3601#endif
3602
3603#if defined(PNG_oFFs_SUPPORTED)
3604 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3605 {
cristy3b6fd2e2011-05-20 12:53:50 +00003606 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003607 (double) image->page.x,(double) image->page.y);
cristy5d6fc9c2011-12-27 03:10:42 +00003608 (void) SetImageProperty(image,"png:oFFs ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003609 exception);
glennrp4eb39312011-03-30 21:34:55 +00003610 }
3611#endif
3612
glennrp07523c72011-03-31 18:12:10 +00003613 if ((image->page.width != 0 && image->page.width != image->columns) ||
3614 (image->page.height != 0 && image->page.height != image->rows))
3615 {
cristy3b6fd2e2011-05-20 12:53:50 +00003616 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003617 "width=%.20g, height=%.20g",
3618 (double) image->page.width,(double) image->page.height);
cristy5d6fc9c2011-12-27 03:10:42 +00003619 (void) SetImageProperty(image,"png:vpAg ",msg,
glennrp1a2061f2011-10-18 12:30:45 +00003620 exception);
glennrp07523c72011-03-31 18:12:10 +00003621 }
glennrpcb395ac2011-03-30 19:50:23 +00003622 }
3623
cristy3ed852e2009-09-05 21:47:34 +00003624 /*
3625 Relinquish resources.
3626 */
3627 png_destroy_read_struct(&ping,&ping_info,&end_info);
3628
glennrpcf002022011-01-30 02:38:15 +00003629 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003630#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003631 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003632#endif
3633
3634 if (logging != MagickFalse)
3635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3636 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003637
cristy3ed852e2009-09-05 21:47:34 +00003638 return(image);
3639
3640/* end of reading one PNG image */
3641}
3642
3643static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3644{
3645 Image
3646 *image,
3647 *previous;
3648
3649 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003650 have_mng_structure,
3651 logging,
cristy3ed852e2009-09-05 21:47:34 +00003652 status;
3653
3654 MngInfo
3655 *mng_info;
3656
3657 char
3658 magic_number[MaxTextExtent];
3659
cristy3ed852e2009-09-05 21:47:34 +00003660 ssize_t
3661 count;
3662
3663 /*
3664 Open image file.
3665 */
3666 assert(image_info != (const ImageInfo *) NULL);
3667 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003668
cristy3ed852e2009-09-05 21:47:34 +00003669 if (image_info->debug != MagickFalse)
3670 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3671 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003672
cristy3ed852e2009-09-05 21:47:34 +00003673 assert(exception != (ExceptionInfo *) NULL);
3674 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003675 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy9950d572011-10-01 18:22:35 +00003676 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003677 mng_info=(MngInfo *) NULL;
3678 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003679
cristy3ed852e2009-09-05 21:47:34 +00003680 if (status == MagickFalse)
3681 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003682
cristy3ed852e2009-09-05 21:47:34 +00003683 /*
3684 Verify PNG signature.
3685 */
3686 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003687
glennrpdde35db2011-02-21 12:06:32 +00003688 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003689 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003690
cristy3ed852e2009-09-05 21:47:34 +00003691 /*
3692 Allocate a MngInfo structure.
3693 */
3694 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003695 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003696
cristy3ed852e2009-09-05 21:47:34 +00003697 if (mng_info == (MngInfo *) NULL)
3698 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003699
cristy3ed852e2009-09-05 21:47:34 +00003700 /*
3701 Initialize members of the MngInfo structure.
3702 */
3703 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3704 mng_info->image=image;
3705 have_mng_structure=MagickTrue;
3706
3707 previous=image;
3708 image=ReadOnePNGImage(mng_info,image_info,exception);
3709 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003710
cristy3ed852e2009-09-05 21:47:34 +00003711 if (image == (Image *) NULL)
3712 {
3713 if (previous != (Image *) NULL)
3714 {
3715 if (previous->signature != MagickSignature)
3716 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003717
cristy3ed852e2009-09-05 21:47:34 +00003718 (void) CloseBlob(previous);
3719 (void) DestroyImageList(previous);
3720 }
glennrp0fe50b42010-11-16 03:52:51 +00003721
cristy3ed852e2009-09-05 21:47:34 +00003722 if (logging != MagickFalse)
3723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3724 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003725
cristy3ed852e2009-09-05 21:47:34 +00003726 return((Image *) NULL);
3727 }
glennrp47b9dd52010-11-24 18:12:06 +00003728
cristy3ed852e2009-09-05 21:47:34 +00003729 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003730
cristy3ed852e2009-09-05 21:47:34 +00003731 if ((image->columns == 0) || (image->rows == 0))
3732 {
3733 if (logging != MagickFalse)
3734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3735 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003736
cristy3ed852e2009-09-05 21:47:34 +00003737 ThrowReaderException(CorruptImageError,"CorruptImage");
3738 }
glennrp47b9dd52010-11-24 18:12:06 +00003739
cristy3ed852e2009-09-05 21:47:34 +00003740 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3741 {
cristy018f07f2011-09-04 21:15:19 +00003742 (void) SetImageType(image,TrueColorType,exception);
cristy3ed852e2009-09-05 21:47:34 +00003743 image->matte=MagickFalse;
3744 }
glennrp0fe50b42010-11-16 03:52:51 +00003745
cristy3ed852e2009-09-05 21:47:34 +00003746 if (LocaleCompare(image_info->magick,"PNG32") == 0)
cristy018f07f2011-09-04 21:15:19 +00003747 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003748
cristy3ed852e2009-09-05 21:47:34 +00003749 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3751 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3752 (double) image->page.width,(double) image->page.height,
3753 (double) image->page.x,(double) image->page.y);
3754
3755 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003757
cristy3ed852e2009-09-05 21:47:34 +00003758 return(image);
3759}
3760
3761
3762
3763#if defined(JNG_SUPPORTED)
3764/*
3765%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3766% %
3767% %
3768% %
3769% R e a d O n e J N G I m a g e %
3770% %
3771% %
3772% %
3773%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3774%
3775% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3776% (minus the 8-byte signature) and returns it. It allocates the memory
3777% necessary for the new Image structure and returns a pointer to the new
3778% image.
3779%
3780% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3781%
3782% The format of the ReadOneJNGImage method is:
3783%
3784% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3785% ExceptionInfo *exception)
3786%
3787% A description of each parameter follows:
3788%
3789% o mng_info: Specifies a pointer to a MngInfo structure.
3790%
3791% o image_info: the image info.
3792%
3793% o exception: return any errors or warnings in this structure.
3794%
3795*/
3796static Image *ReadOneJNGImage(MngInfo *mng_info,
3797 const ImageInfo *image_info, ExceptionInfo *exception)
3798{
3799 Image
3800 *alpha_image,
3801 *color_image,
3802 *image,
3803 *jng_image;
3804
3805 ImageInfo
3806 *alpha_image_info,
3807 *color_image_info;
3808
cristy4383ec82011-01-05 15:42:32 +00003809 MagickBooleanType
3810 logging;
3811
cristybb503372010-05-27 20:51:26 +00003812 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003813 y;
3814
3815 MagickBooleanType
3816 status;
3817
3818 png_uint_32
3819 jng_height,
3820 jng_width;
3821
3822 png_byte
3823 jng_color_type,
3824 jng_image_sample_depth,
3825 jng_image_compression_method,
3826 jng_image_interlace_method,
3827 jng_alpha_sample_depth,
3828 jng_alpha_compression_method,
3829 jng_alpha_filter_method,
3830 jng_alpha_interlace_method;
3831
cristy4c08aed2011-07-01 19:47:50 +00003832 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003833 *s;
3834
cristybb503372010-05-27 20:51:26 +00003835 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003836 i,
3837 x;
3838
cristy4c08aed2011-07-01 19:47:50 +00003839 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003840 *q;
3841
3842 register unsigned char
3843 *p;
3844
3845 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003846 read_JSEP,
3847 reading_idat,
3848 skip_to_iend;
3849
cristybb503372010-05-27 20:51:26 +00003850 size_t
cristy3ed852e2009-09-05 21:47:34 +00003851 length;
3852
3853 jng_alpha_compression_method=0;
3854 jng_alpha_sample_depth=8;
3855 jng_color_type=0;
3856 jng_height=0;
3857 jng_width=0;
3858 alpha_image=(Image *) NULL;
3859 color_image=(Image *) NULL;
3860 alpha_image_info=(ImageInfo *) NULL;
3861 color_image_info=(ImageInfo *) NULL;
3862
3863 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003864 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003865
3866 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003867
cristy4c08aed2011-07-01 19:47:50 +00003868 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003869 {
3870 /*
3871 Allocate next image structure.
3872 */
3873 if (logging != MagickFalse)
3874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3875 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003876
cristy9950d572011-10-01 18:22:35 +00003877 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003878
cristy3ed852e2009-09-05 21:47:34 +00003879 if (GetNextImageInList(image) == (Image *) NULL)
3880 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003881
cristy3ed852e2009-09-05 21:47:34 +00003882 image=SyncNextImageInList(image);
3883 }
3884 mng_info->image=image;
3885
3886 /*
3887 Signature bytes have already been read.
3888 */
3889
3890 read_JSEP=MagickFalse;
3891 reading_idat=MagickFalse;
3892 skip_to_iend=MagickFalse;
3893 for (;;)
3894 {
3895 char
3896 type[MaxTextExtent];
3897
3898 unsigned char
3899 *chunk;
3900
3901 unsigned int
3902 count;
3903
3904 /*
3905 Read a new JNG chunk.
3906 */
3907 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3908 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003909
cristy3ed852e2009-09-05 21:47:34 +00003910 if (status == MagickFalse)
3911 break;
glennrp0fe50b42010-11-16 03:52:51 +00003912
cristy3ed852e2009-09-05 21:47:34 +00003913 type[0]='\0';
3914 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3915 length=ReadBlobMSBLong(image);
3916 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3917
3918 if (logging != MagickFalse)
3919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003920 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3921 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003922
3923 if (length > PNG_UINT_31_MAX || count == 0)
3924 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003925
cristy3ed852e2009-09-05 21:47:34 +00003926 p=NULL;
3927 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003928
cristy3ed852e2009-09-05 21:47:34 +00003929 if (length)
3930 {
3931 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003932
cristy3ed852e2009-09-05 21:47:34 +00003933 if (chunk == (unsigned char *) NULL)
3934 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003935
cristybb503372010-05-27 20:51:26 +00003936 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003937 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003938
cristy3ed852e2009-09-05 21:47:34 +00003939 p=chunk;
3940 }
glennrp47b9dd52010-11-24 18:12:06 +00003941
cristy3ed852e2009-09-05 21:47:34 +00003942 (void) ReadBlobMSBLong(image); /* read crc word */
3943
3944 if (skip_to_iend)
3945 {
3946 if (length)
3947 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003948
cristy3ed852e2009-09-05 21:47:34 +00003949 continue;
3950 }
3951
3952 if (memcmp(type,mng_JHDR,4) == 0)
3953 {
3954 if (length == 16)
3955 {
cristybb503372010-05-27 20:51:26 +00003956 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003957 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003958 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003959 (p[6] << 8) | p[7]);
3960 jng_color_type=p[8];
3961 jng_image_sample_depth=p[9];
3962 jng_image_compression_method=p[10];
3963 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003964
cristy3ed852e2009-09-05 21:47:34 +00003965 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3966 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003967
cristy3ed852e2009-09-05 21:47:34 +00003968 jng_alpha_sample_depth=p[12];
3969 jng_alpha_compression_method=p[13];
3970 jng_alpha_filter_method=p[14];
3971 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003972
cristy3ed852e2009-09-05 21:47:34 +00003973 if (logging != MagickFalse)
3974 {
3975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003976 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003977
cristy3ed852e2009-09-05 21:47:34 +00003978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003979 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003980
cristy3ed852e2009-09-05 21:47:34 +00003981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3982 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003983
cristy3ed852e2009-09-05 21:47:34 +00003984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3985 " jng_image_sample_depth: %3d",
3986 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003987
cristy3ed852e2009-09-05 21:47:34 +00003988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3989 " jng_image_compression_method:%3d",
3990 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003991
cristy3ed852e2009-09-05 21:47:34 +00003992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3993 " jng_image_interlace_method: %3d",
3994 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003995
cristy3ed852e2009-09-05 21:47:34 +00003996 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3997 " jng_alpha_sample_depth: %3d",
3998 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003999
cristy3ed852e2009-09-05 21:47:34 +00004000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4001 " jng_alpha_compression_method:%3d",
4002 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004003
cristy3ed852e2009-09-05 21:47:34 +00004004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4005 " jng_alpha_filter_method: %3d",
4006 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00004007
cristy3ed852e2009-09-05 21:47:34 +00004008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4009 " jng_alpha_interlace_method: %3d",
4010 jng_alpha_interlace_method);
4011 }
4012 }
glennrp47b9dd52010-11-24 18:12:06 +00004013
cristy3ed852e2009-09-05 21:47:34 +00004014 if (length)
4015 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004016
cristy3ed852e2009-09-05 21:47:34 +00004017 continue;
4018 }
4019
4020
4021 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4022 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4023 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4024 {
4025 /*
4026 o create color_image
4027 o open color_blob, attached to color_image
4028 o if (color type has alpha)
4029 open alpha_blob, attached to alpha_image
4030 */
4031
cristy73bd4a52010-10-05 11:24:23 +00004032 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004033
cristy3ed852e2009-09-05 21:47:34 +00004034 if (color_image_info == (ImageInfo *) NULL)
4035 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004036
cristy3ed852e2009-09-05 21:47:34 +00004037 GetImageInfo(color_image_info);
cristy9950d572011-10-01 18:22:35 +00004038 color_image=AcquireImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004039
cristy3ed852e2009-09-05 21:47:34 +00004040 if (color_image == (Image *) NULL)
4041 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4042
4043 if (logging != MagickFalse)
4044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4045 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004046
cristy3ed852e2009-09-05 21:47:34 +00004047 (void) AcquireUniqueFilename(color_image->filename);
4048 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4049 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004050
cristy3ed852e2009-09-05 21:47:34 +00004051 if (status == MagickFalse)
4052 return((Image *) NULL);
4053
4054 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4055 {
4056 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004057 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004058
cristy3ed852e2009-09-05 21:47:34 +00004059 if (alpha_image_info == (ImageInfo *) NULL)
4060 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004061
cristy3ed852e2009-09-05 21:47:34 +00004062 GetImageInfo(alpha_image_info);
cristy9950d572011-10-01 18:22:35 +00004063 alpha_image=AcquireImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004064
cristy3ed852e2009-09-05 21:47:34 +00004065 if (alpha_image == (Image *) NULL)
4066 {
4067 alpha_image=DestroyImage(alpha_image);
4068 ThrowReaderException(ResourceLimitError,
4069 "MemoryAllocationFailed");
4070 }
glennrp0fe50b42010-11-16 03:52:51 +00004071
cristy3ed852e2009-09-05 21:47:34 +00004072 if (logging != MagickFalse)
4073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4074 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004075
cristy3ed852e2009-09-05 21:47:34 +00004076 (void) AcquireUniqueFilename(alpha_image->filename);
4077 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4078 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004079
cristy3ed852e2009-09-05 21:47:34 +00004080 if (status == MagickFalse)
4081 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004082
cristy3ed852e2009-09-05 21:47:34 +00004083 if (jng_alpha_compression_method == 0)
4084 {
4085 unsigned char
4086 data[18];
4087
4088 if (logging != MagickFalse)
4089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4090 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004091
cristy3ed852e2009-09-05 21:47:34 +00004092 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4093 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004094
cristy3ed852e2009-09-05 21:47:34 +00004095 (void) WriteBlobMSBULong(alpha_image,13L);
4096 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004097 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004098 PNGLong(data+4,jng_width);
4099 PNGLong(data+8,jng_height);
4100 data[12]=jng_alpha_sample_depth;
4101 data[13]=0; /* color_type gray */
4102 data[14]=0; /* compression method 0 */
4103 data[15]=0; /* filter_method 0 */
4104 data[16]=0; /* interlace_method 0 */
4105 (void) WriteBlob(alpha_image,17,data);
4106 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4107 }
4108 }
4109 reading_idat=MagickTrue;
4110 }
4111
4112 if (memcmp(type,mng_JDAT,4) == 0)
4113 {
glennrp47b9dd52010-11-24 18:12:06 +00004114 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004115
4116 if (logging != MagickFalse)
4117 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4118 " Copying JDAT chunk data to color_blob.");
4119
4120 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004121
cristy3ed852e2009-09-05 21:47:34 +00004122 if (length)
4123 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004124
cristy3ed852e2009-09-05 21:47:34 +00004125 continue;
4126 }
4127
4128 if (memcmp(type,mng_IDAT,4) == 0)
4129 {
4130 png_byte
4131 data[5];
4132
glennrp47b9dd52010-11-24 18:12:06 +00004133 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004134
4135 if (image_info->ping == MagickFalse)
4136 {
4137 if (logging != MagickFalse)
4138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4139 " Copying IDAT chunk data to alpha_blob.");
4140
cristybb503372010-05-27 20:51:26 +00004141 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004142 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004143 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004144 (void) WriteBlob(alpha_image,4,data);
4145 (void) WriteBlob(alpha_image,length,chunk);
4146 (void) WriteBlobMSBULong(alpha_image,
4147 crc32(crc32(0,data,4),chunk,(uInt) length));
4148 }
glennrp0fe50b42010-11-16 03:52:51 +00004149
cristy3ed852e2009-09-05 21:47:34 +00004150 if (length)
4151 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004152
cristy3ed852e2009-09-05 21:47:34 +00004153 continue;
4154 }
4155
4156 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4157 {
glennrp47b9dd52010-11-24 18:12:06 +00004158 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004159
4160 if (image_info->ping == MagickFalse)
4161 {
4162 if (logging != MagickFalse)
4163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4164 " Copying JDAA chunk data to alpha_blob.");
4165
4166 (void) WriteBlob(alpha_image,length,chunk);
4167 }
glennrp0fe50b42010-11-16 03:52:51 +00004168
cristy3ed852e2009-09-05 21:47:34 +00004169 if (length)
4170 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004171
cristy3ed852e2009-09-05 21:47:34 +00004172 continue;
4173 }
4174
4175 if (memcmp(type,mng_JSEP,4) == 0)
4176 {
4177 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004178
cristy3ed852e2009-09-05 21:47:34 +00004179 if (length)
4180 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004181
cristy3ed852e2009-09-05 21:47:34 +00004182 continue;
4183 }
4184
4185 if (memcmp(type,mng_bKGD,4) == 0)
4186 {
4187 if (length == 2)
4188 {
4189 image->background_color.red=ScaleCharToQuantum(p[1]);
4190 image->background_color.green=image->background_color.red;
4191 image->background_color.blue=image->background_color.red;
4192 }
glennrp0fe50b42010-11-16 03:52:51 +00004193
cristy3ed852e2009-09-05 21:47:34 +00004194 if (length == 6)
4195 {
4196 image->background_color.red=ScaleCharToQuantum(p[1]);
4197 image->background_color.green=ScaleCharToQuantum(p[3]);
4198 image->background_color.blue=ScaleCharToQuantum(p[5]);
4199 }
glennrp0fe50b42010-11-16 03:52:51 +00004200
cristy3ed852e2009-09-05 21:47:34 +00004201 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4202 continue;
4203 }
4204
4205 if (memcmp(type,mng_gAMA,4) == 0)
4206 {
4207 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004208 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004209
cristy3ed852e2009-09-05 21:47:34 +00004210 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4211 continue;
4212 }
4213
4214 if (memcmp(type,mng_cHRM,4) == 0)
4215 {
4216 if (length == 32)
4217 {
cristy8182b072010-05-30 20:10:53 +00004218 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4219 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4220 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4221 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4222 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4223 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4224 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4225 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004226 }
glennrp47b9dd52010-11-24 18:12:06 +00004227
cristy3ed852e2009-09-05 21:47:34 +00004228 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4229 continue;
4230 }
4231
4232 if (memcmp(type,mng_sRGB,4) == 0)
4233 {
4234 if (length == 1)
4235 {
glennrpe610a072010-08-05 17:08:46 +00004236 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004237 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004238 image->gamma=0.45455f;
4239 image->chromaticity.red_primary.x=0.6400f;
4240 image->chromaticity.red_primary.y=0.3300f;
4241 image->chromaticity.green_primary.x=0.3000f;
4242 image->chromaticity.green_primary.y=0.6000f;
4243 image->chromaticity.blue_primary.x=0.1500f;
4244 image->chromaticity.blue_primary.y=0.0600f;
4245 image->chromaticity.white_point.x=0.3127f;
4246 image->chromaticity.white_point.y=0.3290f;
4247 }
glennrp47b9dd52010-11-24 18:12:06 +00004248
cristy3ed852e2009-09-05 21:47:34 +00004249 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4250 continue;
4251 }
4252
4253 if (memcmp(type,mng_oFFs,4) == 0)
4254 {
4255 if (length > 8)
4256 {
glennrp5eae7602011-02-22 15:21:32 +00004257 image->page.x=(ssize_t) mng_get_long(p);
4258 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004259
cristy3ed852e2009-09-05 21:47:34 +00004260 if ((int) p[8] != 0)
4261 {
4262 image->page.x/=10000;
4263 image->page.y/=10000;
4264 }
4265 }
glennrp47b9dd52010-11-24 18:12:06 +00004266
cristy3ed852e2009-09-05 21:47:34 +00004267 if (length)
4268 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004269
cristy3ed852e2009-09-05 21:47:34 +00004270 continue;
4271 }
4272
4273 if (memcmp(type,mng_pHYs,4) == 0)
4274 {
4275 if (length > 8)
4276 {
cristy2a11bef2011-10-28 18:33:11 +00004277 image->resolution.x=(double) mng_get_long(p);
4278 image->resolution.y=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004279 if ((int) p[8] == PNG_RESOLUTION_METER)
4280 {
4281 image->units=PixelsPerCentimeterResolution;
cristy2a11bef2011-10-28 18:33:11 +00004282 image->resolution.x=image->resolution.x/100.0f;
4283 image->resolution.y=image->resolution.y/100.0f;
cristy3ed852e2009-09-05 21:47:34 +00004284 }
4285 }
glennrp0fe50b42010-11-16 03:52:51 +00004286
cristy3ed852e2009-09-05 21:47:34 +00004287 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4288 continue;
4289 }
4290
4291#if 0
4292 if (memcmp(type,mng_iCCP,4) == 0)
4293 {
glennrpfd05d622011-02-25 04:10:33 +00004294 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004295 if (length)
4296 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004297
cristy3ed852e2009-09-05 21:47:34 +00004298 continue;
4299 }
4300#endif
4301
4302 if (length)
4303 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4304
4305 if (memcmp(type,mng_IEND,4))
4306 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004307
cristy3ed852e2009-09-05 21:47:34 +00004308 break;
4309 }
4310
4311
4312 /* IEND found */
4313
4314 /*
4315 Finish up reading image data:
4316
4317 o read main image from color_blob.
4318
4319 o close color_blob.
4320
4321 o if (color_type has alpha)
4322 if alpha_encoding is PNG
4323 read secondary image from alpha_blob via ReadPNG
4324 if alpha_encoding is JPEG
4325 read secondary image from alpha_blob via ReadJPEG
4326
4327 o close alpha_blob.
4328
4329 o copy intensity of secondary image into
cristy4c08aed2011-07-01 19:47:50 +00004330 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004331
4332 o destroy the secondary image.
4333 */
4334
4335 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004336
cristy3ed852e2009-09-05 21:47:34 +00004337 if (logging != MagickFalse)
4338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4339 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004340
cristy3b6fd2e2011-05-20 12:53:50 +00004341 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004342 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004343
cristy3ed852e2009-09-05 21:47:34 +00004344 color_image_info->ping=MagickFalse; /* To do: avoid this */
4345 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004346
cristy3ed852e2009-09-05 21:47:34 +00004347 if (jng_image == (Image *) NULL)
4348 return((Image *) NULL);
4349
4350 (void) RelinquishUniqueFileResource(color_image->filename);
4351 color_image=DestroyImage(color_image);
4352 color_image_info=DestroyImageInfo(color_image_info);
4353
4354 if (jng_image == (Image *) NULL)
4355 return((Image *) NULL);
4356
4357 if (logging != MagickFalse)
4358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4359 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004360
cristy3ed852e2009-09-05 21:47:34 +00004361 image->rows=jng_height;
4362 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004363
cristybb503372010-05-27 20:51:26 +00004364 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004365 {
cristyc82a27b2011-10-21 01:07:16 +00004366 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00004367 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004368 for (x=(ssize_t) image->columns; x != 0; x--)
4369 {
4370 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4371 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4372 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004373 q+=GetPixelChannels(image);
4374 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004375 }
glennrp47b9dd52010-11-24 18:12:06 +00004376
cristy3ed852e2009-09-05 21:47:34 +00004377 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4378 break;
4379 }
glennrp0fe50b42010-11-16 03:52:51 +00004380
cristy3ed852e2009-09-05 21:47:34 +00004381 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004382
cristy3ed852e2009-09-05 21:47:34 +00004383 if (image_info->ping == MagickFalse)
4384 {
4385 if (jng_color_type >= 12)
4386 {
4387 if (jng_alpha_compression_method == 0)
4388 {
4389 png_byte
4390 data[5];
4391 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4392 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004393 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004394 (void) WriteBlob(alpha_image,4,data);
4395 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4396 }
glennrp0fe50b42010-11-16 03:52:51 +00004397
cristy3ed852e2009-09-05 21:47:34 +00004398 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004399
cristy3ed852e2009-09-05 21:47:34 +00004400 if (logging != MagickFalse)
4401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00004402 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004403
cristy3b6fd2e2011-05-20 12:53:50 +00004404 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004405 "%s",alpha_image->filename);
4406
4407 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004408
cristy3ed852e2009-09-05 21:47:34 +00004409 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004410 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004411 {
4412 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristyc82a27b2011-10-21 01:07:16 +00004413 exception);
cristy3ed852e2009-09-05 21:47:34 +00004414 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004415
cristy3ed852e2009-09-05 21:47:34 +00004416 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00004417 for (x=(ssize_t) image->columns; x != 0; x--)
4418 {
4419 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004420 q+=GetPixelChannels(image);
4421 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004422 }
glennrp0fe50b42010-11-16 03:52:51 +00004423
cristy3ed852e2009-09-05 21:47:34 +00004424 else
cristy4c08aed2011-07-01 19:47:50 +00004425 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004426 {
cristy4c08aed2011-07-01 19:47:50 +00004427 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4428 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004429 image->matte=MagickTrue;
cristyed231572011-07-14 02:18:59 +00004430 q+=GetPixelChannels(image);
4431 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004432 }
glennrp0fe50b42010-11-16 03:52:51 +00004433
cristy3ed852e2009-09-05 21:47:34 +00004434 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4435 break;
4436 }
4437 (void) RelinquishUniqueFileResource(alpha_image->filename);
4438 alpha_image=DestroyImage(alpha_image);
4439 alpha_image_info=DestroyImageInfo(alpha_image_info);
4440 if (jng_image != (Image *) NULL)
4441 jng_image=DestroyImage(jng_image);
4442 }
4443 }
4444
glennrp47b9dd52010-11-24 18:12:06 +00004445 /* Read the JNG image. */
4446
cristy3ed852e2009-09-05 21:47:34 +00004447 if (mng_info->mng_type == 0)
4448 {
4449 mng_info->mng_width=jng_width;
4450 mng_info->mng_height=jng_height;
4451 }
glennrp0fe50b42010-11-16 03:52:51 +00004452
cristy3ed852e2009-09-05 21:47:34 +00004453 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004454 {
4455 image->page.width=jng_width;
4456 image->page.height=jng_height;
4457 }
4458
cristy3ed852e2009-09-05 21:47:34 +00004459 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004460 {
4461 image->page.x=mng_info->x_off[mng_info->object_id];
4462 image->page.y=mng_info->y_off[mng_info->object_id];
4463 }
4464
cristy3ed852e2009-09-05 21:47:34 +00004465 else
glennrp0fe50b42010-11-16 03:52:51 +00004466 {
4467 image->page.y=mng_info->y_off[mng_info->object_id];
4468 }
4469
cristy3ed852e2009-09-05 21:47:34 +00004470 mng_info->image_found++;
4471 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4472 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004473
cristy3ed852e2009-09-05 21:47:34 +00004474 if (logging != MagickFalse)
4475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4476 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004477
cristy3ed852e2009-09-05 21:47:34 +00004478 return(image);
4479}
4480
4481/*
4482%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4483% %
4484% %
4485% %
4486% R e a d J N G I m a g e %
4487% %
4488% %
4489% %
4490%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4491%
4492% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4493% (including the 8-byte signature) and returns it. It allocates the memory
4494% necessary for the new Image structure and returns a pointer to the new
4495% image.
4496%
4497% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4498%
4499% The format of the ReadJNGImage method is:
4500%
4501% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4502% *exception)
4503%
4504% A description of each parameter follows:
4505%
4506% o image_info: the image info.
4507%
4508% o exception: return any errors or warnings in this structure.
4509%
4510*/
4511
4512static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4513{
4514 Image
4515 *image,
4516 *previous;
4517
4518 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004519 have_mng_structure,
4520 logging,
cristy3ed852e2009-09-05 21:47:34 +00004521 status;
4522
4523 MngInfo
4524 *mng_info;
4525
4526 char
4527 magic_number[MaxTextExtent];
4528
cristy3ed852e2009-09-05 21:47:34 +00004529 size_t
4530 count;
4531
4532 /*
4533 Open image file.
4534 */
4535 assert(image_info != (const ImageInfo *) NULL);
4536 assert(image_info->signature == MagickSignature);
4537 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4538 assert(exception != (ExceptionInfo *) NULL);
4539 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004540 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy9950d572011-10-01 18:22:35 +00004541 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004542 mng_info=(MngInfo *) NULL;
4543 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004544
cristy3ed852e2009-09-05 21:47:34 +00004545 if (status == MagickFalse)
4546 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004547
cristy3ed852e2009-09-05 21:47:34 +00004548 if (LocaleCompare(image_info->magick,"JNG") != 0)
4549 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004550
glennrp47b9dd52010-11-24 18:12:06 +00004551 /* Verify JNG signature. */
4552
cristy3ed852e2009-09-05 21:47:34 +00004553 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004554
glennrp3b8763e2011-02-21 12:08:18 +00004555 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004556 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004557
glennrp47b9dd52010-11-24 18:12:06 +00004558 /* Allocate a MngInfo structure. */
4559
cristy3ed852e2009-09-05 21:47:34 +00004560 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004561 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004562
cristy3ed852e2009-09-05 21:47:34 +00004563 if (mng_info == (MngInfo *) NULL)
4564 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004565
glennrp47b9dd52010-11-24 18:12:06 +00004566 /* Initialize members of the MngInfo structure. */
4567
cristy3ed852e2009-09-05 21:47:34 +00004568 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4569 have_mng_structure=MagickTrue;
4570
4571 mng_info->image=image;
4572 previous=image;
4573 image=ReadOneJNGImage(mng_info,image_info,exception);
4574 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004575
cristy3ed852e2009-09-05 21:47:34 +00004576 if (image == (Image *) NULL)
4577 {
4578 if (IsImageObject(previous) != MagickFalse)
4579 {
4580 (void) CloseBlob(previous);
4581 (void) DestroyImageList(previous);
4582 }
glennrp0fe50b42010-11-16 03:52:51 +00004583
cristy3ed852e2009-09-05 21:47:34 +00004584 if (logging != MagickFalse)
4585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4586 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004587
cristy3ed852e2009-09-05 21:47:34 +00004588 return((Image *) NULL);
4589 }
4590 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004591
cristy3ed852e2009-09-05 21:47:34 +00004592 if (image->columns == 0 || image->rows == 0)
4593 {
4594 if (logging != MagickFalse)
4595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4596 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004597
cristy3ed852e2009-09-05 21:47:34 +00004598 ThrowReaderException(CorruptImageError,"CorruptImage");
4599 }
glennrp0fe50b42010-11-16 03:52:51 +00004600
cristy3ed852e2009-09-05 21:47:34 +00004601 if (logging != MagickFalse)
4602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004603
cristy3ed852e2009-09-05 21:47:34 +00004604 return(image);
4605}
4606#endif
4607
4608static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4609{
4610 char
4611 page_geometry[MaxTextExtent];
4612
4613 Image
4614 *image,
4615 *previous;
4616
cristy4383ec82011-01-05 15:42:32 +00004617 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004618 logging,
4619 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004620
cristy3ed852e2009-09-05 21:47:34 +00004621 volatile int
4622 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004623 object_id,
4624 term_chunk_found,
4625 skip_to_iend;
4626
cristybb503372010-05-27 20:51:26 +00004627 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004628 image_count=0;
4629
4630 MagickBooleanType
4631 status;
4632
4633 MagickOffsetType
4634 offset;
4635
4636 MngInfo
4637 *mng_info;
4638
4639 MngBox
4640 default_fb,
4641 fb,
4642 previous_fb;
4643
4644#if defined(MNG_INSERT_LAYERS)
cristy101ab702011-10-13 13:06:32 +00004645 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004646 mng_background_color;
4647#endif
4648
4649 register unsigned char
4650 *p;
4651
cristybb503372010-05-27 20:51:26 +00004652 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004653 i;
4654
4655 size_t
4656 count;
4657
cristybb503372010-05-27 20:51:26 +00004658 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004659 loop_level;
4660
4661 volatile short
4662 skipping_loop;
4663
4664#if defined(MNG_INSERT_LAYERS)
4665 unsigned int
4666 mandatory_back=0;
4667#endif
4668
4669 volatile unsigned int
4670#ifdef MNG_OBJECT_BUFFERS
4671 mng_background_object=0,
4672#endif
4673 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4674
cristybb503372010-05-27 20:51:26 +00004675 size_t
cristy3ed852e2009-09-05 21:47:34 +00004676 default_frame_timeout,
4677 frame_timeout,
4678#if defined(MNG_INSERT_LAYERS)
4679 image_height,
4680 image_width,
4681#endif
4682 length;
4683
glennrp38ea0832010-06-02 18:50:28 +00004684 /* These delays are all measured in image ticks_per_second,
4685 * not in MNG ticks_per_second
4686 */
cristybb503372010-05-27 20:51:26 +00004687 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004688 default_frame_delay,
4689 final_delay,
4690 final_image_delay,
4691 frame_delay,
4692#if defined(MNG_INSERT_LAYERS)
4693 insert_layers,
4694#endif
4695 mng_iterations=1,
4696 simplicity=0,
4697 subframe_height=0,
4698 subframe_width=0;
4699
4700 previous_fb.top=0;
4701 previous_fb.bottom=0;
4702 previous_fb.left=0;
4703 previous_fb.right=0;
4704 default_fb.top=0;
4705 default_fb.bottom=0;
4706 default_fb.left=0;
4707 default_fb.right=0;
4708
glennrp47b9dd52010-11-24 18:12:06 +00004709 /* Open image file. */
4710
cristy3ed852e2009-09-05 21:47:34 +00004711 assert(image_info != (const ImageInfo *) NULL);
4712 assert(image_info->signature == MagickSignature);
4713 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4714 assert(exception != (ExceptionInfo *) NULL);
4715 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004716 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy9950d572011-10-01 18:22:35 +00004717 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004718 mng_info=(MngInfo *) NULL;
4719 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004720
cristy3ed852e2009-09-05 21:47:34 +00004721 if (status == MagickFalse)
4722 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004723
cristy3ed852e2009-09-05 21:47:34 +00004724 first_mng_object=MagickFalse;
4725 skipping_loop=(-1);
4726 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004727
4728 /* Allocate a MngInfo structure. */
4729
cristy73bd4a52010-10-05 11:24:23 +00004730 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004731
cristy3ed852e2009-09-05 21:47:34 +00004732 if (mng_info == (MngInfo *) NULL)
4733 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004734
glennrp47b9dd52010-11-24 18:12:06 +00004735 /* Initialize members of the MngInfo structure. */
4736
cristy3ed852e2009-09-05 21:47:34 +00004737 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4738 mng_info->image=image;
4739 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004740
4741 if (LocaleCompare(image_info->magick,"MNG") == 0)
4742 {
4743 char
4744 magic_number[MaxTextExtent];
4745
glennrp47b9dd52010-11-24 18:12:06 +00004746 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004747 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4748 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4749 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004750
4751 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004752 for (i=0; i < MNG_MAX_OBJECTS; i++)
4753 {
cristybb503372010-05-27 20:51:26 +00004754 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4755 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004756 }
4757 mng_info->exists[0]=MagickTrue;
4758 }
glennrp47b9dd52010-11-24 18:12:06 +00004759
cristy3ed852e2009-09-05 21:47:34 +00004760 first_mng_object=MagickTrue;
4761 mng_type=0;
4762#if defined(MNG_INSERT_LAYERS)
4763 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4764#endif
4765 default_frame_delay=0;
4766 default_frame_timeout=0;
4767 frame_delay=0;
4768 final_delay=1;
4769 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4770 object_id=0;
4771 skip_to_iend=MagickFalse;
4772 term_chunk_found=MagickFalse;
4773 mng_info->framing_mode=1;
4774#if defined(MNG_INSERT_LAYERS)
4775 mandatory_back=MagickFalse;
4776#endif
4777#if defined(MNG_INSERT_LAYERS)
4778 mng_background_color=image->background_color;
4779#endif
4780 default_fb=mng_info->frame;
4781 previous_fb=mng_info->frame;
4782 do
4783 {
4784 char
4785 type[MaxTextExtent];
4786
4787 if (LocaleCompare(image_info->magick,"MNG") == 0)
4788 {
4789 unsigned char
4790 *chunk;
4791
4792 /*
4793 Read a new chunk.
4794 */
4795 type[0]='\0';
4796 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4797 length=ReadBlobMSBLong(image);
4798 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4799
4800 if (logging != MagickFalse)
4801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004802 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4803 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004804
4805 if (length > PNG_UINT_31_MAX)
4806 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004807
cristy3ed852e2009-09-05 21:47:34 +00004808 if (count == 0)
4809 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004810
cristy3ed852e2009-09-05 21:47:34 +00004811 p=NULL;
4812 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004813
cristy3ed852e2009-09-05 21:47:34 +00004814 if (length)
4815 {
4816 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004817
cristy3ed852e2009-09-05 21:47:34 +00004818 if (chunk == (unsigned char *) NULL)
4819 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004820
cristybb503372010-05-27 20:51:26 +00004821 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004822 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004823
cristy3ed852e2009-09-05 21:47:34 +00004824 p=chunk;
4825 }
glennrp0fe50b42010-11-16 03:52:51 +00004826
cristy3ed852e2009-09-05 21:47:34 +00004827 (void) ReadBlobMSBLong(image); /* read crc word */
4828
4829#if !defined(JNG_SUPPORTED)
4830 if (memcmp(type,mng_JHDR,4) == 0)
4831 {
4832 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004833
cristy3ed852e2009-09-05 21:47:34 +00004834 if (mng_info->jhdr_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00004835 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004836 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004837
cristy3ed852e2009-09-05 21:47:34 +00004838 mng_info->jhdr_warning++;
4839 }
4840#endif
4841 if (memcmp(type,mng_DHDR,4) == 0)
4842 {
4843 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004844
cristy3ed852e2009-09-05 21:47:34 +00004845 if (mng_info->dhdr_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00004846 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004847 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004848
cristy3ed852e2009-09-05 21:47:34 +00004849 mng_info->dhdr_warning++;
4850 }
4851 if (memcmp(type,mng_MEND,4) == 0)
4852 break;
glennrp47b9dd52010-11-24 18:12:06 +00004853
cristy3ed852e2009-09-05 21:47:34 +00004854 if (skip_to_iend)
4855 {
4856 if (memcmp(type,mng_IEND,4) == 0)
4857 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004858
cristy3ed852e2009-09-05 21:47:34 +00004859 if (length)
4860 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004861
cristy3ed852e2009-09-05 21:47:34 +00004862 if (logging != MagickFalse)
4863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4864 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004865
cristy3ed852e2009-09-05 21:47:34 +00004866 continue;
4867 }
glennrp0fe50b42010-11-16 03:52:51 +00004868
cristy3ed852e2009-09-05 21:47:34 +00004869 if (memcmp(type,mng_MHDR,4) == 0)
4870 {
cristybb503372010-05-27 20:51:26 +00004871 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004872 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004873
cristybb503372010-05-27 20:51:26 +00004874 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004875 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004876
cristy3ed852e2009-09-05 21:47:34 +00004877 if (logging != MagickFalse)
4878 {
4879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004880 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004882 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004883 }
glennrp0fe50b42010-11-16 03:52:51 +00004884
cristy3ed852e2009-09-05 21:47:34 +00004885 p+=8;
cristy8182b072010-05-30 20:10:53 +00004886 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004887
cristy3ed852e2009-09-05 21:47:34 +00004888 if (mng_info->ticks_per_second == 0)
4889 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004890
cristy3ed852e2009-09-05 21:47:34 +00004891 else
4892 default_frame_delay=1UL*image->ticks_per_second/
4893 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004894
cristy3ed852e2009-09-05 21:47:34 +00004895 frame_delay=default_frame_delay;
4896 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004897
cristy3ed852e2009-09-05 21:47:34 +00004898 if (length > 16)
4899 {
4900 p+=16;
cristy8182b072010-05-30 20:10:53 +00004901 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004902 }
glennrp0fe50b42010-11-16 03:52:51 +00004903
cristy3ed852e2009-09-05 21:47:34 +00004904 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004905
cristy3ed852e2009-09-05 21:47:34 +00004906 if ((simplicity != 0) && ((simplicity | 11) == 11))
4907 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004908
cristy3ed852e2009-09-05 21:47:34 +00004909 if ((simplicity != 0) && ((simplicity | 9) == 9))
4910 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004911
cristy3ed852e2009-09-05 21:47:34 +00004912#if defined(MNG_INSERT_LAYERS)
4913 if (mng_type != 3)
4914 insert_layers=MagickTrue;
4915#endif
cristy4c08aed2011-07-01 19:47:50 +00004916 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004917 {
glennrp47b9dd52010-11-24 18:12:06 +00004918 /* Allocate next image structure. */
cristy9950d572011-10-01 18:22:35 +00004919 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004920
cristy3ed852e2009-09-05 21:47:34 +00004921 if (GetNextImageInList(image) == (Image *) NULL)
4922 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004923
cristy3ed852e2009-09-05 21:47:34 +00004924 image=SyncNextImageInList(image);
4925 mng_info->image=image;
4926 }
4927
4928 if ((mng_info->mng_width > 65535L) ||
4929 (mng_info->mng_height > 65535L))
4930 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004931
cristy3b6fd2e2011-05-20 12:53:50 +00004932 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004933 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004934 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004935
cristy3ed852e2009-09-05 21:47:34 +00004936 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004937 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004938 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004939 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004940 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004941
cristy3ed852e2009-09-05 21:47:34 +00004942 for (i=0; i < MNG_MAX_OBJECTS; i++)
4943 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004944
cristy3ed852e2009-09-05 21:47:34 +00004945 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4946 continue;
4947 }
4948
4949 if (memcmp(type,mng_TERM,4) == 0)
4950 {
4951 int
4952 repeat=0;
4953
4954
4955 if (length)
4956 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004957
cristy3ed852e2009-09-05 21:47:34 +00004958 if (repeat == 3)
4959 {
cristy8182b072010-05-30 20:10:53 +00004960 final_delay=(png_uint_32) mng_get_long(&p[2]);
4961 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004962
cristy3ed852e2009-09-05 21:47:34 +00004963 if (mng_iterations == PNG_UINT_31_MAX)
4964 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004965
cristy3ed852e2009-09-05 21:47:34 +00004966 image->iterations=mng_iterations;
4967 term_chunk_found=MagickTrue;
4968 }
glennrp0fe50b42010-11-16 03:52:51 +00004969
cristy3ed852e2009-09-05 21:47:34 +00004970 if (logging != MagickFalse)
4971 {
4972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4973 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004974
cristy3ed852e2009-09-05 21:47:34 +00004975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004976 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004977
cristy3ed852e2009-09-05 21:47:34 +00004978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004979 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004980 }
glennrp0fe50b42010-11-16 03:52:51 +00004981
cristy3ed852e2009-09-05 21:47:34 +00004982 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4983 continue;
4984 }
4985 if (memcmp(type,mng_DEFI,4) == 0)
4986 {
4987 if (mng_type == 3)
cristyc82a27b2011-10-21 01:07:16 +00004988 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004989 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4990 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004991
cristy3ed852e2009-09-05 21:47:34 +00004992 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004993
cristy3ed852e2009-09-05 21:47:34 +00004994 if (mng_type == 2 && object_id != 0)
cristyc82a27b2011-10-21 01:07:16 +00004995 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004996 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4997 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004998
cristy3ed852e2009-09-05 21:47:34 +00004999 if (object_id > MNG_MAX_OBJECTS)
5000 {
5001 /*
5002 Instead ofsuing a warning we should allocate a larger
5003 MngInfo structure and continue.
5004 */
cristyc82a27b2011-10-21 01:07:16 +00005005 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005006 CoderError,"object id too large","`%s'",image->filename);
5007 object_id=MNG_MAX_OBJECTS;
5008 }
glennrp0fe50b42010-11-16 03:52:51 +00005009
cristy3ed852e2009-09-05 21:47:34 +00005010 if (mng_info->exists[object_id])
5011 if (mng_info->frozen[object_id])
5012 {
5013 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
cristyc82a27b2011-10-21 01:07:16 +00005014 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005015 GetMagickModule(),CoderError,
5016 "DEFI cannot redefine a frozen MNG object","`%s'",
5017 image->filename);
5018 continue;
5019 }
glennrp0fe50b42010-11-16 03:52:51 +00005020
cristy3ed852e2009-09-05 21:47:34 +00005021 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005022
cristy3ed852e2009-09-05 21:47:34 +00005023 if (length > 2)
5024 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00005025
cristy3ed852e2009-09-05 21:47:34 +00005026 /*
5027 Extract object offset info.
5028 */
5029 if (length > 11)
5030 {
glennrp0fe50b42010-11-16 03:52:51 +00005031 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5032 (p[5] << 16) | (p[6] << 8) | p[7]);
5033
5034 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5035 (p[9] << 16) | (p[10] << 8) | p[11]);
5036
cristy3ed852e2009-09-05 21:47:34 +00005037 if (logging != MagickFalse)
5038 {
5039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005040 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005041 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005042
cristy3ed852e2009-09-05 21:47:34 +00005043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005044 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005045 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005046 }
5047 }
glennrp0fe50b42010-11-16 03:52:51 +00005048
cristy3ed852e2009-09-05 21:47:34 +00005049 /*
5050 Extract object clipping info.
5051 */
5052 if (length > 27)
5053 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5054 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005055
cristy3ed852e2009-09-05 21:47:34 +00005056 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5057 continue;
5058 }
5059 if (memcmp(type,mng_bKGD,4) == 0)
5060 {
5061 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005062
cristy3ed852e2009-09-05 21:47:34 +00005063 if (length > 5)
5064 {
5065 mng_info->mng_global_bkgd.red=
5066 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005067
cristy3ed852e2009-09-05 21:47:34 +00005068 mng_info->mng_global_bkgd.green=
5069 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005070
cristy3ed852e2009-09-05 21:47:34 +00005071 mng_info->mng_global_bkgd.blue=
5072 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005073
cristy3ed852e2009-09-05 21:47:34 +00005074 mng_info->have_global_bkgd=MagickTrue;
5075 }
glennrp0fe50b42010-11-16 03:52:51 +00005076
cristy3ed852e2009-09-05 21:47:34 +00005077 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5078 continue;
5079 }
5080 if (memcmp(type,mng_BACK,4) == 0)
5081 {
5082#if defined(MNG_INSERT_LAYERS)
5083 if (length > 6)
5084 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005085
cristy3ed852e2009-09-05 21:47:34 +00005086 else
5087 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005088
cristy3ed852e2009-09-05 21:47:34 +00005089 if (mandatory_back && length > 5)
5090 {
5091 mng_background_color.red=
5092 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005093
cristy3ed852e2009-09-05 21:47:34 +00005094 mng_background_color.green=
5095 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005096
cristy3ed852e2009-09-05 21:47:34 +00005097 mng_background_color.blue=
5098 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005099
cristy4c08aed2011-07-01 19:47:50 +00005100 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005101 }
glennrp0fe50b42010-11-16 03:52:51 +00005102
cristy3ed852e2009-09-05 21:47:34 +00005103#ifdef MNG_OBJECT_BUFFERS
5104 if (length > 8)
5105 mng_background_object=(p[7] << 8) | p[8];
5106#endif
5107#endif
5108 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5109 continue;
5110 }
glennrp47b9dd52010-11-24 18:12:06 +00005111
cristy3ed852e2009-09-05 21:47:34 +00005112 if (memcmp(type,mng_PLTE,4) == 0)
5113 {
glennrp47b9dd52010-11-24 18:12:06 +00005114 /* Read global PLTE. */
5115
cristy3ed852e2009-09-05 21:47:34 +00005116 if (length && (length < 769))
5117 {
5118 if (mng_info->global_plte == (png_colorp) NULL)
5119 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5120 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005121
cristybb503372010-05-27 20:51:26 +00005122 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005123 {
5124 mng_info->global_plte[i].red=p[3*i];
5125 mng_info->global_plte[i].green=p[3*i+1];
5126 mng_info->global_plte[i].blue=p[3*i+2];
5127 }
glennrp0fe50b42010-11-16 03:52:51 +00005128
cristy35ef8242010-06-03 16:24:13 +00005129 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005130 }
5131#ifdef MNG_LOOSE
5132 for ( ; i < 256; i++)
5133 {
5134 mng_info->global_plte[i].red=i;
5135 mng_info->global_plte[i].green=i;
5136 mng_info->global_plte[i].blue=i;
5137 }
glennrp0fe50b42010-11-16 03:52:51 +00005138
cristy3ed852e2009-09-05 21:47:34 +00005139 if (length)
5140 mng_info->global_plte_length=256;
5141#endif
5142 else
5143 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005144
cristy3ed852e2009-09-05 21:47:34 +00005145 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5146 continue;
5147 }
glennrp47b9dd52010-11-24 18:12:06 +00005148
cristy3ed852e2009-09-05 21:47:34 +00005149 if (memcmp(type,mng_tRNS,4) == 0)
5150 {
5151 /* read global tRNS */
5152
5153 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005154 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005155 mng_info->global_trns[i]=p[i];
5156
5157#ifdef MNG_LOOSE
5158 for ( ; i < 256; i++)
5159 mng_info->global_trns[i]=255;
5160#endif
cristy12560f32010-06-03 16:51:08 +00005161 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005162 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5163 continue;
5164 }
5165 if (memcmp(type,mng_gAMA,4) == 0)
5166 {
5167 if (length == 4)
5168 {
cristybb503372010-05-27 20:51:26 +00005169 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005170 igamma;
5171
cristy8182b072010-05-30 20:10:53 +00005172 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005173 mng_info->global_gamma=((float) igamma)*0.00001;
5174 mng_info->have_global_gama=MagickTrue;
5175 }
glennrp0fe50b42010-11-16 03:52:51 +00005176
cristy3ed852e2009-09-05 21:47:34 +00005177 else
5178 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005179
cristy3ed852e2009-09-05 21:47:34 +00005180 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5181 continue;
5182 }
5183
5184 if (memcmp(type,mng_cHRM,4) == 0)
5185 {
glennrp47b9dd52010-11-24 18:12:06 +00005186 /* Read global cHRM */
5187
cristy3ed852e2009-09-05 21:47:34 +00005188 if (length == 32)
5189 {
cristy8182b072010-05-30 20:10:53 +00005190 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5191 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5192 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005193 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005194 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005195 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005196 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005197 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005198 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005199 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005200 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005201 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005202 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005203 mng_info->have_global_chrm=MagickTrue;
5204 }
5205 else
5206 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005207
cristy3ed852e2009-09-05 21:47:34 +00005208 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5209 continue;
5210 }
glennrp47b9dd52010-11-24 18:12:06 +00005211
cristy3ed852e2009-09-05 21:47:34 +00005212 if (memcmp(type,mng_sRGB,4) == 0)
5213 {
5214 /*
5215 Read global sRGB.
5216 */
5217 if (length)
5218 {
glennrpe610a072010-08-05 17:08:46 +00005219 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005220 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005221 mng_info->have_global_srgb=MagickTrue;
5222 }
5223 else
5224 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005225
cristy3ed852e2009-09-05 21:47:34 +00005226 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5227 continue;
5228 }
glennrp47b9dd52010-11-24 18:12:06 +00005229
cristy3ed852e2009-09-05 21:47:34 +00005230 if (memcmp(type,mng_iCCP,4) == 0)
5231 {
glennrpfd05d622011-02-25 04:10:33 +00005232 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005233
5234 /*
5235 Read global iCCP.
5236 */
5237 if (length)
5238 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005239
cristy3ed852e2009-09-05 21:47:34 +00005240 continue;
5241 }
glennrp47b9dd52010-11-24 18:12:06 +00005242
cristy3ed852e2009-09-05 21:47:34 +00005243 if (memcmp(type,mng_FRAM,4) == 0)
5244 {
5245 if (mng_type == 3)
cristyc82a27b2011-10-21 01:07:16 +00005246 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005247 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5248 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005249
cristy3ed852e2009-09-05 21:47:34 +00005250 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5251 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005252
cristy3ed852e2009-09-05 21:47:34 +00005253 frame_delay=default_frame_delay;
5254 frame_timeout=default_frame_timeout;
5255 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005256
cristy3ed852e2009-09-05 21:47:34 +00005257 if (length)
5258 if (p[0])
5259 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005260
cristy3ed852e2009-09-05 21:47:34 +00005261 if (logging != MagickFalse)
5262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5263 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005264
cristy3ed852e2009-09-05 21:47:34 +00005265 if (length > 6)
5266 {
glennrp47b9dd52010-11-24 18:12:06 +00005267 /* Note the delay and frame clipping boundaries. */
5268
cristy3ed852e2009-09-05 21:47:34 +00005269 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005270
cristybb503372010-05-27 20:51:26 +00005271 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005272 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005273
cristy3ed852e2009-09-05 21:47:34 +00005274 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005275
cristybb503372010-05-27 20:51:26 +00005276 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005277 {
5278 int
5279 change_delay,
5280 change_timeout,
5281 change_clipping;
5282
5283 change_delay=(*p++);
5284 change_timeout=(*p++);
5285 change_clipping=(*p++);
5286 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005287
cristy3ed852e2009-09-05 21:47:34 +00005288 if (change_delay)
5289 {
cristy8182b072010-05-30 20:10:53 +00005290 frame_delay=1UL*image->ticks_per_second*
5291 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005292
cristy8182b072010-05-30 20:10:53 +00005293 if (mng_info->ticks_per_second != 0)
5294 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005295
glennrpbb010dd2010-06-01 13:07:15 +00005296 else
5297 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005298
cristy3ed852e2009-09-05 21:47:34 +00005299 if (change_delay == 2)
5300 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005301
cristy3ed852e2009-09-05 21:47:34 +00005302 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005303
cristy3ed852e2009-09-05 21:47:34 +00005304 if (logging != MagickFalse)
5305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005306 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005307 }
glennrp47b9dd52010-11-24 18:12:06 +00005308
cristy3ed852e2009-09-05 21:47:34 +00005309 if (change_timeout)
5310 {
glennrpbb010dd2010-06-01 13:07:15 +00005311 frame_timeout=1UL*image->ticks_per_second*
5312 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005313
glennrpbb010dd2010-06-01 13:07:15 +00005314 if (mng_info->ticks_per_second != 0)
5315 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005316
glennrpbb010dd2010-06-01 13:07:15 +00005317 else
5318 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005319
cristy3ed852e2009-09-05 21:47:34 +00005320 if (change_delay == 2)
5321 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005322
cristy3ed852e2009-09-05 21:47:34 +00005323 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005324
cristy3ed852e2009-09-05 21:47:34 +00005325 if (logging != MagickFalse)
5326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005327 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005328 }
glennrp47b9dd52010-11-24 18:12:06 +00005329
cristy3ed852e2009-09-05 21:47:34 +00005330 if (change_clipping)
5331 {
5332 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5333 p+=17;
5334 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005335
cristy3ed852e2009-09-05 21:47:34 +00005336 if (logging != MagickFalse)
5337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005338 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005339 (double) fb.left,(double) fb.right,(double) fb.top,
5340 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005341
cristy3ed852e2009-09-05 21:47:34 +00005342 if (change_clipping == 2)
5343 default_fb=fb;
5344 }
5345 }
5346 }
5347 mng_info->clip=fb;
5348 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005349
cristybb503372010-05-27 20:51:26 +00005350 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005351 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005352
cristybb503372010-05-27 20:51:26 +00005353 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005354 -mng_info->clip.top);
5355 /*
5356 Insert a background layer behind the frame if framing_mode is 4.
5357 */
5358#if defined(MNG_INSERT_LAYERS)
5359 if (logging != MagickFalse)
5360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005361 " subframe_width=%.20g, subframe_height=%.20g",(double)
5362 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005363
cristy3ed852e2009-09-05 21:47:34 +00005364 if (insert_layers && (mng_info->framing_mode == 4) &&
5365 (subframe_width) && (subframe_height))
5366 {
glennrp47b9dd52010-11-24 18:12:06 +00005367 /* Allocate next image structure. */
cristy4c08aed2011-07-01 19:47:50 +00005368 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005369 {
cristy9950d572011-10-01 18:22:35 +00005370 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005371
cristy3ed852e2009-09-05 21:47:34 +00005372 if (GetNextImageInList(image) == (Image *) NULL)
5373 {
5374 image=DestroyImageList(image);
5375 MngInfoFreeStruct(mng_info,&have_mng_structure);
5376 return((Image *) NULL);
5377 }
glennrp47b9dd52010-11-24 18:12:06 +00005378
cristy3ed852e2009-09-05 21:47:34 +00005379 image=SyncNextImageInList(image);
5380 }
glennrp0fe50b42010-11-16 03:52:51 +00005381
cristy3ed852e2009-09-05 21:47:34 +00005382 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005383
cristy3ed852e2009-09-05 21:47:34 +00005384 if (term_chunk_found)
5385 {
5386 image->start_loop=MagickTrue;
5387 image->iterations=mng_iterations;
5388 term_chunk_found=MagickFalse;
5389 }
glennrp0fe50b42010-11-16 03:52:51 +00005390
cristy3ed852e2009-09-05 21:47:34 +00005391 else
5392 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005393
cristy3ed852e2009-09-05 21:47:34 +00005394 image->columns=subframe_width;
5395 image->rows=subframe_height;
5396 image->page.width=subframe_width;
5397 image->page.height=subframe_height;
5398 image->page.x=mng_info->clip.left;
5399 image->page.y=mng_info->clip.top;
5400 image->background_color=mng_background_color;
5401 image->matte=MagickFalse;
5402 image->delay=0;
cristyea1a8aa2011-10-20 13:24:06 +00005403 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005404
cristy3ed852e2009-09-05 21:47:34 +00005405 if (logging != MagickFalse)
5406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005407 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005408 (double) mng_info->clip.left,(double) mng_info->clip.right,
5409 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005410 }
5411#endif
5412 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5413 continue;
5414 }
5415 if (memcmp(type,mng_CLIP,4) == 0)
5416 {
5417 unsigned int
5418 first_object,
5419 last_object;
5420
5421 /*
5422 Read CLIP.
5423 */
5424 first_object=(p[0] << 8) | p[1];
5425 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005426
cristy3ed852e2009-09-05 21:47:34 +00005427 for (i=(int) first_object; i <= (int) last_object; i++)
5428 {
5429 if (mng_info->exists[i] && !mng_info->frozen[i])
5430 {
5431 MngBox
5432 box;
5433
5434 box=mng_info->object_clip[i];
5435 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5436 }
5437 }
glennrp47b9dd52010-11-24 18:12:06 +00005438
cristy3ed852e2009-09-05 21:47:34 +00005439 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5440 continue;
5441 }
5442 if (memcmp(type,mng_SAVE,4) == 0)
5443 {
5444 for (i=1; i < MNG_MAX_OBJECTS; i++)
5445 if (mng_info->exists[i])
5446 {
5447 mng_info->frozen[i]=MagickTrue;
5448#ifdef MNG_OBJECT_BUFFERS
5449 if (mng_info->ob[i] != (MngBuffer *) NULL)
5450 mng_info->ob[i]->frozen=MagickTrue;
5451#endif
5452 }
glennrp0fe50b42010-11-16 03:52:51 +00005453
cristy3ed852e2009-09-05 21:47:34 +00005454 if (length)
5455 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005456
cristy3ed852e2009-09-05 21:47:34 +00005457 continue;
5458 }
5459
5460 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5461 {
glennrp47b9dd52010-11-24 18:12:06 +00005462 /* Read DISC or SEEK. */
5463
cristy3ed852e2009-09-05 21:47:34 +00005464 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5465 {
5466 for (i=1; i < MNG_MAX_OBJECTS; i++)
5467 MngInfoDiscardObject(mng_info,i);
5468 }
glennrp0fe50b42010-11-16 03:52:51 +00005469
cristy3ed852e2009-09-05 21:47:34 +00005470 else
5471 {
cristybb503372010-05-27 20:51:26 +00005472 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005473 j;
5474
cristybb503372010-05-27 20:51:26 +00005475 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005476 {
5477 i=p[j] << 8 | p[j+1];
5478 MngInfoDiscardObject(mng_info,i);
5479 }
5480 }
glennrp0fe50b42010-11-16 03:52:51 +00005481
cristy3ed852e2009-09-05 21:47:34 +00005482 if (length)
5483 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005484
cristy3ed852e2009-09-05 21:47:34 +00005485 continue;
5486 }
glennrp47b9dd52010-11-24 18:12:06 +00005487
cristy3ed852e2009-09-05 21:47:34 +00005488 if (memcmp(type,mng_MOVE,4) == 0)
5489 {
cristybb503372010-05-27 20:51:26 +00005490 size_t
cristy3ed852e2009-09-05 21:47:34 +00005491 first_object,
5492 last_object;
5493
glennrp47b9dd52010-11-24 18:12:06 +00005494 /* read MOVE */
5495
cristy3ed852e2009-09-05 21:47:34 +00005496 first_object=(p[0] << 8) | p[1];
5497 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005498 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005499 {
5500 if (mng_info->exists[i] && !mng_info->frozen[i])
5501 {
5502 MngPair
5503 new_pair;
5504
5505 MngPair
5506 old_pair;
5507
5508 old_pair.a=mng_info->x_off[i];
5509 old_pair.b=mng_info->y_off[i];
5510 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5511 mng_info->x_off[i]=new_pair.a;
5512 mng_info->y_off[i]=new_pair.b;
5513 }
5514 }
glennrp47b9dd52010-11-24 18:12:06 +00005515
cristy3ed852e2009-09-05 21:47:34 +00005516 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5517 continue;
5518 }
5519
5520 if (memcmp(type,mng_LOOP,4) == 0)
5521 {
cristybb503372010-05-27 20:51:26 +00005522 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005523 loop_level=chunk[0];
5524 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005525
5526 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005527 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005528
cristy3ed852e2009-09-05 21:47:34 +00005529 if (logging != MagickFalse)
5530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005531 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5532 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005533
cristy3ed852e2009-09-05 21:47:34 +00005534 if (loop_iters == 0)
5535 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005536
cristy3ed852e2009-09-05 21:47:34 +00005537 else
5538 {
5539 mng_info->loop_jump[loop_level]=TellBlob(image);
5540 mng_info->loop_count[loop_level]=loop_iters;
5541 }
glennrp0fe50b42010-11-16 03:52:51 +00005542
cristy3ed852e2009-09-05 21:47:34 +00005543 mng_info->loop_iteration[loop_level]=0;
5544 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5545 continue;
5546 }
glennrp47b9dd52010-11-24 18:12:06 +00005547
cristy3ed852e2009-09-05 21:47:34 +00005548 if (memcmp(type,mng_ENDL,4) == 0)
5549 {
5550 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005551
cristy3ed852e2009-09-05 21:47:34 +00005552 if (skipping_loop > 0)
5553 {
5554 if (skipping_loop == loop_level)
5555 {
5556 /*
5557 Found end of zero-iteration loop.
5558 */
5559 skipping_loop=(-1);
5560 mng_info->loop_active[loop_level]=0;
5561 }
5562 }
glennrp47b9dd52010-11-24 18:12:06 +00005563
cristy3ed852e2009-09-05 21:47:34 +00005564 else
5565 {
5566 if (mng_info->loop_active[loop_level] == 1)
5567 {
5568 mng_info->loop_count[loop_level]--;
5569 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005570
cristy3ed852e2009-09-05 21:47:34 +00005571 if (logging != MagickFalse)
5572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005573 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005574 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005575 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005576
cristy3ed852e2009-09-05 21:47:34 +00005577 if (mng_info->loop_count[loop_level] != 0)
5578 {
5579 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5580 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005581
cristy3ed852e2009-09-05 21:47:34 +00005582 if (offset < 0)
5583 ThrowReaderException(CorruptImageError,
5584 "ImproperImageHeader");
5585 }
glennrp47b9dd52010-11-24 18:12:06 +00005586
cristy3ed852e2009-09-05 21:47:34 +00005587 else
5588 {
5589 short
5590 last_level;
5591
5592 /*
5593 Finished loop.
5594 */
5595 mng_info->loop_active[loop_level]=0;
5596 last_level=(-1);
5597 for (i=0; i < loop_level; i++)
5598 if (mng_info->loop_active[i] == 1)
5599 last_level=(short) i;
5600 loop_level=last_level;
5601 }
5602 }
5603 }
glennrp47b9dd52010-11-24 18:12:06 +00005604
cristy3ed852e2009-09-05 21:47:34 +00005605 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5606 continue;
5607 }
glennrp47b9dd52010-11-24 18:12:06 +00005608
cristy3ed852e2009-09-05 21:47:34 +00005609 if (memcmp(type,mng_CLON,4) == 0)
5610 {
5611 if (mng_info->clon_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005612 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005613 CoderError,"CLON is not implemented yet","`%s'",
5614 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005615
cristy3ed852e2009-09-05 21:47:34 +00005616 mng_info->clon_warning++;
5617 }
glennrp47b9dd52010-11-24 18:12:06 +00005618
cristy3ed852e2009-09-05 21:47:34 +00005619 if (memcmp(type,mng_MAGN,4) == 0)
5620 {
5621 png_uint_16
5622 magn_first,
5623 magn_last,
5624 magn_mb,
5625 magn_ml,
5626 magn_mr,
5627 magn_mt,
5628 magn_mx,
5629 magn_my,
5630 magn_methx,
5631 magn_methy;
5632
5633 if (length > 1)
5634 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005635
cristy3ed852e2009-09-05 21:47:34 +00005636 else
5637 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005638
cristy3ed852e2009-09-05 21:47:34 +00005639 if (length > 3)
5640 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005641
cristy3ed852e2009-09-05 21:47:34 +00005642 else
5643 magn_last=magn_first;
5644#ifndef MNG_OBJECT_BUFFERS
5645 if (magn_first || magn_last)
5646 if (mng_info->magn_warning == 0)
5647 {
cristyc82a27b2011-10-21 01:07:16 +00005648 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005649 GetMagickModule(),CoderError,
5650 "MAGN is not implemented yet for nonzero objects",
5651 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005652
cristy3ed852e2009-09-05 21:47:34 +00005653 mng_info->magn_warning++;
5654 }
5655#endif
5656 if (length > 4)
5657 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005658
cristy3ed852e2009-09-05 21:47:34 +00005659 else
5660 magn_methx=0;
5661
5662 if (length > 6)
5663 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005664
cristy3ed852e2009-09-05 21:47:34 +00005665 else
5666 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005667
cristy3ed852e2009-09-05 21:47:34 +00005668 if (magn_mx == 0)
5669 magn_mx=1;
5670
5671 if (length > 8)
5672 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005673
cristy3ed852e2009-09-05 21:47:34 +00005674 else
5675 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005676
cristy3ed852e2009-09-05 21:47:34 +00005677 if (magn_my == 0)
5678 magn_my=1;
5679
5680 if (length > 10)
5681 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005682
cristy3ed852e2009-09-05 21:47:34 +00005683 else
5684 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005685
cristy3ed852e2009-09-05 21:47:34 +00005686 if (magn_ml == 0)
5687 magn_ml=1;
5688
5689 if (length > 12)
5690 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005691
cristy3ed852e2009-09-05 21:47:34 +00005692 else
5693 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005694
cristy3ed852e2009-09-05 21:47:34 +00005695 if (magn_mr == 0)
5696 magn_mr=1;
5697
5698 if (length > 14)
5699 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005700
cristy3ed852e2009-09-05 21:47:34 +00005701 else
5702 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005703
cristy3ed852e2009-09-05 21:47:34 +00005704 if (magn_mt == 0)
5705 magn_mt=1;
5706
5707 if (length > 16)
5708 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005709
cristy3ed852e2009-09-05 21:47:34 +00005710 else
5711 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005712
cristy3ed852e2009-09-05 21:47:34 +00005713 if (magn_mb == 0)
5714 magn_mb=1;
5715
5716 if (length > 17)
5717 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005718
cristy3ed852e2009-09-05 21:47:34 +00005719 else
5720 magn_methy=magn_methx;
5721
glennrp47b9dd52010-11-24 18:12:06 +00005722
cristy3ed852e2009-09-05 21:47:34 +00005723 if (magn_methx > 5 || magn_methy > 5)
5724 if (mng_info->magn_warning == 0)
5725 {
cristyc82a27b2011-10-21 01:07:16 +00005726 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005727 GetMagickModule(),CoderError,
5728 "Unknown MAGN method in MNG datastream","`%s'",
5729 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005730
cristy3ed852e2009-09-05 21:47:34 +00005731 mng_info->magn_warning++;
5732 }
5733#ifdef MNG_OBJECT_BUFFERS
5734 /* Magnify existing objects in the range magn_first to magn_last */
5735#endif
5736 if (magn_first == 0 || magn_last == 0)
5737 {
5738 /* Save the magnification factors for object 0 */
5739 mng_info->magn_mb=magn_mb;
5740 mng_info->magn_ml=magn_ml;
5741 mng_info->magn_mr=magn_mr;
5742 mng_info->magn_mt=magn_mt;
5743 mng_info->magn_mx=magn_mx;
5744 mng_info->magn_my=magn_my;
5745 mng_info->magn_methx=magn_methx;
5746 mng_info->magn_methy=magn_methy;
5747 }
5748 }
glennrp47b9dd52010-11-24 18:12:06 +00005749
cristy3ed852e2009-09-05 21:47:34 +00005750 if (memcmp(type,mng_PAST,4) == 0)
5751 {
5752 if (mng_info->past_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005753 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005754 CoderError,"PAST is not implemented yet","`%s'",
5755 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005756
cristy3ed852e2009-09-05 21:47:34 +00005757 mng_info->past_warning++;
5758 }
glennrp47b9dd52010-11-24 18:12:06 +00005759
cristy3ed852e2009-09-05 21:47:34 +00005760 if (memcmp(type,mng_SHOW,4) == 0)
5761 {
5762 if (mng_info->show_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005763 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005764 CoderError,"SHOW is not implemented yet","`%s'",
5765 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005766
cristy3ed852e2009-09-05 21:47:34 +00005767 mng_info->show_warning++;
5768 }
glennrp47b9dd52010-11-24 18:12:06 +00005769
cristy3ed852e2009-09-05 21:47:34 +00005770 if (memcmp(type,mng_sBIT,4) == 0)
5771 {
5772 if (length < 4)
5773 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005774
cristy3ed852e2009-09-05 21:47:34 +00005775 else
5776 {
5777 mng_info->global_sbit.gray=p[0];
5778 mng_info->global_sbit.red=p[0];
5779 mng_info->global_sbit.green=p[1];
5780 mng_info->global_sbit.blue=p[2];
5781 mng_info->global_sbit.alpha=p[3];
5782 mng_info->have_global_sbit=MagickTrue;
5783 }
5784 }
5785 if (memcmp(type,mng_pHYs,4) == 0)
5786 {
5787 if (length > 8)
5788 {
5789 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005790 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005791 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005792 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005793 mng_info->global_phys_unit_type=p[8];
5794 mng_info->have_global_phys=MagickTrue;
5795 }
glennrp47b9dd52010-11-24 18:12:06 +00005796
cristy3ed852e2009-09-05 21:47:34 +00005797 else
5798 mng_info->have_global_phys=MagickFalse;
5799 }
5800 if (memcmp(type,mng_pHYg,4) == 0)
5801 {
5802 if (mng_info->phyg_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005803 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005804 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005805
cristy3ed852e2009-09-05 21:47:34 +00005806 mng_info->phyg_warning++;
5807 }
5808 if (memcmp(type,mng_BASI,4) == 0)
5809 {
5810 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005811
cristy3ed852e2009-09-05 21:47:34 +00005812 if (mng_info->basi_warning == 0)
cristyc82a27b2011-10-21 01:07:16 +00005813 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005814 CoderError,"BASI is not implemented yet","`%s'",
5815 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005816
cristy3ed852e2009-09-05 21:47:34 +00005817 mng_info->basi_warning++;
5818#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005819 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005820 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005821 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005822 (p[6] << 8) | p[7]);
5823 basi_color_type=p[8];
5824 basi_compression_method=p[9];
5825 basi_filter_type=p[10];
5826 basi_interlace_method=p[11];
5827 if (length > 11)
5828 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005829
cristy3ed852e2009-09-05 21:47:34 +00005830 else
5831 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005832
cristy3ed852e2009-09-05 21:47:34 +00005833 if (length > 13)
5834 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005835
cristy3ed852e2009-09-05 21:47:34 +00005836 else
5837 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005838
cristy3ed852e2009-09-05 21:47:34 +00005839 if (length > 15)
5840 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005841
cristy3ed852e2009-09-05 21:47:34 +00005842 else
5843 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005844
cristy3ed852e2009-09-05 21:47:34 +00005845 if (length > 17)
5846 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005847
cristy3ed852e2009-09-05 21:47:34 +00005848 else
5849 {
5850 if (basi_sample_depth == 16)
5851 basi_alpha=65535L;
5852 else
5853 basi_alpha=255;
5854 }
glennrp47b9dd52010-11-24 18:12:06 +00005855
cristy3ed852e2009-09-05 21:47:34 +00005856 if (length > 19)
5857 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005858
cristy3ed852e2009-09-05 21:47:34 +00005859 else
5860 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005861
cristy3ed852e2009-09-05 21:47:34 +00005862#endif
5863 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5864 continue;
5865 }
glennrp47b9dd52010-11-24 18:12:06 +00005866
cristy3ed852e2009-09-05 21:47:34 +00005867 if (memcmp(type,mng_IHDR,4)
5868#if defined(JNG_SUPPORTED)
5869 && memcmp(type,mng_JHDR,4)
5870#endif
5871 )
5872 {
5873 /* Not an IHDR or JHDR chunk */
5874 if (length)
5875 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005876
cristy3ed852e2009-09-05 21:47:34 +00005877 continue;
5878 }
5879/* Process IHDR */
5880 if (logging != MagickFalse)
5881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5882 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005883
cristy3ed852e2009-09-05 21:47:34 +00005884 mng_info->exists[object_id]=MagickTrue;
5885 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005886
cristy3ed852e2009-09-05 21:47:34 +00005887 if (mng_info->invisible[object_id])
5888 {
5889 if (logging != MagickFalse)
5890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5891 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005892
cristy3ed852e2009-09-05 21:47:34 +00005893 skip_to_iend=MagickTrue;
5894 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5895 continue;
5896 }
5897#if defined(MNG_INSERT_LAYERS)
5898 if (length < 8)
5899 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005900
cristy8182b072010-05-30 20:10:53 +00005901 image_width=(size_t) mng_get_long(p);
5902 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005903#endif
5904 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5905
5906 /*
5907 Insert a transparent background layer behind the entire animation
5908 if it is not full screen.
5909 */
5910#if defined(MNG_INSERT_LAYERS)
5911 if (insert_layers && mng_type && first_mng_object)
5912 {
5913 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5914 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005915 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005916 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005917 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005918 {
cristy4c08aed2011-07-01 19:47:50 +00005919 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005920 {
5921 /*
5922 Allocate next image structure.
5923 */
cristy9950d572011-10-01 18:22:35 +00005924 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005925
cristy3ed852e2009-09-05 21:47:34 +00005926 if (GetNextImageInList(image) == (Image *) NULL)
5927 {
5928 image=DestroyImageList(image);
5929 MngInfoFreeStruct(mng_info,&have_mng_structure);
5930 return((Image *) NULL);
5931 }
glennrp47b9dd52010-11-24 18:12:06 +00005932
cristy3ed852e2009-09-05 21:47:34 +00005933 image=SyncNextImageInList(image);
5934 }
5935 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005936
cristy3ed852e2009-09-05 21:47:34 +00005937 if (term_chunk_found)
5938 {
5939 image->start_loop=MagickTrue;
5940 image->iterations=mng_iterations;
5941 term_chunk_found=MagickFalse;
5942 }
glennrp47b9dd52010-11-24 18:12:06 +00005943
cristy3ed852e2009-09-05 21:47:34 +00005944 else
5945 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005946
5947 /* Make a background rectangle. */
5948
cristy3ed852e2009-09-05 21:47:34 +00005949 image->delay=0;
5950 image->columns=mng_info->mng_width;
5951 image->rows=mng_info->mng_height;
5952 image->page.width=mng_info->mng_width;
5953 image->page.height=mng_info->mng_height;
5954 image->page.x=0;
5955 image->page.y=0;
5956 image->background_color=mng_background_color;
cristyea1a8aa2011-10-20 13:24:06 +00005957 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005958 if (logging != MagickFalse)
5959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005960 " Inserted transparent background layer, W=%.20g, H=%.20g",
5961 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005962 }
5963 }
5964 /*
5965 Insert a background layer behind the upcoming image if
5966 framing_mode is 3, and we haven't already inserted one.
5967 */
5968 if (insert_layers && (mng_info->framing_mode == 3) &&
5969 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5970 (simplicity & 0x08)))
5971 {
cristy4c08aed2011-07-01 19:47:50 +00005972 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005973 {
5974 /*
5975 Allocate next image structure.
5976 */
cristy9950d572011-10-01 18:22:35 +00005977 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005978
cristy3ed852e2009-09-05 21:47:34 +00005979 if (GetNextImageInList(image) == (Image *) NULL)
5980 {
5981 image=DestroyImageList(image);
5982 MngInfoFreeStruct(mng_info,&have_mng_structure);
5983 return((Image *) NULL);
5984 }
glennrp47b9dd52010-11-24 18:12:06 +00005985
cristy3ed852e2009-09-05 21:47:34 +00005986 image=SyncNextImageInList(image);
5987 }
glennrp0fe50b42010-11-16 03:52:51 +00005988
cristy3ed852e2009-09-05 21:47:34 +00005989 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005990
cristy3ed852e2009-09-05 21:47:34 +00005991 if (term_chunk_found)
5992 {
5993 image->start_loop=MagickTrue;
5994 image->iterations=mng_iterations;
5995 term_chunk_found=MagickFalse;
5996 }
glennrp0fe50b42010-11-16 03:52:51 +00005997
cristy3ed852e2009-09-05 21:47:34 +00005998 else
5999 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006000
cristy3ed852e2009-09-05 21:47:34 +00006001 image->delay=0;
6002 image->columns=subframe_width;
6003 image->rows=subframe_height;
6004 image->page.width=subframe_width;
6005 image->page.height=subframe_height;
6006 image->page.x=mng_info->clip.left;
6007 image->page.y=mng_info->clip.top;
6008 image->background_color=mng_background_color;
6009 image->matte=MagickFalse;
cristyea1a8aa2011-10-20 13:24:06 +00006010 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006011
cristy3ed852e2009-09-05 21:47:34 +00006012 if (logging != MagickFalse)
6013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00006014 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00006015 (double) mng_info->clip.left,(double) mng_info->clip.right,
6016 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00006017 }
6018#endif /* MNG_INSERT_LAYERS */
6019 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006020
cristy4c08aed2011-07-01 19:47:50 +00006021 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006022 {
6023 /*
6024 Allocate next image structure.
6025 */
cristy9950d572011-10-01 18:22:35 +00006026 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006027
cristy3ed852e2009-09-05 21:47:34 +00006028 if (GetNextImageInList(image) == (Image *) NULL)
6029 {
6030 image=DestroyImageList(image);
6031 MngInfoFreeStruct(mng_info,&have_mng_structure);
6032 return((Image *) NULL);
6033 }
glennrp47b9dd52010-11-24 18:12:06 +00006034
cristy3ed852e2009-09-05 21:47:34 +00006035 image=SyncNextImageInList(image);
6036 }
6037 mng_info->image=image;
6038 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6039 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006040
cristy3ed852e2009-09-05 21:47:34 +00006041 if (status == MagickFalse)
6042 break;
glennrp0fe50b42010-11-16 03:52:51 +00006043
cristy3ed852e2009-09-05 21:47:34 +00006044 if (term_chunk_found)
6045 {
6046 image->start_loop=MagickTrue;
6047 term_chunk_found=MagickFalse;
6048 }
glennrp0fe50b42010-11-16 03:52:51 +00006049
cristy3ed852e2009-09-05 21:47:34 +00006050 else
6051 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006052
cristy3ed852e2009-09-05 21:47:34 +00006053 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6054 {
6055 image->delay=frame_delay;
6056 frame_delay=default_frame_delay;
6057 }
glennrp0fe50b42010-11-16 03:52:51 +00006058
cristy3ed852e2009-09-05 21:47:34 +00006059 else
6060 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006061
cristy3ed852e2009-09-05 21:47:34 +00006062 image->page.width=mng_info->mng_width;
6063 image->page.height=mng_info->mng_height;
6064 image->page.x=mng_info->x_off[object_id];
6065 image->page.y=mng_info->y_off[object_id];
6066 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006067
cristy3ed852e2009-09-05 21:47:34 +00006068 /*
6069 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6070 */
glennrp47b9dd52010-11-24 18:12:06 +00006071
cristy3ed852e2009-09-05 21:47:34 +00006072 if (logging != MagickFalse)
6073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6074 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6075 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006076
cristybb503372010-05-27 20:51:26 +00006077 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006078
cristy3ed852e2009-09-05 21:47:34 +00006079 if (offset < 0)
6080 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6081 }
6082
6083 previous=image;
6084 mng_info->image=image;
6085 mng_info->mng_type=mng_type;
6086 mng_info->object_id=object_id;
6087
6088 if (memcmp(type,mng_IHDR,4) == 0)
6089 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006090
cristy3ed852e2009-09-05 21:47:34 +00006091#if defined(JNG_SUPPORTED)
6092 else
6093 image=ReadOneJNGImage(mng_info,image_info,exception);
6094#endif
6095
6096 if (image == (Image *) NULL)
6097 {
6098 if (IsImageObject(previous) != MagickFalse)
6099 {
6100 (void) DestroyImageList(previous);
6101 (void) CloseBlob(previous);
6102 }
glennrp47b9dd52010-11-24 18:12:06 +00006103
cristy3ed852e2009-09-05 21:47:34 +00006104 MngInfoFreeStruct(mng_info,&have_mng_structure);
6105 return((Image *) NULL);
6106 }
glennrp0fe50b42010-11-16 03:52:51 +00006107
cristy3ed852e2009-09-05 21:47:34 +00006108 if (image->columns == 0 || image->rows == 0)
6109 {
6110 (void) CloseBlob(image);
6111 image=DestroyImageList(image);
6112 MngInfoFreeStruct(mng_info,&have_mng_structure);
6113 return((Image *) NULL);
6114 }
glennrp0fe50b42010-11-16 03:52:51 +00006115
cristy3ed852e2009-09-05 21:47:34 +00006116 mng_info->image=image;
6117
6118 if (mng_type)
6119 {
6120 MngBox
6121 crop_box;
6122
6123 if (mng_info->magn_methx || mng_info->magn_methy)
6124 {
6125 png_uint_32
6126 magnified_height,
6127 magnified_width;
6128
6129 if (logging != MagickFalse)
6130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6131 " Processing MNG MAGN chunk");
6132
6133 if (mng_info->magn_methx == 1)
6134 {
6135 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006136
cristy3ed852e2009-09-05 21:47:34 +00006137 if (image->columns > 1)
6138 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006139
cristy3ed852e2009-09-05 21:47:34 +00006140 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006141 magnified_width += (png_uint_32)
6142 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006143 }
glennrp47b9dd52010-11-24 18:12:06 +00006144
cristy3ed852e2009-09-05 21:47:34 +00006145 else
6146 {
cristy4e5bc842010-06-09 13:56:01 +00006147 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006148
cristy3ed852e2009-09-05 21:47:34 +00006149 if (image->columns > 1)
6150 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006151
cristy3ed852e2009-09-05 21:47:34 +00006152 if (image->columns > 2)
6153 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006154
cristy3ed852e2009-09-05 21:47:34 +00006155 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006156 magnified_width += (png_uint_32)
6157 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006158 }
glennrp47b9dd52010-11-24 18:12:06 +00006159
cristy3ed852e2009-09-05 21:47:34 +00006160 if (mng_info->magn_methy == 1)
6161 {
6162 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006163
cristy3ed852e2009-09-05 21:47:34 +00006164 if (image->rows > 1)
6165 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006166
cristy3ed852e2009-09-05 21:47:34 +00006167 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006168 magnified_height += (png_uint_32)
6169 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006170 }
glennrp47b9dd52010-11-24 18:12:06 +00006171
cristy3ed852e2009-09-05 21:47:34 +00006172 else
6173 {
cristy4e5bc842010-06-09 13:56:01 +00006174 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006175
cristy3ed852e2009-09-05 21:47:34 +00006176 if (image->rows > 1)
6177 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006178
cristy3ed852e2009-09-05 21:47:34 +00006179 if (image->rows > 2)
6180 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006181
cristy3ed852e2009-09-05 21:47:34 +00006182 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006183 magnified_height += (png_uint_32)
6184 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006185 }
glennrp47b9dd52010-11-24 18:12:06 +00006186
cristy3ed852e2009-09-05 21:47:34 +00006187 if (magnified_height > image->rows ||
6188 magnified_width > image->columns)
6189 {
6190 Image
6191 *large_image;
6192
6193 int
6194 yy;
6195
cristy4c08aed2011-07-01 19:47:50 +00006196 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006197 *next,
6198 *prev;
6199
6200 png_uint_16
6201 magn_methx,
6202 magn_methy;
6203
cristy4c08aed2011-07-01 19:47:50 +00006204 ssize_t
6205 m,
6206 y;
6207
6208 register Quantum
6209 *n,
6210 *q;
6211
6212 register ssize_t
6213 x;
6214
glennrp47b9dd52010-11-24 18:12:06 +00006215 /* Allocate next image structure. */
6216
cristy3ed852e2009-09-05 21:47:34 +00006217 if (logging != MagickFalse)
6218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6219 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006220
cristy9950d572011-10-01 18:22:35 +00006221 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006222
cristy3ed852e2009-09-05 21:47:34 +00006223 if (GetNextImageInList(image) == (Image *) NULL)
6224 {
6225 image=DestroyImageList(image);
6226 MngInfoFreeStruct(mng_info,&have_mng_structure);
6227 return((Image *) NULL);
6228 }
6229
6230 large_image=SyncNextImageInList(image);
6231
6232 large_image->columns=magnified_width;
6233 large_image->rows=magnified_height;
6234
6235 magn_methx=mng_info->magn_methx;
6236 magn_methy=mng_info->magn_methy;
6237
glennrp3faa9a32011-04-23 14:00:25 +00006238#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006239#define QM unsigned short
6240 if (magn_methx != 1 || magn_methy != 1)
6241 {
6242 /*
6243 Scale pixels to unsigned shorts to prevent
6244 overflow of intermediate values of interpolations
6245 */
cristybb503372010-05-27 20:51:26 +00006246 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006247 {
6248 q=GetAuthenticPixels(image,0,y,image->columns,1,
6249 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006250
cristybb503372010-05-27 20:51:26 +00006251 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006252 {
cristy4c08aed2011-07-01 19:47:50 +00006253 SetPixelRed(image,ScaleQuantumToShort(
6254 GetPixelRed(image,q)),q);
6255 SetPixelGreen(image,ScaleQuantumToShort(
6256 GetPixelGreen(image,q)),q);
6257 SetPixelBlue(image,ScaleQuantumToShort(
6258 GetPixelBlue(image,q)),q);
6259 SetPixelAlpha(image,ScaleQuantumToShort(
6260 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006261 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006262 }
glennrp47b9dd52010-11-24 18:12:06 +00006263
cristy3ed852e2009-09-05 21:47:34 +00006264 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6265 break;
6266 }
6267 }
6268#else
6269#define QM Quantum
6270#endif
6271
6272 if (image->matte != MagickFalse)
cristyea1a8aa2011-10-20 13:24:06 +00006273 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006274
cristy3ed852e2009-09-05 21:47:34 +00006275 else
6276 {
cristy4c08aed2011-07-01 19:47:50 +00006277 large_image->background_color.alpha=OpaqueAlpha;
cristyea1a8aa2011-10-20 13:24:06 +00006278 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006279
cristy3ed852e2009-09-05 21:47:34 +00006280 if (magn_methx == 4)
6281 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006282
cristy3ed852e2009-09-05 21:47:34 +00006283 if (magn_methx == 5)
6284 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006285
cristy3ed852e2009-09-05 21:47:34 +00006286 if (magn_methy == 4)
6287 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006288
cristy3ed852e2009-09-05 21:47:34 +00006289 if (magn_methy == 5)
6290 magn_methy=3;
6291 }
6292
6293 /* magnify the rows into the right side of the large image */
6294
6295 if (logging != MagickFalse)
6296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006297 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006298 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006299 yy=0;
cristy8a20fa02011-12-27 15:54:31 +00006300 length=(size_t) image->columns*GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00006301 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6302 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006303
cristy4c08aed2011-07-01 19:47:50 +00006304 if ((prev == (Quantum *) NULL) ||
6305 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006306 {
6307 image=DestroyImageList(image);
6308 MngInfoFreeStruct(mng_info,&have_mng_structure);
6309 ThrowReaderException(ResourceLimitError,
6310 "MemoryAllocationFailed");
6311 }
glennrp47b9dd52010-11-24 18:12:06 +00006312
cristy3ed852e2009-09-05 21:47:34 +00006313 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6314 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006315
cristybb503372010-05-27 20:51:26 +00006316 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006317 {
6318 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006319 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006320
cristybb503372010-05-27 20:51:26 +00006321 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6322 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006323
cristybb503372010-05-27 20:51:26 +00006324 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6325 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006326
cristybb503372010-05-27 20:51:26 +00006327 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006328 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006329
cristy3ed852e2009-09-05 21:47:34 +00006330 else
cristybb503372010-05-27 20:51:26 +00006331 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006332
cristy3ed852e2009-09-05 21:47:34 +00006333 n=prev;
6334 prev=next;
6335 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006336
cristybb503372010-05-27 20:51:26 +00006337 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006338 {
6339 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6340 exception);
6341 (void) CopyMagickMemory(next,n,length);
6342 }
glennrp47b9dd52010-11-24 18:12:06 +00006343
cristy3ed852e2009-09-05 21:47:34 +00006344 for (i=0; i < m; i++, yy++)
6345 {
cristy4c08aed2011-07-01 19:47:50 +00006346 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006347 *pixels;
6348
cristybb503372010-05-27 20:51:26 +00006349 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006350 pixels=prev;
6351 n=next;
6352 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006353 1,exception);
cristy97707062011-12-27 18:25:00 +00006354 q+=(large_image->columns-image->columns)*
6355 GetPixelChannels(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006356
cristybb503372010-05-27 20:51:26 +00006357 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006358 {
glennrpfd05d622011-02-25 04:10:33 +00006359 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006360 /*
6361 if (image->storage_class == PseudoClass)
6362 {
6363 }
6364 */
6365
6366 if (magn_methy <= 1)
6367 {
glennrpbb4f99d2011-05-22 11:13:17 +00006368 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006369 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
glennrp847370c2011-07-05 17:37:15 +00006370 SetPixelGreen(large_image,GetPixelGreen(image,
6371 pixels),q);
6372 SetPixelBlue(large_image,GetPixelBlue(image,
6373 pixels),q);
6374 SetPixelAlpha(large_image,GetPixelAlpha(image,
6375 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006376 }
glennrp47b9dd52010-11-24 18:12:06 +00006377
cristy3ed852e2009-09-05 21:47:34 +00006378 else if (magn_methy == 2 || magn_methy == 4)
6379 {
6380 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006381 {
glennrp847370c2011-07-05 17:37:15 +00006382 SetPixelRed(large_image,GetPixelRed(image,
6383 pixels),q);
6384 SetPixelGreen(large_image,GetPixelGreen(image,
6385 pixels),q);
6386 SetPixelBlue(large_image,GetPixelBlue(image,
6387 pixels),q);
6388 SetPixelAlpha(large_image,GetPixelAlpha(image,
6389 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006390 }
glennrp47b9dd52010-11-24 18:12:06 +00006391
cristy3ed852e2009-09-05 21:47:34 +00006392 else
6393 {
6394 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006395 SetPixelRed(large_image,((QM) (((ssize_t)
6396 (2*i*(GetPixelRed(image,n)
6397 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006398 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006399 +GetPixelRed(image,pixels)))),q);
6400 SetPixelGreen(large_image,((QM) (((ssize_t)
6401 (2*i*(GetPixelGreen(image,n)
6402 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006403 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006404 +GetPixelGreen(image,pixels)))),q);
6405 SetPixelBlue(large_image,((QM) (((ssize_t)
6406 (2*i*(GetPixelBlue(image,n)
6407 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006408 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006409 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006410
cristy3ed852e2009-09-05 21:47:34 +00006411 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006412 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6413 (2*i*(GetPixelAlpha(image,n)
6414 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006415 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006416 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006417 }
glennrp47b9dd52010-11-24 18:12:06 +00006418
cristy3ed852e2009-09-05 21:47:34 +00006419 if (magn_methy == 4)
6420 {
6421 /* Replicate nearest */
6422 if (i <= ((m+1) << 1))
glennrp847370c2011-07-05 17:37:15 +00006423 SetPixelAlpha(large_image,GetPixelAlpha(image,
6424 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006425 else
glennrp847370c2011-07-05 17:37:15 +00006426 SetPixelAlpha(large_image,GetPixelAlpha(image,
6427 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006428 }
6429 }
glennrp47b9dd52010-11-24 18:12:06 +00006430
cristy3ed852e2009-09-05 21:47:34 +00006431 else /* if (magn_methy == 3 || magn_methy == 5) */
6432 {
6433 /* Replicate nearest */
6434 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006435 {
glennrp847370c2011-07-05 17:37:15 +00006436 SetPixelRed(large_image,GetPixelRed(image,
6437 pixels),q);
6438 SetPixelGreen(large_image,GetPixelGreen(image,
6439 pixels),q);
6440 SetPixelBlue(large_image,GetPixelBlue(image,
6441 pixels),q);
6442 SetPixelAlpha(large_image,GetPixelAlpha(image,
6443 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006444 }
glennrp47b9dd52010-11-24 18:12:06 +00006445
cristy3ed852e2009-09-05 21:47:34 +00006446 else
glennrpbb4f99d2011-05-22 11:13:17 +00006447 {
cristy4c08aed2011-07-01 19:47:50 +00006448 SetPixelRed(large_image,GetPixelRed(image,n),q);
glennrp847370c2011-07-05 17:37:15 +00006449 SetPixelGreen(large_image,GetPixelGreen(image,n),
6450 q);
6451 SetPixelBlue(large_image,GetPixelBlue(image,n),
6452 q);
6453 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6454 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006455 }
glennrp47b9dd52010-11-24 18:12:06 +00006456
cristy3ed852e2009-09-05 21:47:34 +00006457 if (magn_methy == 5)
6458 {
cristy4c08aed2011-07-01 19:47:50 +00006459 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6460 (GetPixelAlpha(image,n)
6461 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006462 +m))/((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006463 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006464 }
6465 }
cristyed231572011-07-14 02:18:59 +00006466 n+=GetPixelChannels(image);
6467 q+=GetPixelChannels(large_image);
6468 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006469 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006470
cristy3ed852e2009-09-05 21:47:34 +00006471 if (SyncAuthenticPixels(large_image,exception) == 0)
6472 break;
glennrp47b9dd52010-11-24 18:12:06 +00006473
cristy3ed852e2009-09-05 21:47:34 +00006474 } /* i */
6475 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006476
cristy4c08aed2011-07-01 19:47:50 +00006477 prev=(Quantum *) RelinquishMagickMemory(prev);
6478 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006479
6480 length=image->columns;
6481
6482 if (logging != MagickFalse)
6483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6484 " Delete original image");
6485
6486 DeleteImageFromList(&image);
6487
6488 image=large_image;
6489
6490 mng_info->image=image;
6491
6492 /* magnify the columns */
6493 if (logging != MagickFalse)
6494 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006495 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006496
cristybb503372010-05-27 20:51:26 +00006497 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006498 {
cristy4c08aed2011-07-01 19:47:50 +00006499 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006500 *pixels;
6501
6502 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristyed231572011-07-14 02:18:59 +00006503 pixels=q+(image->columns-length)*GetPixelChannels(image);
6504 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006505
cristybb503372010-05-27 20:51:26 +00006506 for (x=(ssize_t) (image->columns-length);
6507 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006508 {
cristyed231572011-07-14 02:18:59 +00006509 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006510
cristybb503372010-05-27 20:51:26 +00006511 if (x == (ssize_t) (image->columns-length))
6512 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006513
cristybb503372010-05-27 20:51:26 +00006514 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6515 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006516
cristybb503372010-05-27 20:51:26 +00006517 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6518 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006519
cristybb503372010-05-27 20:51:26 +00006520 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006521 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006522
cristy3ed852e2009-09-05 21:47:34 +00006523 else
cristybb503372010-05-27 20:51:26 +00006524 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006525
cristy3ed852e2009-09-05 21:47:34 +00006526 for (i=0; i < m; i++)
6527 {
6528 if (magn_methx <= 1)
6529 {
6530 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006531 SetPixelRed(image,GetPixelRed(image,pixels),q);
6532 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6533 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6534 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006535 }
glennrp47b9dd52010-11-24 18:12:06 +00006536
cristy3ed852e2009-09-05 21:47:34 +00006537 else if (magn_methx == 2 || magn_methx == 4)
6538 {
6539 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006540 {
cristy4c08aed2011-07-01 19:47:50 +00006541 SetPixelRed(image,GetPixelRed(image,pixels),q);
6542 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6543 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6544 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006545 }
glennrp47b9dd52010-11-24 18:12:06 +00006546
cristyed231572011-07-14 02:18:59 +00006547 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006548 else
6549 {
6550 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006551 SetPixelRed(image,(QM) ((2*i*(
6552 GetPixelRed(image,n)
6553 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006554 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006555 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006556
cristy4c08aed2011-07-01 19:47:50 +00006557 SetPixelGreen(image,(QM) ((2*i*(
6558 GetPixelGreen(image,n)
6559 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006560 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006561 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006562
cristy4c08aed2011-07-01 19:47:50 +00006563 SetPixelBlue(image,(QM) ((2*i*(
6564 GetPixelBlue(image,n)
6565 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006566 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006567 GetPixelBlue(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006568 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006569 SetPixelAlpha(image,(QM) ((2*i*(
6570 GetPixelAlpha(image,n)
6571 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006572 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006573 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006574 }
glennrp47b9dd52010-11-24 18:12:06 +00006575
cristy3ed852e2009-09-05 21:47:34 +00006576 if (magn_methx == 4)
6577 {
6578 /* Replicate nearest */
6579 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006580 {
cristy4c08aed2011-07-01 19:47:50 +00006581 SetPixelAlpha(image,
6582 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006583 }
cristy3ed852e2009-09-05 21:47:34 +00006584 else
glennrpbb4f99d2011-05-22 11:13:17 +00006585 {
cristy4c08aed2011-07-01 19:47:50 +00006586 SetPixelAlpha(image,
6587 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006588 }
cristy3ed852e2009-09-05 21:47:34 +00006589 }
6590 }
glennrp47b9dd52010-11-24 18:12:06 +00006591
cristy3ed852e2009-09-05 21:47:34 +00006592 else /* if (magn_methx == 3 || magn_methx == 5) */
6593 {
6594 /* Replicate nearest */
6595 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006596 {
cristy4c08aed2011-07-01 19:47:50 +00006597 SetPixelRed(image,GetPixelRed(image,pixels),q);
6598 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6599 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6600 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006601 }
glennrp47b9dd52010-11-24 18:12:06 +00006602
cristy3ed852e2009-09-05 21:47:34 +00006603 else
glennrpbb4f99d2011-05-22 11:13:17 +00006604 {
cristy4c08aed2011-07-01 19:47:50 +00006605 SetPixelRed(image,GetPixelRed(image,n),q);
6606 SetPixelGreen(image,GetPixelGreen(image,n),q);
6607 SetPixelBlue(image,GetPixelBlue(image,n),q);
6608 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006609 }
glennrp47b9dd52010-11-24 18:12:06 +00006610
cristy3ed852e2009-09-05 21:47:34 +00006611 if (magn_methx == 5)
6612 {
6613 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006614 SetPixelAlpha(image,
6615 (QM) ((2*i*( GetPixelAlpha(image,n)
6616 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006617 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006618 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006619 }
6620 }
cristyed231572011-07-14 02:18:59 +00006621 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006622 }
cristyed231572011-07-14 02:18:59 +00006623 n+=GetPixelChannels(image);
6624 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006625 }
glennrp47b9dd52010-11-24 18:12:06 +00006626
cristy3ed852e2009-09-05 21:47:34 +00006627 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6628 break;
6629 }
glennrp3faa9a32011-04-23 14:00:25 +00006630#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006631 if (magn_methx != 1 || magn_methy != 1)
6632 {
6633 /*
6634 Rescale pixels to Quantum
6635 */
cristybb503372010-05-27 20:51:26 +00006636 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006637 {
6638 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006639
cristybb503372010-05-27 20:51:26 +00006640 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006641 {
cristy4c08aed2011-07-01 19:47:50 +00006642 SetPixelRed(image,ScaleShortToQuantum(
6643 GetPixelRed(image,q)),q);
6644 SetPixelGreen(image,ScaleShortToQuantum(
6645 GetPixelGreen(image,q)),q);
6646 SetPixelBlue(image,ScaleShortToQuantum(
6647 GetPixelBlue(image,q)),q);
6648 SetPixelAlpha(image,ScaleShortToQuantum(
6649 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006650 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006651 }
glennrp47b9dd52010-11-24 18:12:06 +00006652
cristy3ed852e2009-09-05 21:47:34 +00006653 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6654 break;
6655 }
6656 }
6657#endif
6658 if (logging != MagickFalse)
6659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6660 " Finished MAGN processing");
6661 }
6662 }
6663
6664 /*
6665 Crop_box is with respect to the upper left corner of the MNG.
6666 */
6667 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6668 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6669 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6670 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6671 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6672 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6673 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6674 if ((crop_box.left != (mng_info->image_box.left
6675 +mng_info->x_off[object_id])) ||
6676 (crop_box.right != (mng_info->image_box.right
6677 +mng_info->x_off[object_id])) ||
6678 (crop_box.top != (mng_info->image_box.top
6679 +mng_info->y_off[object_id])) ||
6680 (crop_box.bottom != (mng_info->image_box.bottom
6681 +mng_info->y_off[object_id])))
6682 {
6683 if (logging != MagickFalse)
6684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6685 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006686
cristy3ed852e2009-09-05 21:47:34 +00006687 if ((crop_box.left < crop_box.right) &&
6688 (crop_box.top < crop_box.bottom))
6689 {
6690 Image
6691 *im;
6692
6693 RectangleInfo
6694 crop_info;
6695
6696 /*
6697 Crop_info is with respect to the upper left corner of
6698 the image.
6699 */
6700 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6701 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006702 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6703 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006704 image->page.width=image->columns;
6705 image->page.height=image->rows;
6706 image->page.x=0;
6707 image->page.y=0;
6708 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006709
cristy3ed852e2009-09-05 21:47:34 +00006710 if (im != (Image *) NULL)
6711 {
6712 image->columns=im->columns;
6713 image->rows=im->rows;
6714 im=DestroyImage(im);
6715 image->page.width=image->columns;
6716 image->page.height=image->rows;
6717 image->page.x=crop_box.left;
6718 image->page.y=crop_box.top;
6719 }
6720 }
glennrp47b9dd52010-11-24 18:12:06 +00006721
cristy3ed852e2009-09-05 21:47:34 +00006722 else
6723 {
6724 /*
6725 No pixels in crop area. The MNG spec still requires
6726 a layer, though, so make a single transparent pixel in
6727 the top left corner.
6728 */
6729 image->columns=1;
6730 image->rows=1;
6731 image->colors=2;
cristyea1a8aa2011-10-20 13:24:06 +00006732 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006733 image->page.width=1;
6734 image->page.height=1;
6735 image->page.x=0;
6736 image->page.y=0;
6737 }
6738 }
6739#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6740 image=mng_info->image;
6741#endif
6742 }
6743
glennrp2b013e42010-11-24 16:55:50 +00006744#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6745 /* PNG does not handle depths greater than 16 so reduce it even
6746 * if lossy
6747 */
6748 if (image->depth > 16)
6749 image->depth=16;
6750#endif
6751
glennrp3faa9a32011-04-23 14:00:25 +00006752#if (MAGICKCORE_QUANTUM_DEPTH > 8)
cristyc82a27b2011-10-21 01:07:16 +00006753 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00006754 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006755#endif
glennrpd6afd542010-11-19 01:53:05 +00006756
cristy3ed852e2009-09-05 21:47:34 +00006757 if (image_info->number_scenes != 0)
6758 {
6759 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006760 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006761 break;
6762 }
glennrpd6afd542010-11-19 01:53:05 +00006763
cristy3ed852e2009-09-05 21:47:34 +00006764 if (logging != MagickFalse)
6765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6766 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006767
cristy3ed852e2009-09-05 21:47:34 +00006768 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006769
cristy3ed852e2009-09-05 21:47:34 +00006770 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006771
cristy3ed852e2009-09-05 21:47:34 +00006772 if (logging != MagickFalse)
6773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6774 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006775
cristy3ed852e2009-09-05 21:47:34 +00006776#if defined(MNG_INSERT_LAYERS)
6777 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6778 (mng_info->mng_height))
6779 {
6780 /*
6781 Insert a background layer if nothing else was found.
6782 */
6783 if (logging != MagickFalse)
6784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6785 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006786
cristy4c08aed2011-07-01 19:47:50 +00006787 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006788 {
6789 /*
6790 Allocate next image structure.
6791 */
cristy9950d572011-10-01 18:22:35 +00006792 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006793 if (GetNextImageInList(image) == (Image *) NULL)
6794 {
6795 image=DestroyImageList(image);
6796 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006797
cristy3ed852e2009-09-05 21:47:34 +00006798 if (logging != MagickFalse)
6799 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6800 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006801
cristy3ed852e2009-09-05 21:47:34 +00006802 return((Image *) NULL);
6803 }
6804 image=SyncNextImageInList(image);
6805 }
6806 image->columns=mng_info->mng_width;
6807 image->rows=mng_info->mng_height;
6808 image->page.width=mng_info->mng_width;
6809 image->page.height=mng_info->mng_height;
6810 image->page.x=0;
6811 image->page.y=0;
6812 image->background_color=mng_background_color;
6813 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006814
cristy3ed852e2009-09-05 21:47:34 +00006815 if (image_info->ping == MagickFalse)
cristyea1a8aa2011-10-20 13:24:06 +00006816 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006817
cristy3ed852e2009-09-05 21:47:34 +00006818 mng_info->image_found++;
6819 }
6820#endif
6821 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006822
cristy3ed852e2009-09-05 21:47:34 +00006823 if (mng_iterations == 1)
6824 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006825
cristy3ed852e2009-09-05 21:47:34 +00006826 while (GetPreviousImageInList(image) != (Image *) NULL)
6827 {
6828 image_count++;
6829 if (image_count > 10*mng_info->image_found)
6830 {
6831 if (logging != MagickFalse)
6832 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006833
cristyc82a27b2011-10-21 01:07:16 +00006834 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006835 CoderError,"Linked list is corrupted, beginning of list not found",
6836 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006837
cristy3ed852e2009-09-05 21:47:34 +00006838 return((Image *) NULL);
6839 }
glennrp0fe50b42010-11-16 03:52:51 +00006840
cristy3ed852e2009-09-05 21:47:34 +00006841 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006842
cristy3ed852e2009-09-05 21:47:34 +00006843 if (GetNextImageInList(image) == (Image *) NULL)
6844 {
6845 if (logging != MagickFalse)
6846 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006847
cristyc82a27b2011-10-21 01:07:16 +00006848 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006849 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6850 image_info->filename);
6851 }
6852 }
glennrp47b9dd52010-11-24 18:12:06 +00006853
cristy3ed852e2009-09-05 21:47:34 +00006854 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6855 GetNextImageInList(image) ==
6856 (Image *) NULL)
6857 {
6858 if (logging != MagickFalse)
6859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6860 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006861
cristyc82a27b2011-10-21 01:07:16 +00006862 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006863 CoderError,"image->next for first image is NULL but shouldn't be.",
6864 "`%s'",image_info->filename);
6865 }
glennrp47b9dd52010-11-24 18:12:06 +00006866
cristy3ed852e2009-09-05 21:47:34 +00006867 if (mng_info->image_found == 0)
6868 {
6869 if (logging != MagickFalse)
6870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6871 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006872
cristyc82a27b2011-10-21 01:07:16 +00006873 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006874 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006875
cristy3ed852e2009-09-05 21:47:34 +00006876 if (image != (Image *) NULL)
6877 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006878
cristy3ed852e2009-09-05 21:47:34 +00006879 MngInfoFreeStruct(mng_info,&have_mng_structure);
6880 return((Image *) NULL);
6881 }
6882
6883 if (mng_info->ticks_per_second)
6884 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6885 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006886
cristy3ed852e2009-09-05 21:47:34 +00006887 else
6888 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006889
cristy3ed852e2009-09-05 21:47:34 +00006890 /* Find final nonzero image delay */
6891 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006892
cristy3ed852e2009-09-05 21:47:34 +00006893 while (GetNextImageInList(image) != (Image *) NULL)
6894 {
6895 if (image->delay)
6896 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006897
cristy3ed852e2009-09-05 21:47:34 +00006898 image=GetNextImageInList(image);
6899 }
glennrp0fe50b42010-11-16 03:52:51 +00006900
cristy3ed852e2009-09-05 21:47:34 +00006901 if (final_delay < final_image_delay)
6902 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006903
cristy3ed852e2009-09-05 21:47:34 +00006904 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006905
cristy3ed852e2009-09-05 21:47:34 +00006906 if (logging != MagickFalse)
6907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006908 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6909 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006910
cristy3ed852e2009-09-05 21:47:34 +00006911 if (logging != MagickFalse)
6912 {
6913 int
6914 scene;
6915
6916 scene=0;
6917 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006918
cristy3ed852e2009-09-05 21:47:34 +00006919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6920 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006921
cristy3ed852e2009-09-05 21:47:34 +00006922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006923 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006924
cristy3ed852e2009-09-05 21:47:34 +00006925 while (GetNextImageInList(image) != (Image *) NULL)
6926 {
6927 image=GetNextImageInList(image);
6928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006929 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006930 }
6931 }
6932
6933 image=GetFirstImageInList(image);
6934#ifdef MNG_COALESCE_LAYERS
6935 if (insert_layers)
6936 {
6937 Image
6938 *next_image,
6939 *next;
6940
cristybb503372010-05-27 20:51:26 +00006941 size_t
cristy3ed852e2009-09-05 21:47:34 +00006942 scene;
6943
6944 if (logging != MagickFalse)
6945 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006946
cristy3ed852e2009-09-05 21:47:34 +00006947 scene=image->scene;
cristyc82a27b2011-10-21 01:07:16 +00006948 next_image=CoalesceImages(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006949
cristy3ed852e2009-09-05 21:47:34 +00006950 if (next_image == (Image *) NULL)
6951 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006952
cristy3ed852e2009-09-05 21:47:34 +00006953 image=DestroyImageList(image);
6954 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006955
cristy3ed852e2009-09-05 21:47:34 +00006956 for (next=image; next != (Image *) NULL; next=next_image)
6957 {
6958 next->page.width=mng_info->mng_width;
6959 next->page.height=mng_info->mng_height;
6960 next->page.x=0;
6961 next->page.y=0;
6962 next->scene=scene++;
6963 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006964
cristy3ed852e2009-09-05 21:47:34 +00006965 if (next_image == (Image *) NULL)
6966 break;
glennrp47b9dd52010-11-24 18:12:06 +00006967
cristy3ed852e2009-09-05 21:47:34 +00006968 if (next->delay == 0)
6969 {
6970 scene--;
6971 next_image->previous=GetPreviousImageInList(next);
6972 if (GetPreviousImageInList(next) == (Image *) NULL)
6973 image=next_image;
6974 else
6975 next->previous->next=next_image;
6976 next=DestroyImage(next);
6977 }
6978 }
6979 }
6980#endif
6981
6982 while (GetNextImageInList(image) != (Image *) NULL)
6983 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006984
cristy3ed852e2009-09-05 21:47:34 +00006985 image->dispose=BackgroundDispose;
6986
6987 if (logging != MagickFalse)
6988 {
6989 int
6990 scene;
6991
6992 scene=0;
6993 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006994
cristy3ed852e2009-09-05 21:47:34 +00006995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6996 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006997
cristy3ed852e2009-09-05 21:47:34 +00006998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006999 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7000 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00007001
cristy3ed852e2009-09-05 21:47:34 +00007002 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00007003 {
7004 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007005
cristyf2faecf2010-05-28 19:19:36 +00007006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007007 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7008 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00007009 }
7010 }
glennrp47b9dd52010-11-24 18:12:06 +00007011
cristy3ed852e2009-09-05 21:47:34 +00007012 image=GetFirstImageInList(image);
7013 MngInfoFreeStruct(mng_info,&have_mng_structure);
7014 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00007015
cristy3ed852e2009-09-05 21:47:34 +00007016 if (logging != MagickFalse)
7017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00007018
cristy3ed852e2009-09-05 21:47:34 +00007019 return(GetFirstImageInList(image));
7020}
glennrp25c1e2b2010-03-25 01:39:56 +00007021#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007022static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7023{
7024 printf("Your PNG library is too old: You have libpng-%s\n",
7025 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00007026
cristy3ed852e2009-09-05 21:47:34 +00007027 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7028 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007029
cristy3ed852e2009-09-05 21:47:34 +00007030 return(Image *) NULL;
7031}
glennrp47b9dd52010-11-24 18:12:06 +00007032
cristy3ed852e2009-09-05 21:47:34 +00007033static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7034{
7035 return(ReadPNGImage(image_info,exception));
7036}
glennrp25c1e2b2010-03-25 01:39:56 +00007037#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007038#endif
7039
7040/*
7041%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7042% %
7043% %
7044% %
7045% R e g i s t e r P N G I m a g e %
7046% %
7047% %
7048% %
7049%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7050%
7051% RegisterPNGImage() adds properties for the PNG image format to
7052% the list of supported formats. The properties include the image format
7053% tag, a method to read and/or write the format, whether the format
7054% supports the saving of more than one frame to the same file or blob,
7055% whether the format supports native in-memory I/O, and a brief
7056% description of the format.
7057%
7058% The format of the RegisterPNGImage method is:
7059%
cristybb503372010-05-27 20:51:26 +00007060% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007061%
7062*/
cristybb503372010-05-27 20:51:26 +00007063ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007064{
7065 char
7066 version[MaxTextExtent];
7067
7068 MagickInfo
7069 *entry;
7070
7071 static const char
7072 *PNGNote=
7073 {
7074 "See http://www.libpng.org/ for details about the PNG format."
7075 },
glennrp47b9dd52010-11-24 18:12:06 +00007076
cristy3ed852e2009-09-05 21:47:34 +00007077 *JNGNote=
7078 {
7079 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7080 "format."
7081 },
glennrp47b9dd52010-11-24 18:12:06 +00007082
cristy3ed852e2009-09-05 21:47:34 +00007083 *MNGNote=
7084 {
7085 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7086 "format."
7087 };
7088
7089 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007090
cristy3ed852e2009-09-05 21:47:34 +00007091#if defined(PNG_LIBPNG_VER_STRING)
7092 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7093 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007094
cristy3ed852e2009-09-05 21:47:34 +00007095 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7096 {
7097 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7098 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7099 MaxTextExtent);
7100 }
7101#endif
glennrp47b9dd52010-11-24 18:12:06 +00007102
cristy3ed852e2009-09-05 21:47:34 +00007103 entry=SetMagickInfo("MNG");
7104 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007105
cristy3ed852e2009-09-05 21:47:34 +00007106#if defined(MAGICKCORE_PNG_DELEGATE)
7107 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7108 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7109#endif
glennrp47b9dd52010-11-24 18:12:06 +00007110
cristy3ed852e2009-09-05 21:47:34 +00007111 entry->magick=(IsImageFormatHandler *) IsMNG;
7112 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007113
cristy3ed852e2009-09-05 21:47:34 +00007114 if (*version != '\0')
7115 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007116
cristy3ed852e2009-09-05 21:47:34 +00007117 entry->module=ConstantString("PNG");
7118 entry->note=ConstantString(MNGNote);
7119 (void) RegisterMagickInfo(entry);
7120
7121 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007122
cristy3ed852e2009-09-05 21:47:34 +00007123#if defined(MAGICKCORE_PNG_DELEGATE)
7124 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7125 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7126#endif
glennrp47b9dd52010-11-24 18:12:06 +00007127
cristy3ed852e2009-09-05 21:47:34 +00007128 entry->magick=(IsImageFormatHandler *) IsPNG;
7129 entry->adjoin=MagickFalse;
7130 entry->description=ConstantString("Portable Network Graphics");
7131 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007132
cristy3ed852e2009-09-05 21:47:34 +00007133 if (*version != '\0')
7134 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007135
cristy3ed852e2009-09-05 21:47:34 +00007136 entry->note=ConstantString(PNGNote);
7137 (void) RegisterMagickInfo(entry);
7138
7139 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007140
cristy3ed852e2009-09-05 21:47:34 +00007141#if defined(MAGICKCORE_PNG_DELEGATE)
7142 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7143 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7144#endif
glennrp47b9dd52010-11-24 18:12:06 +00007145
cristy3ed852e2009-09-05 21:47:34 +00007146 entry->magick=(IsImageFormatHandler *) IsPNG;
7147 entry->adjoin=MagickFalse;
7148 entry->description=ConstantString(
7149 "8-bit indexed with optional binary transparency");
7150 entry->module=ConstantString("PNG");
7151 (void) RegisterMagickInfo(entry);
7152
7153 entry=SetMagickInfo("PNG24");
7154 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007155
cristy3ed852e2009-09-05 21:47:34 +00007156#if defined(ZLIB_VERSION)
7157 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7158 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007159
cristy3ed852e2009-09-05 21:47:34 +00007160 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7161 {
7162 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7163 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7164 }
7165#endif
glennrp47b9dd52010-11-24 18:12:06 +00007166
cristy3ed852e2009-09-05 21:47:34 +00007167 if (*version != '\0')
7168 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007169
cristy3ed852e2009-09-05 21:47:34 +00007170#if defined(MAGICKCORE_PNG_DELEGATE)
7171 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7172 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7173#endif
glennrp47b9dd52010-11-24 18:12:06 +00007174
cristy3ed852e2009-09-05 21:47:34 +00007175 entry->magick=(IsImageFormatHandler *) IsPNG;
7176 entry->adjoin=MagickFalse;
7177 entry->description=ConstantString("opaque 24-bit RGB");
7178 entry->module=ConstantString("PNG");
7179 (void) RegisterMagickInfo(entry);
7180
7181 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007182
cristy3ed852e2009-09-05 21:47:34 +00007183#if defined(MAGICKCORE_PNG_DELEGATE)
7184 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7185 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7186#endif
glennrp47b9dd52010-11-24 18:12:06 +00007187
cristy3ed852e2009-09-05 21:47:34 +00007188 entry->magick=(IsImageFormatHandler *) IsPNG;
7189 entry->adjoin=MagickFalse;
7190 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7191 entry->module=ConstantString("PNG");
7192 (void) RegisterMagickInfo(entry);
7193
7194 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007195
cristy3ed852e2009-09-05 21:47:34 +00007196#if defined(JNG_SUPPORTED)
7197#if defined(MAGICKCORE_PNG_DELEGATE)
7198 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7199 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7200#endif
7201#endif
glennrp47b9dd52010-11-24 18:12:06 +00007202
cristy3ed852e2009-09-05 21:47:34 +00007203 entry->magick=(IsImageFormatHandler *) IsJNG;
7204 entry->adjoin=MagickFalse;
7205 entry->description=ConstantString("JPEG Network Graphics");
7206 entry->module=ConstantString("PNG");
7207 entry->note=ConstantString(JNGNote);
7208 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007209
cristy18b17442009-10-25 18:36:48 +00007210#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007211 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007212#endif
glennrp47b9dd52010-11-24 18:12:06 +00007213
cristy3ed852e2009-09-05 21:47:34 +00007214 return(MagickImageCoderSignature);
7215}
7216
7217/*
7218%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7219% %
7220% %
7221% %
7222% U n r e g i s t e r P N G I m a g e %
7223% %
7224% %
7225% %
7226%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7227%
7228% UnregisterPNGImage() removes format registrations made by the
7229% PNG module from the list of supported formats.
7230%
7231% The format of the UnregisterPNGImage method is:
7232%
7233% UnregisterPNGImage(void)
7234%
7235*/
7236ModuleExport void UnregisterPNGImage(void)
7237{
7238 (void) UnregisterMagickInfo("MNG");
7239 (void) UnregisterMagickInfo("PNG");
7240 (void) UnregisterMagickInfo("PNG8");
7241 (void) UnregisterMagickInfo("PNG24");
7242 (void) UnregisterMagickInfo("PNG32");
7243 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007244
cristy3ed852e2009-09-05 21:47:34 +00007245#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007246 if (ping_semaphore != (SemaphoreInfo *) NULL)
7247 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007248#endif
7249}
7250
7251#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007252#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007253/*
7254%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7255% %
7256% %
7257% %
7258% W r i t e M N G I m a g e %
7259% %
7260% %
7261% %
7262%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7263%
7264% WriteMNGImage() writes an image in the Portable Network Graphics
7265% Group's "Multiple-image Network Graphics" encoded image format.
7266%
7267% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7268%
7269% The format of the WriteMNGImage method is:
7270%
cristy1e178e72011-08-28 19:44:34 +00007271% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7272% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007273%
7274% A description of each parameter follows.
7275%
7276% o image_info: the image info.
7277%
7278% o image: The image.
7279%
cristy1e178e72011-08-28 19:44:34 +00007280% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007281%
7282% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7283% "To do" under ReadPNGImage):
7284%
cristy3ed852e2009-09-05 21:47:34 +00007285% Preserve all unknown and not-yet-handled known chunks found in input
7286% PNG file and copy them into output PNG files according to the PNG
7287% copying rules.
7288%
7289% Write the iCCP chunk at MNG level when (icc profile length > 0)
7290%
7291% Improve selection of color type (use indexed-colour or indexed-colour
7292% with tRNS when 256 or fewer unique RGBA values are present).
7293%
7294% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7295% This will be complicated if we limit ourselves to generating MNG-LC
7296% files. For now we ignore disposal method 3 and simply overlay the next
7297% image on it.
7298%
7299% Check for identical PLTE's or PLTE/tRNS combinations and use a
7300% global MNG PLTE or PLTE/tRNS combination when appropriate.
7301% [mostly done 15 June 1999 but still need to take care of tRNS]
7302%
7303% Check for identical sRGB and replace with a global sRGB (and remove
7304% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7305% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7306% local gAMA/cHRM with local sRGB if appropriate).
7307%
7308% Check for identical sBIT chunks and write global ones.
7309%
7310% Provide option to skip writing the signature tEXt chunks.
7311%
7312% Use signatures to detect identical objects and reuse the first
7313% instance of such objects instead of writing duplicate objects.
7314%
7315% Use a smaller-than-32k value of compression window size when
7316% appropriate.
7317%
7318% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7319% ancillary text chunks and save profiles.
7320%
7321% Provide an option to force LC files (to ensure exact framing rate)
7322% instead of VLC.
7323%
7324% Provide an option to force VLC files instead of LC, even when offsets
7325% are present. This will involve expanding the embedded images with a
7326% transparent region at the top and/or left.
7327*/
7328
cristy3ed852e2009-09-05 21:47:34 +00007329static void
glennrpcf002022011-01-30 02:38:15 +00007330Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007331 png_info *ping_info, unsigned char *profile_type, unsigned char
7332 *profile_description, unsigned char *profile_data, png_uint_32 length)
7333{
cristy3ed852e2009-09-05 21:47:34 +00007334 png_textp
7335 text;
7336
cristybb503372010-05-27 20:51:26 +00007337 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007338 i;
7339
7340 unsigned char
7341 *sp;
7342
7343 png_charp
7344 dp;
7345
7346 png_uint_32
7347 allocated_length,
7348 description_length;
7349
7350 unsigned char
7351 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007352
cristy3ed852e2009-09-05 21:47:34 +00007353 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7354 return;
7355
7356 if (image_info->verbose)
7357 {
glennrp0fe50b42010-11-16 03:52:51 +00007358 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7359 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007360 }
glennrp0fe50b42010-11-16 03:52:51 +00007361
cristy3ed852e2009-09-05 21:47:34 +00007362 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7363 description_length=(png_uint_32) strlen((const char *) profile_description);
7364 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7365 + description_length);
7366 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7367 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7368 text[0].key[0]='\0';
7369 (void) ConcatenateMagickString(text[0].key,
7370 "Raw profile type ",MaxTextExtent);
7371 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7372 sp=profile_data;
7373 dp=text[0].text;
7374 *dp++='\n';
7375 (void) CopyMagickString(dp,(const char *) profile_description,
7376 allocated_length);
7377 dp+=description_length;
7378 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007379 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007380 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007381 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007382
cristybb503372010-05-27 20:51:26 +00007383 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007384 {
7385 if (i%36 == 0)
7386 *dp++='\n';
7387 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7388 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7389 }
glennrp47b9dd52010-11-24 18:12:06 +00007390
cristy3ed852e2009-09-05 21:47:34 +00007391 *dp++='\n';
7392 *dp='\0';
7393 text[0].text_length=(png_size_t) (dp-text[0].text);
7394 text[0].compression=image_info->compression == NoCompression ||
7395 (image_info->compression == UndefinedCompression &&
7396 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007397
cristy3ed852e2009-09-05 21:47:34 +00007398 if (text[0].text_length <= allocated_length)
7399 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007400
cristy3ed852e2009-09-05 21:47:34 +00007401 png_free(ping,text[0].text);
7402 png_free(ping,text[0].key);
7403 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007404}
7405
glennrpcf002022011-01-30 02:38:15 +00007406static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007407 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007408{
7409 char
7410 *name;
7411
7412 const StringInfo
7413 *profile;
7414
7415 unsigned char
7416 *data;
7417
7418 png_uint_32 length;
7419
7420 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007421
7422 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7423 {
cristy3ed852e2009-09-05 21:47:34 +00007424 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007425
cristy3ed852e2009-09-05 21:47:34 +00007426 if (profile != (const StringInfo *) NULL)
7427 {
7428 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007429 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007430
glennrp47b9dd52010-11-24 18:12:06 +00007431 if (LocaleNCompare(name,string,11) == 0)
7432 {
7433 if (logging != MagickFalse)
7434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7435 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007436
glennrpcf002022011-01-30 02:38:15 +00007437 ping_profile=CloneStringInfo(profile);
7438 data=GetStringInfoDatum(ping_profile),
7439 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007440 data[4]=data[3];
7441 data[3]=data[2];
7442 data[2]=data[1];
7443 data[1]=data[0];
7444 (void) WriteBlobMSBULong(image,length-5); /* data length */
7445 (void) WriteBlob(image,length-1,data+1);
7446 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007447 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007448 }
cristy3ed852e2009-09-05 21:47:34 +00007449 }
glennrp47b9dd52010-11-24 18:12:06 +00007450
cristy3ed852e2009-09-05 21:47:34 +00007451 name=GetNextImageProfile(image);
7452 }
glennrp47b9dd52010-11-24 18:12:06 +00007453
cristy3ed852e2009-09-05 21:47:34 +00007454 return(MagickTrue);
7455}
7456
glennrpb9cfe272010-12-21 15:08:06 +00007457
cristy3ed852e2009-09-05 21:47:34 +00007458/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007459static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +00007460 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007461{
7462 Image
7463 *image;
7464
7465 ImageInfo
7466 *image_info;
7467
cristy3ed852e2009-09-05 21:47:34 +00007468 char
7469 s[2];
7470
7471 const char
7472 *name,
7473 *property,
7474 *value;
7475
7476 const StringInfo
7477 *profile;
7478
cristy3ed852e2009-09-05 21:47:34 +00007479 int
cristy3ed852e2009-09-05 21:47:34 +00007480 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007481 pass;
7482
glennrpe9c26dc2010-05-30 01:56:35 +00007483 png_byte
7484 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007485
glennrp39992b42010-11-14 00:03:43 +00007486 png_color
7487 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007488
glennrp5af765f2010-03-30 11:12:18 +00007489 png_color_16
7490 ping_background,
7491 ping_trans_color;
7492
cristy3ed852e2009-09-05 21:47:34 +00007493 png_info
7494 *ping_info;
7495
7496 png_struct
7497 *ping;
7498
glennrp5af765f2010-03-30 11:12:18 +00007499 png_uint_32
7500 ping_height,
7501 ping_width;
7502
cristybb503372010-05-27 20:51:26 +00007503 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007504 y;
7505
7506 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007507 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007508 logging,
glennrp58e01762011-01-07 15:28:54 +00007509 matte,
7510
glennrpda8f3a72011-02-27 23:54:12 +00007511 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007512 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007513 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007514 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007515 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007516 ping_have_bKGD,
7517 ping_have_pHYs,
7518 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007519
7520 ping_exclude_bKGD,
7521 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007522 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007523 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007524 ping_exclude_gAMA,
7525 ping_exclude_iCCP,
7526 /* ping_exclude_iTXt, */
7527 ping_exclude_oFFs,
7528 ping_exclude_pHYs,
7529 ping_exclude_sRGB,
7530 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007531 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007532 ping_exclude_vpAg,
7533 ping_exclude_zCCP, /* hex-encoded iCCP */
7534 ping_exclude_zTXt,
7535
glennrp8d3d6e52011-04-19 04:39:51 +00007536 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007537 ping_need_colortype_warning,
7538
glennrp82b3c532011-03-22 19:20:54 +00007539 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007540 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007541 tried_333,
7542 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007543
7544 QuantumInfo
7545 *quantum_info;
7546
cristyc82a27b2011-10-21 01:07:16 +00007547 PNGErrorInfo
7548 error_info;
7549
cristybb503372010-05-27 20:51:26 +00007550 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007551 i,
7552 x;
7553
7554 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007555 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007556
glennrp5af765f2010-03-30 11:12:18 +00007557 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007558 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007559 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007560 ping_color_type,
7561 ping_interlace_method,
7562 ping_compression_method,
7563 ping_filter_method,
7564 ping_num_trans;
7565
cristybb503372010-05-27 20:51:26 +00007566 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007567 image_depth,
7568 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007569
cristybb503372010-05-27 20:51:26 +00007570 size_t
cristy3ed852e2009-09-05 21:47:34 +00007571 quality,
7572 rowbytes,
7573 save_image_depth;
7574
glennrpdfd70802010-11-14 01:23:35 +00007575 int
glennrpfd05d622011-02-25 04:10:33 +00007576 j,
glennrpf09bded2011-01-08 01:15:59 +00007577 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007578 number_opaque,
7579 number_semitransparent,
7580 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007581 ping_pHYs_unit_type;
7582
7583 png_uint_32
7584 ping_pHYs_x_resolution,
7585 ping_pHYs_y_resolution;
7586
cristy3ed852e2009-09-05 21:47:34 +00007587 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007588 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007589
cristyc82a27b2011-10-21 01:07:16 +00007590 image = CloneImage(IMimage,0,0,MagickFalse,exception);
glennrpb9cfe272010-12-21 15:08:06 +00007591 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007592 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007593 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007594
cristy3ed852e2009-09-05 21:47:34 +00007595#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007596 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007597#endif
7598
glennrp5af765f2010-03-30 11:12:18 +00007599 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007600 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007601 ping_color_type=0,
7602 ping_interlace_method=0,
7603 ping_compression_method=0,
7604 ping_filter_method=0,
7605 ping_num_trans = 0;
7606
7607 ping_background.red = 0;
7608 ping_background.green = 0;
7609 ping_background.blue = 0;
7610 ping_background.gray = 0;
7611 ping_background.index = 0;
7612
7613 ping_trans_color.red=0;
7614 ping_trans_color.green=0;
7615 ping_trans_color.blue=0;
7616 ping_trans_color.gray=0;
7617
glennrpdfd70802010-11-14 01:23:35 +00007618 ping_pHYs_unit_type = 0;
7619 ping_pHYs_x_resolution = 0;
7620 ping_pHYs_y_resolution = 0;
7621
glennrpda8f3a72011-02-27 23:54:12 +00007622 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007623 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007624 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007625 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007626 ping_have_bKGD=MagickFalse;
7627 ping_have_pHYs=MagickFalse;
7628 ping_have_tRNS=MagickFalse;
7629
glennrp0e8ea192010-12-24 18:00:33 +00007630 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7631 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007632 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007633 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007634 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007635 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7636 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7637 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7638 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7639 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7640 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007641 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007642 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7643 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7644 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7645
glennrp8d3d6e52011-04-19 04:39:51 +00007646 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007647 ping_need_colortype_warning = MagickFalse;
7648
cristy0d57eec2011-09-04 22:13:56 +00007649 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7650 * i.e., eliminate the ICC profile and set image->rendering_intent.
7651 * Note that this will not involve any changes to the actual pixels
7652 * but merely passes information to applications that read the resulting
7653 * PNG image.
7654 */
7655 if (ping_exclude_sRGB == MagickFalse)
7656 {
7657 char
7658 *name;
7659
7660 const StringInfo
7661 *profile;
7662
7663 ResetImageProfileIterator(image);
7664 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7665 {
7666 profile=GetImageProfile(image,name);
7667
7668 if (profile != (StringInfo *) NULL)
7669 {
7670 if ((LocaleCompare(name,"ICC") == 0) ||
glennrp29a106e2011-09-06 17:11:42 +00007671 (LocaleCompare(name,"ICM") == 0))
7672 {
glennrpee7b4c02011-10-04 01:21:09 +00007673 int
7674 icheck;
7675
7676 /* 0: not a known sRGB profile
7677 * 1: HP-Microsoft sRGB v2
7678 * 2: ICC sRGB v4 perceptual
7679 * 3: ICC sRGB v2 perceptual no black-compensation
7680 */
7681 png_uint_32
7682 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7683 check_len[4] = {0, 3144, 60960, 3052};
7684
7685 png_uint_32
7686 length,
7687 profile_crc;
7688
cristy0d57eec2011-09-04 22:13:56 +00007689 unsigned char
7690 *data;
7691
glennrp29a106e2011-09-06 17:11:42 +00007692 length=(png_uint_32) GetStringInfoLength(profile);
7693
glennrpee7b4c02011-10-04 01:21:09 +00007694 for (icheck=3; icheck > 0; icheck--)
cristy0d57eec2011-09-04 22:13:56 +00007695 {
glennrpee7b4c02011-10-04 01:21:09 +00007696 if (length == check_len[icheck])
glennrp29a106e2011-09-06 17:11:42 +00007697 {
glennrpee7b4c02011-10-04 01:21:09 +00007698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7699 " Got a %lu-byte ICC profile (potentially sRGB)",
7700 (unsigned long) length);
glennrp29a106e2011-09-06 17:11:42 +00007701
glennrpee7b4c02011-10-04 01:21:09 +00007702 data=GetStringInfoDatum(profile);
7703 profile_crc=crc32(0,data,length);
glennrp29a106e2011-09-06 17:11:42 +00007704
glennrpee7b4c02011-10-04 01:21:09 +00007705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe54cd4b2011-10-04 01:31:35 +00007706 " with crc=%8x",(unsigned int) profile_crc);
glennrpee7b4c02011-10-04 01:21:09 +00007707
7708 if (profile_crc == check_crc[icheck])
7709 {
7710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7711 " It is sRGB.");
7712 if (image->rendering_intent==UndefinedIntent)
7713 image->rendering_intent=PerceptualIntent;
7714 break;
7715 }
glennrp29a106e2011-09-06 17:11:42 +00007716 }
glennrp29a106e2011-09-06 17:11:42 +00007717 }
glennrpee7b4c02011-10-04 01:21:09 +00007718 if (icheck == 0)
glennrp29a106e2011-09-06 17:11:42 +00007719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpee7b4c02011-10-04 01:21:09 +00007720 " Got a %lu-byte ICC profile",
glennrp29a106e2011-09-06 17:11:42 +00007721 (unsigned long) length);
7722 }
cristy0d57eec2011-09-04 22:13:56 +00007723 }
7724 name=GetNextImageProfile(image);
7725 }
7726 }
7727
glennrp8bb3a022010-12-13 20:40:04 +00007728 number_opaque = 0;
7729 number_semitransparent = 0;
7730 number_transparent = 0;
7731
glennrpfd05d622011-02-25 04:10:33 +00007732 if (logging != MagickFalse)
7733 {
7734 if (image->storage_class == UndefinedClass)
7735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7736 " storage_class=UndefinedClass");
7737 if (image->storage_class == DirectClass)
7738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7739 " storage_class=DirectClass");
7740 if (image->storage_class == PseudoClass)
7741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7742 " storage_class=PseudoClass");
7743 }
glennrp28af3712011-04-06 18:07:30 +00007744
glennrp7e65e932011-08-19 02:31:16 +00007745 if (image->storage_class == PseudoClass &&
7746 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7747 (mng_info->write_png_colortype != 0 &&
7748 mng_info->write_png_colortype != 4)))
7749 {
cristyea1a8aa2011-10-20 13:24:06 +00007750 (void) SyncImage(image,exception);
glennrp7e65e932011-08-19 02:31:16 +00007751 image->storage_class = DirectClass;
7752 }
7753
glennrpc6c391a2011-04-27 02:23:56 +00007754 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007755 {
glennrpc6c391a2011-04-27 02:23:56 +00007756 if (image->storage_class != PseudoClass && image->colormap != NULL)
7757 {
7758 /* Free the bogus colormap; it can cause trouble later */
7759 if (logging != MagickFalse)
7760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7761 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00007762 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00007763 image->colormap=NULL;
7764 }
glennrp28af3712011-04-06 18:07:30 +00007765 }
glennrpbb4f99d2011-05-22 11:13:17 +00007766
cristy510d06a2011-07-06 23:43:54 +00007767 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristye941a752011-10-15 01:52:48 +00007768 (void) TransformImageColorspace(image,RGBColorspace,exception);
glennrp0fe50b42010-11-16 03:52:51 +00007769
glennrp3241bd02010-12-12 04:36:28 +00007770 /*
7771 Sometimes we get PseudoClass images whose RGB values don't match
7772 the colors in the colormap. This code syncs the RGB values.
7773 */
7774 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
cristyea1a8aa2011-10-20 13:24:06 +00007775 (void) SyncImage(image,exception);
glennrp3241bd02010-12-12 04:36:28 +00007776
glennrpa6a06632011-01-19 15:15:34 +00007777#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7778 if (image->depth > 8)
7779 {
7780 if (logging != MagickFalse)
7781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7782 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7783
7784 image->depth=8;
7785 }
7786#endif
7787
glennrp8e58efd2011-05-20 12:16:29 +00007788 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007789 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7790 {
cristy4c08aed2011-07-01 19:47:50 +00007791 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007792 *r;
7793
glennrp8e58efd2011-05-20 12:16:29 +00007794 if (image->depth > 8)
7795 {
7796#if MAGICKCORE_QUANTUM_DEPTH > 16
7797 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007798 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007799
7800 for (y=0; y < (ssize_t) image->rows; y++)
7801 {
cristy8a20fa02011-12-27 15:54:31 +00007802 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007803
cristy4c08aed2011-07-01 19:47:50 +00007804 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007805 break;
7806
7807 for (x=0; x < (ssize_t) image->columns; x++)
7808 {
glennrp54cf7972011-08-06 14:28:09 +00007809 LBR16PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007810 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007811 }
glennrpbb4f99d2011-05-22 11:13:17 +00007812
glennrp8e58efd2011-05-20 12:16:29 +00007813 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7814 break;
7815 }
7816
7817 if (image->storage_class == PseudoClass && image->colormap != NULL)
7818 {
cristy3e08f112011-05-24 13:19:30 +00007819 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007820 {
glennrp91d99252011-06-25 14:30:13 +00007821 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007822 }
7823 }
7824#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7825 }
7826
7827 else if (image->depth > 4)
7828 {
7829#if MAGICKCORE_QUANTUM_DEPTH > 8
7830 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007831 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007832
7833 for (y=0; y < (ssize_t) image->rows; y++)
7834 {
cristyc82a27b2011-10-21 01:07:16 +00007835 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007836
cristy4c08aed2011-07-01 19:47:50 +00007837 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007838 break;
7839
7840 for (x=0; x < (ssize_t) image->columns; x++)
7841 {
glennrp54cf7972011-08-06 14:28:09 +00007842 LBR08PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007843 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007844 }
glennrpbb4f99d2011-05-22 11:13:17 +00007845
glennrp8e58efd2011-05-20 12:16:29 +00007846 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7847 break;
7848 }
7849
7850 if (image->storage_class == PseudoClass && image->colormap != NULL)
7851 {
cristy3e08f112011-05-24 13:19:30 +00007852 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007853 {
glennrp91d99252011-06-25 14:30:13 +00007854 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007855 }
7856 }
7857#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7858 }
7859 else
7860 if (image->depth > 2)
7861 {
7862 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007863 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007864
7865 for (y=0; y < (ssize_t) image->rows; y++)
7866 {
cristy8a20fa02011-12-27 15:54:31 +00007867 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007868
cristy4c08aed2011-07-01 19:47:50 +00007869 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007870 break;
7871
7872 for (x=0; x < (ssize_t) image->columns; x++)
7873 {
glennrp54cf7972011-08-06 14:28:09 +00007874 LBR04PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007875 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007876 }
glennrpbb4f99d2011-05-22 11:13:17 +00007877
glennrp8e58efd2011-05-20 12:16:29 +00007878 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7879 break;
7880 }
7881
7882 if (image->storage_class == PseudoClass && image->colormap != NULL)
7883 {
cristy3e08f112011-05-24 13:19:30 +00007884 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007885 {
glennrp91d99252011-06-25 14:30:13 +00007886 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007887 }
7888 }
7889 }
7890
7891 else if (image->depth > 1)
7892 {
7893 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007894 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007895
7896 for (y=0; y < (ssize_t) image->rows; y++)
7897 {
cristy8a20fa02011-12-27 15:54:31 +00007898 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007899
cristy4c08aed2011-07-01 19:47:50 +00007900 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007901 break;
7902
7903 for (x=0; x < (ssize_t) image->columns; x++)
7904 {
glennrp54cf7972011-08-06 14:28:09 +00007905 LBR02PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007906 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007907 }
glennrpbb4f99d2011-05-22 11:13:17 +00007908
glennrp8e58efd2011-05-20 12:16:29 +00007909 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7910 break;
7911 }
7912
7913 if (image->storage_class == PseudoClass && image->colormap != NULL)
7914 {
cristy3e08f112011-05-24 13:19:30 +00007915 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007916 {
glennrp91d99252011-06-25 14:30:13 +00007917 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007918 }
7919 }
7920 }
7921 else
7922 {
7923 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007924 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007925
7926 for (y=0; y < (ssize_t) image->rows; y++)
7927 {
cristy8a20fa02011-12-27 15:54:31 +00007928 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007929
cristy4c08aed2011-07-01 19:47:50 +00007930 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007931 break;
7932
7933 for (x=0; x < (ssize_t) image->columns; x++)
7934 {
glennrp54cf7972011-08-06 14:28:09 +00007935 LBR01PixelRGBA(r);
cristy8a20fa02011-12-27 15:54:31 +00007936 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007937 }
glennrpbb4f99d2011-05-22 11:13:17 +00007938
glennrp8e58efd2011-05-20 12:16:29 +00007939 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7940 break;
7941 }
7942
7943 if (image->storage_class == PseudoClass && image->colormap != NULL)
7944 {
cristy3e08f112011-05-24 13:19:30 +00007945 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007946 {
glennrp91d99252011-06-25 14:30:13 +00007947 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007948 }
7949 }
7950 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007951 }
7952
glennrp67b9c1a2011-04-22 18:47:36 +00007953 /* To do: set to next higher multiple of 8 */
7954 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007955 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007956
glennrp2b013e42010-11-24 16:55:50 +00007957#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7958 /* PNG does not handle depths greater than 16 so reduce it even
7959 * if lossy
7960 */
glennrp8e58efd2011-05-20 12:16:29 +00007961 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007962 image->depth=16;
7963#endif
7964
glennrp3faa9a32011-04-23 14:00:25 +00007965#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007966 if (image->depth == 16 && mng_info->write_png_depth != 16)
cristyc82a27b2011-10-21 01:07:16 +00007967 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007968 image->depth = 8;
7969#endif
7970
glennrpc8c2f062011-02-25 19:00:33 +00007971 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007972 * we reduce the transparency to binary and run again, then if there
7973 * 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 +00007974 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7975 * palette. Then (To do) we take care of a final reduction that is only
7976 * needed if there are still 256 colors present and one of them has both
7977 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007978 */
glennrp82b3c532011-03-22 19:20:54 +00007979
glennrp8ca51ad2011-05-12 21:22:32 +00007980 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007981 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007982 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007983
glennrp8ca51ad2011-05-12 21:22:32 +00007984 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007985 {
7986 /* BUILD_PALETTE
7987 *
7988 * Sometimes we get DirectClass images that have 256 colors or fewer.
7989 * This code will build a colormap.
7990 *
7991 * Also, sometimes we get PseudoClass images with an out-of-date
7992 * colormap. This code will replace the colormap with a new one.
7993 * Sometimes we get PseudoClass images that have more than 256 colors.
7994 * This code will delete the colormap and change the image to
7995 * DirectClass.
7996 *
cristy4c08aed2011-07-01 19:47:50 +00007997 * If image->matte is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00007998 * even though it sometimes contains left-over non-opaque values.
7999 *
8000 * Also we gather some information (number of opaque, transparent,
8001 * and semitransparent pixels, and whether the image has any non-gray
8002 * pixels or only black-and-white pixels) that we might need later.
8003 *
8004 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8005 * we need to check for bogus non-opaque values, at least.
8006 */
glennrp3c218112010-11-27 15:31:26 +00008007
glennrpd71e86a2011-02-24 01:28:37 +00008008 int
8009 n;
glennrp3c218112010-11-27 15:31:26 +00008010
cristy101ab702011-10-13 13:06:32 +00008011 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008012 opaque[260],
8013 semitransparent[260],
8014 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00008015
cristy4c08aed2011-07-01 19:47:50 +00008016 register const Quantum
8017 *s;
glennrp8bb3a022010-12-13 20:40:04 +00008018
cristy4c08aed2011-07-01 19:47:50 +00008019 register Quantum
8020 *q,
glennrpfd05d622011-02-25 04:10:33 +00008021 *r;
8022
glennrpd71e86a2011-02-24 01:28:37 +00008023 if (logging != MagickFalse)
8024 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8025 " Enter BUILD_PALETTE:");
8026
8027 if (logging != MagickFalse)
8028 {
glennrp03812ae2010-12-24 01:31:34 +00008029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008030 " image->columns=%.20g",(double) image->columns);
8031 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8032 " image->rows=%.20g",(double) image->rows);
8033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8034 " image->matte=%.20g",(double) image->matte);
8035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8036 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008037
glennrpfd05d622011-02-25 04:10:33 +00008038 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008039 {
8040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008041 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008043 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008044
glennrpd71e86a2011-02-24 01:28:37 +00008045 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008046 {
glennrpd71e86a2011-02-24 01:28:37 +00008047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8048 " %d (%d,%d,%d,%d)",
8049 (int) i,
8050 (int) image->colormap[i].red,
8051 (int) image->colormap[i].green,
8052 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008053 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008054 }
glennrp2cc891a2010-12-24 13:44:32 +00008055
glennrpd71e86a2011-02-24 01:28:37 +00008056 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8057 {
8058 if (i > 255)
8059 {
8060 (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);
glennrpd71e86a2011-02-24 01:28:37 +00008067 }
8068 }
glennrp03812ae2010-12-24 01:31:34 +00008069 }
glennrp7ddcc222010-12-11 05:01:05 +00008070
glennrpd71e86a2011-02-24 01:28:37 +00008071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8072 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008073
glennrpd71e86a2011-02-24 01:28:37 +00008074 if (image->colors == 0)
8075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8076 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008077
glennrp8d3d6e52011-04-19 04:39:51 +00008078 if (ping_preserve_colormap == MagickFalse)
8079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8080 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008081 }
8082
glennrpd71e86a2011-02-24 01:28:37 +00008083 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008084 number_opaque = 0;
8085 number_semitransparent = 0;
8086 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008087
8088 for (y=0; y < (ssize_t) image->rows; y++)
8089 {
8090 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8091
cristyacd2ed22011-08-30 01:44:23 +00008092 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008093 break;
8094
8095 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008096 {
glennrp4737d522011-04-29 03:33:42 +00008097 if (image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008098 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008099 {
8100 if (number_opaque < 259)
8101 {
8102 if (number_opaque == 0)
8103 {
cristy101ab702011-10-13 13:06:32 +00008104 GetPixelInfoPixel(image, q, opaque);
cristy4c08aed2011-07-01 19:47:50 +00008105 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008106 number_opaque=1;
8107 }
glennrp2cc891a2010-12-24 13:44:32 +00008108
glennrpd71e86a2011-02-24 01:28:37 +00008109 for (i=0; i< (ssize_t) number_opaque; i++)
8110 {
cristy4c08aed2011-07-01 19:47:50 +00008111 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008112 break;
8113 }
glennrp7ddcc222010-12-11 05:01:05 +00008114
glennrpd71e86a2011-02-24 01:28:37 +00008115 if (i == (ssize_t) number_opaque &&
8116 number_opaque < 259)
8117 {
8118 number_opaque++;
cristy101ab702011-10-13 13:06:32 +00008119 GetPixelInfoPixel(image, q, opaque+i);
cristy4c08aed2011-07-01 19:47:50 +00008120 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008121 }
8122 }
8123 }
cristy4c08aed2011-07-01 19:47:50 +00008124 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008125 {
8126 if (number_transparent < 259)
8127 {
8128 if (number_transparent == 0)
8129 {
cristy101ab702011-10-13 13:06:32 +00008130 GetPixelInfoPixel(image, q, transparent);
cristy4c08aed2011-07-01 19:47:50 +00008131 ping_trans_color.red=(unsigned short)
8132 GetPixelRed(image,q);
8133 ping_trans_color.green=(unsigned short)
8134 GetPixelGreen(image,q);
8135 ping_trans_color.blue=(unsigned short)
8136 GetPixelBlue(image,q);
8137 ping_trans_color.gray=(unsigned short)
8138 GetPixelRed(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008139 number_transparent = 1;
8140 }
8141
8142 for (i=0; i< (ssize_t) number_transparent; i++)
8143 {
cristy4c08aed2011-07-01 19:47:50 +00008144 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008145 break;
8146 }
8147
8148 if (i == (ssize_t) number_transparent &&
8149 number_transparent < 259)
8150 {
8151 number_transparent++;
cristy101ab702011-10-13 13:06:32 +00008152 GetPixelInfoPixel(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008153 }
8154 }
8155 }
8156 else
8157 {
8158 if (number_semitransparent < 259)
8159 {
8160 if (number_semitransparent == 0)
8161 {
cristy101ab702011-10-13 13:06:32 +00008162 GetPixelInfoPixel(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008163 number_semitransparent = 1;
8164 }
8165
8166 for (i=0; i< (ssize_t) number_semitransparent; i++)
8167 {
cristy4c08aed2011-07-01 19:47:50 +00008168 if (IsPixelEquivalent(image,q, semitransparent+i)
8169 && GetPixelAlpha(image,q) ==
8170 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008171 break;
8172 }
8173
8174 if (i == (ssize_t) number_semitransparent &&
8175 number_semitransparent < 259)
8176 {
8177 number_semitransparent++;
cristy101ab702011-10-13 13:06:32 +00008178 GetPixelInfoPixel(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008179 }
8180 }
8181 }
cristyed231572011-07-14 02:18:59 +00008182 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008183 }
8184 }
8185
cristy4054bfb2011-08-29 23:41:39 +00008186 if (mng_info->write_png8 == MagickFalse &&
8187 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008188 {
8189 /* Add the background color to the palette, if it
8190 * isn't already there.
8191 */
glennrpc6c391a2011-04-27 02:23:56 +00008192 if (logging != MagickFalse)
8193 {
8194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8195 " Check colormap for background (%d,%d,%d)",
8196 (int) image->background_color.red,
8197 (int) image->background_color.green,
8198 (int) image->background_color.blue);
8199 }
glennrpd71e86a2011-02-24 01:28:37 +00008200 for (i=0; i<number_opaque; i++)
8201 {
glennrpca7ad3a2011-04-26 04:44:54 +00008202 if (opaque[i].red == image->background_color.red &&
8203 opaque[i].green == image->background_color.green &&
8204 opaque[i].blue == image->background_color.blue)
8205 break;
glennrpd71e86a2011-02-24 01:28:37 +00008206 }
glennrpd71e86a2011-02-24 01:28:37 +00008207 if (number_opaque < 259 && i == number_opaque)
8208 {
glennrp8e045c82011-04-27 16:40:27 +00008209 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008210 ping_background.index = i;
8211 if (logging != MagickFalse)
8212 {
8213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8214 " background_color index is %d",(int) i);
8215 }
8216
glennrpd71e86a2011-02-24 01:28:37 +00008217 }
glennrpa080bc32011-03-11 18:03:44 +00008218 else if (logging != MagickFalse)
8219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8220 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008221 }
8222
8223 image_colors=number_opaque+number_transparent+number_semitransparent;
8224
glennrpa080bc32011-03-11 18:03:44 +00008225 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8226 {
8227 /* No room for the background color; remove it. */
8228 number_opaque--;
8229 image_colors--;
8230 }
8231
glennrpd71e86a2011-02-24 01:28:37 +00008232 if (logging != MagickFalse)
8233 {
8234 if (image_colors > 256)
8235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8236 " image has more than 256 colors");
8237
8238 else
8239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8240 " image has %d colors",image_colors);
8241 }
8242
glennrp8d3d6e52011-04-19 04:39:51 +00008243 if (ping_preserve_colormap != MagickFalse)
8244 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008245
glennrpfd05d622011-02-25 04:10:33 +00008246 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008247 {
8248 ping_have_color=MagickFalse;
8249 ping_have_non_bw=MagickFalse;
8250
8251 if(image_colors > 256)
8252 {
8253 for (y=0; y < (ssize_t) image->rows; y++)
8254 {
8255 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8256
cristyacd2ed22011-08-30 01:44:23 +00008257 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008258 break;
8259
glennrpe5e6b802011-07-20 14:44:40 +00008260 s=q;
8261 for (x=0; x < (ssize_t) image->columns; x++)
8262 {
8263 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8264 GetPixelRed(image,s) != GetPixelBlue(image,s))
8265 {
8266 ping_have_color=MagickTrue;
8267 ping_have_non_bw=MagickTrue;
8268 break;
8269 }
8270 s+=GetPixelChannels(image);
8271 }
8272
8273 if (ping_have_color != MagickFalse)
8274 break;
8275
glennrpd71e86a2011-02-24 01:28:37 +00008276 /* Worst case is black-and-white; we are looking at every
8277 * pixel twice.
8278 */
8279
glennrpd71e86a2011-02-24 01:28:37 +00008280 if (ping_have_non_bw == MagickFalse)
8281 {
8282 s=q;
8283 for (x=0; x < (ssize_t) image->columns; x++)
8284 {
cristy4c08aed2011-07-01 19:47:50 +00008285 if (GetPixelRed(image,s) != 0 &&
8286 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008287 {
8288 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008289 break;
glennrpd71e86a2011-02-24 01:28:37 +00008290 }
cristyed231572011-07-14 02:18:59 +00008291 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008292 }
glennrpe5e6b802011-07-20 14:44:40 +00008293 }
glennrpd71e86a2011-02-24 01:28:37 +00008294 }
glennrpbb4f99d2011-05-22 11:13:17 +00008295 }
8296 }
glennrpd71e86a2011-02-24 01:28:37 +00008297
8298 if (image_colors < 257)
8299 {
cristy101ab702011-10-13 13:06:32 +00008300 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008301 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008302
glennrpd71e86a2011-02-24 01:28:37 +00008303 /*
8304 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008305 */
8306
glennrpd71e86a2011-02-24 01:28:37 +00008307 if (logging != MagickFalse)
8308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8309 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008310
glennrpd71e86a2011-02-24 01:28:37 +00008311 /* Sort palette, transparent first */;
8312
8313 n = 0;
8314
8315 for (i=0; i<number_transparent; i++)
8316 colormap[n++] = transparent[i];
8317
8318 for (i=0; i<number_semitransparent; i++)
8319 colormap[n++] = semitransparent[i];
8320
8321 for (i=0; i<number_opaque; i++)
8322 colormap[n++] = opaque[i];
8323
glennrpc6c391a2011-04-27 02:23:56 +00008324 ping_background.index +=
8325 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008326
glennrpd71e86a2011-02-24 01:28:37 +00008327 /* image_colors < 257; search the colormap instead of the pixels
8328 * to get ping_have_color and ping_have_non_bw
8329 */
8330 for (i=0; i<n; i++)
8331 {
8332 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008333 {
glennrpd71e86a2011-02-24 01:28:37 +00008334 if (colormap[i].red != colormap[i].green ||
8335 colormap[i].red != colormap[i].blue)
8336 {
8337 ping_have_color=MagickTrue;
8338 ping_have_non_bw=MagickTrue;
8339 break;
8340 }
8341 }
8342
8343 if (ping_have_non_bw == MagickFalse)
8344 {
8345 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008346 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008347 }
glennrp8bb3a022010-12-13 20:40:04 +00008348 }
8349
glennrpd71e86a2011-02-24 01:28:37 +00008350 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8351 (number_transparent == 0 && number_semitransparent == 0)) &&
8352 (((mng_info->write_png_colortype-1) ==
8353 PNG_COLOR_TYPE_PALETTE) ||
8354 (mng_info->write_png_colortype == 0)))
8355 {
glennrp6185c532011-01-14 17:58:40 +00008356 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008357 {
glennrpd71e86a2011-02-24 01:28:37 +00008358 if (n != (ssize_t) image_colors)
8359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8360 " image_colors (%d) and n (%d) don't match",
8361 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008362
glennrpd71e86a2011-02-24 01:28:37 +00008363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8364 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008365 }
glennrp03812ae2010-12-24 01:31:34 +00008366
glennrpd71e86a2011-02-24 01:28:37 +00008367 image->colors = image_colors;
8368
cristy018f07f2011-09-04 21:15:19 +00008369 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008370 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008371 ThrowWriterException(ResourceLimitError,
8372 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008373
8374 for (i=0; i< (ssize_t) image_colors; i++)
8375 image->colormap[i] = colormap[i];
8376
8377 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008378 {
glennrpd71e86a2011-02-24 01:28:37 +00008379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8380 " image->colors=%d (%d)",
8381 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008382
glennrpd71e86a2011-02-24 01:28:37 +00008383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8384 " Update the pixel indexes");
8385 }
glennrp6185c532011-01-14 17:58:40 +00008386
glennrpfd05d622011-02-25 04:10:33 +00008387 /* Sync the pixel indices with the new colormap */
8388
glennrpd71e86a2011-02-24 01:28:37 +00008389 for (y=0; y < (ssize_t) image->rows; y++)
8390 {
cristy97707062011-12-27 18:25:00 +00008391 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp6185c532011-01-14 17:58:40 +00008392
cristyacd2ed22011-08-30 01:44:23 +00008393 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008394 break;
glennrp6185c532011-01-14 17:58:40 +00008395
glennrpd71e86a2011-02-24 01:28:37 +00008396 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008397 {
glennrpd71e86a2011-02-24 01:28:37 +00008398 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008399 {
glennrpd71e86a2011-02-24 01:28:37 +00008400 if ((image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008401 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8402 image->colormap[i].red == GetPixelRed(image,q) &&
8403 image->colormap[i].green == GetPixelGreen(image,q) &&
8404 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008405 {
cristy4c08aed2011-07-01 19:47:50 +00008406 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008407 break;
glennrp6185c532011-01-14 17:58:40 +00008408 }
glennrp6185c532011-01-14 17:58:40 +00008409 }
cristyed231572011-07-14 02:18:59 +00008410 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008411 }
glennrp6185c532011-01-14 17:58:40 +00008412
glennrpd71e86a2011-02-24 01:28:37 +00008413 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8414 break;
8415 }
8416 }
8417 }
8418
8419 if (logging != MagickFalse)
8420 {
8421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8422 " image->colors=%d", (int) image->colors);
8423
8424 if (image->colormap != NULL)
8425 {
8426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008427 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008428
8429 for (i=0; i < (ssize_t) image->colors; i++)
8430 {
cristy72988482011-03-29 16:34:38 +00008431 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008432 {
8433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8434 " %d (%d,%d,%d,%d)",
8435 (int) i,
8436 (int) image->colormap[i].red,
8437 (int) image->colormap[i].green,
8438 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008439 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008440 }
glennrp6185c532011-01-14 17:58:40 +00008441 }
8442 }
glennrp03812ae2010-12-24 01:31:34 +00008443
glennrpd71e86a2011-02-24 01:28:37 +00008444 if (number_transparent < 257)
8445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8446 " number_transparent = %d",
8447 number_transparent);
8448 else
glennrp03812ae2010-12-24 01:31:34 +00008449
glennrpd71e86a2011-02-24 01:28:37 +00008450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8451 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008452
glennrpd71e86a2011-02-24 01:28:37 +00008453 if (number_opaque < 257)
8454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8455 " number_opaque = %d",
8456 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008457
glennrpd71e86a2011-02-24 01:28:37 +00008458 else
8459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8460 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008461
glennrpd71e86a2011-02-24 01:28:37 +00008462 if (number_semitransparent < 257)
8463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8464 " number_semitransparent = %d",
8465 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008466
glennrpd71e86a2011-02-24 01:28:37 +00008467 else
8468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8469 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008470
glennrpd71e86a2011-02-24 01:28:37 +00008471 if (ping_have_non_bw == MagickFalse)
8472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8473 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008474
glennrpd71e86a2011-02-24 01:28:37 +00008475 else if (ping_have_color == MagickFalse)
8476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8477 " All pixels and the background are gray");
8478
8479 else
8480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8481 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008482
glennrp03812ae2010-12-24 01:31:34 +00008483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8484 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008485 }
glennrpfd05d622011-02-25 04:10:33 +00008486
glennrpc8c2f062011-02-25 19:00:33 +00008487 if (mng_info->write_png8 == MagickFalse)
8488 break;
glennrpfd05d622011-02-25 04:10:33 +00008489
glennrpc8c2f062011-02-25 19:00:33 +00008490 /* Make any reductions necessary for the PNG8 format */
8491 if (image_colors <= 256 &&
8492 image_colors != 0 && image->colormap != NULL &&
8493 number_semitransparent == 0 &&
8494 number_transparent <= 1)
8495 break;
8496
8497 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008498 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8499 * transparent color so if more than one is transparent we merge
8500 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008501 */
glennrp130fc452011-08-20 03:43:18 +00008502 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008503 {
8504 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8505 " Thresholding the alpha channel to binary");
8506
8507 for (y=0; y < (ssize_t) image->rows; y++)
8508 {
cristy8a20fa02011-12-27 15:54:31 +00008509 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008510
cristy4c08aed2011-07-01 19:47:50 +00008511 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008512 break;
8513
8514 for (x=0; x < (ssize_t) image->columns; x++)
8515 {
glennrpf73547f2011-08-20 04:40:26 +00008516 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008517 {
cristy803640d2011-11-17 02:11:32 +00008518 SetPixelInfoPixel(image,&image->background_color,r);
cristy4c08aed2011-07-01 19:47:50 +00008519 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008520 }
8521 else
cristy4c08aed2011-07-01 19:47:50 +00008522 SetPixelAlpha(image,OpaqueAlpha,r);
cristyed231572011-07-14 02:18:59 +00008523 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008524 }
glennrpbb4f99d2011-05-22 11:13:17 +00008525
glennrpc8c2f062011-02-25 19:00:33 +00008526 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8527 break;
8528
8529 if (image_colors != 0 && image_colors <= 256 &&
8530 image->colormap != NULL)
8531 for (i=0; i<image_colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00008532 image->colormap[i].alpha =
8533 (image->colormap[i].alpha > TransparentAlpha/2 ?
8534 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008535 }
8536 continue;
8537 }
8538
8539 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008540 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8541 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8542 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008543 */
glennrpd3371642011-03-22 19:42:23 +00008544 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8545 {
8546 if (logging != MagickFalse)
8547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8548 " Quantizing the background color to 4-4-4");
8549
8550 tried_444 = MagickTrue;
8551
glennrp91d99252011-06-25 14:30:13 +00008552 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008553
8554 if (logging != MagickFalse)
8555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8556 " Quantizing the pixel colors to 4-4-4");
8557
8558 if (image->colormap == NULL)
8559 {
8560 for (y=0; y < (ssize_t) image->rows; y++)
8561 {
cristy8a20fa02011-12-27 15:54:31 +00008562 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpd3371642011-03-22 19:42:23 +00008563
cristy4c08aed2011-07-01 19:47:50 +00008564 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008565 break;
8566
8567 for (x=0; x < (ssize_t) image->columns; x++)
8568 {
cristy4c08aed2011-07-01 19:47:50 +00008569 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008570 LBR04PixelRGB(r);
cristy8a20fa02011-12-27 15:54:31 +00008571 r+=GetPixelChannels(image);
glennrpd3371642011-03-22 19:42:23 +00008572 }
glennrpbb4f99d2011-05-22 11:13:17 +00008573
glennrpd3371642011-03-22 19:42:23 +00008574 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8575 break;
8576 }
8577 }
8578
8579 else /* Should not reach this; colormap already exists and
8580 must be <= 256 */
8581 {
8582 if (logging != MagickFalse)
8583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8584 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008585
glennrpd3371642011-03-22 19:42:23 +00008586 for (i=0; i<image_colors; i++)
8587 {
glennrp91d99252011-06-25 14:30:13 +00008588 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008589 }
8590 }
8591 continue;
8592 }
8593
glennrp82b3c532011-03-22 19:20:54 +00008594 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8595 {
8596 if (logging != MagickFalse)
8597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8598 " Quantizing the background color to 3-3-3");
8599
8600 tried_333 = MagickTrue;
8601
glennrp91d99252011-06-25 14:30:13 +00008602 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008603
8604 if (logging != MagickFalse)
8605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008606 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008607
8608 if (image->colormap == NULL)
8609 {
8610 for (y=0; y < (ssize_t) image->rows; y++)
8611 {
cristy8a20fa02011-12-27 15:54:31 +00008612 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp82b3c532011-03-22 19:20:54 +00008613
cristy4c08aed2011-07-01 19:47:50 +00008614 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008615 break;
8616
8617 for (x=0; x < (ssize_t) image->columns; x++)
8618 {
cristy4c08aed2011-07-01 19:47:50 +00008619 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8620 LBR03RGB(r);
cristy8a20fa02011-12-27 15:54:31 +00008621 r+=GetPixelChannels(image);
glennrp82b3c532011-03-22 19:20:54 +00008622 }
glennrpbb4f99d2011-05-22 11:13:17 +00008623
glennrp82b3c532011-03-22 19:20:54 +00008624 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8625 break;
8626 }
8627 }
8628
8629 else /* Should not reach this; colormap already exists and
8630 must be <= 256 */
8631 {
8632 if (logging != MagickFalse)
8633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008634 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008635 for (i=0; i<image_colors; i++)
8636 {
glennrp91d99252011-06-25 14:30:13 +00008637 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008638 }
glennrpd3371642011-03-22 19:42:23 +00008639 }
8640 continue;
glennrp82b3c532011-03-22 19:20:54 +00008641 }
glennrpc8c2f062011-02-25 19:00:33 +00008642
glennrp8ca51ad2011-05-12 21:22:32 +00008643 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008644 {
8645 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008647 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008648
glennrp8ca51ad2011-05-12 21:22:32 +00008649 tried_332 = MagickTrue;
8650
glennrp3faa9a32011-04-23 14:00:25 +00008651 /* Red and green were already done so we only quantize the blue
8652 * channel
8653 */
8654
glennrp91d99252011-06-25 14:30:13 +00008655 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008656
glennrpc8c2f062011-02-25 19:00:33 +00008657 if (logging != MagickFalse)
8658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008659 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008660
glennrpc8c2f062011-02-25 19:00:33 +00008661 if (image->colormap == NULL)
8662 {
8663 for (y=0; y < (ssize_t) image->rows; y++)
8664 {
cristy8a20fa02011-12-27 15:54:31 +00008665 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008666
cristy4c08aed2011-07-01 19:47:50 +00008667 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008668 break;
8669
8670 for (x=0; x < (ssize_t) image->columns; x++)
8671 {
cristy4c08aed2011-07-01 19:47:50 +00008672 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008673 LBR02PixelBlue(r);
cristy8a20fa02011-12-27 15:54:31 +00008674 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008675 }
glennrpbb4f99d2011-05-22 11:13:17 +00008676
glennrpc8c2f062011-02-25 19:00:33 +00008677 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8678 break;
8679 }
8680 }
glennrpfd05d622011-02-25 04:10:33 +00008681
glennrpc8c2f062011-02-25 19:00:33 +00008682 else /* Should not reach this; colormap already exists and
8683 must be <= 256 */
8684 {
8685 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008687 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008688 for (i=0; i<image_colors; i++)
8689 {
glennrp91d99252011-06-25 14:30:13 +00008690 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008691 }
8692 }
8693 continue;
8694 }
8695 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008696
8697 if (image_colors == 0 || image_colors > 256)
8698 {
8699 /* Take care of special case with 256 colors + 1 transparent
8700 * color. We don't need to quantize to 2-3-2-1; we only need to
8701 * eliminate one color, so we'll merge the two darkest red
8702 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8703 */
8704 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8705 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8706 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8707 {
8708 image->background_color.red=ScaleCharToQuantum(0x24);
8709 }
glennrpbb4f99d2011-05-22 11:13:17 +00008710
glennrp8ca51ad2011-05-12 21:22:32 +00008711 if (image->colormap == NULL)
8712 {
8713 for (y=0; y < (ssize_t) image->rows; y++)
8714 {
cristy8a20fa02011-12-27 15:54:31 +00008715 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008716
cristy4c08aed2011-07-01 19:47:50 +00008717 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008718 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008719
glennrp8ca51ad2011-05-12 21:22:32 +00008720 for (x=0; x < (ssize_t) image->columns; x++)
8721 {
cristy4c08aed2011-07-01 19:47:50 +00008722 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8723 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8724 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8725 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008726 {
cristy4c08aed2011-07-01 19:47:50 +00008727 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008728 }
cristyed231572011-07-14 02:18:59 +00008729 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008730 }
glennrpbb4f99d2011-05-22 11:13:17 +00008731
glennrp8ca51ad2011-05-12 21:22:32 +00008732 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8733 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008734
glennrp8ca51ad2011-05-12 21:22:32 +00008735 }
8736 }
8737
8738 else
8739 {
8740 for (i=0; i<image_colors; i++)
8741 {
8742 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8743 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8744 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8745 {
8746 image->colormap[i].red=ScaleCharToQuantum(0x24);
8747 }
8748 }
8749 }
8750 }
glennrpd71e86a2011-02-24 01:28:37 +00008751 }
glennrpfd05d622011-02-25 04:10:33 +00008752 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008753
glennrpfd05d622011-02-25 04:10:33 +00008754 /* If we are excluding the tRNS chunk and there is transparency,
8755 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8756 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008757 */
glennrp0e8ea192010-12-24 18:00:33 +00008758 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8759 (number_transparent != 0 || number_semitransparent != 0))
8760 {
glennrpd17915c2011-04-29 14:24:22 +00008761 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008762
8763 if (ping_have_color == MagickFalse)
8764 mng_info->write_png_colortype = 5;
8765
8766 else
8767 mng_info->write_png_colortype = 7;
8768
glennrp8d579662011-02-23 02:05:02 +00008769 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008770 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008771 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008772
glennrp0e8ea192010-12-24 18:00:33 +00008773 }
8774
glennrpfd05d622011-02-25 04:10:33 +00008775 /* See if cheap transparency is possible. It is only possible
8776 * when there is a single transparent color, no semitransparent
8777 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008778 * as the transparent color. We only need this information if
8779 * we are writing a PNG with colortype 0 or 2, and we have not
8780 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008781 */
glennrp5a39f372011-02-25 04:52:16 +00008782 if (number_transparent == 1 &&
8783 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008784 {
8785 ping_have_cheap_transparency = MagickTrue;
8786
8787 if (number_semitransparent != 0)
8788 ping_have_cheap_transparency = MagickFalse;
8789
8790 else if (image_colors == 0 || image_colors > 256 ||
8791 image->colormap == NULL)
8792 {
cristy4c08aed2011-07-01 19:47:50 +00008793 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008794 *q;
8795
glennrpfd05d622011-02-25 04:10:33 +00008796 for (y=0; y < (ssize_t) image->rows; y++)
8797 {
8798 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8799
cristyacd2ed22011-08-30 01:44:23 +00008800 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008801 break;
8802
8803 for (x=0; x < (ssize_t) image->columns; x++)
8804 {
cristy4c08aed2011-07-01 19:47:50 +00008805 if (GetPixelAlpha(image,q) != TransparentAlpha &&
glennrp847370c2011-07-05 17:37:15 +00008806 (unsigned short) GetPixelRed(image,q) ==
8807 ping_trans_color.red &&
8808 (unsigned short) GetPixelGreen(image,q) ==
8809 ping_trans_color.green &&
8810 (unsigned short) GetPixelBlue(image,q) ==
8811 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008812 {
8813 ping_have_cheap_transparency = MagickFalse;
8814 break;
8815 }
8816
cristyed231572011-07-14 02:18:59 +00008817 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008818 }
glennrpbb4f99d2011-05-22 11:13:17 +00008819
glennrpfd05d622011-02-25 04:10:33 +00008820 if (ping_have_cheap_transparency == MagickFalse)
8821 break;
8822 }
8823 }
8824 else
8825 {
glennrp67b9c1a2011-04-22 18:47:36 +00008826 /* Assuming that image->colormap[0] is the one transparent color
8827 * and that all others are opaque.
8828 */
glennrpfd05d622011-02-25 04:10:33 +00008829 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008830 for (i=1; i<image_colors; i++)
8831 if (image->colormap[i].red == image->colormap[0].red &&
8832 image->colormap[i].green == image->colormap[0].green &&
8833 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008834 {
glennrp67b9c1a2011-04-22 18:47:36 +00008835 ping_have_cheap_transparency = MagickFalse;
8836 break;
glennrpfd05d622011-02-25 04:10:33 +00008837 }
8838 }
glennrpbb4f99d2011-05-22 11:13:17 +00008839
glennrpfd05d622011-02-25 04:10:33 +00008840 if (logging != MagickFalse)
8841 {
8842 if (ping_have_cheap_transparency == MagickFalse)
8843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8844 " Cheap transparency is not possible.");
8845
8846 else
8847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8848 " Cheap transparency is possible.");
8849 }
8850 }
8851 else
8852 ping_have_cheap_transparency = MagickFalse;
8853
glennrp8640fb52010-11-23 15:48:26 +00008854 image_depth=image->depth;
8855
glennrp26c990a2010-11-23 02:23:20 +00008856 quantum_info = (QuantumInfo *) NULL;
8857 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008858 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008859 image_matte=image->matte;
8860
glennrp0fe50b42010-11-16 03:52:51 +00008861 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008862 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008863
glennrp52a479c2011-02-26 21:14:38 +00008864 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8865 (image->colors == 0 || image->colormap == NULL))
8866 {
glennrp52a479c2011-02-26 21:14:38 +00008867 image_info=DestroyImageInfo(image_info);
8868 image=DestroyImage(image);
cristyc82a27b2011-10-21 01:07:16 +00008869 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
glennrp15e01552011-03-06 23:29:17 +00008870 "Cannot write PNG8 or color-type 3; colormap is NULL",
8871 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008872#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8873 UnlockSemaphoreInfo(ping_semaphore);
8874#endif
8875 return(MagickFalse);
8876 }
8877
cristy3ed852e2009-09-05 21:47:34 +00008878 /*
8879 Allocate the PNG structures
8880 */
8881#ifdef PNG_USER_MEM_SUPPORTED
cristyc82a27b2011-10-21 01:07:16 +00008882 error_info.image=image;
8883 error_info.exception=exception;
8884 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008885 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8886 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008887
cristy3ed852e2009-09-05 21:47:34 +00008888#else
cristyc82a27b2011-10-21 01:07:16 +00008889 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008890 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008891
cristy3ed852e2009-09-05 21:47:34 +00008892#endif
8893 if (ping == (png_struct *) NULL)
8894 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008895
cristy3ed852e2009-09-05 21:47:34 +00008896 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008897
cristy3ed852e2009-09-05 21:47:34 +00008898 if (ping_info == (png_info *) NULL)
8899 {
8900 png_destroy_write_struct(&ping,(png_info **) NULL);
8901 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8902 }
glennrp0fe50b42010-11-16 03:52:51 +00008903
cristy3ed852e2009-09-05 21:47:34 +00008904 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008905 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008906
glennrp5af765f2010-03-30 11:12:18 +00008907 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008908 {
8909 /*
8910 PNG write failed.
8911 */
8912#ifdef PNG_DEBUG
8913 if (image_info->verbose)
8914 (void) printf("PNG write has failed.\n");
8915#endif
8916 png_destroy_write_struct(&ping,&ping_info);
8917#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008918 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008919#endif
glennrpda8f3a72011-02-27 23:54:12 +00008920 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008921 (void) CloseBlob(image);
8922 image_info=DestroyImageInfo(image_info);
8923 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008924 return(MagickFalse);
8925 }
8926 /*
8927 Prepare PNG for writing.
8928 */
8929#if defined(PNG_MNG_FEATURES_SUPPORTED)
8930 if (mng_info->write_mng)
8931 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008932
cristy3ed852e2009-09-05 21:47:34 +00008933#else
8934# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8935 if (mng_info->write_mng)
8936 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008937
cristy3ed852e2009-09-05 21:47:34 +00008938# endif
8939#endif
glennrp2b013e42010-11-24 16:55:50 +00008940
cristy3ed852e2009-09-05 21:47:34 +00008941 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008942
cristy4e5bc842010-06-09 13:56:01 +00008943 ping_width=(png_uint_32) image->columns;
8944 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008945
cristy3ed852e2009-09-05 21:47:34 +00008946 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8947 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008948
cristy3ed852e2009-09-05 21:47:34 +00008949 if (mng_info->write_png_depth != 0)
8950 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008951
cristy3ed852e2009-09-05 21:47:34 +00008952 /* Adjust requested depth to next higher valid depth if necessary */
8953 if (image_depth > 8)
8954 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008955
cristy3ed852e2009-09-05 21:47:34 +00008956 if ((image_depth > 4) && (image_depth < 8))
8957 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008958
cristy3ed852e2009-09-05 21:47:34 +00008959 if (image_depth == 3)
8960 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008961
cristy3ed852e2009-09-05 21:47:34 +00008962 if (logging != MagickFalse)
8963 {
8964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008965 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008967 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008969 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008971 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008973 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008974 }
glennrp8640fb52010-11-23 15:48:26 +00008975
cristy3ed852e2009-09-05 21:47:34 +00008976 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008977 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008978
glennrp26f37912010-12-23 16:22:42 +00008979
cristy3ed852e2009-09-05 21:47:34 +00008980#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008981 if (ping_exclude_pHYs == MagickFalse)
8982 {
cristy2a11bef2011-10-28 18:33:11 +00008983 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00008984 (!mng_info->write_mng || !mng_info->equal_physs))
8985 {
glennrp0fe50b42010-11-16 03:52:51 +00008986 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8988 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008989
8990 if (image->units == PixelsPerInchResolution)
8991 {
glennrpdfd70802010-11-14 01:23:35 +00008992 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008993 ping_pHYs_x_resolution=
cristy2a11bef2011-10-28 18:33:11 +00008994 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
glennrp823b55c2011-03-14 18:46:46 +00008995 ping_pHYs_y_resolution=
cristy2a11bef2011-10-28 18:33:11 +00008996 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008997 }
glennrpdfd70802010-11-14 01:23:35 +00008998
cristy3ed852e2009-09-05 21:47:34 +00008999 else if (image->units == PixelsPerCentimeterResolution)
9000 {
glennrpdfd70802010-11-14 01:23:35 +00009001 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
cristy2a11bef2011-10-28 18:33:11 +00009002 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9003 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00009004 }
glennrp991d11d2010-11-12 21:55:28 +00009005
cristy3ed852e2009-09-05 21:47:34 +00009006 else
9007 {
glennrpdfd70802010-11-14 01:23:35 +00009008 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
cristy2a11bef2011-10-28 18:33:11 +00009009 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9010 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00009011 }
glennrp991d11d2010-11-12 21:55:28 +00009012
glennrp823b55c2011-03-14 18:46:46 +00009013 if (logging != MagickFalse)
9014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9015 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9016 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9017 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009018 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009019 }
glennrp26f37912010-12-23 16:22:42 +00009020 }
cristy3ed852e2009-09-05 21:47:34 +00009021#endif
glennrpa521b2f2010-10-29 04:11:03 +00009022
glennrp26f37912010-12-23 16:22:42 +00009023 if (ping_exclude_bKGD == MagickFalse)
9024 {
glennrpa521b2f2010-10-29 04:11:03 +00009025 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009026 {
glennrpa521b2f2010-10-29 04:11:03 +00009027 unsigned int
9028 mask;
cristy3ed852e2009-09-05 21:47:34 +00009029
glennrpa521b2f2010-10-29 04:11:03 +00009030 mask=0xffff;
9031 if (ping_bit_depth == 8)
9032 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009033
glennrpa521b2f2010-10-29 04:11:03 +00009034 if (ping_bit_depth == 4)
9035 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009036
glennrpa521b2f2010-10-29 04:11:03 +00009037 if (ping_bit_depth == 2)
9038 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009039
glennrpa521b2f2010-10-29 04:11:03 +00009040 if (ping_bit_depth == 1)
9041 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009042
glennrpa521b2f2010-10-29 04:11:03 +00009043 ping_background.red=(png_uint_16)
9044 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009045
glennrpa521b2f2010-10-29 04:11:03 +00009046 ping_background.green=(png_uint_16)
9047 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009048
glennrpa521b2f2010-10-29 04:11:03 +00009049 ping_background.blue=(png_uint_16)
9050 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009051
9052 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009053 }
cristy3ed852e2009-09-05 21:47:34 +00009054
glennrp0fe50b42010-11-16 03:52:51 +00009055 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009056 {
9057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9058 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9060 " background_color index is %d",
9061 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009062
9063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9064 " ping_bit_depth=%d",ping_bit_depth);
9065 }
glennrp0fe50b42010-11-16 03:52:51 +00009066
9067 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009068 }
glennrp0fe50b42010-11-16 03:52:51 +00009069
cristy3ed852e2009-09-05 21:47:34 +00009070 /*
9071 Select the color type.
9072 */
9073 matte=image_matte;
9074 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009075
glennrp1273f7b2011-02-24 03:20:30 +00009076 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009077 {
glennrp0fe50b42010-11-16 03:52:51 +00009078
glennrpfd05d622011-02-25 04:10:33 +00009079 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009080 for reducing the sample depth from 8. */
9081
glennrp0fe50b42010-11-16 03:52:51 +00009082 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009083
glennrp8bb3a022010-12-13 20:40:04 +00009084 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009085
9086 /*
9087 Set image palette.
9088 */
9089 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9090
glennrp0fe50b42010-11-16 03:52:51 +00009091 if (logging != MagickFalse)
9092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9093 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009094 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009095
9096 for (i=0; i < (ssize_t) number_colors; i++)
9097 {
9098 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9099 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9100 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9101 if (logging != MagickFalse)
9102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009103#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009104 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009105#else
9106 " %5ld (%5d,%5d,%5d)",
9107#endif
glennrp0fe50b42010-11-16 03:52:51 +00009108 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9109
9110 }
glennrp2b013e42010-11-24 16:55:50 +00009111
glennrp8bb3a022010-12-13 20:40:04 +00009112 ping_have_PLTE=MagickTrue;
9113 image_depth=ping_bit_depth;
9114 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009115
glennrp58e01762011-01-07 15:28:54 +00009116 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009117 {
glennrp0fe50b42010-11-16 03:52:51 +00009118 /*
9119 Identify which colormap entry is transparent.
9120 */
9121 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009122 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009123
glennrp8bb3a022010-12-13 20:40:04 +00009124 for (i=0; i < (ssize_t) number_transparent; i++)
9125 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009126
glennrp0fe50b42010-11-16 03:52:51 +00009127
glennrp2cc891a2010-12-24 13:44:32 +00009128 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009129 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009130
9131 if (ping_num_trans == 0)
9132 ping_have_tRNS=MagickFalse;
9133
glennrp8bb3a022010-12-13 20:40:04 +00009134 else
9135 ping_have_tRNS=MagickTrue;
9136 }
glennrp0fe50b42010-11-16 03:52:51 +00009137
glennrp1273f7b2011-02-24 03:20:30 +00009138 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009139 {
glennrp1273f7b2011-02-24 03:20:30 +00009140 /*
9141 * Identify which colormap entry is the background color.
9142 */
9143
glennrp4f25bd02011-01-01 18:51:28 +00009144 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9145 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9146 break;
glennrp0fe50b42010-11-16 03:52:51 +00009147
glennrp4f25bd02011-01-01 18:51:28 +00009148 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009149
9150 if (logging != MagickFalse)
9151 {
9152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9153 " background_color index is %d",
9154 (int) ping_background.index);
9155 }
glennrp4f25bd02011-01-01 18:51:28 +00009156 }
cristy3ed852e2009-09-05 21:47:34 +00009157 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009158
glennrp7e65e932011-08-19 02:31:16 +00009159 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009160 {
9161 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009162 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009163 }
glennrp0fe50b42010-11-16 03:52:51 +00009164
glennrp7e65e932011-08-19 02:31:16 +00009165 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009166 {
9167 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009168 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009169 }
glennrp0fe50b42010-11-16 03:52:51 +00009170
glennrp8bb3a022010-12-13 20:40:04 +00009171 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009172 {
glennrp5af765f2010-03-30 11:12:18 +00009173 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009174
glennrp8bb3a022010-12-13 20:40:04 +00009175 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009176 {
glennrp5af765f2010-03-30 11:12:18 +00009177 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009178
glennrp5af765f2010-03-30 11:12:18 +00009179 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9180 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009181 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009182
glennrp8bb3a022010-12-13 20:40:04 +00009183 else
9184 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009185
9186 if (logging != MagickFalse)
9187 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9188 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009189 }
glennrp0fe50b42010-11-16 03:52:51 +00009190
glennrp7c4c9e62011-03-21 20:23:32 +00009191 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009192 {
9193 if (logging != MagickFalse)
9194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009195 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009196
glennrpd6bf1612010-12-17 17:28:54 +00009197 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009198 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009199
glennrpd6bf1612010-12-17 17:28:54 +00009200 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009201 {
glennrp5af765f2010-03-30 11:12:18 +00009202 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009203 image_matte=MagickFalse;
9204 }
glennrp0fe50b42010-11-16 03:52:51 +00009205
glennrpd6bf1612010-12-17 17:28:54 +00009206 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009207 {
glennrp5af765f2010-03-30 11:12:18 +00009208 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009209 image_matte=MagickTrue;
9210 }
glennrp0fe50b42010-11-16 03:52:51 +00009211
glennrp5aa37f62011-01-02 03:07:57 +00009212 if (image_info->type == PaletteType ||
9213 image_info->type == PaletteMatteType)
9214 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9215
glennrp7c4c9e62011-03-21 20:23:32 +00009216 if (mng_info->write_png_colortype == 0 &&
9217 (image_info->type == UndefinedType ||
9218 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009219 {
glennrp5aa37f62011-01-02 03:07:57 +00009220 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009221 {
glennrp5aa37f62011-01-02 03:07:57 +00009222 if (image_matte == MagickFalse)
9223 {
9224 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9225 image_matte=MagickFalse;
9226 }
glennrp0fe50b42010-11-16 03:52:51 +00009227
glennrp0b206f52011-01-07 04:55:32 +00009228 else
glennrp5aa37f62011-01-02 03:07:57 +00009229 {
9230 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9231 image_matte=MagickTrue;
9232 }
9233 }
9234 else
glennrp8bb3a022010-12-13 20:40:04 +00009235 {
glennrp5aa37f62011-01-02 03:07:57 +00009236 if (image_matte == MagickFalse)
9237 {
9238 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9239 image_matte=MagickFalse;
9240 }
glennrp8bb3a022010-12-13 20:40:04 +00009241
glennrp0b206f52011-01-07 04:55:32 +00009242 else
glennrp5aa37f62011-01-02 03:07:57 +00009243 {
9244 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9245 image_matte=MagickTrue;
9246 }
9247 }
glennrp0fe50b42010-11-16 03:52:51 +00009248 }
glennrp5aa37f62011-01-02 03:07:57 +00009249
cristy3ed852e2009-09-05 21:47:34 +00009250 }
glennrp0fe50b42010-11-16 03:52:51 +00009251
cristy3ed852e2009-09-05 21:47:34 +00009252 if (logging != MagickFalse)
9253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009254 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009255
glennrp5af765f2010-03-30 11:12:18 +00009256 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009257 {
9258 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9259 ping_color_type == PNG_COLOR_TYPE_RGB ||
9260 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9261 ping_bit_depth=8;
9262 }
cristy3ed852e2009-09-05 21:47:34 +00009263
glennrpd6bf1612010-12-17 17:28:54 +00009264 old_bit_depth=ping_bit_depth;
9265
glennrp5af765f2010-03-30 11:12:18 +00009266 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009267 {
glennrp8d579662011-02-23 02:05:02 +00009268 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9269 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009270 }
glennrp8640fb52010-11-23 15:48:26 +00009271
glennrp5af765f2010-03-30 11:12:18 +00009272 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009273 {
cristy35ef8242010-06-03 16:24:13 +00009274 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009275 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009276
9277 if (image->colors == 0)
9278 {
glennrp0fe50b42010-11-16 03:52:51 +00009279 /* DO SOMETHING */
cristyc82a27b2011-10-21 01:07:16 +00009280 (void) ThrowMagickException(exception,
glennrp0f111982010-07-07 20:18:33 +00009281 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009282 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009283 }
9284
cristy35ef8242010-06-03 16:24:13 +00009285 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009286 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009287 }
glennrp2b013e42010-11-24 16:55:50 +00009288
glennrpd6bf1612010-12-17 17:28:54 +00009289 if (logging != MagickFalse)
9290 {
9291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9292 " Number of colors: %.20g",(double) image_colors);
9293
9294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9295 " Tentative PNG bit depth: %d",ping_bit_depth);
9296 }
9297
9298 if (ping_bit_depth < (int) mng_info->write_png_depth)
9299 ping_bit_depth = mng_info->write_png_depth;
9300 }
glennrp2cc891a2010-12-24 13:44:32 +00009301
glennrp5af765f2010-03-30 11:12:18 +00009302 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009303
cristy3ed852e2009-09-05 21:47:34 +00009304 if (logging != MagickFalse)
9305 {
9306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009307 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009308
cristy3ed852e2009-09-05 21:47:34 +00009309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009310 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009311
cristy3ed852e2009-09-05 21:47:34 +00009312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009313 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009314
cristy3ed852e2009-09-05 21:47:34 +00009315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009316
glennrp8640fb52010-11-23 15:48:26 +00009317 " image->depth: %.20g",(double) image->depth);
9318
9319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009320 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009321 }
9322
glennrp58e01762011-01-07 15:28:54 +00009323 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009324 {
glennrp4f25bd02011-01-01 18:51:28 +00009325 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009326 {
glennrp7c4c9e62011-03-21 20:23:32 +00009327 if (mng_info->write_png_colortype == 0)
9328 {
9329 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009330
glennrp7c4c9e62011-03-21 20:23:32 +00009331 if (ping_have_color != MagickFalse)
9332 ping_color_type=PNG_COLOR_TYPE_RGBA;
9333 }
glennrp4f25bd02011-01-01 18:51:28 +00009334
9335 /*
9336 * Determine if there is any transparent color.
9337 */
9338 if (number_transparent + number_semitransparent == 0)
9339 {
9340 /*
9341 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9342 */
glennrpa6a06632011-01-19 15:15:34 +00009343
glennrp4f25bd02011-01-01 18:51:28 +00009344 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009345
9346 if (mng_info->write_png_colortype == 0)
9347 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009348 }
9349
9350 else
9351 {
9352 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009353 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009354
9355 mask=0xffff;
9356
9357 if (ping_bit_depth == 8)
9358 mask=0x00ff;
9359
9360 if (ping_bit_depth == 4)
9361 mask=0x000f;
9362
9363 if (ping_bit_depth == 2)
9364 mask=0x0003;
9365
9366 if (ping_bit_depth == 1)
9367 mask=0x0001;
9368
9369 ping_trans_color.red=(png_uint_16)
9370 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9371
9372 ping_trans_color.green=(png_uint_16)
9373 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9374
9375 ping_trans_color.blue=(png_uint_16)
9376 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9377
9378 ping_trans_color.gray=(png_uint_16)
cristy101ab702011-10-13 13:06:32 +00009379 (ScaleQuantumToShort(GetPixelInfoIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009380 image->colormap)) & mask);
9381
9382 ping_trans_color.index=(png_byte) 0;
9383
9384 ping_have_tRNS=MagickTrue;
9385 }
9386
9387 if (ping_have_tRNS != MagickFalse)
9388 {
9389 /*
glennrpfd05d622011-02-25 04:10:33 +00009390 * Determine if there is one and only one transparent color
9391 * and if so if it is fully transparent.
9392 */
9393 if (ping_have_cheap_transparency == MagickFalse)
9394 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009395 }
9396
9397 if (ping_have_tRNS != MagickFalse)
9398 {
glennrp7c4c9e62011-03-21 20:23:32 +00009399 if (mng_info->write_png_colortype == 0)
9400 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009401
9402 if (image_depth == 8)
9403 {
9404 ping_trans_color.red&=0xff;
9405 ping_trans_color.green&=0xff;
9406 ping_trans_color.blue&=0xff;
9407 ping_trans_color.gray&=0xff;
9408 }
9409 }
9410 }
cristy3ed852e2009-09-05 21:47:34 +00009411 else
9412 {
cristy3ed852e2009-09-05 21:47:34 +00009413 if (image_depth == 8)
9414 {
glennrp5af765f2010-03-30 11:12:18 +00009415 ping_trans_color.red&=0xff;
9416 ping_trans_color.green&=0xff;
9417 ping_trans_color.blue&=0xff;
9418 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009419 }
9420 }
9421 }
glennrp8640fb52010-11-23 15:48:26 +00009422
cristy3ed852e2009-09-05 21:47:34 +00009423 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009424
glennrp2e09f552010-11-14 00:38:48 +00009425 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009426 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009427
glennrp39992b42010-11-14 00:03:43 +00009428 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009429 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009430 ping_have_color == MagickFalse &&
9431 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009432 {
cristy35ef8242010-06-03 16:24:13 +00009433 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009434
cristy3ed852e2009-09-05 21:47:34 +00009435 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009436 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009437
glennrp7c4c9e62011-03-21 20:23:32 +00009438 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009439 {
glennrp5af765f2010-03-30 11:12:18 +00009440 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009441
cristy3ed852e2009-09-05 21:47:34 +00009442 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009443 {
9444 if (logging != MagickFalse)
9445 {
9446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9447 " Scaling ping_trans_color (0)");
9448 }
9449 ping_trans_color.gray*=0x0101;
9450 }
cristy3ed852e2009-09-05 21:47:34 +00009451 }
glennrp0fe50b42010-11-16 03:52:51 +00009452
cristy3ed852e2009-09-05 21:47:34 +00009453 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9454 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009455
glennrp136ee3a2011-04-27 15:47:45 +00009456 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009457 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009458 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009459
cristy3ed852e2009-09-05 21:47:34 +00009460 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009461 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009462
cristy3ed852e2009-09-05 21:47:34 +00009463 else
9464 {
glennrp5af765f2010-03-30 11:12:18 +00009465 ping_bit_depth=8;
9466 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009467 {
9468 if(!mng_info->write_png_depth)
9469 {
glennrp5af765f2010-03-30 11:12:18 +00009470 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009471
cristy35ef8242010-06-03 16:24:13 +00009472 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009473 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009474 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009475 }
9476 }
glennrp2b013e42010-11-24 16:55:50 +00009477
glennrp0fe50b42010-11-16 03:52:51 +00009478 else if (ping_color_type ==
9479 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009480 mng_info->IsPalette)
9481 {
cristy3ed852e2009-09-05 21:47:34 +00009482 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009483
cristy3ed852e2009-09-05 21:47:34 +00009484 int
9485 depth_4_ok=MagickTrue,
9486 depth_2_ok=MagickTrue,
9487 depth_1_ok=MagickTrue;
9488
cristybb503372010-05-27 20:51:26 +00009489 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009490 {
9491 unsigned char
9492 intensity;
9493
9494 intensity=ScaleQuantumToChar(image->colormap[i].red);
9495
9496 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9497 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9498 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9499 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009500 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009501 depth_1_ok=MagickFalse;
9502 }
glennrp2b013e42010-11-24 16:55:50 +00009503
cristy3ed852e2009-09-05 21:47:34 +00009504 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009505 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009506
cristy3ed852e2009-09-05 21:47:34 +00009507 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009508 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009509
cristy3ed852e2009-09-05 21:47:34 +00009510 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009511 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009512 }
9513 }
glennrp2b013e42010-11-24 16:55:50 +00009514
glennrp5af765f2010-03-30 11:12:18 +00009515 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009516 }
glennrp0fe50b42010-11-16 03:52:51 +00009517
cristy3ed852e2009-09-05 21:47:34 +00009518 else
glennrp0fe50b42010-11-16 03:52:51 +00009519
cristy3ed852e2009-09-05 21:47:34 +00009520 if (mng_info->IsPalette)
9521 {
glennrp17a14852010-05-10 03:01:59 +00009522 number_colors=image_colors;
9523
cristy3ed852e2009-09-05 21:47:34 +00009524 if (image_depth <= 8)
9525 {
cristy3ed852e2009-09-05 21:47:34 +00009526 /*
9527 Set image palette.
9528 */
glennrp5af765f2010-03-30 11:12:18 +00009529 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009530
glennrp58e01762011-01-07 15:28:54 +00009531 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009532 {
glennrp9c1eb072010-06-06 22:19:15 +00009533 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009534
glennrp3b51f0e2010-11-27 18:14:08 +00009535 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9537 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009538 }
glennrp0fe50b42010-11-16 03:52:51 +00009539
cristy3ed852e2009-09-05 21:47:34 +00009540 else
9541 {
cristybb503372010-05-27 20:51:26 +00009542 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009543 {
9544 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9545 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9546 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9547 }
glennrp0fe50b42010-11-16 03:52:51 +00009548
glennrp3b51f0e2010-11-27 18:14:08 +00009549 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009551 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009552 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009553
glennrp39992b42010-11-14 00:03:43 +00009554 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009555 }
glennrp0fe50b42010-11-16 03:52:51 +00009556
cristy3ed852e2009-09-05 21:47:34 +00009557 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009558 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009559 {
cristybefe4d22010-06-07 01:18:58 +00009560 size_t
9561 one;
9562
glennrp5af765f2010-03-30 11:12:18 +00009563 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009564 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009565
cristy94b11832011-09-08 19:46:03 +00009566 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009567 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009568 }
glennrp0fe50b42010-11-16 03:52:51 +00009569
glennrp5af765f2010-03-30 11:12:18 +00009570 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009571
glennrp58e01762011-01-07 15:28:54 +00009572 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009573 {
glennrp0fe50b42010-11-16 03:52:51 +00009574 /*
glennrpd6bf1612010-12-17 17:28:54 +00009575 * Set up trans_colors array.
9576 */
glennrp0fe50b42010-11-16 03:52:51 +00009577 assert(number_colors <= 256);
9578
glennrpd6bf1612010-12-17 17:28:54 +00009579 ping_num_trans=(unsigned short) (number_transparent +
9580 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009581
9582 if (ping_num_trans == 0)
9583 ping_have_tRNS=MagickFalse;
9584
glennrpd6bf1612010-12-17 17:28:54 +00009585 else
glennrp0fe50b42010-11-16 03:52:51 +00009586 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009587 if (logging != MagickFalse)
9588 {
9589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9590 " Scaling ping_trans_color (1)");
9591 }
glennrpd6bf1612010-12-17 17:28:54 +00009592 ping_have_tRNS=MagickTrue;
9593
9594 for (i=0; i < ping_num_trans; i++)
9595 {
cristy4c08aed2011-07-01 19:47:50 +00009596 ping_trans_alpha[i]= (png_byte)
9597 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009598 }
glennrp0fe50b42010-11-16 03:52:51 +00009599 }
9600 }
cristy3ed852e2009-09-05 21:47:34 +00009601 }
9602 }
glennrp0fe50b42010-11-16 03:52:51 +00009603
cristy3ed852e2009-09-05 21:47:34 +00009604 else
9605 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009606
cristy3ed852e2009-09-05 21:47:34 +00009607 if (image_depth < 8)
9608 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009609
cristy3ed852e2009-09-05 21:47:34 +00009610 if ((save_image_depth == 16) && (image_depth == 8))
9611 {
glennrp4f25bd02011-01-01 18:51:28 +00009612 if (logging != MagickFalse)
9613 {
9614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9615 " Scaling ping_trans_color from (%d,%d,%d)",
9616 (int) ping_trans_color.red,
9617 (int) ping_trans_color.green,
9618 (int) ping_trans_color.blue);
9619 }
9620
glennrp5af765f2010-03-30 11:12:18 +00009621 ping_trans_color.red*=0x0101;
9622 ping_trans_color.green*=0x0101;
9623 ping_trans_color.blue*=0x0101;
9624 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009625
9626 if (logging != MagickFalse)
9627 {
9628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9629 " to (%d,%d,%d)",
9630 (int) ping_trans_color.red,
9631 (int) ping_trans_color.green,
9632 (int) ping_trans_color.blue);
9633 }
cristy3ed852e2009-09-05 21:47:34 +00009634 }
9635 }
9636
cristy4383ec82011-01-05 15:42:32 +00009637 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9638 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009639
cristy3ed852e2009-09-05 21:47:34 +00009640 /*
9641 Adjust background and transparency samples in sub-8-bit grayscale files.
9642 */
glennrp5af765f2010-03-30 11:12:18 +00009643 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009644 PNG_COLOR_TYPE_GRAY)
9645 {
9646 png_uint_16
9647 maxval;
9648
cristy35ef8242010-06-03 16:24:13 +00009649 size_t
9650 one=1;
9651
cristy22ffd972010-06-03 16:51:47 +00009652 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009653
glennrp4f25bd02011-01-01 18:51:28 +00009654 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009655 {
cristy3ed852e2009-09-05 21:47:34 +00009656
glennrp9f0fa852011-12-15 12:20:50 +00009657 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9658 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9659 &image->background_color))) +.5)));
cristy3ed852e2009-09-05 21:47:34 +00009660
9661 if (logging != MagickFalse)
9662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009663 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9665 " background_color index is %d",
9666 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009667
glennrp991d11d2010-11-12 21:55:28 +00009668 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009669 }
cristy3ed852e2009-09-05 21:47:34 +00009670
glennrp3e3e20f2011-06-09 04:21:43 +00009671 if (logging != MagickFalse)
9672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9673 " Scaling ping_trans_color.gray from %d",
9674 (int)ping_trans_color.gray);
9675
glennrp9be9b1c2011-06-09 12:21:45 +00009676 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009677 ping_trans_color.gray)+.5);
9678
9679 if (logging != MagickFalse)
9680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9681 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009682 }
glennrp17a14852010-05-10 03:01:59 +00009683
glennrp26f37912010-12-23 16:22:42 +00009684 if (ping_exclude_bKGD == MagickFalse)
9685 {
glennrp1273f7b2011-02-24 03:20:30 +00009686 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009687 {
9688 /*
9689 Identify which colormap entry is the background color.
9690 */
9691
glennrp17a14852010-05-10 03:01:59 +00009692 number_colors=image_colors;
9693
glennrpa521b2f2010-10-29 04:11:03 +00009694 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9695 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009696 break;
9697
9698 ping_background.index=(png_byte) i;
9699
glennrp3b51f0e2010-11-27 18:14:08 +00009700 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009701 {
9702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009703 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009704 }
glennrp0fe50b42010-11-16 03:52:51 +00009705
cristy13d07042010-11-21 20:56:18 +00009706 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009707 {
9708 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009709
9710 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009711 {
9712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9713 " background =(%d,%d,%d)",
9714 (int) ping_background.red,
9715 (int) ping_background.green,
9716 (int) ping_background.blue);
9717 }
9718 }
glennrpa521b2f2010-10-29 04:11:03 +00009719
glennrpd6bf1612010-12-17 17:28:54 +00009720 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009721 {
glennrp3b51f0e2010-11-27 18:14:08 +00009722 if (logging != MagickFalse)
9723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9724 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009725 ping_have_bKGD = MagickFalse;
9726 }
glennrp17a14852010-05-10 03:01:59 +00009727 }
glennrp26f37912010-12-23 16:22:42 +00009728 }
glennrp17a14852010-05-10 03:01:59 +00009729
cristy3ed852e2009-09-05 21:47:34 +00009730 if (logging != MagickFalse)
9731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009732 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009733 /*
9734 Initialize compression level and filtering.
9735 */
9736 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009737 {
9738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9739 " Setting up deflate compression");
9740
9741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9742 " Compression buffer size: 32768");
9743 }
9744
cristy3ed852e2009-09-05 21:47:34 +00009745 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009746
cristy3ed852e2009-09-05 21:47:34 +00009747 if (logging != MagickFalse)
9748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9749 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009750
cristy4054bfb2011-08-29 23:41:39 +00009751 png_set_compression_mem_level(ping, 9);
9752
glennrp10d739e2011-06-29 18:00:52 +00009753 /* Untangle the "-quality" setting:
9754
9755 Undefined is 0; the default is used.
9756 Default is 75
9757
9758 10's digit:
9759
9760 0: Use Z_HUFFMAN_ONLY strategy with the
9761 zlib default compression level
9762
9763 1-9: the zlib compression level
9764
9765 1's digit:
9766
9767 0-4: the PNG filter method
9768
9769 5: libpng adaptive filtering if compression level > 5
9770 libpng filter type "none" if compression level <= 5
9771 or if image is grayscale or palette
9772
9773 6: libpng adaptive filtering
9774
9775 7: "LOCO" filtering (intrapixel differing) if writing
9776 a MNG, othewise "none". Did not work in IM-6.7.0-9
9777 and earlier because of a missing "else".
9778
9779 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009780 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009781
9782 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009783 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009784
9785 Note that using the -quality option, not all combinations of
9786 PNG filter type, zlib compression level, and zlib compression
cristy5e6be1e2011-07-16 01:23:39 +00009787 strategy are possible. This will be addressed soon in a
cristy5d6fc9c2011-12-27 03:10:42 +00009788 release that accomodates "-define png:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +00009789
9790 */
9791
cristy3ed852e2009-09-05 21:47:34 +00009792 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9793 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009794
glennrp18682582011-06-30 18:11:47 +00009795 if (quality <= 9)
9796 {
9797 if (mng_info->write_png_compression_strategy == 0)
9798 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9799 }
9800
9801 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009802 {
9803 int
9804 level;
9805
cristybb503372010-05-27 20:51:26 +00009806 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009807
glennrp18682582011-06-30 18:11:47 +00009808 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009809 }
glennrp0fe50b42010-11-16 03:52:51 +00009810
glennrp18682582011-06-30 18:11:47 +00009811 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009812 {
glennrp18682582011-06-30 18:11:47 +00009813 if ((quality %10) == 8 || (quality %10) == 9)
9814 mng_info->write_png_compression_strategy=Z_RLE;
cristy3ed852e2009-09-05 21:47:34 +00009815 }
glennrp0fe50b42010-11-16 03:52:51 +00009816
glennrp18682582011-06-30 18:11:47 +00009817 if (mng_info->write_png_compression_filter == 0)
9818 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9819
cristy3ed852e2009-09-05 21:47:34 +00009820 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009821 {
glennrp18682582011-06-30 18:11:47 +00009822 if (mng_info->write_png_compression_level)
9823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9824 " Compression level: %d",
9825 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009826
glennrp18682582011-06-30 18:11:47 +00009827 if (mng_info->write_png_compression_strategy)
9828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9829 " Compression strategy: %d",
9830 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009831
glennrp18682582011-06-30 18:11:47 +00009832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9833 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009834
cristy4054bfb2011-08-29 23:41:39 +00009835 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +00009836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9837 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +00009838 else if (mng_info->write_png_compression_filter == 0 ||
9839 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +00009840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9841 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009842 else
9843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9844 " Base filter method: %d",
9845 (int) mng_info->write_png_compression_filter-1);
9846 }
glennrp2b013e42010-11-24 16:55:50 +00009847
glennrp18682582011-06-30 18:11:47 +00009848 if (mng_info->write_png_compression_level != 0)
9849 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9850
9851 if (mng_info->write_png_compression_filter == 6)
9852 {
9853 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9854 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9855 (quality < 50))
9856 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9857 else
9858 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9859 }
cristy4054bfb2011-08-29 23:41:39 +00009860 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +00009861 mng_info->write_png_compression_filter == 10)
9862 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9863
9864 else if (mng_info->write_png_compression_filter == 8)
9865 {
9866#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9867 if (mng_info->write_mng)
9868 {
9869 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9870 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9871 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9872 }
9873#endif
cristy4054bfb2011-08-29 23:41:39 +00009874 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +00009875 }
9876
9877 else if (mng_info->write_png_compression_filter == 9)
9878 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9879
9880 else if (mng_info->write_png_compression_filter != 0)
9881 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9882 mng_info->write_png_compression_filter-1);
9883
9884 if (mng_info->write_png_compression_strategy != 0)
9885 png_set_compression_strategy(ping,
9886 mng_info->write_png_compression_strategy-1);
9887
cristy0d57eec2011-09-04 22:13:56 +00009888 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9889 if (ping_exclude_sRGB != MagickFalse ||
9890 (image->rendering_intent == UndefinedIntent))
9891 {
9892 if ((ping_exclude_tEXt == MagickFalse ||
9893 ping_exclude_zTXt == MagickFalse) &&
9894 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009895 {
9896 ResetImageProfileIterator(image);
9897 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009898 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009899 profile=GetImageProfile(image,name);
9900
9901 if (profile != (StringInfo *) NULL)
9902 {
glennrp5af765f2010-03-30 11:12:18 +00009903#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009904 if ((LocaleCompare(name,"ICC") == 0) ||
9905 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009906 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009907
9908 if (ping_exclude_iCCP == MagickFalse)
9909 {
cristy9f027d12011-09-21 01:17:17 +00009910 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009911#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009912 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009913#else
9914 (png_const_bytep) GetStringInfoDatum(profile),
9915#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009916 (png_uint_32) GetStringInfoLength(profile));
9917 }
glennrp26f37912010-12-23 16:22:42 +00009918 }
glennrp0fe50b42010-11-16 03:52:51 +00009919
glennrpc8cbc5d2011-01-01 00:12:34 +00009920 else
cristy3ed852e2009-09-05 21:47:34 +00009921#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009922 if (ping_exclude_zCCP == MagickFalse)
9923 {
glennrpcf002022011-01-30 02:38:15 +00009924 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009925 (unsigned char *) name,(unsigned char *) name,
9926 GetStringInfoDatum(profile),
9927 (png_uint_32) GetStringInfoLength(profile));
9928 }
9929 }
glennrp0b206f52011-01-07 04:55:32 +00009930
glennrpc8cbc5d2011-01-01 00:12:34 +00009931 if (logging != MagickFalse)
9932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9933 " Setting up text chunk with %s profile",name);
9934
9935 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009936 }
cristy0d57eec2011-09-04 22:13:56 +00009937 }
cristy3ed852e2009-09-05 21:47:34 +00009938 }
9939
9940#if defined(PNG_WRITE_sRGB_SUPPORTED)
9941 if ((mng_info->have_write_global_srgb == 0) &&
9942 ((image->rendering_intent != UndefinedIntent) ||
9943 (image->colorspace == sRGBColorspace)))
9944 {
glennrp26f37912010-12-23 16:22:42 +00009945 if (ping_exclude_sRGB == MagickFalse)
9946 {
9947 /*
9948 Note image rendering intent.
9949 */
9950 if (logging != MagickFalse)
9951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9952 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009953
glennrp26f37912010-12-23 16:22:42 +00009954 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009955 Magick_RenderingIntent_to_PNG_RenderingIntent(
9956 image->rendering_intent)));
glennrp26f37912010-12-23 16:22:42 +00009957 }
cristy3ed852e2009-09-05 21:47:34 +00009958 }
glennrp26f37912010-12-23 16:22:42 +00009959
glennrp5af765f2010-03-30 11:12:18 +00009960 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009961#endif
9962 {
glennrp2cc891a2010-12-24 13:44:32 +00009963 if (ping_exclude_gAMA == MagickFalse &&
9964 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009965 (image->gamma < .45 || image->gamma > .46)))
9966 {
cristy3ed852e2009-09-05 21:47:34 +00009967 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9968 {
9969 /*
9970 Note image gamma.
9971 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9972 */
9973 if (logging != MagickFalse)
9974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9975 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009976
cristy3ed852e2009-09-05 21:47:34 +00009977 png_set_gAMA(ping,ping_info,image->gamma);
9978 }
glennrp26f37912010-12-23 16:22:42 +00009979 }
glennrp2b013e42010-11-24 16:55:50 +00009980
glennrp26f37912010-12-23 16:22:42 +00009981 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009982 {
glennrp26f37912010-12-23 16:22:42 +00009983 if ((mng_info->have_write_global_chrm == 0) &&
9984 (image->chromaticity.red_primary.x != 0.0))
9985 {
9986 /*
9987 Note image chromaticity.
9988 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9989 */
9990 PrimaryInfo
9991 bp,
9992 gp,
9993 rp,
9994 wp;
cristy3ed852e2009-09-05 21:47:34 +00009995
glennrp26f37912010-12-23 16:22:42 +00009996 wp=image->chromaticity.white_point;
9997 rp=image->chromaticity.red_primary;
9998 gp=image->chromaticity.green_primary;
9999 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +000010000
glennrp26f37912010-12-23 16:22:42 +000010001 if (logging != MagickFalse)
10002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10003 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010004
glennrp26f37912010-12-23 16:22:42 +000010005 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10006 bp.x,bp.y);
10007 }
10008 }
cristy3ed852e2009-09-05 21:47:34 +000010009 }
glennrpdfd70802010-11-14 01:23:35 +000010010
glennrp5af765f2010-03-30 11:12:18 +000010011 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +000010012
10013 if (mng_info->write_mng)
10014 png_set_sig_bytes(ping,8);
10015
cristy5d6fc9c2011-12-27 03:10:42 +000010016 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
cristy3ed852e2009-09-05 21:47:34 +000010017
glennrpd6bf1612010-12-17 17:28:54 +000010018 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +000010019 {
10020 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +000010021 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010022 {
glennrp5af765f2010-03-30 11:12:18 +000010023 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +000010024
glennrp5af765f2010-03-30 11:12:18 +000010025 if (ping_bit_depth < 8)
10026 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +000010027 }
glennrp0fe50b42010-11-16 03:52:51 +000010028
cristy3ed852e2009-09-05 21:47:34 +000010029 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +000010030 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +000010031 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +000010032 }
10033
glennrp0e8ea192010-12-24 18:00:33 +000010034 if (ping_need_colortype_warning != MagickFalse ||
10035 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +000010036 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +000010037 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +000010038 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +000010039 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +000010040 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +000010041 {
10042 if (logging != MagickFalse)
10043 {
glennrp0e8ea192010-12-24 18:00:33 +000010044 if (ping_need_colortype_warning != MagickFalse)
10045 {
10046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10047 " Image has transparency but tRNS chunk was excluded");
10048 }
10049
cristy3ed852e2009-09-05 21:47:34 +000010050 if (mng_info->write_png_depth)
10051 {
10052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010053 " Defined png:bit-depth=%u, Computed depth=%u",
cristy3ed852e2009-09-05 21:47:34 +000010054 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +000010055 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +000010056 }
glennrp0e8ea192010-12-24 18:00:33 +000010057
cristy3ed852e2009-09-05 21:47:34 +000010058 if (mng_info->write_png_colortype)
10059 {
10060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010061 " Defined png:color-type=%u, Computed color type=%u",
cristy3ed852e2009-09-05 21:47:34 +000010062 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +000010063 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010064 }
10065 }
glennrp0e8ea192010-12-24 18:00:33 +000010066
glennrp3bd2e412010-08-10 13:34:52 +000010067 png_warning(ping,
cristy5d6fc9c2011-12-27 03:10:42 +000010068 "Cannot write image with defined png:bit-depth or png:color-type.");
cristy3ed852e2009-09-05 21:47:34 +000010069 }
10070
glennrp58e01762011-01-07 15:28:54 +000010071 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010072 {
10073 /* Add an opaque matte channel */
10074 image->matte = MagickTrue;
cristye941a752011-10-15 01:52:48 +000010075 (void) SetImageAlpha(image,OpaqueAlpha,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010076
glennrpb4a13412010-05-05 12:47:19 +000010077 if (logging != MagickFalse)
10078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10079 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +000010080 }
10081
glennrp0e319732011-01-25 21:53:13 +000010082 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +000010083 {
glennrp991d11d2010-11-12 21:55:28 +000010084 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +000010085 {
glennrp991d11d2010-11-12 21:55:28 +000010086 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010087 if (logging != MagickFalse)
10088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10089 " Setting ping_have_tRNS=MagickTrue.");
10090 }
glennrpe9c26dc2010-05-30 01:56:35 +000010091 }
10092
cristy3ed852e2009-09-05 21:47:34 +000010093 if (logging != MagickFalse)
10094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10095 " Writing PNG header chunks");
10096
glennrp5af765f2010-03-30 11:12:18 +000010097 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10098 ping_bit_depth,ping_color_type,
10099 ping_interlace_method,ping_compression_method,
10100 ping_filter_method);
10101
glennrp39992b42010-11-14 00:03:43 +000010102 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10103 {
glennrpf09bded2011-01-08 01:15:59 +000010104 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010105
glennrp3b51f0e2010-11-27 18:14:08 +000010106 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +000010107 {
glennrp8640fb52010-11-23 15:48:26 +000010108 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +000010109 {
glennrpd6bf1612010-12-17 17:28:54 +000010110 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +000010111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010112 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10113 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010114 (int) palette[i].red,
10115 (int) palette[i].green,
10116 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +000010117 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010118 (int) ping_trans_alpha[i]);
10119 else
10120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010121 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010122 (int) i,
10123 (int) palette[i].red,
10124 (int) palette[i].green,
10125 (int) palette[i].blue);
10126 }
glennrp39992b42010-11-14 00:03:43 +000010127 }
glennrp39992b42010-11-14 00:03:43 +000010128 }
10129
glennrp26f37912010-12-23 16:22:42 +000010130 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010131 {
glennrp26f37912010-12-23 16:22:42 +000010132 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010133 {
glennrp26f37912010-12-23 16:22:42 +000010134 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010135 if (logging)
10136 {
10137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10138 " Setting up bKGD chunk");
10139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10140 " background color = (%d,%d,%d)",
10141 (int) ping_background.red,
10142 (int) ping_background.green,
10143 (int) ping_background.blue);
10144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10145 " index = %d, gray=%d",
10146 (int) ping_background.index,
10147 (int) ping_background.gray);
10148 }
10149 }
glennrp26f37912010-12-23 16:22:42 +000010150 }
10151
10152 if (ping_exclude_pHYs == MagickFalse)
10153 {
10154 if (ping_have_pHYs != MagickFalse)
10155 {
10156 png_set_pHYs(ping,ping_info,
10157 ping_pHYs_x_resolution,
10158 ping_pHYs_y_resolution,
10159 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010160
10161 if (logging)
10162 {
10163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10164 " Setting up pHYs chunk");
10165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10166 " x_resolution=%lu",
10167 (unsigned long) ping_pHYs_x_resolution);
10168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10169 " y_resolution=%lu",
10170 (unsigned long) ping_pHYs_y_resolution);
10171 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10172 " unit_type=%lu",
10173 (unsigned long) ping_pHYs_unit_type);
10174 }
glennrp26f37912010-12-23 16:22:42 +000010175 }
glennrpdfd70802010-11-14 01:23:35 +000010176 }
10177
10178#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010179 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010180 {
glennrp26f37912010-12-23 16:22:42 +000010181 if (image->page.x || image->page.y)
10182 {
10183 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10184 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010185
glennrp26f37912010-12-23 16:22:42 +000010186 if (logging != MagickFalse)
10187 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10188 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10189 (int) image->page.x, (int) image->page.y);
10190 }
glennrpdfd70802010-11-14 01:23:35 +000010191 }
10192#endif
10193
glennrpda8f3a72011-02-27 23:54:12 +000010194 if (mng_info->need_blob != MagickFalse)
10195 {
cristyc82a27b2011-10-21 01:07:16 +000010196 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
glennrpda8f3a72011-02-27 23:54:12 +000010197 MagickFalse)
10198 png_error(ping,"WriteBlob Failed");
10199
10200 ping_have_blob=MagickTrue;
10201 }
10202
cristy3ed852e2009-09-05 21:47:34 +000010203 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010204
glennrp39992b42010-11-14 00:03:43 +000010205 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010206 {
glennrp3b51f0e2010-11-27 18:14:08 +000010207 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010208 {
10209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10210 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10211 }
10212
10213 if (ping_color_type == 3)
10214 (void) png_set_tRNS(ping, ping_info,
10215 ping_trans_alpha,
10216 ping_num_trans,
10217 NULL);
10218
10219 else
10220 {
10221 (void) png_set_tRNS(ping, ping_info,
10222 NULL,
10223 0,
10224 &ping_trans_color);
10225
glennrp3b51f0e2010-11-27 18:14:08 +000010226 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010227 {
10228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010229 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010230 (int) ping_trans_color.red,
10231 (int) ping_trans_color.green,
10232 (int) ping_trans_color.blue);
10233 }
10234 }
glennrp991d11d2010-11-12 21:55:28 +000010235 }
10236
cristy3ed852e2009-09-05 21:47:34 +000010237 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010238 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010239
cristy3ed852e2009-09-05 21:47:34 +000010240 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010241
cristy3ed852e2009-09-05 21:47:34 +000010242 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010243 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010244
glennrp26f37912010-12-23 16:22:42 +000010245 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010246 {
glennrp4f25bd02011-01-01 18:51:28 +000010247 if ((image->page.width != 0 && image->page.width != image->columns) ||
10248 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010249 {
10250 unsigned char
10251 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010252
glennrp26f37912010-12-23 16:22:42 +000010253 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10254 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010255 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010256 PNGLong(chunk+4,(png_uint_32) image->page.width);
10257 PNGLong(chunk+8,(png_uint_32) image->page.height);
10258 chunk[12]=0; /* unit = pixels */
10259 (void) WriteBlob(image,13,chunk);
10260 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10261 }
cristy3ed852e2009-09-05 21:47:34 +000010262 }
10263
10264#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010265 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010266#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010267 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010268#undef PNG_HAVE_IDAT
10269#endif
10270
10271 png_set_packing(ping);
10272 /*
10273 Allocate memory.
10274 */
10275 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010276 if (image_depth > 8)
10277 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010278 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010279 {
glennrpb4a13412010-05-05 12:47:19 +000010280 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010281 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010282 break;
glennrp0fe50b42010-11-16 03:52:51 +000010283
glennrpb4a13412010-05-05 12:47:19 +000010284 case PNG_COLOR_TYPE_GRAY_ALPHA:
10285 rowbytes*=2;
10286 break;
glennrp0fe50b42010-11-16 03:52:51 +000010287
glennrpb4a13412010-05-05 12:47:19 +000010288 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010289 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010290 break;
glennrp0fe50b42010-11-16 03:52:51 +000010291
glennrpb4a13412010-05-05 12:47:19 +000010292 default:
10293 break;
cristy3ed852e2009-09-05 21:47:34 +000010294 }
glennrp3b51f0e2010-11-27 18:14:08 +000010295
10296 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010297 {
10298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10299 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010300
glennrpb4a13412010-05-05 12:47:19 +000010301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010302 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010303 }
glennrpcf002022011-01-30 02:38:15 +000010304 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10305 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010306
glennrpcf002022011-01-30 02:38:15 +000010307 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010308 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010309
cristy3ed852e2009-09-05 21:47:34 +000010310 /*
10311 Initialize image scanlines.
10312 */
glennrp5af765f2010-03-30 11:12:18 +000010313 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010314 {
10315 /*
10316 PNG write failed.
10317 */
10318#ifdef PNG_DEBUG
10319 if (image_info->verbose)
10320 (void) printf("PNG write has failed.\n");
10321#endif
10322 png_destroy_write_struct(&ping,&ping_info);
10323 if (quantum_info != (QuantumInfo *) NULL)
10324 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010325 if (ping_pixels != (unsigned char *) NULL)
10326 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010327#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010328 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010329#endif
glennrpda8f3a72011-02-27 23:54:12 +000010330 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010331 (void) CloseBlob(image);
10332 image_info=DestroyImageInfo(image_info);
10333 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010334 return(MagickFalse);
10335 }
cristyed552522009-10-16 14:04:35 +000010336 quantum_info=AcquireQuantumInfo(image_info,image);
10337 if (quantum_info == (QuantumInfo *) NULL)
10338 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010339 quantum_info->format=UndefinedQuantumFormat;
10340 quantum_info->depth=image_depth;
10341 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010342
cristy3ed852e2009-09-05 21:47:34 +000010343 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010344 !mng_info->write_png32) &&
10345 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010346 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010347 image_matte == MagickFalse &&
10348 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010349 {
glennrp8bb3a022010-12-13 20:40:04 +000010350 /* Palette, Bilevel, or Opaque Monochrome */
cristy4c08aed2011-07-01 19:47:50 +000010351 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010352 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010353
cristy3ed852e2009-09-05 21:47:34 +000010354 quantum_info->depth=8;
10355 for (pass=0; pass < num_passes; pass++)
10356 {
10357 /*
10358 Convert PseudoClass image to a PNG monochrome image.
10359 */
cristybb503372010-05-27 20:51:26 +000010360 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010361 {
glennrpd71e86a2011-02-24 01:28:37 +000010362 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10364 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010365
cristyc82a27b2011-10-21 01:07:16 +000010366 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010367
cristy4c08aed2011-07-01 19:47:50 +000010368 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010369 break;
glennrp0fe50b42010-11-16 03:52:51 +000010370
cristy3ed852e2009-09-05 21:47:34 +000010371 if (mng_info->IsPalette)
10372 {
cristy4c08aed2011-07-01 19:47:50 +000010373 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010374 quantum_info,GrayQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010375 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10376 mng_info->write_png_depth &&
10377 mng_info->write_png_depth != old_bit_depth)
10378 {
10379 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010380 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010381 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010382 >> (8-old_bit_depth));
10383 }
10384 }
glennrp0fe50b42010-11-16 03:52:51 +000010385
cristy3ed852e2009-09-05 21:47:34 +000010386 else
10387 {
cristy4c08aed2011-07-01 19:47:50 +000010388 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010389 quantum_info,RedQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010390 }
glennrp0fe50b42010-11-16 03:52:51 +000010391
cristy3ed852e2009-09-05 21:47:34 +000010392 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010393 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010394 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010395 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010396
glennrp3b51f0e2010-11-27 18:14:08 +000010397 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10399 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010400
glennrpcf002022011-01-30 02:38:15 +000010401 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010402 }
10403 if (image->previous == (Image *) NULL)
10404 {
10405 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10406 if (status == MagickFalse)
10407 break;
10408 }
10409 }
10410 }
glennrp0fe50b42010-11-16 03:52:51 +000010411
glennrp8bb3a022010-12-13 20:40:04 +000010412 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010413 {
glennrp0fe50b42010-11-16 03:52:51 +000010414 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010415 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010416 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010417 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010418 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010419 {
cristy4c08aed2011-07-01 19:47:50 +000010420 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010421 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010422
glennrp8bb3a022010-12-13 20:40:04 +000010423 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010424 {
glennrp8bb3a022010-12-13 20:40:04 +000010425
cristybb503372010-05-27 20:51:26 +000010426 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010427 {
cristyc82a27b2011-10-21 01:07:16 +000010428 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010429
cristy4c08aed2011-07-01 19:47:50 +000010430 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010431 break;
glennrp2cc891a2010-12-24 13:44:32 +000010432
glennrp5af765f2010-03-30 11:12:18 +000010433 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010434 {
glennrp8bb3a022010-12-13 20:40:04 +000010435 if (mng_info->IsPalette)
cristy4c08aed2011-07-01 19:47:50 +000010436 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010437 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010438
glennrp8bb3a022010-12-13 20:40:04 +000010439 else
cristy4c08aed2011-07-01 19:47:50 +000010440 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010441 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010442
glennrp3b51f0e2010-11-27 18:14:08 +000010443 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010445 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010446 }
glennrp2cc891a2010-12-24 13:44:32 +000010447
glennrp8bb3a022010-12-13 20:40:04 +000010448 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10449 {
10450 if (logging != MagickFalse && y == 0)
10451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10452 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010453
cristy4c08aed2011-07-01 19:47:50 +000010454 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010455 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010456 }
glennrp2cc891a2010-12-24 13:44:32 +000010457
glennrp3b51f0e2010-11-27 18:14:08 +000010458 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010460 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010461
glennrpcf002022011-01-30 02:38:15 +000010462 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010463 }
glennrp2cc891a2010-12-24 13:44:32 +000010464
glennrp8bb3a022010-12-13 20:40:04 +000010465 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010466 {
glennrp8bb3a022010-12-13 20:40:04 +000010467 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10468 if (status == MagickFalse)
10469 break;
cristy3ed852e2009-09-05 21:47:34 +000010470 }
cristy3ed852e2009-09-05 21:47:34 +000010471 }
10472 }
glennrp8bb3a022010-12-13 20:40:04 +000010473
10474 else
10475 {
cristy4c08aed2011-07-01 19:47:50 +000010476 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010477 *p;
10478
10479 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010480 {
glennrp8bb3a022010-12-13 20:40:04 +000010481 if ((image_depth > 8) || (mng_info->write_png24 ||
10482 mng_info->write_png32 ||
10483 (!mng_info->write_png8 && !mng_info->IsPalette)))
10484 {
10485 for (y=0; y < (ssize_t) image->rows; y++)
10486 {
10487 p=GetVirtualPixels(image,0,y,image->columns,1,
cristyc82a27b2011-10-21 01:07:16 +000010488 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010489
cristy4c08aed2011-07-01 19:47:50 +000010490 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010491 break;
glennrp2cc891a2010-12-24 13:44:32 +000010492
glennrp8bb3a022010-12-13 20:40:04 +000010493 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10494 {
10495 if (image->storage_class == DirectClass)
cristy4c08aed2011-07-01 19:47:50 +000010496 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010497 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010498
glennrp8bb3a022010-12-13 20:40:04 +000010499 else
cristy4c08aed2011-07-01 19:47:50 +000010500 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010501 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010502 }
glennrp2cc891a2010-12-24 13:44:32 +000010503
glennrp8bb3a022010-12-13 20:40:04 +000010504 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10505 {
cristy4c08aed2011-07-01 19:47:50 +000010506 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010507 quantum_info,GrayAlphaQuantum,ping_pixels,
cristyc82a27b2011-10-21 01:07:16 +000010508 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010509
glennrp8bb3a022010-12-13 20:40:04 +000010510 if (logging != MagickFalse && y == 0)
10511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10512 " Writing GRAY_ALPHA PNG pixels (3)");
10513 }
glennrp2cc891a2010-12-24 13:44:32 +000010514
glennrp8bb3a022010-12-13 20:40:04 +000010515 else if (image_matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +000010516 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010517 quantum_info,RGBAQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010518
glennrp8bb3a022010-12-13 20:40:04 +000010519 else
cristy4c08aed2011-07-01 19:47:50 +000010520 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010521 quantum_info,RGBQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010522
glennrp8bb3a022010-12-13 20:40:04 +000010523 if (logging != MagickFalse && y == 0)
10524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10525 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010526
glennrpcf002022011-01-30 02:38:15 +000010527 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010528 }
10529 }
glennrp2cc891a2010-12-24 13:44:32 +000010530
glennrp8bb3a022010-12-13 20:40:04 +000010531 else
10532 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10533 mng_info->write_png32 ||
10534 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10535 {
10536 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10537 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10538 {
10539 if (logging != MagickFalse)
10540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10541 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010542
glennrp8bb3a022010-12-13 20:40:04 +000010543 quantum_info->depth=8;
10544 image_depth=8;
10545 }
glennrp2cc891a2010-12-24 13:44:32 +000010546
glennrp8bb3a022010-12-13 20:40:04 +000010547 for (y=0; y < (ssize_t) image->rows; y++)
10548 {
10549 if (logging != MagickFalse && y == 0)
10550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10551 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010552
cristy97707062011-12-27 18:25:00 +000010553 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000010554
cristy4c08aed2011-07-01 19:47:50 +000010555 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010556 break;
glennrp2cc891a2010-12-24 13:44:32 +000010557
glennrp8bb3a022010-12-13 20:40:04 +000010558 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010559 {
glennrp4bf89732011-03-21 13:48:28 +000010560 quantum_info->depth=image->depth;
10561
cristy4c08aed2011-07-01 19:47:50 +000010562 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010563 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp44757ab2011-03-17 12:57:03 +000010564 }
glennrp2cc891a2010-12-24 13:44:32 +000010565
glennrp8bb3a022010-12-13 20:40:04 +000010566 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10567 {
10568 if (logging != MagickFalse && y == 0)
10569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10570 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010571
cristy4c08aed2011-07-01 19:47:50 +000010572 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010573 quantum_info,GrayAlphaQuantum,ping_pixels,
cristyc82a27b2011-10-21 01:07:16 +000010574 exception);
glennrp8bb3a022010-12-13 20:40:04 +000010575 }
glennrp2cc891a2010-12-24 13:44:32 +000010576
glennrp8bb3a022010-12-13 20:40:04 +000010577 else
glennrp8bb3a022010-12-13 20:40:04 +000010578 {
cristy4c08aed2011-07-01 19:47:50 +000010579 (void) ExportQuantumPixels(image,(CacheView *) NULL,
cristyc82a27b2011-10-21 01:07:16 +000010580 quantum_info,IndexQuantum,ping_pixels,exception);
glennrp5eae7602011-02-22 15:21:32 +000010581
10582 if (logging != MagickFalse && y <= 2)
10583 {
10584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010585 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010586
10587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10588 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10589 (int)ping_pixels[0],(int)ping_pixels[1]);
10590 }
glennrp8bb3a022010-12-13 20:40:04 +000010591 }
glennrpcf002022011-01-30 02:38:15 +000010592 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010593 }
10594 }
glennrp2cc891a2010-12-24 13:44:32 +000010595
glennrp8bb3a022010-12-13 20:40:04 +000010596 if (image->previous == (Image *) NULL)
10597 {
10598 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10599 if (status == MagickFalse)
10600 break;
10601 }
cristy3ed852e2009-09-05 21:47:34 +000010602 }
glennrp8bb3a022010-12-13 20:40:04 +000010603 }
10604 }
10605
cristyb32b90a2009-09-07 21:45:48 +000010606 if (quantum_info != (QuantumInfo *) NULL)
10607 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010608
10609 if (logging != MagickFalse)
10610 {
10611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010612 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010613
cristy3ed852e2009-09-05 21:47:34 +000010614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010615 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010616
cristy3ed852e2009-09-05 21:47:34 +000010617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010618 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010619
cristy3ed852e2009-09-05 21:47:34 +000010620 if (mng_info->write_png_depth)
10621 {
10622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010623 " Defined png:bit-depth: %d",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010624 }
glennrp0fe50b42010-11-16 03:52:51 +000010625
cristy3ed852e2009-09-05 21:47:34 +000010626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010627 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010628
cristy3ed852e2009-09-05 21:47:34 +000010629 if (mng_info->write_png_colortype)
10630 {
10631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010632 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010633 }
glennrp0fe50b42010-11-16 03:52:51 +000010634
cristy3ed852e2009-09-05 21:47:34 +000010635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010636 " PNG color-type written: %d",ping_color_type);
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 Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010640 }
10641 /*
glennrpa0ed0092011-04-18 16:36:29 +000010642 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010643 */
glennrp823b55c2011-03-14 18:46:46 +000010644 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010645 {
glennrp26f37912010-12-23 16:22:42 +000010646 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010647 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010648 while (property != (const char *) NULL)
10649 {
10650 png_textp
10651 text;
glennrp2cc891a2010-12-24 13:44:32 +000010652
cristyd15e6592011-10-15 00:13:06 +000010653 value=GetImageProperty(image,property,exception);
glennrpa0ed0092011-04-18 16:36:29 +000010654
10655 /* Don't write any "png:" properties; those are just for "identify" */
10656 if (LocaleNCompare(property,"png:",4) != 0 &&
10657
10658 /* Suppress density and units if we wrote a pHYs chunk */
10659 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010660 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010661 LocaleCompare(property,"units") != 0) &&
10662
10663 /* Suppress the IM-generated Date:create and Date:modify */
10664 (ping_exclude_date == MagickFalse ||
10665 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010666 {
glennrpc70af4a2011-03-07 00:08:23 +000010667 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010668 {
glennrpc70af4a2011-03-07 00:08:23 +000010669 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10670 text[0].key=(char *) property;
10671 text[0].text=(char *) value;
10672 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010673
glennrpc70af4a2011-03-07 00:08:23 +000010674 if (ping_exclude_tEXt != MagickFalse)
10675 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10676
10677 else if (ping_exclude_zTXt != MagickFalse)
10678 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10679
10680 else
glennrp26f37912010-12-23 16:22:42 +000010681 {
glennrpc70af4a2011-03-07 00:08:23 +000010682 text[0].compression=image_info->compression == NoCompression ||
10683 (image_info->compression == UndefinedCompression &&
10684 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10685 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010686 }
glennrp2cc891a2010-12-24 13:44:32 +000010687
glennrpc70af4a2011-03-07 00:08:23 +000010688 if (logging != MagickFalse)
10689 {
10690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10691 " Setting up text chunk");
10692
10693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10694 " keyword: %s",text[0].key);
10695 }
10696
10697 png_set_text(ping,ping_info,text,1);
10698 png_free(ping,text);
10699 }
glennrp26f37912010-12-23 16:22:42 +000010700 }
10701 property=GetNextImageProperty(image);
10702 }
cristy3ed852e2009-09-05 21:47:34 +000010703 }
10704
10705 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010706 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010707
10708 if (logging != MagickFalse)
10709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10710 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010711
cristy3ed852e2009-09-05 21:47:34 +000010712 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010713
cristy3ed852e2009-09-05 21:47:34 +000010714 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10715 {
10716 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010717 (ping_width != mng_info->page.width) ||
10718 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010719 {
10720 unsigned char
10721 chunk[32];
10722
10723 /*
10724 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10725 */
10726 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10727 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010728 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010729 chunk[4]=4;
10730 chunk[5]=0; /* frame name separator (no name) */
10731 chunk[6]=1; /* flag for changing delay, for next frame only */
10732 chunk[7]=0; /* flag for changing frame timeout */
10733 chunk[8]=1; /* flag for changing frame clipping for next frame */
10734 chunk[9]=0; /* flag for changing frame sync_id */
10735 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10736 chunk[14]=0; /* clipping boundaries delta type */
10737 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10738 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010739 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010740 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10741 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010742 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010743 (void) WriteBlob(image,31,chunk);
10744 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10745 mng_info->old_framing_mode=4;
10746 mng_info->framing_mode=1;
10747 }
glennrp0fe50b42010-11-16 03:52:51 +000010748
cristy3ed852e2009-09-05 21:47:34 +000010749 else
10750 mng_info->framing_mode=3;
10751 }
10752 if (mng_info->write_mng && !mng_info->need_fram &&
10753 ((int) image->dispose == 3))
cristyc82a27b2011-10-21 01:07:16 +000010754 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010755 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010756 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010757
cristy3ed852e2009-09-05 21:47:34 +000010758 /*
10759 Free PNG resources.
10760 */
glennrp5af765f2010-03-30 11:12:18 +000010761
cristy3ed852e2009-09-05 21:47:34 +000010762 png_destroy_write_struct(&ping,&ping_info);
10763
glennrpcf002022011-01-30 02:38:15 +000010764 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010765
10766#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010767 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010768#endif
10769
glennrpda8f3a72011-02-27 23:54:12 +000010770 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010771 (void) CloseBlob(image);
10772
10773 image_info=DestroyImageInfo(image_info);
10774 image=DestroyImage(image);
10775
10776 /* Store bit depth actually written */
10777 s[0]=(char) ping_bit_depth;
10778 s[1]='\0';
10779
cristyd15e6592011-10-15 00:13:06 +000010780 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
glennrpb9cfe272010-12-21 15:08:06 +000010781
cristy3ed852e2009-09-05 21:47:34 +000010782 if (logging != MagickFalse)
10783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10784 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010785
cristy3ed852e2009-09-05 21:47:34 +000010786 return(MagickTrue);
10787/* End write one PNG image */
10788}
10789
10790/*
10791%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10792% %
10793% %
10794% %
10795% W r i t e P N G I m a g e %
10796% %
10797% %
10798% %
10799%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10800%
10801% WritePNGImage() writes a Portable Network Graphics (PNG) or
10802% Multiple-image Network Graphics (MNG) image file.
10803%
10804% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10805%
10806% The format of the WritePNGImage method is:
10807%
cristy1e178e72011-08-28 19:44:34 +000010808% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10809% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010810%
10811% A description of each parameter follows:
10812%
10813% o image_info: the image info.
10814%
10815% o image: The image.
10816%
cristy1e178e72011-08-28 19:44:34 +000010817% o exception: return any errors or warnings in this structure.
10818%
cristy3ed852e2009-09-05 21:47:34 +000010819% Returns MagickTrue on success, MagickFalse on failure.
10820%
10821% Communicating with the PNG encoder:
10822%
10823% While the datastream written is always in PNG format and normally would
10824% be given the "png" file extension, this method also writes the following
cristy5d6fc9c2011-12-27 03:10:42 +000010825% pseudo-formats which are subsets of png:
cristy3ed852e2009-09-05 21:47:34 +000010826%
glennrp5a39f372011-02-25 04:52:16 +000010827% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10828% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010829% is present, the tRNS chunk must only have values 0 and 255
10830% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010831% transparent). If other values are present they will be
10832% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010833% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000010834% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10835% of any resulting fully-transparent pixels is changed to
10836% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000010837%
10838% If you want better quantization or dithering of the colors
10839% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010840% PNG encoder. The pixels contain 8-bit indices even if
10841% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010842% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010843% PNG grayscale type might be slightly more efficient. Please
10844% note that writing to the PNG8 format may result in loss
10845% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010846%
10847% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10848% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010849% one of the colors as transparent. The only loss incurred
10850% is reduction of sample depth to 8. If the image has more
10851% than one transparent color, has semitransparent pixels, or
10852% has an opaque pixel with the same RGB components as the
10853% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010854%
10855% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10856% transparency is permitted, i.e., the alpha sample for
10857% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010858% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010859% The only loss in data is the reduction of the sample depth
10860% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010861%
10862% o -define: For more precise control of the PNG output, you can use the
10863% Image options "png:bit-depth" and "png:color-type". These
10864% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010865% from the application programming interfaces. The options
10866% are case-independent and are converted to lowercase before
10867% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010868%
10869% png:color-type can be 0, 2, 3, 4, or 6.
10870%
10871% When png:color-type is 0 (Grayscale), png:bit-depth can
10872% be 1, 2, 4, 8, or 16.
10873%
10874% When png:color-type is 2 (RGB), png:bit-depth can
10875% be 8 or 16.
10876%
10877% When png:color-type is 3 (Indexed), png:bit-depth can
10878% be 1, 2, 4, or 8. This refers to the number of bits
10879% used to store the index. The color samples always have
10880% bit-depth 8 in indexed PNG files.
10881%
10882% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10883% png:bit-depth can be 8 or 16.
10884%
glennrp5a39f372011-02-25 04:52:16 +000010885% If the image cannot be written without loss with the requested bit-depth
10886% and color-type, a PNG file will not be written, and the encoder will
10887% return MagickFalse.
10888%
cristy3ed852e2009-09-05 21:47:34 +000010889% Since image encoders should not be responsible for the "heavy lifting",
10890% the user should make sure that ImageMagick has already reduced the
10891% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010892% transparency prior to attempting to write the image with depth, color,
glennrp54dc0692011-07-01 19:02:13 +000010893% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010894%
cristy3ed852e2009-09-05 21:47:34 +000010895% Note that another definition, "png:bit-depth-written" exists, but it
10896% is not intended for external use. It is only used internally by the
10897% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10898%
10899% It is possible to request that the PNG encoder write previously-formatted
10900% ancillary chunks in the output PNG file, using the "-profile" commandline
10901% option as shown below or by setting the profile via a programming
10902% interface:
10903%
10904% -profile PNG-chunk-x:<file>
10905%
10906% where x is a location flag and <file> is a file containing the chunk
10907% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010908% This encoder will compute the chunk length and CRC, so those must not
10909% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010910%
10911% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10912% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10913% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010914% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010915%
glennrpbb8a7332010-11-13 15:17:35 +000010916% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010917%
glennrp3241bd02010-12-12 04:36:28 +000010918% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010919%
glennrpd6afd542010-11-19 01:53:05 +000010920% o 32-bit depth is reduced to 16.
10921% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10922% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010923% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010924% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010925% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010926% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10927% this can be done without loss and a larger bit depth N was not
cristy5d6fc9c2011-12-27 03:10:42 +000010928% requested via the "-define png:bit-depth=N" option.
glennrpd6afd542010-11-19 01:53:05 +000010929% o If matte channel is present but only one transparent color is
10930% present, RGB+tRNS is written instead of RGBA
10931% o Opaque matte channel is removed (or added, if color-type 4 or 6
10932% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010933%
cristy3ed852e2009-09-05 21:47:34 +000010934%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10935*/
10936static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +000010937 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010938{
10939 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010940 excluding,
10941 logging,
10942 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010943 status;
10944
10945 MngInfo
10946 *mng_info;
10947
10948 const char
10949 *value;
10950
10951 int
glennrp21f0e622011-01-07 16:20:57 +000010952 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010953 source;
10954
cristy3ed852e2009-09-05 21:47:34 +000010955 /*
10956 Open image file.
10957 */
10958 assert(image_info != (const ImageInfo *) NULL);
10959 assert(image_info->signature == MagickSignature);
10960 assert(image != (Image *) NULL);
10961 assert(image->signature == MagickSignature);
10962 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010963 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010964 /*
10965 Allocate a MngInfo structure.
10966 */
10967 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010968 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010969
cristy3ed852e2009-09-05 21:47:34 +000010970 if (mng_info == (MngInfo *) NULL)
10971 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010972
cristy3ed852e2009-09-05 21:47:34 +000010973 /*
10974 Initialize members of the MngInfo structure.
10975 */
10976 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10977 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010978 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010979 have_mng_structure=MagickTrue;
10980
10981 /* See if user has requested a specific PNG subformat */
10982
10983 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10984 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10985 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10986
10987 if (mng_info->write_png8)
10988 {
glennrp9c1eb072010-06-06 22:19:15 +000010989 mng_info->write_png_colortype = /* 3 */ 4;
10990 mng_info->write_png_depth = 8;
10991 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010992 }
10993
10994 if (mng_info->write_png24)
10995 {
glennrp9c1eb072010-06-06 22:19:15 +000010996 mng_info->write_png_colortype = /* 2 */ 3;
10997 mng_info->write_png_depth = 8;
10998 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010999
glennrp9c1eb072010-06-06 22:19:15 +000011000 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011001 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011002
glennrp9c1eb072010-06-06 22:19:15 +000011003 else
cristy018f07f2011-09-04 21:15:19 +000011004 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011005
cristyea1a8aa2011-10-20 13:24:06 +000011006 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011007 }
11008
11009 if (mng_info->write_png32)
11010 {
glennrp9c1eb072010-06-06 22:19:15 +000011011 mng_info->write_png_colortype = /* 6 */ 7;
11012 mng_info->write_png_depth = 8;
11013 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011014
glennrp9c1eb072010-06-06 22:19:15 +000011015 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011016 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011017
glennrp9c1eb072010-06-06 22:19:15 +000011018 else
cristy018f07f2011-09-04 21:15:19 +000011019 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011020
cristyea1a8aa2011-10-20 13:24:06 +000011021 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011022 }
11023
11024 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011025
cristy3ed852e2009-09-05 21:47:34 +000011026 if (value != (char *) NULL)
11027 {
11028 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011029 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011030
cristy3ed852e2009-09-05 21:47:34 +000011031 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011032 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011033
cristy3ed852e2009-09-05 21:47:34 +000011034 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011035 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011036
cristy3ed852e2009-09-05 21:47:34 +000011037 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011038 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011039
cristy3ed852e2009-09-05 21:47:34 +000011040 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011041 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011042
glennrpbb8a7332010-11-13 15:17:35 +000011043 else
cristyc82a27b2011-10-21 01:07:16 +000011044 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011045 GetMagickModule(),CoderWarning,
11046 "ignoring invalid defined png:bit-depth",
11047 "=%s",value);
11048
cristy3ed852e2009-09-05 21:47:34 +000011049 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011051 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011052 }
glennrp0fe50b42010-11-16 03:52:51 +000011053
cristy3ed852e2009-09-05 21:47:34 +000011054 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011055
cristy3ed852e2009-09-05 21:47:34 +000011056 if (value != (char *) NULL)
11057 {
11058 /* We must store colortype+1 because 0 is a valid colortype */
11059 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011060 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011061
cristy3ed852e2009-09-05 21:47:34 +000011062 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011063 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011064
cristy3ed852e2009-09-05 21:47:34 +000011065 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011066 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011067
cristy3ed852e2009-09-05 21:47:34 +000011068 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011069 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011070
cristy3ed852e2009-09-05 21:47:34 +000011071 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011072 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011073
glennrpbb8a7332010-11-13 15:17:35 +000011074 else
cristyc82a27b2011-10-21 01:07:16 +000011075 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011076 GetMagickModule(),CoderWarning,
11077 "ignoring invalid defined png:color-type",
11078 "=%s",value);
11079
cristy3ed852e2009-09-05 21:47:34 +000011080 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011082 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011083 }
11084
glennrp0e8ea192010-12-24 18:00:33 +000011085 /* Check for chunks to be excluded:
11086 *
glennrp0dff56c2011-01-29 19:10:02 +000011087 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011088 * listed in the "unused_chunks" array, above.
11089 *
cristy5d6fc9c2011-12-27 03:10:42 +000011090 * Chunks can be listed for exclusion via a "png:exclude-chunk"
glennrp0e8ea192010-12-24 18:00:33 +000011091 * define (in the image properties or in the image artifacts)
11092 * or via a mng_info member. For convenience, in addition
11093 * to or instead of a comma-separated list of chunks, the
11094 * "exclude-chunk" string can be simply "all" or "none".
11095 *
11096 * The exclude-chunk define takes priority over the mng_info.
11097 *
cristy5d6fc9c2011-12-27 03:10:42 +000011098 * A "png:include-chunk" define takes priority over both the
11099 * mng_info and the "png:exclude-chunk" define. Like the
glennrp0e8ea192010-12-24 18:00:33 +000011100 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011101 * well as a comma-separated list. Chunks that are unknown to
11102 * ImageMagick are always excluded, regardless of their "copy-safe"
11103 * status according to the PNG specification, and even if they
11104 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000011105 *
11106 * Finally, all chunks listed in the "unused_chunks" array are
11107 * automatically excluded, regardless of the other instructions
11108 * or lack thereof.
11109 *
11110 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11111 * will not be written and the gAMA chunk will only be written if it
11112 * is not between .45 and .46, or approximately (1.0/2.2).
11113 *
11114 * If you exclude tRNS and the image has transparency, the colortype
11115 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11116 *
11117 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011118 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011119 */
11120
glennrp26f37912010-12-23 16:22:42 +000011121 mng_info->ping_exclude_bKGD=MagickFalse;
11122 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011123 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011124 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11125 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011126 mng_info->ping_exclude_iCCP=MagickFalse;
11127 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11128 mng_info->ping_exclude_oFFs=MagickFalse;
11129 mng_info->ping_exclude_pHYs=MagickFalse;
11130 mng_info->ping_exclude_sRGB=MagickFalse;
11131 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011132 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011133 mng_info->ping_exclude_vpAg=MagickFalse;
11134 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11135 mng_info->ping_exclude_zTXt=MagickFalse;
11136
glennrp8d3d6e52011-04-19 04:39:51 +000011137 mng_info->ping_preserve_colormap=MagickFalse;
11138
11139 value=GetImageArtifact(image,"png:preserve-colormap");
11140 if (value == NULL)
11141 value=GetImageOption(image_info,"png:preserve-colormap");
11142 if (value != NULL)
11143 mng_info->ping_preserve_colormap=MagickTrue;
11144
glennrp18682582011-06-30 18:11:47 +000011145 /* Thes compression-level, compression-strategy, and compression-filter
11146 * defines take precedence over values from the -quality option.
11147 */
11148 value=GetImageArtifact(image,"png:compression-level");
11149 if (value == NULL)
11150 value=GetImageOption(image_info,"png:compression-level");
11151 if (value != NULL)
11152 {
glennrp18682582011-06-30 18:11:47 +000011153 /* We have to add 1 to everything because 0 is a valid input,
11154 * and we want to use 0 (the default) to mean undefined.
11155 */
11156 if (LocaleCompare(value,"0") == 0)
11157 mng_info->write_png_compression_level = 1;
11158
11159 if (LocaleCompare(value,"1") == 0)
11160 mng_info->write_png_compression_level = 2;
11161
11162 else if (LocaleCompare(value,"2") == 0)
11163 mng_info->write_png_compression_level = 3;
11164
11165 else if (LocaleCompare(value,"3") == 0)
11166 mng_info->write_png_compression_level = 4;
11167
11168 else if (LocaleCompare(value,"4") == 0)
11169 mng_info->write_png_compression_level = 5;
11170
11171 else if (LocaleCompare(value,"5") == 0)
11172 mng_info->write_png_compression_level = 6;
11173
11174 else if (LocaleCompare(value,"6") == 0)
11175 mng_info->write_png_compression_level = 7;
11176
11177 else if (LocaleCompare(value,"7") == 0)
11178 mng_info->write_png_compression_level = 8;
11179
11180 else if (LocaleCompare(value,"8") == 0)
11181 mng_info->write_png_compression_level = 9;
11182
11183 else if (LocaleCompare(value,"9") == 0)
11184 mng_info->write_png_compression_level = 10;
11185
11186 else
cristyc82a27b2011-10-21 01:07:16 +000011187 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011188 GetMagickModule(),CoderWarning,
11189 "ignoring invalid defined png:compression-level",
11190 "=%s",value);
11191 }
11192
11193 value=GetImageArtifact(image,"png:compression-strategy");
11194 if (value == NULL)
11195 value=GetImageOption(image_info,"png:compression-strategy");
11196 if (value != NULL)
11197 {
11198
11199 if (LocaleCompare(value,"0") == 0)
11200 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11201
11202 else if (LocaleCompare(value,"1") == 0)
11203 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11204
11205 else if (LocaleCompare(value,"2") == 0)
11206 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11207
11208 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011209#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011210 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011211#else
11212 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11213#endif
glennrp18682582011-06-30 18:11:47 +000011214
11215 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011216#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011217 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011218#else
11219 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11220#endif
glennrp18682582011-06-30 18:11:47 +000011221
11222 else
cristyc82a27b2011-10-21 01:07:16 +000011223 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011224 GetMagickModule(),CoderWarning,
11225 "ignoring invalid defined png:compression-strategy",
11226 "=%s",value);
11227 }
11228
11229 value=GetImageArtifact(image,"png:compression-filter");
11230 if (value == NULL)
11231 value=GetImageOption(image_info,"png:compression-filter");
11232 if (value != NULL)
11233 {
11234
11235 /* To do: combinations of filters allowed by libpng
11236 * masks 0x08 through 0xf8
11237 *
11238 * Implement this as a comma-separated list of 0,1,2,3,4,5
11239 * where 5 is a special case meaning PNG_ALL_FILTERS.
11240 */
11241
11242 if (LocaleCompare(value,"0") == 0)
11243 mng_info->write_png_compression_filter = 1;
11244
11245 if (LocaleCompare(value,"1") == 0)
11246 mng_info->write_png_compression_filter = 2;
11247
11248 else if (LocaleCompare(value,"2") == 0)
11249 mng_info->write_png_compression_filter = 3;
11250
11251 else if (LocaleCompare(value,"3") == 0)
11252 mng_info->write_png_compression_filter = 4;
11253
11254 else if (LocaleCompare(value,"4") == 0)
11255 mng_info->write_png_compression_filter = 5;
11256
11257 else if (LocaleCompare(value,"5") == 0)
11258 mng_info->write_png_compression_filter = 6;
11259
glennrp18682582011-06-30 18:11:47 +000011260 else
cristyc82a27b2011-10-21 01:07:16 +000011261 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011262 GetMagickModule(),CoderWarning,
11263 "ignoring invalid defined png:compression-filter",
11264 "=%s",value);
11265 }
11266
glennrp03812ae2010-12-24 01:31:34 +000011267 excluding=MagickFalse;
11268
glennrp5c7cf4e2010-12-24 00:30:00 +000011269 for (source=0; source<1; source++)
11270 {
11271 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011272 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011273 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011274
11275 if (value == NULL)
11276 value=GetImageArtifact(image,"png:exclude-chunks");
11277 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011278 else
glennrpacba0042010-12-24 14:27:26 +000011279 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011280 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011281
glennrpacba0042010-12-24 14:27:26 +000011282 if (value == NULL)
11283 value=GetImageOption(image_info,"png:exclude-chunks");
11284 }
11285
glennrp03812ae2010-12-24 01:31:34 +000011286 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011287 {
glennrp03812ae2010-12-24 01:31:34 +000011288
11289 size_t
11290 last;
11291
11292 excluding=MagickTrue;
11293
11294 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011295 {
11296 if (source == 0)
11297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11298 " png:exclude-chunk=%s found in image artifacts.\n", value);
11299 else
11300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11301 " png:exclude-chunk=%s found in image properties.\n", value);
11302 }
glennrp03812ae2010-12-24 01:31:34 +000011303
11304 last=strlen(value);
11305
11306 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011307 {
glennrp03812ae2010-12-24 01:31:34 +000011308
11309 if (LocaleNCompare(value+i,"all",3) == 0)
11310 {
11311 mng_info->ping_exclude_bKGD=MagickTrue;
11312 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011313 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011314 mng_info->ping_exclude_EXIF=MagickTrue;
11315 mng_info->ping_exclude_gAMA=MagickTrue;
11316 mng_info->ping_exclude_iCCP=MagickTrue;
11317 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11318 mng_info->ping_exclude_oFFs=MagickTrue;
11319 mng_info->ping_exclude_pHYs=MagickTrue;
11320 mng_info->ping_exclude_sRGB=MagickTrue;
11321 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011322 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011323 mng_info->ping_exclude_vpAg=MagickTrue;
11324 mng_info->ping_exclude_zCCP=MagickTrue;
11325 mng_info->ping_exclude_zTXt=MagickTrue;
11326 i--;
11327 }
glennrp2cc891a2010-12-24 13:44:32 +000011328
glennrp03812ae2010-12-24 01:31:34 +000011329 if (LocaleNCompare(value+i,"none",4) == 0)
11330 {
11331 mng_info->ping_exclude_bKGD=MagickFalse;
11332 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011333 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011334 mng_info->ping_exclude_EXIF=MagickFalse;
11335 mng_info->ping_exclude_gAMA=MagickFalse;
11336 mng_info->ping_exclude_iCCP=MagickFalse;
11337 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11338 mng_info->ping_exclude_oFFs=MagickFalse;
11339 mng_info->ping_exclude_pHYs=MagickFalse;
11340 mng_info->ping_exclude_sRGB=MagickFalse;
11341 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011342 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011343 mng_info->ping_exclude_vpAg=MagickFalse;
11344 mng_info->ping_exclude_zCCP=MagickFalse;
11345 mng_info->ping_exclude_zTXt=MagickFalse;
11346 }
glennrp2cc891a2010-12-24 13:44:32 +000011347
glennrp03812ae2010-12-24 01:31:34 +000011348 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11349 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011350
glennrp03812ae2010-12-24 01:31:34 +000011351 if (LocaleNCompare(value+i,"chrm",4) == 0)
11352 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011353
glennrpa0ed0092011-04-18 16:36:29 +000011354 if (LocaleNCompare(value+i,"date",4) == 0)
11355 mng_info->ping_exclude_date=MagickTrue;
11356
glennrp03812ae2010-12-24 01:31:34 +000011357 if (LocaleNCompare(value+i,"exif",4) == 0)
11358 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011359
glennrp03812ae2010-12-24 01:31:34 +000011360 if (LocaleNCompare(value+i,"gama",4) == 0)
11361 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011362
glennrp03812ae2010-12-24 01:31:34 +000011363 if (LocaleNCompare(value+i,"iccp",4) == 0)
11364 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011365
glennrp03812ae2010-12-24 01:31:34 +000011366 /*
11367 if (LocaleNCompare(value+i,"itxt",4) == 0)
11368 mng_info->ping_exclude_iTXt=MagickTrue;
11369 */
glennrp2cc891a2010-12-24 13:44:32 +000011370
glennrp03812ae2010-12-24 01:31:34 +000011371 if (LocaleNCompare(value+i,"gama",4) == 0)
11372 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011373
glennrp03812ae2010-12-24 01:31:34 +000011374 if (LocaleNCompare(value+i,"offs",4) == 0)
11375 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011376
glennrp03812ae2010-12-24 01:31:34 +000011377 if (LocaleNCompare(value+i,"phys",4) == 0)
11378 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011379
glennrpa1e3b7b2010-12-24 16:37:33 +000011380 if (LocaleNCompare(value+i,"srgb",4) == 0)
11381 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011382
glennrp03812ae2010-12-24 01:31:34 +000011383 if (LocaleNCompare(value+i,"text",4) == 0)
11384 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011385
glennrpa1e3b7b2010-12-24 16:37:33 +000011386 if (LocaleNCompare(value+i,"trns",4) == 0)
11387 mng_info->ping_exclude_tRNS=MagickTrue;
11388
glennrp03812ae2010-12-24 01:31:34 +000011389 if (LocaleNCompare(value+i,"vpag",4) == 0)
11390 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011391
glennrp03812ae2010-12-24 01:31:34 +000011392 if (LocaleNCompare(value+i,"zccp",4) == 0)
11393 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011394
glennrp03812ae2010-12-24 01:31:34 +000011395 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11396 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011397
glennrp03812ae2010-12-24 01:31:34 +000011398 }
glennrpce91ed52010-12-23 22:37:49 +000011399 }
glennrp26f37912010-12-23 16:22:42 +000011400 }
11401
glennrp5c7cf4e2010-12-24 00:30:00 +000011402 for (source=0; source<1; source++)
11403 {
11404 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011405 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011406 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011407
11408 if (value == NULL)
11409 value=GetImageArtifact(image,"png:include-chunks");
11410 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011411 else
glennrpacba0042010-12-24 14:27:26 +000011412 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011413 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011414
glennrpacba0042010-12-24 14:27:26 +000011415 if (value == NULL)
11416 value=GetImageOption(image_info,"png:include-chunks");
11417 }
11418
glennrp03812ae2010-12-24 01:31:34 +000011419 if (value != NULL)
11420 {
11421 size_t
11422 last;
glennrp26f37912010-12-23 16:22:42 +000011423
glennrp03812ae2010-12-24 01:31:34 +000011424 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011425
glennrp03812ae2010-12-24 01:31:34 +000011426 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011427 {
11428 if (source == 0)
11429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11430 " png:include-chunk=%s found in image artifacts.\n", value);
11431 else
11432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11433 " png:include-chunk=%s found in image properties.\n", value);
11434 }
glennrp03812ae2010-12-24 01:31:34 +000011435
11436 last=strlen(value);
11437
11438 for (i=0; i<(int) last; i+=5)
11439 {
11440 if (LocaleNCompare(value+i,"all",3) == 0)
11441 {
11442 mng_info->ping_exclude_bKGD=MagickFalse;
11443 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011444 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011445 mng_info->ping_exclude_EXIF=MagickFalse;
11446 mng_info->ping_exclude_gAMA=MagickFalse;
11447 mng_info->ping_exclude_iCCP=MagickFalse;
11448 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11449 mng_info->ping_exclude_oFFs=MagickFalse;
11450 mng_info->ping_exclude_pHYs=MagickFalse;
11451 mng_info->ping_exclude_sRGB=MagickFalse;
11452 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011453 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011454 mng_info->ping_exclude_vpAg=MagickFalse;
11455 mng_info->ping_exclude_zCCP=MagickFalse;
11456 mng_info->ping_exclude_zTXt=MagickFalse;
11457 i--;
11458 }
glennrp2cc891a2010-12-24 13:44:32 +000011459
glennrp03812ae2010-12-24 01:31:34 +000011460 if (LocaleNCompare(value+i,"none",4) == 0)
11461 {
11462 mng_info->ping_exclude_bKGD=MagickTrue;
11463 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011464 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011465 mng_info->ping_exclude_EXIF=MagickTrue;
11466 mng_info->ping_exclude_gAMA=MagickTrue;
11467 mng_info->ping_exclude_iCCP=MagickTrue;
11468 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11469 mng_info->ping_exclude_oFFs=MagickTrue;
11470 mng_info->ping_exclude_pHYs=MagickTrue;
11471 mng_info->ping_exclude_sRGB=MagickTrue;
11472 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011473 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011474 mng_info->ping_exclude_vpAg=MagickTrue;
11475 mng_info->ping_exclude_zCCP=MagickTrue;
11476 mng_info->ping_exclude_zTXt=MagickTrue;
11477 }
glennrp2cc891a2010-12-24 13:44:32 +000011478
glennrp03812ae2010-12-24 01:31:34 +000011479 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11480 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011481
glennrp03812ae2010-12-24 01:31:34 +000011482 if (LocaleNCompare(value+i,"chrm",4) == 0)
11483 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011484
glennrpa0ed0092011-04-18 16:36:29 +000011485 if (LocaleNCompare(value+i,"date",4) == 0)
11486 mng_info->ping_exclude_date=MagickFalse;
11487
glennrp03812ae2010-12-24 01:31:34 +000011488 if (LocaleNCompare(value+i,"exif",4) == 0)
11489 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011490
glennrp03812ae2010-12-24 01:31:34 +000011491 if (LocaleNCompare(value+i,"gama",4) == 0)
11492 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011493
glennrp03812ae2010-12-24 01:31:34 +000011494 if (LocaleNCompare(value+i,"iccp",4) == 0)
11495 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011496
glennrp03812ae2010-12-24 01:31:34 +000011497 /*
11498 if (LocaleNCompare(value+i,"itxt",4) == 0)
11499 mng_info->ping_exclude_iTXt=MagickFalse;
11500 */
glennrp2cc891a2010-12-24 13:44:32 +000011501
glennrp03812ae2010-12-24 01:31:34 +000011502 if (LocaleNCompare(value+i,"gama",4) == 0)
11503 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011504
glennrp03812ae2010-12-24 01:31:34 +000011505 if (LocaleNCompare(value+i,"offs",4) == 0)
11506 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011507
glennrp03812ae2010-12-24 01:31:34 +000011508 if (LocaleNCompare(value+i,"phys",4) == 0)
11509 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011510
glennrpa1e3b7b2010-12-24 16:37:33 +000011511 if (LocaleNCompare(value+i,"srgb",4) == 0)
11512 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011513
glennrp03812ae2010-12-24 01:31:34 +000011514 if (LocaleNCompare(value+i,"text",4) == 0)
11515 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011516
glennrpa1e3b7b2010-12-24 16:37:33 +000011517 if (LocaleNCompare(value+i,"trns",4) == 0)
11518 mng_info->ping_exclude_tRNS=MagickFalse;
11519
glennrp03812ae2010-12-24 01:31:34 +000011520 if (LocaleNCompare(value+i,"vpag",4) == 0)
11521 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011522
glennrp03812ae2010-12-24 01:31:34 +000011523 if (LocaleNCompare(value+i,"zccp",4) == 0)
11524 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011525
glennrp03812ae2010-12-24 01:31:34 +000011526 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11527 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011528
glennrp03812ae2010-12-24 01:31:34 +000011529 }
glennrpce91ed52010-12-23 22:37:49 +000011530 }
glennrp26f37912010-12-23 16:22:42 +000011531 }
11532
glennrp03812ae2010-12-24 01:31:34 +000011533 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011534 {
11535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011536 " Chunks to be excluded from the output png:");
glennrp26f37912010-12-23 16:22:42 +000011537 if (mng_info->ping_exclude_bKGD != MagickFalse)
11538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11539 " bKGD");
11540 if (mng_info->ping_exclude_cHRM != MagickFalse)
11541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11542 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011543 if (mng_info->ping_exclude_date != MagickFalse)
11544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11545 " date");
glennrp26f37912010-12-23 16:22:42 +000011546 if (mng_info->ping_exclude_EXIF != MagickFalse)
11547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11548 " EXIF");
11549 if (mng_info->ping_exclude_gAMA != MagickFalse)
11550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11551 " gAMA");
11552 if (mng_info->ping_exclude_iCCP != MagickFalse)
11553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11554 " iCCP");
11555/*
11556 if (mng_info->ping_exclude_iTXt != MagickFalse)
11557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11558 " iTXt");
11559*/
11560 if (mng_info->ping_exclude_oFFs != MagickFalse)
11561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11562 " oFFs");
11563 if (mng_info->ping_exclude_pHYs != MagickFalse)
11564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11565 " pHYs");
11566 if (mng_info->ping_exclude_sRGB != MagickFalse)
11567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11568 " sRGB");
11569 if (mng_info->ping_exclude_tEXt != MagickFalse)
11570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11571 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011572 if (mng_info->ping_exclude_tRNS != MagickFalse)
11573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11574 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011575 if (mng_info->ping_exclude_vpAg != MagickFalse)
11576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11577 " vpAg");
11578 if (mng_info->ping_exclude_zCCP != MagickFalse)
11579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11580 " zCCP");
11581 if (mng_info->ping_exclude_zTXt != MagickFalse)
11582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11583 " zTXt");
11584 }
11585
glennrpb9cfe272010-12-21 15:08:06 +000011586 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011587
cristy018f07f2011-09-04 21:15:19 +000011588 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011589
11590 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011591
cristy3ed852e2009-09-05 21:47:34 +000011592 if (logging != MagickFalse)
11593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011594
cristy3ed852e2009-09-05 21:47:34 +000011595 return(status);
11596}
11597
11598#if defined(JNG_SUPPORTED)
11599
11600/* Write one JNG image */
11601static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +000011602 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011603{
11604 Image
11605 *jpeg_image;
11606
11607 ImageInfo
11608 *jpeg_image_info;
11609
11610 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011611 logging,
cristy3ed852e2009-09-05 21:47:34 +000011612 status;
11613
11614 size_t
11615 length;
11616
11617 unsigned char
11618 *blob,
11619 chunk[80],
11620 *p;
11621
11622 unsigned int
11623 jng_alpha_compression_method,
11624 jng_alpha_sample_depth,
11625 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011626 transparent;
11627
cristybb503372010-05-27 20:51:26 +000011628 size_t
cristy3ed852e2009-09-05 21:47:34 +000011629 jng_quality;
11630
11631 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011632 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011633
11634 blob=(unsigned char *) NULL;
11635 jpeg_image=(Image *) NULL;
11636 jpeg_image_info=(ImageInfo *) NULL;
11637
11638 status=MagickTrue;
11639 transparent=image_info->type==GrayscaleMatteType ||
11640 image_info->type==TrueColorMatteType;
11641 jng_color_type=10;
11642 jng_alpha_sample_depth=0;
11643 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11644 jng_alpha_compression_method=0;
11645
11646 if (image->matte != MagickFalse)
11647 {
11648 /* if any pixels are transparent */
11649 transparent=MagickTrue;
11650 if (image_info->compression==JPEGCompression)
11651 jng_alpha_compression_method=8;
11652 }
11653
11654 if (transparent)
11655 {
cristybd5a96c2011-08-21 00:04:26 +000011656 ChannelType
11657 channel_mask;
11658
cristy3ed852e2009-09-05 21:47:34 +000011659 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011660
cristy3ed852e2009-09-05 21:47:34 +000011661 /* Create JPEG blob, image, and image_info */
11662 if (logging != MagickFalse)
11663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +000011664 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011665
cristy3ed852e2009-09-05 21:47:34 +000011666 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011667
cristy3ed852e2009-09-05 21:47:34 +000011668 if (jpeg_image_info == (ImageInfo *) NULL)
11669 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011670
cristy3ed852e2009-09-05 21:47:34 +000011671 if (logging != MagickFalse)
11672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11673 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011674
cristyc82a27b2011-10-21 01:07:16 +000011675 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011676
cristy3ed852e2009-09-05 21:47:34 +000011677 if (jpeg_image == (Image *) NULL)
11678 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011679
cristy3ed852e2009-09-05 21:47:34 +000011680 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristybd5a96c2011-08-21 00:04:26 +000011681 channel_mask=SetPixelChannelMask(jpeg_image,AlphaChannel);
cristye941a752011-10-15 01:52:48 +000011682 status=SeparateImage(jpeg_image,exception);
cristye2a912b2011-12-05 20:02:07 +000011683 (void) SetPixelChannelMapMask(jpeg_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +000011684 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011685
cristy3ed852e2009-09-05 21:47:34 +000011686 if (jng_quality >= 1000)
11687 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000011688
cristy3ed852e2009-09-05 21:47:34 +000011689 else
11690 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000011691
cristy3ed852e2009-09-05 21:47:34 +000011692 jpeg_image_info->type=GrayscaleType;
cristy018f07f2011-09-04 21:15:19 +000011693 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000011694 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011695 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011696 "%s",jpeg_image->filename);
11697 }
11698
11699 /* To do: check bit depth of PNG alpha channel */
11700
11701 /* Check if image is grayscale. */
11702 if (image_info->type != TrueColorMatteType && image_info->type !=
cristyc82a27b2011-10-21 01:07:16 +000011703 TrueColorType && ImageIsGray(image,exception))
cristy3ed852e2009-09-05 21:47:34 +000011704 jng_color_type-=2;
11705
11706 if (transparent)
11707 {
11708 if (jng_alpha_compression_method==0)
11709 {
11710 const char
11711 *value;
11712
cristy4c08aed2011-07-01 19:47:50 +000011713 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011714 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000011715 exception);
cristy3ed852e2009-09-05 21:47:34 +000011716 if (logging != MagickFalse)
11717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11718 " Creating PNG blob.");
11719 length=0;
11720
11721 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11722 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11723 jpeg_image_info->interlace=NoInterlace;
11724
11725 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyc82a27b2011-10-21 01:07:16 +000011726 exception);
cristy3ed852e2009-09-05 21:47:34 +000011727
11728 /* Retrieve sample depth used */
cristyd15e6592011-10-15 00:13:06 +000011729 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
cristy3ed852e2009-09-05 21:47:34 +000011730 if (value != (char *) NULL)
11731 jng_alpha_sample_depth= (unsigned int) value[0];
11732 }
11733 else
11734 {
cristy4c08aed2011-07-01 19:47:50 +000011735 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011736
11737 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000011738 exception);
cristy3ed852e2009-09-05 21:47:34 +000011739
11740 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11741 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11742 jpeg_image_info->interlace=NoInterlace;
11743 if (logging != MagickFalse)
11744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11745 " Creating blob.");
11746 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyc82a27b2011-10-21 01:07:16 +000011747 exception);
cristy3ed852e2009-09-05 21:47:34 +000011748 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011749
cristy3ed852e2009-09-05 21:47:34 +000011750 if (logging != MagickFalse)
11751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011752 " Successfully read jpeg_image into a blob, length=%.20g.",
11753 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011754
11755 }
11756 /* Destroy JPEG image and image_info */
11757 jpeg_image=DestroyImage(jpeg_image);
11758 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11759 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11760 }
11761
11762 /* Write JHDR chunk */
11763 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11764 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011765 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011766 PNGLong(chunk+4,(png_uint_32) image->columns);
11767 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011768 chunk[12]=jng_color_type;
11769 chunk[13]=8; /* sample depth */
11770 chunk[14]=8; /*jng_image_compression_method */
11771 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11772 chunk[16]=jng_alpha_sample_depth;
11773 chunk[17]=jng_alpha_compression_method;
11774 chunk[18]=0; /*jng_alpha_filter_method */
11775 chunk[19]=0; /*jng_alpha_interlace_method */
11776 (void) WriteBlob(image,20,chunk);
11777 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11778 if (logging != MagickFalse)
11779 {
11780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011781 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011782
cristy3ed852e2009-09-05 21:47:34 +000011783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011784 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011785
cristy3ed852e2009-09-05 21:47:34 +000011786 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11787 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011788
cristy3ed852e2009-09-05 21:47:34 +000011789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11790 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011791
cristy3ed852e2009-09-05 21:47:34 +000011792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11793 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011794
cristy3ed852e2009-09-05 21:47:34 +000011795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11796 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011797
cristy3ed852e2009-09-05 21:47:34 +000011798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11799 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011800
cristy3ed852e2009-09-05 21:47:34 +000011801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11802 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011803
cristy3ed852e2009-09-05 21:47:34 +000011804 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11805 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011806
cristy3ed852e2009-09-05 21:47:34 +000011807 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11808 " JNG alpha interlace:%5d",0);
11809 }
11810
glennrp0fe50b42010-11-16 03:52:51 +000011811 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011812 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011813
11814 /*
11815 Write leading ancillary chunks
11816 */
11817
11818 if (transparent)
11819 {
11820 /*
11821 Write JNG bKGD chunk
11822 */
11823
11824 unsigned char
11825 blue,
11826 green,
11827 red;
11828
cristybb503372010-05-27 20:51:26 +000011829 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011830 num_bytes;
11831
11832 if (jng_color_type == 8 || jng_color_type == 12)
11833 num_bytes=6L;
11834 else
11835 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011836 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011837 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011838 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011839 red=ScaleQuantumToChar(image->background_color.red);
11840 green=ScaleQuantumToChar(image->background_color.green);
11841 blue=ScaleQuantumToChar(image->background_color.blue);
11842 *(chunk+4)=0;
11843 *(chunk+5)=red;
11844 *(chunk+6)=0;
11845 *(chunk+7)=green;
11846 *(chunk+8)=0;
11847 *(chunk+9)=blue;
11848 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11849 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11850 }
11851
11852 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11853 {
11854 /*
11855 Write JNG sRGB chunk
11856 */
11857 (void) WriteBlobMSBULong(image,1L);
11858 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011859 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011860
cristy3ed852e2009-09-05 21:47:34 +000011861 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011862 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011863 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011864 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011865
cristy3ed852e2009-09-05 21:47:34 +000011866 else
glennrpe610a072010-08-05 17:08:46 +000011867 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011868 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011869 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011870
cristy3ed852e2009-09-05 21:47:34 +000011871 (void) WriteBlob(image,5,chunk);
11872 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11873 }
11874 else
11875 {
11876 if (image->gamma != 0.0)
11877 {
11878 /*
11879 Write JNG gAMA chunk
11880 */
11881 (void) WriteBlobMSBULong(image,4L);
11882 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011883 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011884 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011885 (void) WriteBlob(image,8,chunk);
11886 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11887 }
glennrp0fe50b42010-11-16 03:52:51 +000011888
cristy3ed852e2009-09-05 21:47:34 +000011889 if ((mng_info->equal_chrms == MagickFalse) &&
11890 (image->chromaticity.red_primary.x != 0.0))
11891 {
11892 PrimaryInfo
11893 primary;
11894
11895 /*
11896 Write JNG cHRM chunk
11897 */
11898 (void) WriteBlobMSBULong(image,32L);
11899 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011900 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011901 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011902 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11903 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011904 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011905 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11906 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011907 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011908 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11909 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011910 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011911 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11912 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011913 (void) WriteBlob(image,36,chunk);
11914 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11915 }
11916 }
glennrp0fe50b42010-11-16 03:52:51 +000011917
cristy2a11bef2011-10-28 18:33:11 +000011918 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000011919 {
11920 /*
11921 Write JNG pHYs chunk
11922 */
11923 (void) WriteBlobMSBULong(image,9L);
11924 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011925 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011926 if (image->units == PixelsPerInchResolution)
11927 {
cristy35ef8242010-06-03 16:24:13 +000011928 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011929 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011930
cristy35ef8242010-06-03 16:24:13 +000011931 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011932 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011933
cristy3ed852e2009-09-05 21:47:34 +000011934 chunk[12]=1;
11935 }
glennrp0fe50b42010-11-16 03:52:51 +000011936
cristy3ed852e2009-09-05 21:47:34 +000011937 else
11938 {
11939 if (image->units == PixelsPerCentimeterResolution)
11940 {
cristy35ef8242010-06-03 16:24:13 +000011941 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011942 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011943
cristy35ef8242010-06-03 16:24:13 +000011944 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000011945 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011946
cristy3ed852e2009-09-05 21:47:34 +000011947 chunk[12]=1;
11948 }
glennrp0fe50b42010-11-16 03:52:51 +000011949
cristy3ed852e2009-09-05 21:47:34 +000011950 else
11951 {
cristy2a11bef2011-10-28 18:33:11 +000011952 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
11953 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011954 chunk[12]=0;
11955 }
11956 }
11957 (void) WriteBlob(image,13,chunk);
11958 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11959 }
11960
11961 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11962 {
11963 /*
11964 Write JNG oFFs chunk
11965 */
11966 (void) WriteBlobMSBULong(image,9L);
11967 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011968 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011969 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11970 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011971 chunk[12]=0;
11972 (void) WriteBlob(image,13,chunk);
11973 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11974 }
11975 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11976 {
11977 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11978 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011979 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011980 PNGLong(chunk+4,(png_uint_32) image->page.width);
11981 PNGLong(chunk+8,(png_uint_32) image->page.height);
11982 chunk[12]=0; /* unit = pixels */
11983 (void) WriteBlob(image,13,chunk);
11984 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11985 }
11986
11987
11988 if (transparent)
11989 {
11990 if (jng_alpha_compression_method==0)
11991 {
cristybb503372010-05-27 20:51:26 +000011992 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011993 i;
11994
cristybb503372010-05-27 20:51:26 +000011995 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011996 len;
11997
11998 /* Write IDAT chunk header */
11999 if (logging != MagickFalse)
12000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012001 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000012002 length);
cristy3ed852e2009-09-05 21:47:34 +000012003
12004 /* Copy IDAT chunks */
12005 len=0;
12006 p=blob+8;
cristybb503372010-05-27 20:51:26 +000012007 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000012008 {
12009 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12010 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012011
cristy3ed852e2009-09-05 21:47:34 +000012012 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12013 {
12014 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000012015 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000012016 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000012017 (void) WriteBlob(image,(size_t) len+4,p);
12018 (void) WriteBlobMSBULong(image,
12019 crc32(0,p,(uInt) len+4));
12020 }
glennrp0fe50b42010-11-16 03:52:51 +000012021
cristy3ed852e2009-09-05 21:47:34 +000012022 else
12023 {
12024 if (logging != MagickFalse)
12025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012026 " Skipping %c%c%c%c chunk, length=%.20g.",
12027 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012028 }
12029 p+=(8+len);
12030 }
12031 }
12032 else
12033 {
12034 /* Write JDAA chunk header */
12035 if (logging != MagickFalse)
12036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012037 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012038 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012039 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012040 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012041 /* Write JDAT chunk(s) data */
12042 (void) WriteBlob(image,4,chunk);
12043 (void) WriteBlob(image,length,blob);
12044 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12045 (uInt) length));
12046 }
12047 blob=(unsigned char *) RelinquishMagickMemory(blob);
12048 }
12049
12050 /* Encode image as a JPEG blob */
12051 if (logging != MagickFalse)
12052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12053 " Creating jpeg_image_info.");
12054 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12055 if (jpeg_image_info == (ImageInfo *) NULL)
12056 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12057
12058 if (logging != MagickFalse)
12059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12060 " Creating jpeg_image.");
12061
cristyc82a27b2011-10-21 01:07:16 +000012062 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012063 if (jpeg_image == (Image *) NULL)
12064 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12065 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12066
12067 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012068 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012069 jpeg_image->filename);
12070
12071 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristyc82a27b2011-10-21 01:07:16 +000012072 exception);
cristy3ed852e2009-09-05 21:47:34 +000012073
12074 if (logging != MagickFalse)
12075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012076 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12077 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012078
12079 if (jng_color_type == 8 || jng_color_type == 12)
12080 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012081
cristy3ed852e2009-09-05 21:47:34 +000012082 jpeg_image_info->quality=jng_quality % 1000;
12083 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12084 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012085
cristy3ed852e2009-09-05 21:47:34 +000012086 if (logging != MagickFalse)
12087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12088 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012089
cristyc82a27b2011-10-21 01:07:16 +000012090 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
glennrp0fe50b42010-11-16 03:52:51 +000012091
cristy3ed852e2009-09-05 21:47:34 +000012092 if (logging != MagickFalse)
12093 {
12094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012095 " Successfully read jpeg_image into a blob, length=%.20g.",
12096 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012097
12098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012099 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012100 }
glennrp0fe50b42010-11-16 03:52:51 +000012101
cristy3ed852e2009-09-05 21:47:34 +000012102 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012103 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012104 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012105 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012106 (void) WriteBlob(image,4,chunk);
12107 (void) WriteBlob(image,length,blob);
12108 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12109
12110 jpeg_image=DestroyImage(jpeg_image);
12111 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12112 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12113 blob=(unsigned char *) RelinquishMagickMemory(blob);
12114
12115 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012116 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012117
12118 /* Write IEND chunk */
12119 (void) WriteBlobMSBULong(image,0L);
12120 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012121 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012122 (void) WriteBlob(image,4,chunk);
12123 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12124
12125 if (logging != MagickFalse)
12126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12127 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012128
cristy3ed852e2009-09-05 21:47:34 +000012129 return(status);
12130}
12131
12132
12133/*
12134%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12135% %
12136% %
12137% %
12138% W r i t e J N G I m a g e %
12139% %
12140% %
12141% %
12142%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12143%
12144% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12145%
12146% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12147%
12148% The format of the WriteJNGImage method is:
12149%
cristy1e178e72011-08-28 19:44:34 +000012150% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12151% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012152%
12153% A description of each parameter follows:
12154%
12155% o image_info: the image info.
12156%
12157% o image: The image.
12158%
cristy1e178e72011-08-28 19:44:34 +000012159% o exception: return any errors or warnings in this structure.
12160%
cristy3ed852e2009-09-05 21:47:34 +000012161%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12162*/
cristy1e178e72011-08-28 19:44:34 +000012163static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12164 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012165{
12166 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012167 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012168 logging,
cristy3ed852e2009-09-05 21:47:34 +000012169 status;
12170
12171 MngInfo
12172 *mng_info;
12173
cristy3ed852e2009-09-05 21:47:34 +000012174 /*
12175 Open image file.
12176 */
12177 assert(image_info != (const ImageInfo *) NULL);
12178 assert(image_info->signature == MagickSignature);
12179 assert(image != (Image *) NULL);
12180 assert(image->signature == MagickSignature);
12181 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012182 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristyc82a27b2011-10-21 01:07:16 +000012183 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012184 if (status == MagickFalse)
12185 return(status);
12186
12187 /*
12188 Allocate a MngInfo structure.
12189 */
12190 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012191 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012192 if (mng_info == (MngInfo *) NULL)
12193 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12194 /*
12195 Initialize members of the MngInfo structure.
12196 */
12197 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12198 mng_info->image=image;
12199 have_mng_structure=MagickTrue;
12200
12201 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12202
cristy018f07f2011-09-04 21:15:19 +000012203 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012204 (void) CloseBlob(image);
12205
12206 (void) CatchImageException(image);
12207 MngInfoFreeStruct(mng_info,&have_mng_structure);
12208 if (logging != MagickFalse)
12209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12210 return(status);
12211}
12212#endif
12213
cristy1e178e72011-08-28 19:44:34 +000012214static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12215 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012216{
12217 const char
12218 *option;
12219
12220 Image
12221 *next_image;
12222
12223 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012224 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012225 status;
12226
glennrp03812ae2010-12-24 01:31:34 +000012227 volatile MagickBooleanType
12228 logging;
12229
cristy3ed852e2009-09-05 21:47:34 +000012230 MngInfo
12231 *mng_info;
12232
12233 int
cristy3ed852e2009-09-05 21:47:34 +000012234 image_count,
12235 need_iterations,
12236 need_matte;
12237
12238 volatile int
12239#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12240 defined(PNG_MNG_FEATURES_SUPPORTED)
12241 need_local_plte,
12242#endif
12243 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012244 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012245 use_global_plte;
12246
cristybb503372010-05-27 20:51:26 +000012247 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012248 i;
12249
12250 unsigned char
12251 chunk[800];
12252
12253 volatile unsigned int
12254 write_jng,
12255 write_mng;
12256
cristybb503372010-05-27 20:51:26 +000012257 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012258 scene;
12259
cristybb503372010-05-27 20:51:26 +000012260 size_t
cristy3ed852e2009-09-05 21:47:34 +000012261 final_delay=0,
12262 initial_delay;
12263
glennrpd5045b42010-03-24 12:40:35 +000012264#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012265 if (image_info->verbose)
12266 printf("Your PNG library (libpng-%s) is rather old.\n",
12267 PNG_LIBPNG_VER_STRING);
12268#endif
12269
12270 /*
12271 Open image file.
12272 */
12273 assert(image_info != (const ImageInfo *) NULL);
12274 assert(image_info->signature == MagickSignature);
12275 assert(image != (Image *) NULL);
12276 assert(image->signature == MagickSignature);
12277 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012278 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristyc82a27b2011-10-21 01:07:16 +000012279 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012280 if (status == MagickFalse)
12281 return(status);
12282
12283 /*
12284 Allocate a MngInfo structure.
12285 */
12286 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012287 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012288 if (mng_info == (MngInfo *) NULL)
12289 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12290 /*
12291 Initialize members of the MngInfo structure.
12292 */
12293 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12294 mng_info->image=image;
12295 have_mng_structure=MagickTrue;
12296 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12297
12298 /*
12299 * See if user has requested a specific PNG subformat to be used
12300 * for all of the PNGs in the MNG being written, e.g.,
12301 *
12302 * convert *.png png8:animation.mng
12303 *
12304 * To do: check -define png:bit_depth and png:color_type as well,
12305 * or perhaps use mng:bit_depth and mng:color_type instead for
12306 * global settings.
12307 */
12308
12309 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12310 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12311 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12312
12313 write_jng=MagickFalse;
12314 if (image_info->compression == JPEGCompression)
12315 write_jng=MagickTrue;
12316
12317 mng_info->adjoin=image_info->adjoin &&
12318 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12319
cristy3ed852e2009-09-05 21:47:34 +000012320 if (logging != MagickFalse)
12321 {
12322 /* Log some info about the input */
12323 Image
12324 *p;
12325
12326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12327 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012328
cristy3ed852e2009-09-05 21:47:34 +000012329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012330 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012331
cristy3ed852e2009-09-05 21:47:34 +000012332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12333 " Type: %d",image_info->type);
12334
12335 scene=0;
12336 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12337 {
12338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012339 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012340
cristy3ed852e2009-09-05 21:47:34 +000012341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012342 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012343
cristy3ed852e2009-09-05 21:47:34 +000012344 if (p->matte)
12345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12346 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012347
cristy3ed852e2009-09-05 21:47:34 +000012348 else
12349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12350 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012351
cristy3ed852e2009-09-05 21:47:34 +000012352 if (p->storage_class == PseudoClass)
12353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12354 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012355
cristy3ed852e2009-09-05 21:47:34 +000012356 else
12357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12358 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012359
cristy3ed852e2009-09-05 21:47:34 +000012360 if (p->colors)
12361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012362 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012363
cristy3ed852e2009-09-05 21:47:34 +000012364 else
12365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12366 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012367
cristy3ed852e2009-09-05 21:47:34 +000012368 if (mng_info->adjoin == MagickFalse)
12369 break;
12370 }
12371 }
12372
cristy3ed852e2009-09-05 21:47:34 +000012373 use_global_plte=MagickFalse;
12374 all_images_are_gray=MagickFalse;
12375#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12376 need_local_plte=MagickTrue;
12377#endif
12378 need_defi=MagickFalse;
12379 need_matte=MagickFalse;
12380 mng_info->framing_mode=1;
12381 mng_info->old_framing_mode=1;
12382
12383 if (write_mng)
12384 if (image_info->page != (char *) NULL)
12385 {
12386 /*
12387 Determine image bounding box.
12388 */
12389 SetGeometry(image,&mng_info->page);
12390 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12391 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12392 }
12393 if (write_mng)
12394 {
12395 unsigned int
12396 need_geom;
12397
12398 unsigned short
12399 red,
12400 green,
12401 blue;
12402
12403 mng_info->page=image->page;
12404 need_geom=MagickTrue;
12405 if (mng_info->page.width || mng_info->page.height)
12406 need_geom=MagickFalse;
12407 /*
12408 Check all the scenes.
12409 */
12410 initial_delay=image->delay;
12411 need_iterations=MagickFalse;
12412 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12413 mng_info->equal_physs=MagickTrue,
12414 mng_info->equal_gammas=MagickTrue;
12415 mng_info->equal_srgbs=MagickTrue;
12416 mng_info->equal_backgrounds=MagickTrue;
12417 image_count=0;
12418#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12419 defined(PNG_MNG_FEATURES_SUPPORTED)
12420 all_images_are_gray=MagickTrue;
12421 mng_info->equal_palettes=MagickFalse;
12422 need_local_plte=MagickFalse;
12423#endif
12424 for (next_image=image; next_image != (Image *) NULL; )
12425 {
12426 if (need_geom)
12427 {
12428 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12429 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012430
cristy3ed852e2009-09-05 21:47:34 +000012431 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12432 mng_info->page.height=next_image->rows+next_image->page.y;
12433 }
glennrp0fe50b42010-11-16 03:52:51 +000012434
cristy3ed852e2009-09-05 21:47:34 +000012435 if (next_image->page.x || next_image->page.y)
12436 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012437
cristy3ed852e2009-09-05 21:47:34 +000012438 if (next_image->matte)
12439 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012440
cristy3ed852e2009-09-05 21:47:34 +000012441 if ((int) next_image->dispose >= BackgroundDispose)
12442 if (next_image->matte || next_image->page.x || next_image->page.y ||
12443 ((next_image->columns < mng_info->page.width) &&
12444 (next_image->rows < mng_info->page.height)))
12445 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012446
cristy3ed852e2009-09-05 21:47:34 +000012447 if (next_image->iterations)
12448 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012449
cristy3ed852e2009-09-05 21:47:34 +000012450 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012451
cristy3ed852e2009-09-05 21:47:34 +000012452 if (final_delay != initial_delay || final_delay > 1UL*
12453 next_image->ticks_per_second)
12454 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012455
cristy3ed852e2009-09-05 21:47:34 +000012456#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12457 defined(PNG_MNG_FEATURES_SUPPORTED)
12458 /*
12459 check for global palette possibility.
12460 */
12461 if (image->matte != MagickFalse)
12462 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012463
cristy3ed852e2009-09-05 21:47:34 +000012464 if (need_local_plte == 0)
12465 {
cristyc82a27b2011-10-21 01:07:16 +000012466 if (ImageIsGray(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012467 all_images_are_gray=MagickFalse;
12468 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12469 if (use_global_plte == 0)
12470 use_global_plte=mng_info->equal_palettes;
12471 need_local_plte=!mng_info->equal_palettes;
12472 }
12473#endif
12474 if (GetNextImageInList(next_image) != (Image *) NULL)
12475 {
12476 if (next_image->background_color.red !=
12477 next_image->next->background_color.red ||
12478 next_image->background_color.green !=
12479 next_image->next->background_color.green ||
12480 next_image->background_color.blue !=
12481 next_image->next->background_color.blue)
12482 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012483
cristy3ed852e2009-09-05 21:47:34 +000012484 if (next_image->gamma != next_image->next->gamma)
12485 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012486
cristy3ed852e2009-09-05 21:47:34 +000012487 if (next_image->rendering_intent !=
12488 next_image->next->rendering_intent)
12489 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012490
cristy3ed852e2009-09-05 21:47:34 +000012491 if ((next_image->units != next_image->next->units) ||
cristy2a11bef2011-10-28 18:33:11 +000012492 (next_image->resolution.x != next_image->next->resolution.x) ||
12493 (next_image->resolution.y != next_image->next->resolution.y))
cristy3ed852e2009-09-05 21:47:34 +000012494 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012495
cristy3ed852e2009-09-05 21:47:34 +000012496 if (mng_info->equal_chrms)
12497 {
12498 if (next_image->chromaticity.red_primary.x !=
12499 next_image->next->chromaticity.red_primary.x ||
12500 next_image->chromaticity.red_primary.y !=
12501 next_image->next->chromaticity.red_primary.y ||
12502 next_image->chromaticity.green_primary.x !=
12503 next_image->next->chromaticity.green_primary.x ||
12504 next_image->chromaticity.green_primary.y !=
12505 next_image->next->chromaticity.green_primary.y ||
12506 next_image->chromaticity.blue_primary.x !=
12507 next_image->next->chromaticity.blue_primary.x ||
12508 next_image->chromaticity.blue_primary.y !=
12509 next_image->next->chromaticity.blue_primary.y ||
12510 next_image->chromaticity.white_point.x !=
12511 next_image->next->chromaticity.white_point.x ||
12512 next_image->chromaticity.white_point.y !=
12513 next_image->next->chromaticity.white_point.y)
12514 mng_info->equal_chrms=MagickFalse;
12515 }
12516 }
12517 image_count++;
12518 next_image=GetNextImageInList(next_image);
12519 }
12520 if (image_count < 2)
12521 {
12522 mng_info->equal_backgrounds=MagickFalse;
12523 mng_info->equal_chrms=MagickFalse;
12524 mng_info->equal_gammas=MagickFalse;
12525 mng_info->equal_srgbs=MagickFalse;
12526 mng_info->equal_physs=MagickFalse;
12527 use_global_plte=MagickFalse;
12528#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12529 need_local_plte=MagickTrue;
12530#endif
12531 need_iterations=MagickFalse;
12532 }
glennrp0fe50b42010-11-16 03:52:51 +000012533
cristy3ed852e2009-09-05 21:47:34 +000012534 if (mng_info->need_fram == MagickFalse)
12535 {
12536 /*
12537 Only certain framing rates 100/n are exactly representable without
12538 the FRAM chunk but we'll allow some slop in VLC files
12539 */
12540 if (final_delay == 0)
12541 {
12542 if (need_iterations != MagickFalse)
12543 {
12544 /*
12545 It's probably a GIF with loop; don't run it *too* fast.
12546 */
glennrp02617122010-07-28 13:07:35 +000012547 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012548 {
12549 final_delay=10;
cristyc82a27b2011-10-21 01:07:16 +000012550 (void) ThrowMagickException(exception,GetMagickModule(),
12551 CoderWarning,
glennrpd908de42010-07-28 13:28:27 +000012552 "input has zero delay between all frames; assuming",
12553 " 10 cs `%s'","");
12554 }
cristy3ed852e2009-09-05 21:47:34 +000012555 }
12556 else
12557 mng_info->ticks_per_second=0;
12558 }
12559 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012560 mng_info->ticks_per_second=(png_uint_32)
12561 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012562 if (final_delay > 50)
12563 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012564
cristy3ed852e2009-09-05 21:47:34 +000012565 if (final_delay > 75)
12566 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012567
cristy3ed852e2009-09-05 21:47:34 +000012568 if (final_delay > 125)
12569 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012570
cristy3ed852e2009-09-05 21:47:34 +000012571 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12572 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12573 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12574 1UL*image->ticks_per_second))
12575 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12576 }
glennrp0fe50b42010-11-16 03:52:51 +000012577
cristy3ed852e2009-09-05 21:47:34 +000012578 if (mng_info->need_fram != MagickFalse)
12579 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12580 /*
12581 If pseudocolor, we should also check to see if all the
12582 palettes are identical and write a global PLTE if they are.
12583 ../glennrp Feb 99.
12584 */
12585 /*
12586 Write the MNG version 1.0 signature and MHDR chunk.
12587 */
12588 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12589 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12590 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012591 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012592 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12593 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012594 PNGLong(chunk+12,mng_info->ticks_per_second);
12595 PNGLong(chunk+16,0L); /* layer count=unknown */
12596 PNGLong(chunk+20,0L); /* frame count=unknown */
12597 PNGLong(chunk+24,0L); /* play time=unknown */
12598 if (write_jng)
12599 {
12600 if (need_matte)
12601 {
12602 if (need_defi || mng_info->need_fram || use_global_plte)
12603 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012604
cristy3ed852e2009-09-05 21:47:34 +000012605 else
12606 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12607 }
glennrp0fe50b42010-11-16 03:52:51 +000012608
cristy3ed852e2009-09-05 21:47:34 +000012609 else
12610 {
12611 if (need_defi || mng_info->need_fram || use_global_plte)
12612 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012613
cristy3ed852e2009-09-05 21:47:34 +000012614 else
12615 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12616 }
12617 }
glennrp0fe50b42010-11-16 03:52:51 +000012618
cristy3ed852e2009-09-05 21:47:34 +000012619 else
12620 {
12621 if (need_matte)
12622 {
12623 if (need_defi || mng_info->need_fram || use_global_plte)
12624 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012625
cristy3ed852e2009-09-05 21:47:34 +000012626 else
12627 PNGLong(chunk+28,9L); /* simplicity=VLC */
12628 }
glennrp0fe50b42010-11-16 03:52:51 +000012629
cristy3ed852e2009-09-05 21:47:34 +000012630 else
12631 {
12632 if (need_defi || mng_info->need_fram || use_global_plte)
12633 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012634
cristy3ed852e2009-09-05 21:47:34 +000012635 else
12636 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12637 }
12638 }
12639 (void) WriteBlob(image,32,chunk);
12640 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12641 option=GetImageOption(image_info,"mng:need-cacheoff");
12642 if (option != (const char *) NULL)
12643 {
12644 size_t
12645 length;
12646
12647 /*
12648 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12649 */
12650 PNGType(chunk,mng_nEED);
12651 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012652 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012653 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012654 length+=4;
12655 (void) WriteBlob(image,length,chunk);
12656 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12657 }
12658 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12659 (GetNextImageInList(image) != (Image *) NULL) &&
12660 (image->iterations != 1))
12661 {
12662 /*
12663 Write MNG TERM chunk
12664 */
12665 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12666 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012667 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012668 chunk[4]=3; /* repeat animation */
12669 chunk[5]=0; /* show last frame when done */
12670 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12671 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012672
cristy3ed852e2009-09-05 21:47:34 +000012673 if (image->iterations == 0)
12674 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012675
cristy3ed852e2009-09-05 21:47:34 +000012676 else
12677 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012678
cristy3ed852e2009-09-05 21:47:34 +000012679 if (logging != MagickFalse)
12680 {
12681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012682 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12683 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012684
cristy3ed852e2009-09-05 21:47:34 +000012685 if (image->iterations == 0)
12686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012687 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012688
cristy3ed852e2009-09-05 21:47:34 +000012689 else
12690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012691 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012692 }
12693 (void) WriteBlob(image,14,chunk);
12694 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12695 }
12696 /*
12697 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12698 */
12699 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12700 mng_info->equal_srgbs)
12701 {
12702 /*
12703 Write MNG sRGB chunk
12704 */
12705 (void) WriteBlobMSBULong(image,1L);
12706 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012707 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012708
cristy3ed852e2009-09-05 21:47:34 +000012709 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012710 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012711 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012712 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012713
cristy3ed852e2009-09-05 21:47:34 +000012714 else
glennrpe610a072010-08-05 17:08:46 +000012715 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012716 Magick_RenderingIntent_to_PNG_RenderingIntent(
12717 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012718
cristy3ed852e2009-09-05 21:47:34 +000012719 (void) WriteBlob(image,5,chunk);
12720 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12721 mng_info->have_write_global_srgb=MagickTrue;
12722 }
glennrp0fe50b42010-11-16 03:52:51 +000012723
cristy3ed852e2009-09-05 21:47:34 +000012724 else
12725 {
12726 if (image->gamma && mng_info->equal_gammas)
12727 {
12728 /*
12729 Write MNG gAMA chunk
12730 */
12731 (void) WriteBlobMSBULong(image,4L);
12732 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012733 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012734 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012735 (void) WriteBlob(image,8,chunk);
12736 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12737 mng_info->have_write_global_gama=MagickTrue;
12738 }
12739 if (mng_info->equal_chrms)
12740 {
12741 PrimaryInfo
12742 primary;
12743
12744 /*
12745 Write MNG cHRM chunk
12746 */
12747 (void) WriteBlobMSBULong(image,32L);
12748 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012749 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012750 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012751 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12752 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012753 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012754 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12755 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012756 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012757 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12758 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012759 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012760 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12761 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012762 (void) WriteBlob(image,36,chunk);
12763 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12764 mng_info->have_write_global_chrm=MagickTrue;
12765 }
12766 }
cristy2a11bef2011-10-28 18:33:11 +000012767 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012768 {
12769 /*
12770 Write MNG pHYs chunk
12771 */
12772 (void) WriteBlobMSBULong(image,9L);
12773 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012774 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012775
cristy3ed852e2009-09-05 21:47:34 +000012776 if (image->units == PixelsPerInchResolution)
12777 {
cristy35ef8242010-06-03 16:24:13 +000012778 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012779 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012780
cristy35ef8242010-06-03 16:24:13 +000012781 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012782 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012783
cristy3ed852e2009-09-05 21:47:34 +000012784 chunk[12]=1;
12785 }
glennrp0fe50b42010-11-16 03:52:51 +000012786
cristy3ed852e2009-09-05 21:47:34 +000012787 else
12788 {
12789 if (image->units == PixelsPerCentimeterResolution)
12790 {
cristy35ef8242010-06-03 16:24:13 +000012791 PNGLong(chunk+4,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012792 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012793
cristy35ef8242010-06-03 16:24:13 +000012794 PNGLong(chunk+8,(png_uint_32)
cristy2a11bef2011-10-28 18:33:11 +000012795 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012796
cristy3ed852e2009-09-05 21:47:34 +000012797 chunk[12]=1;
12798 }
glennrp0fe50b42010-11-16 03:52:51 +000012799
cristy3ed852e2009-09-05 21:47:34 +000012800 else
12801 {
cristy2a11bef2011-10-28 18:33:11 +000012802 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12803 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012804 chunk[12]=0;
12805 }
12806 }
12807 (void) WriteBlob(image,13,chunk);
12808 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12809 }
12810 /*
12811 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12812 or does not cover the entire frame.
12813 */
12814 if (write_mng && (image->matte || image->page.x > 0 ||
12815 image->page.y > 0 || (image->page.width &&
12816 (image->page.width+image->page.x < mng_info->page.width))
12817 || (image->page.height && (image->page.height+image->page.y
12818 < mng_info->page.height))))
12819 {
12820 (void) WriteBlobMSBULong(image,6L);
12821 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012822 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012823 red=ScaleQuantumToShort(image->background_color.red);
12824 green=ScaleQuantumToShort(image->background_color.green);
12825 blue=ScaleQuantumToShort(image->background_color.blue);
12826 PNGShort(chunk+4,red);
12827 PNGShort(chunk+6,green);
12828 PNGShort(chunk+8,blue);
12829 (void) WriteBlob(image,10,chunk);
12830 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12831 if (mng_info->equal_backgrounds)
12832 {
12833 (void) WriteBlobMSBULong(image,6L);
12834 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012835 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012836 (void) WriteBlob(image,10,chunk);
12837 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12838 }
12839 }
12840
12841#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12842 if ((need_local_plte == MagickFalse) &&
12843 (image->storage_class == PseudoClass) &&
12844 (all_images_are_gray == MagickFalse))
12845 {
cristybb503372010-05-27 20:51:26 +000012846 size_t
cristy3ed852e2009-09-05 21:47:34 +000012847 data_length;
12848
12849 /*
12850 Write MNG PLTE chunk
12851 */
12852 data_length=3*image->colors;
12853 (void) WriteBlobMSBULong(image,data_length);
12854 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012855 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012856
cristybb503372010-05-27 20:51:26 +000012857 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012858 {
cristy5f07f702011-09-26 17:29:10 +000012859 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12860 image->colormap[i].red) & 0xff);
12861 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12862 image->colormap[i].green) & 0xff);
12863 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12864 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000012865 }
glennrp0fe50b42010-11-16 03:52:51 +000012866
cristy3ed852e2009-09-05 21:47:34 +000012867 (void) WriteBlob(image,data_length+4,chunk);
12868 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12869 mng_info->have_write_global_plte=MagickTrue;
12870 }
12871#endif
12872 }
12873 scene=0;
12874 mng_info->delay=0;
12875#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12876 defined(PNG_MNG_FEATURES_SUPPORTED)
12877 mng_info->equal_palettes=MagickFalse;
12878#endif
12879 do
12880 {
12881 if (mng_info->adjoin)
12882 {
12883#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12884 defined(PNG_MNG_FEATURES_SUPPORTED)
12885 /*
12886 If we aren't using a global palette for the entire MNG, check to
12887 see if we can use one for two or more consecutive images.
12888 */
12889 if (need_local_plte && use_global_plte && !all_images_are_gray)
12890 {
12891 if (mng_info->IsPalette)
12892 {
12893 /*
12894 When equal_palettes is true, this image has the same palette
12895 as the previous PseudoClass image
12896 */
12897 mng_info->have_write_global_plte=mng_info->equal_palettes;
12898 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12899 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12900 {
12901 /*
12902 Write MNG PLTE chunk
12903 */
cristybb503372010-05-27 20:51:26 +000012904 size_t
cristy3ed852e2009-09-05 21:47:34 +000012905 data_length;
12906
12907 data_length=3*image->colors;
12908 (void) WriteBlobMSBULong(image,data_length);
12909 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012910 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012911
cristybb503372010-05-27 20:51:26 +000012912 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012913 {
12914 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12915 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12916 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12917 }
glennrp0fe50b42010-11-16 03:52:51 +000012918
cristy3ed852e2009-09-05 21:47:34 +000012919 (void) WriteBlob(image,data_length+4,chunk);
12920 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12921 (uInt) (data_length+4)));
12922 mng_info->have_write_global_plte=MagickTrue;
12923 }
12924 }
12925 else
12926 mng_info->have_write_global_plte=MagickFalse;
12927 }
12928#endif
12929 if (need_defi)
12930 {
cristybb503372010-05-27 20:51:26 +000012931 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012932 previous_x,
12933 previous_y;
12934
12935 if (scene)
12936 {
12937 previous_x=mng_info->page.x;
12938 previous_y=mng_info->page.y;
12939 }
12940 else
12941 {
12942 previous_x=0;
12943 previous_y=0;
12944 }
12945 mng_info->page=image->page;
12946 if ((mng_info->page.x != previous_x) ||
12947 (mng_info->page.y != previous_y))
12948 {
12949 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12950 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012951 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012952 chunk[4]=0; /* object 0 MSB */
12953 chunk[5]=0; /* object 0 LSB */
12954 chunk[6]=0; /* visible */
12955 chunk[7]=0; /* abstract */
12956 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12957 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12958 (void) WriteBlob(image,16,chunk);
12959 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12960 }
12961 }
12962 }
12963
12964 mng_info->write_mng=write_mng;
12965
12966 if ((int) image->dispose >= 3)
12967 mng_info->framing_mode=3;
12968
12969 if (mng_info->need_fram && mng_info->adjoin &&
12970 ((image->delay != mng_info->delay) ||
12971 (mng_info->framing_mode != mng_info->old_framing_mode)))
12972 {
12973 if (image->delay == mng_info->delay)
12974 {
12975 /*
12976 Write a MNG FRAM chunk with the new framing mode.
12977 */
12978 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12979 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012980 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012981 chunk[4]=(unsigned char) mng_info->framing_mode;
12982 (void) WriteBlob(image,5,chunk);
12983 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12984 }
12985 else
12986 {
12987 /*
12988 Write a MNG FRAM chunk with the delay.
12989 */
12990 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12991 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012992 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012993 chunk[4]=(unsigned char) mng_info->framing_mode;
12994 chunk[5]=0; /* frame name separator (no name) */
12995 chunk[6]=2; /* flag for changing default delay */
12996 chunk[7]=0; /* flag for changing frame timeout */
12997 chunk[8]=0; /* flag for changing frame clipping */
12998 chunk[9]=0; /* flag for changing frame sync_id */
12999 PNGLong(chunk+10,(png_uint_32)
13000 ((mng_info->ticks_per_second*
13001 image->delay)/MagickMax(image->ticks_per_second,1)));
13002 (void) WriteBlob(image,14,chunk);
13003 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000013004 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000013005 }
13006 mng_info->old_framing_mode=mng_info->framing_mode;
13007 }
13008
13009#if defined(JNG_SUPPORTED)
13010 if (image_info->compression == JPEGCompression)
13011 {
13012 ImageInfo
13013 *write_info;
13014
13015 if (logging != MagickFalse)
13016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13017 " Writing JNG object.");
13018 /* To do: specify the desired alpha compression method. */
13019 write_info=CloneImageInfo(image_info);
13020 write_info->compression=UndefinedCompression;
cristy018f07f2011-09-04 21:15:19 +000013021 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013022 write_info=DestroyImageInfo(write_info);
13023 }
13024 else
13025#endif
13026 {
13027 if (logging != MagickFalse)
13028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13029 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013030
glennrpb9cfe272010-12-21 15:08:06 +000013031 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013032 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013033
13034 /* We don't want any ancillary chunks written */
13035 mng_info->ping_exclude_bKGD=MagickTrue;
13036 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013037 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013038 mng_info->ping_exclude_EXIF=MagickTrue;
13039 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013040 mng_info->ping_exclude_iCCP=MagickTrue;
13041 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13042 mng_info->ping_exclude_oFFs=MagickTrue;
13043 mng_info->ping_exclude_pHYs=MagickTrue;
13044 mng_info->ping_exclude_sRGB=MagickTrue;
13045 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013046 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013047 mng_info->ping_exclude_vpAg=MagickTrue;
13048 mng_info->ping_exclude_zCCP=MagickTrue;
13049 mng_info->ping_exclude_zTXt=MagickTrue;
13050
cristy018f07f2011-09-04 21:15:19 +000013051 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013052 }
13053
13054 if (status == MagickFalse)
13055 {
13056 MngInfoFreeStruct(mng_info,&have_mng_structure);
13057 (void) CloseBlob(image);
13058 return(MagickFalse);
13059 }
13060 (void) CatchImageException(image);
13061 if (GetNextImageInList(image) == (Image *) NULL)
13062 break;
13063 image=SyncNextImageInList(image);
13064 status=SetImageProgress(image,SaveImagesTag,scene++,
13065 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013066
cristy3ed852e2009-09-05 21:47:34 +000013067 if (status == MagickFalse)
13068 break;
glennrp0fe50b42010-11-16 03:52:51 +000013069
cristy3ed852e2009-09-05 21:47:34 +000013070 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013071
cristy3ed852e2009-09-05 21:47:34 +000013072 if (write_mng)
13073 {
13074 while (GetPreviousImageInList(image) != (Image *) NULL)
13075 image=GetPreviousImageInList(image);
13076 /*
13077 Write the MEND chunk.
13078 */
13079 (void) WriteBlobMSBULong(image,0x00000000L);
13080 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013081 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013082 (void) WriteBlob(image,4,chunk);
13083 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13084 }
13085 /*
13086 Relinquish resources.
13087 */
13088 (void) CloseBlob(image);
13089 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013090
cristy3ed852e2009-09-05 21:47:34 +000013091 if (logging != MagickFalse)
13092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013093
cristy3ed852e2009-09-05 21:47:34 +000013094 return(MagickTrue);
13095}
glennrpd5045b42010-03-24 12:40:35 +000013096#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013097
cristy3ed852e2009-09-05 21:47:34 +000013098static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13099{
glennrp3bd393f2011-12-21 18:54:53 +000013100 (void) image;
cristy3ed852e2009-09-05 21:47:34 +000013101 printf("Your PNG library is too old: You have libpng-%s\n",
13102 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013103
cristy3ed852e2009-09-05 21:47:34 +000013104 ThrowBinaryException(CoderError,"PNG library is too old",
13105 image_info->filename);
13106}
glennrp39992b42010-11-14 00:03:43 +000013107
cristy3ed852e2009-09-05 21:47:34 +000013108static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13109{
13110 return(WritePNGImage(image_info,image));
13111}
glennrpd5045b42010-03-24 12:40:35 +000013112#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013113#endif